tesseract-agent 0.9.20 → 0.9.22

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.
Files changed (2) hide show
  1. package/dist-bundle/cli.js +611 -272
  2. package/package.json +1 -1
@@ -63238,7 +63238,7 @@ var mesh_ca_exports = {};
63238
63238
  __export(mesh_ca_exports, {
63239
63239
  netCaCommand: () => netCaCommand
63240
63240
  });
63241
- import { chmodSync as chmodSync7, existsSync as existsSync35, mkdirSync as mkdirSync31, readFileSync as readFileSync33, writeFileSync as writeFileSync30 } from "node:fs";
63241
+ import { chmodSync as chmodSync7, existsSync as existsSync35, mkdirSync as mkdirSync31, readFileSync as readFileSync34, writeFileSync as writeFileSync31 } from "node:fs";
63242
63242
  import { join as join62, resolve as resolve11 } from "node:path";
63243
63243
  import { createInterface } from "node:readline";
63244
63244
  function die(msg) {
@@ -63270,7 +63270,7 @@ async function readPassphrase(prompt) {
63270
63270
  }
63271
63271
  }
63272
63272
  function writeSecretFile(p, bytes2) {
63273
- writeFileSync30(p, bytes2, { mode: 384 });
63273
+ writeFileSync31(p, bytes2, { mode: 384 });
63274
63274
  try {
63275
63275
  chmodSync7(p, 384);
63276
63276
  } catch {
@@ -63289,9 +63289,9 @@ async function openCa(dir2) {
63289
63289
  if (!existsSync35(gPath) || !existsSync35(sPath)) {
63290
63290
  die(`No mesh CA in ${dir2}. Run: tesseract-agent net init --org NAME --ca-dir ${dir2}`);
63291
63291
  }
63292
- const genesisBytes = new Uint8Array(readFileSync33(gPath));
63292
+ const genesisBytes = new Uint8Array(readFileSync34(gPath));
63293
63293
  const genesis = decodeGenesis(genesisBytes);
63294
- const sealed = decodeRecoveryBundle(new Uint8Array(readFileSync33(sPath)));
63294
+ const sealed = decodeRecoveryBundle(new Uint8Array(readFileSync34(sPath)));
63295
63295
  const pass = await readPassphrase("CA passphrase: ");
63296
63296
  let seed;
63297
63297
  try {
@@ -63329,7 +63329,7 @@ async function netCaCommand(rest, flags) {
63329
63329
  const genesisBytes = encodeGenesis(genesis);
63330
63330
  const fp = await genesisFingerprint(genesis);
63331
63331
  ensureDir(caDir);
63332
- writeFileSync30(join62(caDir, "genesis.bin"), genesisBytes);
63332
+ writeFileSync31(join62(caDir, "genesis.bin"), genesisBytes);
63333
63333
  writeSecretFile(join62(caDir, "ca.sealed"), encodeRecoveryBundle(await sealCaKey(seed, pass, `${org} mesh CA`)));
63334
63334
  writeSecretFile(join62(caDir, "disablement.secret"), hex3(disable.secret));
63335
63335
  console.log(`
@@ -63399,8 +63399,8 @@ Next, issue an identity for a machine (pick a free address in ${formatPrefix(gen
63399
63399
  [{ keyIndex: 0, privateKey: ca.privateKey }]
63400
63400
  );
63401
63401
  ensureDir(outDir);
63402
- writeFileSync30(join62(outDir, "genesis.bin"), genesisBytes);
63403
- writeFileSync30(join62(outDir, "node.cert"), encodeCert(cert));
63402
+ writeFileSync31(join62(outDir, "genesis.bin"), genesisBytes);
63403
+ writeFileSync31(join62(outDir, "node.cert"), encodeCert(cert));
63404
63404
  writeSecretFile(join62(outDir, "node.key"), await exportStaticPrivate(nodeKey));
63405
63405
  console.log(`Minted "${name}" at ${ip4}.`);
63406
63406
  console.log(` ${outDir}`);
@@ -63443,7 +63443,7 @@ Next, issue an identity for a machine (pick a free address in ${formatPrefix(gen
63443
63443
  };
63444
63444
  const signed = await signBundle(bundle, [{ keyIndex: 0, privateKey: ca.privateKey }]);
63445
63445
  const out = resolve11(typeof flags.out === "string" ? flags.out : join62(caDir, "bundle.bin"));
63446
- writeFileSync30(out, encodeSignedBundle(signed));
63446
+ writeFileSync31(out, encodeSignedBundle(signed));
63447
63447
  console.log(`Signed policy bundle (epoch ${epoch2}) \u2192 ${out}`);
63448
63448
  console.log("Every node must receive it; the relay distributes it, or copy it to");
63449
63449
  console.log(`each node's mesh directory as bundle.bin.`);
@@ -63465,9 +63465,9 @@ var init_mesh_ca = __esm({
63465
63465
 
63466
63466
  // src/cli.ts
63467
63467
  init_base_paths();
63468
- import { existsSync as existsSync36, mkdtempSync as mkdtempSync2, readFileSync as readFileSync35, rmSync as rmSync10 } from "node:fs";
63468
+ import { existsSync as existsSync36, mkdtempSync as mkdtempSync2, readFileSync as readFileSync36, rmSync as rmSync10 } from "node:fs";
63469
63469
  import { createHash as createHash8 } from "node:crypto";
63470
- import { execFileSync as execFileSync5, spawn as spawn12 } from "node:child_process";
63470
+ import { execFileSync as execFileSync5, spawn as spawn13 } from "node:child_process";
63471
63471
  import { fileURLToPath as fileURLToPath4 } from "node:url";
63472
63472
  import { tmpdir as tmpdir4 } from "node:os";
63473
63473
  import { dirname as dirname27, join as join63, resolve as resolve13 } from "node:path";
@@ -67085,6 +67085,7 @@ import { join as join20 } from "node:path";
67085
67085
  // ../../packages/host-core/dist/gui/daemon-gui.js
67086
67086
  import { join as join19, dirname as dirname8 } from "node:path";
67087
67087
  import { fileURLToPath as fileURLToPath2 } from "node:url";
67088
+ import { spawn as spawn6, spawnSync } from "node:child_process";
67088
67089
  import { existsSync as existsSync9 } from "node:fs";
67089
67090
  var el = null;
67090
67091
  var guiReady = false;
@@ -67256,6 +67257,9 @@ function electronModule() {
67256
67257
  function daemonGuiTrayActive() {
67257
67258
  return guiReady && tray !== null;
67258
67259
  }
67260
+ function daemonGuiReady() {
67261
+ return guiReady;
67262
+ }
67259
67263
  var uiDirOverride = null;
67260
67264
  var resolvedUiDir;
67261
67265
  function daemonUiDir() {
@@ -67301,14 +67305,48 @@ Set CCB_DAEMON_UI_DIR to the directory containing preload.cjs, or rebuild the da
67301
67305
  function daemonPreloadPath() {
67302
67306
  return join19(daemonUiDir(), "preload.cjs");
67303
67307
  }
67308
+ function devAppPath() {
67309
+ const env = process.env.CCB_APP_DIR;
67310
+ if (env)
67311
+ return env;
67312
+ const daemonEntry = process.argv[1];
67313
+ if (daemonEntry) {
67314
+ const cand = join19(dirname8(daemonEntry), "..", "..", "desktop");
67315
+ if (existsSync9(join19(cand, "package.json")))
67316
+ return cand;
67317
+ }
67318
+ return null;
67319
+ }
67320
+ async function reExecMainApp(url2) {
67321
+ try {
67322
+ const env = { ...process.env };
67323
+ delete env.CCB_DAEMON_GUI;
67324
+ delete env.CCB_FORCE_DAEMON_GUI;
67325
+ delete env.ELECTRON_RUN_AS_NODE;
67326
+ const appPath = el?.app?.isPackaged === false ? devAppPath() : null;
67327
+ const args = appPath ? [appPath, url2] : [url2];
67328
+ log2(`openMainApp: re-exec ${process.execPath} ${args.join(" ")}`);
67329
+ const child = spawn6(process.execPath, args, { detached: true, stdio: "ignore", env });
67330
+ child.on("error", (e) => log2("openMainApp: spawn failed:", e instanceof Error ? e.message : String(e)));
67331
+ child.unref();
67332
+ } catch (e) {
67333
+ log2("openMainApp failed:", e instanceof Error ? e.message : String(e));
67334
+ }
67335
+ }
67336
+ async function openMainApp(sessionId) {
67337
+ return reExecMainApp(sessionId ? `tesseract://resume/${encodeURIComponent(sessionId)}` : "tesseract://open");
67338
+ }
67339
+ async function openMainAppSettings() {
67340
+ return reExecMainApp("tesseract://settings");
67341
+ }
67304
67342
 
67305
67343
  // ../../packages/host-core/dist/os-control-runner.js
67306
- import { spawn as spawn6 } from "node:child_process";
67344
+ import { spawn as spawn7 } from "node:child_process";
67307
67345
  var IS_MAC = process.platform === "darwin";
67308
67346
  var log3 = (...a) => console.log("[os-control]", ...a);
67309
67347
  function capture(command, args, opts = {}) {
67310
67348
  return new Promise((resolve14) => {
67311
- const child = spawn6(command, args, { cwd: opts.cwd, windowsHide: true });
67349
+ const child = spawn7(command, args, { cwd: opts.cwd, windowsHide: true });
67312
67350
  const max = opts.maxBuffer ?? 4 * 1024 * 1024;
67313
67351
  let out = "";
67314
67352
  let err2 = "";
@@ -67667,10 +67705,11 @@ function askRdeskConsent(opts) {
67667
67705
  win3.setAlwaysOnTop(true, "screen-saver");
67668
67706
  win3.setVisibleOnAllWorkspaces(true, { visibleOnFullScreen: true });
67669
67707
  const reqId = randomUUID9();
67670
- const who = opts.viewerName ? `\u201C${opts.viewerName}\u201D` : "Someone";
67708
+ const who = opts.viewerName ? `Someone calling themselves \u201C${opts.viewerName}\u201D` : "Someone";
67671
67709
  void win3.loadFile(uiPath("consent.html")).then(() => {
67672
67710
  try {
67673
- win3.webContents.send("consent:show", { reqId, title: "Allow remote desktop access?", body: `${who} wants to view and control this computer.` });
67711
+ const how = opts.grant === "password" ? " They used this machine\u2019s unattended password." : "";
67712
+ win3.webContents.send("consent:show", { reqId, title: "Allow remote desktop access?", body: `${who} wants to view and control this computer.${how}` });
67674
67713
  win3.show();
67675
67714
  win3.focus();
67676
67715
  el2.app.focus?.({ steal: true });
@@ -71682,14 +71721,14 @@ import { randomUUID as randomUUID14 } from "node:crypto";
71682
71721
 
71683
71722
  // ../../packages/host-core/dist/video-edit.js
71684
71723
  init_base_paths();
71685
- import { spawn as spawn7 } from "node:child_process";
71724
+ import { spawn as spawn8 } from "node:child_process";
71686
71725
  import { mkdirSync as mkdirSync11, readFileSync as readFileSync16, rmSync as rmSync4, writeFileSync as writeFileSync12 } from "node:fs";
71687
71726
  import { join as join25, isAbsolute as isAbsolute4 } from "node:path";
71688
71727
  import { randomUUID as randomUUID13 } from "node:crypto";
71689
71728
 
71690
71729
  // ../../packages/host-core/dist/ffmpeg.js
71691
71730
  import { existsSync as existsSync11 } from "node:fs";
71692
- import { spawnSync } from "node:child_process";
71731
+ import { spawnSync as spawnSync2 } from "node:child_process";
71693
71732
  import { sep as sep4 } from "node:path";
71694
71733
  import { createRequire as createRequire3 } from "node:module";
71695
71734
  var require_ = createRequire3(import.meta.url);
@@ -71708,7 +71747,7 @@ function fromPackage(name) {
71708
71747
  }
71709
71748
  function fromPath(cmd) {
71710
71749
  try {
71711
- const r = spawnSync(cmd, ["-version"], { stdio: "ignore", timeout: 5e3 });
71750
+ const r = spawnSync2(cmd, ["-version"], { stdio: "ignore", timeout: 5e3 });
71712
71751
  return r.status === 0 ? cmd : null;
71713
71752
  } catch {
71714
71753
  return null;
@@ -71790,7 +71829,7 @@ function run(bin, args, opts) {
71790
71829
  let timedOut = false;
71791
71830
  let child;
71792
71831
  try {
71793
- child = spawn7(bin, args, { cwd: opts?.cwd ?? mediaSpawnCwd(), stdio: ["ignore", "pipe", "pipe"] });
71832
+ child = spawn8(bin, args, { cwd: opts?.cwd ?? mediaSpawnCwd(), stdio: ["ignore", "pipe", "pipe"] });
71794
71833
  } catch (e) {
71795
71834
  reject(e instanceof Error ? e : new Error(String(e)));
71796
71835
  return;
@@ -71869,7 +71908,7 @@ async function renderEdl(edl, opts) {
71869
71908
  const { args, expectedDurationSec } = compileEdl(edl, { outPath, assFile, encoder, assetInfo });
71870
71909
  const watchdogMs = Math.min(45 * 6e4, Math.max(LONG_TIMEOUT_MS, Math.round((expectedDurationSec ?? 0) * 4e3)));
71871
71910
  await new Promise((resolve14, reject) => {
71872
- const child = spawn7(resolveFfmpeg(), ["-y", "-hide_banner", "-nostats", "-progress", "pipe:1", ...args], { cwd: mediaSpawnCwd(), stdio: ["ignore", "pipe", "pipe"] });
71911
+ const child = spawn8(resolveFfmpeg(), ["-y", "-hide_banner", "-nostats", "-progress", "pipe:1", ...args], { cwd: mediaSpawnCwd(), stdio: ["ignore", "pipe", "pipe"] });
71873
71912
  let stderrTail = "";
71874
71913
  let stdoutBuf = "";
71875
71914
  let timedOut = false;
@@ -71953,7 +71992,7 @@ async function enhanceVideo(path6, spec, opts) {
71953
71992
  const watchdogMs = Math.min(45 * 6e4, Math.max(LONG_TIMEOUT_MS, Math.round(expected * 8e3)));
71954
71993
  const summary = chain2.steps.join(" \xB7 ");
71955
71994
  await new Promise((resolve14, reject) => {
71956
- const child = spawn7(resolveFfmpeg(), ["-y", "-hide_banner", "-nostats", "-progress", "pipe:1", ...args], { cwd: mediaSpawnCwd(), stdio: ["ignore", "pipe", "pipe"] });
71995
+ const child = spawn8(resolveFfmpeg(), ["-y", "-hide_banner", "-nostats", "-progress", "pipe:1", ...args], { cwd: mediaSpawnCwd(), stdio: ["ignore", "pipe", "pipe"] });
71957
71996
  let stderrTail = "";
71958
71997
  let stdoutBuf = "";
71959
71998
  let timedOut = false;
@@ -72117,7 +72156,7 @@ function readChunkBase64(path6) {
72117
72156
 
72118
72157
  // ../../packages/host-core/dist/video-upscale.js
72119
72158
  init_base_paths();
72120
- import { spawn as spawn8 } from "node:child_process";
72159
+ import { spawn as spawn9 } from "node:child_process";
72121
72160
  import { existsSync as existsSync12, mkdirSync as mkdirSync12, readFileSync as readFileSync17, readdirSync as readdirSync5 } from "node:fs";
72122
72161
  import { join as join26, dirname as dirname11 } from "node:path";
72123
72162
  var upscaleCwd;
@@ -72145,7 +72184,7 @@ function resolveRealesrgan() {
72145
72184
  }
72146
72185
  function fromPath2(cmd) {
72147
72186
  try {
72148
- const r = spawn8(cmd, ["-h"], { stdio: "ignore" });
72187
+ const r = spawn9(cmd, ["-h"], { stdio: "ignore" });
72149
72188
  r.on("error", () => {
72150
72189
  });
72151
72190
  r.kill();
@@ -72190,7 +72229,7 @@ async function realesrganGpuWorks() {
72190
72229
  return false;
72191
72230
  }
72192
72231
  await new Promise((resolve14, reject) => {
72193
- const c = spawn8(re.bin, ["-i", inP, "-o", outP, "-n", model, "-s", "2", "-m", re.models, "-g", "0"], { cwd: upscaleSpawnCwd(), stdio: "ignore" });
72232
+ const c = spawn9(re.bin, ["-i", inP, "-o", outP, "-n", model, "-s", "2", "-m", re.models, "-g", "0"], { cwd: upscaleSpawnCwd(), stdio: "ignore" });
72194
72233
  const t = setTimeout(() => {
72195
72234
  c.kill("SIGKILL");
72196
72235
  reject(new Error("probe timeout"));
@@ -72289,7 +72328,7 @@ async function realesrganUpscale(path6, opts) {
72289
72328
  if (!nFrames)
72290
72329
  throw new Error("No frames extracted.");
72291
72330
  await new Promise((resolve14, reject) => {
72292
- const child = spawn8(re.bin, ["-i", fin, "-o", fup, "-n", model, "-s", String(scale), "-m", re.models, "-f", "png", "-g", "0"], { cwd: upscaleSpawnCwd(), stdio: ["ignore", "pipe", "pipe"] });
72331
+ const child = spawn9(re.bin, ["-i", fin, "-o", fup, "-n", model, "-s", String(scale), "-m", re.models, "-f", "png", "-g", "0"], { cwd: upscaleSpawnCwd(), stdio: ["ignore", "pipe", "pipe"] });
72293
72332
  let tail3 = "";
72294
72333
  const timer = setTimeout(() => child.kill("SIGKILL"), 45 * 6e4);
72295
72334
  child.stderr.on("data", (d) => {
@@ -72368,8 +72407,8 @@ async function falTopazEnhance(path6, spec, opts) {
72368
72407
  const dir2 = opts.outDir ?? workDir();
72369
72408
  mkdirSync12(dir2, { recursive: true });
72370
72409
  const outPath = join26(dir2, "enhanced.mp4");
72371
- const { writeFileSync: writeFileSync31 } = await import("node:fs");
72372
- writeFileSync31(outPath, Buffer.from(await dl.arrayBuffer()));
72410
+ const { writeFileSync: writeFileSync32 } = await import("node:fs");
72411
+ writeFileSync32(outPath, Buffer.from(await dl.arrayBuffer()));
72373
72412
  const out = await probeMedia(outPath).catch(() => null);
72374
72413
  opts.onProgress?.(100, "Enhance complete");
72375
72414
  return { path: outPath, mime: MIME, width: out?.width ?? null, height: out?.height ?? null, durationSec: out?.durationSec ?? p.durationSec, summary: `fal Topaz ${spec.model ?? "Proteus"} \xD7${spec.upscaleFactor ?? 2}` };
@@ -74223,12 +74262,12 @@ async function dirStats(root, opts) {
74223
74262
  }
74224
74263
  async function codeStats(root) {
74225
74264
  const start3 = Date.now();
74226
- const MAX_FILES = 25e3, MAX_BYTES = 256 * 1024 * 1024, TIME_MS = 12e3;
74265
+ const MAX_FILES = 25e3, MAX_BYTES2 = 256 * 1024 * 1024, TIME_MS = 12e3;
74227
74266
  const byLang = /* @__PURE__ */ new Map();
74228
74267
  let totalFiles = 0, totalLines = 0, bytes2 = 0, truncated = false;
74229
74268
  const stack = [root];
74230
74269
  while (stack.length) {
74231
- if (totalFiles >= MAX_FILES || bytes2 >= MAX_BYTES || Date.now() - start3 > TIME_MS) {
74270
+ if (totalFiles >= MAX_FILES || bytes2 >= MAX_BYTES2 || Date.now() - start3 > TIME_MS) {
74232
74271
  truncated = true;
74233
74272
  break;
74234
74273
  }
@@ -74240,7 +74279,7 @@ async function codeStats(root) {
74240
74279
  continue;
74241
74280
  }
74242
74281
  for (const e of ents) {
74243
- if (totalFiles >= MAX_FILES || bytes2 >= MAX_BYTES || Date.now() - start3 > TIME_MS) {
74282
+ if (totalFiles >= MAX_FILES || bytes2 >= MAX_BYTES2 || Date.now() - start3 > TIME_MS) {
74244
74283
  truncated = true;
74245
74284
  break;
74246
74285
  }
@@ -78509,8 +78548,8 @@ var FSWatcher = class extends EventEmitter2 {
78509
78548
  }
78510
78549
  return this._userIgnored(path6, stats);
78511
78550
  }
78512
- _isntIgnored(path6, stat7) {
78513
- return !this._isIgnored(path6, stat7);
78551
+ _isntIgnored(path6, stat8) {
78552
+ return !this._isIgnored(path6, stat8);
78514
78553
  }
78515
78554
  /**
78516
78555
  * Provides a set of common helpers and properties relating to symlink handling.
@@ -80079,18 +80118,40 @@ function assertServerSelection() {
80079
80118
  send2("host", { type: "host:select", hostId: serverSelected ? SERVER_HOST_ID : ownHostId() });
80080
80119
  }
80081
80120
  var iceWaiters = [];
80121
+ var lastIce = null;
80122
+ var ICE_CACHE_MAX_AGE_MS = 5 * 6e4;
80123
+ var iceReqSeq = 0;
80124
+ function rememberIce(servers) {
80125
+ lastIce = { servers, at: Date.now() };
80126
+ }
80082
80127
  function fetchIceServers() {
80083
- return new Promise((resolve14) => {
80084
- iceWaiters.push(resolve14);
80085
- send2("signal", { type: "signal:get-ice" });
80128
+ const ask2 = () => new Promise((resolve14) => {
80129
+ const id = `ice-${++iceReqSeq}`;
80130
+ const waiter = (servers) => resolve14(servers);
80131
+ iceWaiters.push(waiter);
80132
+ send2("signal", { type: "signal:get-ice" }, { browserId: id });
80086
80133
  setTimeout(() => {
80087
- const i = iceWaiters.indexOf(resolve14);
80134
+ const i = iceWaiters.indexOf(waiter);
80088
80135
  if (i >= 0) {
80089
80136
  iceWaiters.splice(i, 1);
80090
- resolve14(DEFAULT_ICE_SERVERS);
80137
+ resolve14(null);
80091
80138
  }
80092
80139
  }, 2500);
80093
80140
  });
80141
+ return (async () => {
80142
+ const first = await ask2();
80143
+ if (first)
80144
+ return first;
80145
+ const second = await ask2();
80146
+ if (second)
80147
+ return second;
80148
+ if (lastIce && Date.now() - lastIce.at < ICE_CACHE_MAX_AGE_MS) {
80149
+ console.log("[control-link] ice: relay did not answer get-ice; reusing the last minted list (TURN still available)");
80150
+ return lastIce.servers;
80151
+ }
80152
+ console.log("[control-link] ice: relay did not answer get-ice; falling back to public STUN \u2014 NO TURN, a session that needs a relay will fail");
80153
+ return DEFAULT_ICE_SERVERS;
80154
+ })();
80094
80155
  }
80095
80156
  function relayBrowser(payload, clientId) {
80096
80157
  send2("browser", payload, { clientId });
@@ -80803,7 +80864,8 @@ function registerAsHost() {
80803
80864
  const kind = process.env.CCB_HOST_KIND === "cloud" ? "cloud" : void 0;
80804
80865
  const name = kind === "cloud" ? "Cloud agent" : hostname3();
80805
80866
  const caps = hostSessions ? ["agent", "fs", "term", "e2e"] : ["agent", "fs", "term"];
80806
- caps.push("rdesk");
80867
+ if (hooks?.onRdesk)
80868
+ caps.push("rdesk");
80807
80869
  caps.push("xfer");
80808
80870
  const upAdvert = upscaleAdvert();
80809
80871
  if (upAdvert.gpu)
@@ -80888,6 +80950,10 @@ function connect() {
80888
80950
  hooks?.onDrop?.(env.payload);
80889
80951
  return;
80890
80952
  }
80953
+ if (channel === "rdesk-req") {
80954
+ hooks?.onRdeskRequest?.(env.payload);
80955
+ return;
80956
+ }
80891
80957
  if (channel === "p2p") {
80892
80958
  const m = env.payload;
80893
80959
  for (const cb of [...p2pSubscribers])
@@ -81042,6 +81108,7 @@ function connect() {
81042
81108
  if (channel === "signal") {
81043
81109
  const p = env.payload;
81044
81110
  if (p?.type === "signal:ready" && Array.isArray(p.iceServers)) {
81111
+ rememberIce(p.iceServers);
81045
81112
  const waiters = iceWaiters;
81046
81113
  iceWaiters = [];
81047
81114
  for (const r of waiters)
@@ -81265,9 +81332,17 @@ async function createRemoteDesktopEngine(kind, ctx2) {
81265
81332
  function grantOf(msg) {
81266
81333
  return msg.auth.kind;
81267
81334
  }
81335
+ var PW_FREE_ATTEMPTS = 3;
81336
+ var PW_BACKOFF_BASE_MS = 2e3;
81337
+ var PW_BACKOFF_MAX_MS = 15 * 6e4;
81338
+ var PW_FAILURE_DECAY_MS = 30 * 6e4;
81268
81339
  var RdeskSessionManager = class {
81269
81340
  sessions = /* @__PURE__ */ new Map();
81270
81341
  deps;
81342
+ /** Consecutive failed password attempts, and the time the lockout expires. */
81343
+ pwFailures = 0;
81344
+ pwLockedUntil = 0;
81345
+ pwLastFailureAt = 0;
81271
81346
  constructor(deps) {
81272
81347
  this.deps = deps;
81273
81348
  }
@@ -81282,7 +81357,14 @@ var RdeskSessionManager = class {
81282
81357
  this.deps.log?.(`[rdesk] ${msg}`);
81283
81358
  }
81284
81359
  now() {
81285
- return Date.now();
81360
+ return this.deps.now?.() ?? Date.now();
81361
+ }
81362
+ /** How long the password gate stays shut after repeated failures (exponential, capped). */
81363
+ pwBackoffMs() {
81364
+ const over = this.pwFailures - PW_FREE_ATTEMPTS;
81365
+ if (over <= 0)
81366
+ return 0;
81367
+ return Math.min(PW_BACKOFF_BASE_MS * 2 ** (over - 1), PW_BACKOFF_MAX_MS);
81286
81368
  }
81287
81369
  audit(e) {
81288
81370
  this.deps.audit?.({ at: this.now(), ...e });
@@ -81303,7 +81385,9 @@ var RdeskSessionManager = class {
81303
81385
  }
81304
81386
  /** Handle a viewer's `rdesk:connect`. Runs the grant→consent policy, then builds +
81305
81387
  * starts the engine and replies `rdesk:accepted`, or replies `rdesk:denied`. */
81306
- async onConnect(sessionId, viewer, msg) {
81388
+ async onConnect(sessionId, viewerIn, msg) {
81389
+ const declared = typeof msg.viewerName === "string" ? msg.viewerName.trim().slice(0, 60) : "";
81390
+ const viewer = viewerIn.name || !declared ? viewerIn : { ...viewerIn, name: declared };
81307
81391
  const grant0 = grantOf(msg);
81308
81392
  this.audit({ event: "connect", sessionId, viewerUserId: viewer.userId, viewerName: viewer.name, grant: grant0 });
81309
81393
  const deny = (reason) => {
@@ -81315,24 +81399,48 @@ var RdeskSessionManager = class {
81315
81399
  return deny("entitlement");
81316
81400
  if (this.sessions.has(sessionId))
81317
81401
  return deny("busy");
81402
+ const maxSessions = this.deps.maxSessions ?? 3;
81403
+ if (maxSessions > 0 && this.sessions.size >= maxSessions) {
81404
+ this.log(`connect refused sessionId=${sessionId} \u2014 at the ${maxSessions}-session limit`);
81405
+ return deny("busy");
81406
+ }
81318
81407
  const grant = grantOf(msg);
81319
81408
  if (grant === "password") {
81409
+ if (this.now() < this.pwLockedUntil) {
81410
+ this.log(`password attempt refused \u2014 locked for ${Math.ceil((this.pwLockedUntil - this.now()) / 1e3)}s`);
81411
+ this.audit({ event: "deny", sessionId, grant, detail: "password-locked" });
81412
+ return deny("password");
81413
+ }
81414
+ if (this.pwLastFailureAt && this.now() - this.pwLastFailureAt > PW_FAILURE_DECAY_MS) {
81415
+ this.pwFailures = 0;
81416
+ this.pwLockedUntil = 0;
81417
+ }
81320
81418
  const pw = msg.auth.kind === "password" ? msg.auth.password : "";
81321
81419
  const ok = await this.deps.checkPassword(pw);
81322
- if (!ok)
81420
+ if (!ok) {
81421
+ this.pwFailures += 1;
81422
+ this.pwLastFailureAt = this.now();
81423
+ const backoff = this.pwBackoffMs();
81424
+ if (backoff > 0) {
81425
+ this.pwLockedUntil = this.now() + backoff;
81426
+ this.log(`password failed ${this.pwFailures}\xD7 \u2014 locking the password gate for ${Math.round(backoff / 1e3)}s`);
81427
+ }
81323
81428
  return deny("password");
81429
+ }
81430
+ this.pwFailures = 0;
81431
+ this.pwLockedUntil = 0;
81432
+ this.pwLastFailureAt = 0;
81324
81433
  } else if (grant === "code") {
81325
81434
  const allowed = await this.deps.consent({ sessionId, viewerName: viewer.name, grant });
81326
81435
  if (!allowed)
81327
81436
  return deny("consent");
81328
81437
  }
81329
- const forceRelay = grant !== "registry";
81330
- const accessLevel = this.deps.defaultAccess ?? "view";
81438
+ const da = this.deps.defaultAccess;
81439
+ const accessLevel = (typeof da === "function" ? da() : da) ?? "view";
81331
81440
  const engineKind = msg.engine ?? "tesseract";
81332
81441
  const ctx2 = {
81333
81442
  sessionId,
81334
81443
  accessLevel,
81335
- forceRelay,
81336
81444
  sendSignal: (m) => this.deps.sendSignal(sessionId, viewer.clientId, m),
81337
81445
  sendControl: (m) => this.deps.sendControl(sessionId, viewer.clientId, m),
81338
81446
  sendData: (_m) => {
@@ -81342,7 +81450,8 @@ var RdeskSessionManager = class {
81342
81450
  let engine;
81343
81451
  try {
81344
81452
  engine = await this.deps.createEngine(engineKind, ctx2);
81345
- await engine.start({ monitorId: void 0, quality: "auto" });
81453
+ const dq = this.deps.defaultQuality;
81454
+ await engine.start({ monitorId: void 0, quality: (typeof dq === "function" ? dq() : dq) ?? "auto" });
81346
81455
  } catch (e) {
81347
81456
  this.log(`engine start failed sessionId=${sessionId}: ${e instanceof Error ? e.message : String(e)}`);
81348
81457
  return deny("offline");
@@ -81351,9 +81460,9 @@ var RdeskSessionManager = class {
81351
81460
  const sources = await engine.listSources?.().catch(() => []) ?? [];
81352
81461
  const activeMonitor = monitors.find((m) => m.primary)?.id ?? monitors[0]?.id ?? "";
81353
81462
  const activeSource = sources.find((s) => s.kind === "screen" && s.primary)?.id ?? sources[0]?.id ?? "";
81354
- const session = { sessionId, viewer, grant, accessLevel, forceRelay, engine, startedAt: this.now() };
81463
+ const session = { sessionId, viewer, grant, accessLevel, engine, startedAt: this.now() };
81355
81464
  this.sessions.set(sessionId, session);
81356
- this.log(`accepted sessionId=${sessionId} grant=${grant} engine=${engine.id} access=${accessLevel} forceRelay=${forceRelay}`);
81465
+ this.log(`accepted sessionId=${sessionId} grant=${grant} engine=${engine.id} access=${accessLevel}`);
81357
81466
  this.audit({ event: "accept", sessionId, viewerUserId: viewer.userId, viewerName: viewer.name, grant, detail: `engine=${engine.id}` });
81358
81467
  this.deps.sendControl(sessionId, viewer.clientId, {
81359
81468
  type: "rdesk:accepted",
@@ -81363,8 +81472,7 @@ var RdeskSessionManager = class {
81363
81472
  activeMonitor,
81364
81473
  sources,
81365
81474
  activeSource,
81366
- caps: engine.capabilities(),
81367
- forceRelay
81475
+ caps: engine.capabilities()
81368
81476
  });
81369
81477
  this.changed();
81370
81478
  }
@@ -81387,12 +81495,20 @@ var RdeskSessionManager = class {
81387
81495
  this.changed();
81388
81496
  }
81389
81497
  /** A viewer asks for control (view→control). Own-fleet auto-grants; cross-account
81390
- * runs the consent prompt. Denial simply leaves the session view-only. */
81498
+ * runs the consent prompt. Denial simply leaves the session view-only.
81499
+ *
81500
+ * The decision keys off `grant` ALONE. It used to also accept `viewer.sameAccount`, but
81501
+ * that flag was hardcoded `true` at every production call site (the relay hook has no
81502
+ * userId to derive it from), so this test always passed and ANY cross-account viewer —
81503
+ * including an unattended password one — could silently escalate view→control with no
81504
+ * prompt, and could instantly undo a host that demoted them back to view. `grant` is
81505
+ * resolved in onConnect, stored per session and immutable, and `registry` IS same-account
81506
+ * by construction (the relay only exposes own-account hosts in host:list). */
81391
81507
  async requestControl(sessionId) {
81392
81508
  const s = this.sessions.get(sessionId);
81393
81509
  if (!s || s.accessLevel === "control")
81394
81510
  return;
81395
- let allow = s.grant === "registry" || s.viewer.sameAccount;
81511
+ let allow = s.grant === "registry";
81396
81512
  if (!allow)
81397
81513
  allow = await this.deps.consent({ sessionId, viewerName: s.viewer.name, grant: s.grant });
81398
81514
  if (allow)
@@ -81469,6 +81585,7 @@ var RdeskSessionManager = class {
81469
81585
  }
81470
81586
  if (msg.type === "rdesk:av") {
81471
81587
  const s = this.sessions.get(sessionId);
81588
+ s?.engine.setAv?.(msg.on);
81472
81589
  this.deps.onCall?.({ sessionId, on: msg.on, viewerName: s?.viewer.name });
81473
81590
  this.audit({ event: "call", sessionId, detail: msg.on ? "viewer joined call" : "viewer left call" });
81474
81591
  return;
@@ -81537,6 +81654,7 @@ function ensureIpc() {
81537
81654
  }));
81538
81655
  el2.ipcMain.on("rdesk:pub:ready", (_e, p) => route2(p, (w) => w.handlers.onReady()));
81539
81656
  el2.ipcMain.on("rdesk:pub:error", (_e, p) => route2(p, (w, x) => w.handlers.onError(String(x.error ?? "publisher error"))));
81657
+ el2.ipcMain.on("rdesk:pub:path", (_e, p) => route2(p, (w, x) => w.handlers.onPath?.(String(x.path ?? "unknown"), { localType: x.localType ?? null, remoteType: x.remoteType ?? null })));
81540
81658
  el2.ipcMain.on("rdesk:pub:stopped", (_e, p) => route2(p, (w) => w.onStoppedAck()));
81541
81659
  }
81542
81660
  async function listPublisherMonitors() {
@@ -81639,7 +81757,6 @@ var PublisherWindow = class {
81639
81757
  sessionId: this.sessionId,
81640
81758
  sourceId,
81641
81759
  iceServers: opts.iceServers,
81642
- forceRelay: opts.forceRelay,
81643
81760
  quality: opts.quality ?? "auto",
81644
81761
  systemAudio: opts.systemAudio ?? false,
81645
81762
  maxWidth: opts.maxWidth,
@@ -81654,6 +81771,10 @@ var PublisherWindow = class {
81654
81771
  /** Create the hidden window in CANVAS mode: no local capture — the RDP/VNC engine
81655
81772
  * pushes framebuffer rects via {@link pushFrame}, which are painted to a canvas that
81656
81773
  * is captureStream()'d into the same WebRTC PeerConnection. Reuses the whole pipeline. */
81774
+ /** Turn two-way voice on/off on THIS peer connection (the publisher re-offers). */
81775
+ setAv(on) {
81776
+ this.win?.webContents.send("rdesk:pub:av", { sessionId: this.sessionId, on });
81777
+ }
81657
81778
  async startCanvas(opts) {
81658
81779
  const el2 = electronModule();
81659
81780
  if (!el2?.BrowserWindow)
@@ -81681,7 +81802,6 @@ var PublisherWindow = class {
81681
81802
  sessionId: this.sessionId,
81682
81803
  mode: "canvas",
81683
81804
  iceServers: opts.iceServers,
81684
- forceRelay: opts.forceRelay,
81685
81805
  quality: opts.quality ?? "auto",
81686
81806
  width: opts.width,
81687
81807
  height: opts.height,
@@ -81719,7 +81839,6 @@ var PublisherWindow = class {
81719
81839
  sessionId: this.sessionId,
81720
81840
  sourceId,
81721
81841
  iceServers: opts.iceServers,
81722
- forceRelay: opts.forceRelay,
81723
81842
  quality: opts.quality ?? "auto",
81724
81843
  systemAudio: opts.systemAudio ?? false,
81725
81844
  maxWidth: opts.maxWidth,
@@ -81931,21 +82050,32 @@ var TesseractEngine = class {
81931
82050
  this.deps = deps;
81932
82051
  this.access = ctx2.accessLevel;
81933
82052
  }
82053
+ /**
82054
+ * What this session can ACTUALLY do, derived at runtime.
82055
+ *
82056
+ * This used to be a hardcoded constant returned identically on every platform, so a
82057
+ * viewer was told `input: true` by a Wayland host that cannot inject a keystroke, and
82058
+ * offered a system-audio toggle on macOS where the capture is known to be silent. A
82059
+ * capability flag that doesn't track reality is worse than no flag: the viewer renders
82060
+ * a control that quietly does nothing.
82061
+ */
81934
82062
  capabilities() {
81935
82063
  return {
81936
- input: true,
82064
+ // False until an input runner was actually obtained (see start()): on Wayland
82065
+ // without the portal, control is impossible and the session is view-only.
82066
+ input: !!this.injector,
81937
82067
  clipboard: true,
81938
- fileTransfer: true,
81939
- // reuses the existing fs channel (Phase 6)
82068
+ // No drag/drop or upload surface exists inside an rdesk session today. It was
82069
+ // advertised as if it did; nothing in either viewer ever read it.
82070
+ fileTransfer: false,
81940
82071
  multiMonitor: true,
81941
82072
  windowCapture: true,
81942
82073
  // share a single window/app (privacy)
82074
+ // Voice now rides this session's own peer connection (setAv).
81943
82075
  bidiAudioVideo: true,
81944
- // Phase 5 (viewer mic ↔ host mic/webcam)
81945
- // System-audio loopback: Windows/Linux capture it natively via getUserMedia
81946
- // (desktop audio); macOS needs a loopback (best-effort — may be silent until a
81947
- // ScreenCaptureKit audio addon lands). Advertised so the viewer shows the toggle.
81948
- systemAudio: true
82076
+ // Windows/Linux capture desktop audio natively; macOS needs a loopback we don't
82077
+ // ship, so claiming it there just produces a toggle that yields silence.
82078
+ systemAudio: process.platform !== "darwin"
81949
82079
  };
81950
82080
  }
81951
82081
  listMonitors() {
@@ -81955,7 +82085,7 @@ var TesseractEngine = class {
81955
82085
  return listPublisherSources();
81956
82086
  }
81957
82087
  startOpts(iceServers) {
81958
- return { iceServers, forceRelay: this.ctx.forceRelay, quality: this.quality, systemAudio: this.systemAudio };
82088
+ return { iceServers, quality: this.quality, systemAudio: this.systemAudio };
81959
82089
  }
81960
82090
  async setSource(sourceId) {
81961
82091
  this.sourceId = sourceId;
@@ -82008,12 +82138,14 @@ var TesseractEngine = class {
82008
82138
  }
82009
82139
  },
82010
82140
  onReady: () => this.ctx.log("publisher connected"),
82011
- onError: (err2) => this.ctx.log(`publisher error: ${err2}`)
82141
+ onError: (err2) => this.ctx.log(`publisher error: ${err2}`),
82142
+ // Media is direct-first; log which path actually won so a regression to relay
82143
+ // (which costs real bandwidth) is visible instead of silent. Types only, no IPs.
82144
+ onPath: (path6, d) => this.ctx.log(`media path=${path6} (local=${d.localType ?? "?"} remote=${d.remoteType ?? "?"})`)
82012
82145
  });
82013
82146
  this.pub = pub;
82014
82147
  await pub.start({
82015
82148
  iceServers,
82016
- forceRelay: this.ctx.forceRelay,
82017
82149
  monitorId: this.monitorId,
82018
82150
  quality: this.quality,
82019
82151
  systemAudio: this.systemAudio
@@ -82043,6 +82175,9 @@ var TesseractEngine = class {
82043
82175
  const iceServers = await this.deps.getIceServers().catch(() => []);
82044
82176
  await this.pub?.setMonitor(monitorId, this.startOpts(iceServers));
82045
82177
  }
82178
+ setAv(on) {
82179
+ this.pub?.setAv(on);
82180
+ }
82046
82181
  setQuality(preset) {
82047
82182
  this.quality = preset;
82048
82183
  this.pub?.setQuality(preset);
@@ -82064,13 +82199,156 @@ function registerTesseractEngine(deps) {
82064
82199
  }
82065
82200
 
82066
82201
  // ../../packages/host-core/dist/local-server.js
82067
- import { timingSafeEqual as timingSafeEqual4 } from "node:crypto";
82202
+ import { timingSafeEqual as timingSafeEqual5 } from "node:crypto";
82203
+
82204
+ // ../../packages/host-core/dist/rdesk/server-prefs.js
82205
+ import { readFileSync as readFileSync25, writeFileSync as writeFileSync17 } from "node:fs";
82206
+ import { join as join44 } from "node:path";
82207
+ function rdeskServerPrefsPath() {
82208
+ return join44(daemonDir2(), "rdesk-server.json");
82209
+ }
82210
+ var DEFAULTS2 = {
82211
+ enabled: true,
82212
+ defaultAccess: "control",
82213
+ quality: "auto",
82214
+ autostart: true,
82215
+ hideTray: false,
82216
+ noticeShownAt: 0
82217
+ };
82218
+ function readRdeskServerPrefs() {
82219
+ try {
82220
+ const p = JSON.parse(readFileSync25(rdeskServerPrefsPath(), "utf8"));
82221
+ return {
82222
+ // `!== false` (not `?? true`): only an explicit opt-out disables sharing, so a
82223
+ // partially-written or older prefs file never silently turns the machine off.
82224
+ enabled: p.enabled !== false,
82225
+ defaultAccess: p.defaultAccess === "view" ? "view" : "control",
82226
+ quality: p.quality ?? DEFAULTS2.quality,
82227
+ autostart: p.autostart ?? DEFAULTS2.autostart,
82228
+ hideTray: p.hideTray ?? DEFAULTS2.hideTray,
82229
+ noticeShownAt: typeof p.noticeShownAt === "number" ? p.noticeShownAt : 0
82230
+ };
82231
+ } catch {
82232
+ return { ...DEFAULTS2 };
82233
+ }
82234
+ }
82235
+ function writeRdeskServerPrefs(patch) {
82236
+ try {
82237
+ const next = { ...readRdeskServerPrefs(), ...patch };
82238
+ writeFileSync17(rdeskServerPrefsPath(), JSON.stringify(next), { mode: 384 });
82239
+ } catch {
82240
+ }
82241
+ }
82242
+ function rdeskSharingEnabled() {
82243
+ if (process.env.CCB_RDESK_ENABLED === "0")
82244
+ return false;
82245
+ if (process.env.CCB_RDESK_ENABLED === "1")
82246
+ return true;
82247
+ return readRdeskServerPrefs().enabled;
82248
+ }
82249
+
82250
+ // ../../packages/host-core/dist/rdesk/password.js
82251
+ import { scryptSync, randomBytes as randomBytes5, timingSafeEqual as timingSafeEqual4, randomInt } from "node:crypto";
82252
+ import { readFileSync as readFileSync26, writeFileSync as writeFileSync18, existsSync as existsSync21, unlinkSync as unlinkSync2 } from "node:fs";
82253
+ import { join as join45 } from "node:path";
82254
+ function pwFile() {
82255
+ return join45(daemonDir2(), "rdesk-password.json");
82256
+ }
82257
+ function pwPlainFile() {
82258
+ return join45(daemonDir2(), "rdesk-password.enc");
82259
+ }
82260
+ var PW_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789";
82261
+ var PW_LENGTH = 16;
82262
+ function generatePassword() {
82263
+ let out = "";
82264
+ for (let i = 0; i < PW_LENGTH; i++)
82265
+ out += PW_ALPHABET[randomInt(PW_ALPHABET.length)];
82266
+ return (out.match(/.{1,4}/g) ?? [out]).join("-");
82267
+ }
82268
+ function writePlain(password) {
82269
+ try {
82270
+ const safe = electronModule()?.safeStorage;
82271
+ if (!safe?.isEncryptionAvailable())
82272
+ return;
82273
+ writeFileSync18(pwPlainFile(), safe.encryptString(password), { mode: 384 });
82274
+ } catch {
82275
+ }
82276
+ }
82277
+ function canRevealRdeskPassword() {
82278
+ try {
82279
+ const safe = electronModule()?.safeStorage;
82280
+ return !!safe?.isEncryptionAvailable() && existsSync21(pwPlainFile());
82281
+ } catch {
82282
+ return false;
82283
+ }
82284
+ }
82285
+ function readRdeskPasswordPlain() {
82286
+ try {
82287
+ const safe = electronModule()?.safeStorage;
82288
+ if (!safe?.isEncryptionAvailable())
82289
+ return null;
82290
+ return safe.decryptString(readFileSync26(pwPlainFile()));
82291
+ } catch {
82292
+ return null;
82293
+ }
82294
+ }
82295
+ function setRdeskPassword(password) {
82296
+ if (!password) {
82297
+ try {
82298
+ unlinkSync2(pwFile());
82299
+ } catch {
82300
+ }
82301
+ try {
82302
+ unlinkSync2(pwPlainFile());
82303
+ } catch {
82304
+ }
82305
+ return;
82306
+ }
82307
+ const salt = randomBytes5(16);
82308
+ const hash2 = scryptSync(password, salt, 32);
82309
+ writeFileSync18(pwFile(), JSON.stringify({ salt: salt.toString("hex"), hash: hash2.toString("hex") }), { mode: 384 });
82310
+ writePlain(password);
82311
+ }
82312
+ function regenerateRdeskPassword() {
82313
+ const pw = generatePassword();
82314
+ setRdeskPassword(pw);
82315
+ return pw;
82316
+ }
82317
+ function ensureRdeskPassword() {
82318
+ if (hasRdeskPassword())
82319
+ return null;
82320
+ return regenerateRdeskPassword();
82321
+ }
82322
+ function hasRdeskPassword() {
82323
+ return !!process.env.CCB_RDESK_PASSWORD || existsSync21(pwFile());
82324
+ }
82325
+ function verifyRdeskPassword(password) {
82326
+ if (!password)
82327
+ return false;
82328
+ const override = process.env.CCB_RDESK_PASSWORD;
82329
+ if (override)
82330
+ return password.length === override.length && timingSafeEqual4(Buffer.from(password), Buffer.from(override));
82331
+ try {
82332
+ const { salt, hash: hash2 } = JSON.parse(readFileSync26(pwFile(), "utf8"));
82333
+ const got = scryptSync(password, Buffer.from(salt, "hex"), 32);
82334
+ const want = Buffer.from(hash2, "hex");
82335
+ return got.length === want.length && timingSafeEqual4(got, want);
82336
+ } catch {
82337
+ return false;
82338
+ }
82339
+ }
82340
+
82341
+ // ../../packages/host-core/dist/rdesk/server-auth.js
82342
+ var DEFAULT_API = (process.env.CCB_TESSERACT_API ?? "https://app.tesseract.dev").replace(/\/$/, "");
82343
+ function getMachinePublicId() {
82344
+ return readCredentials()?.publicId ?? null;
82345
+ }
82068
82346
 
82069
82347
  // ../../packages/host-core/dist/vault/manager.js
82070
82348
  import { createHash as createHash7 } from "node:crypto";
82071
82349
 
82072
82350
  // ../../packages/host-core/dist/vault/passkey.js
82073
- import { createHash as createHash6, createPrivateKey, createSign, sign as rawSign, generateKeyPairSync, randomBytes as randomBytes5 } from "node:crypto";
82351
+ import { createHash as createHash6, createPrivateKey, createSign, sign as rawSign, generateKeyPairSync, randomBytes as randomBytes6 } from "node:crypto";
82074
82352
  function buildAuthenticatorData(rpId, signCount) {
82075
82353
  const rpIdHash = createHash6("sha256").update(rpId).digest();
82076
82354
  const counter = Buffer.alloc(4);
@@ -82126,7 +82404,7 @@ function createPasskeyRegistration(rpId) {
82126
82404
  const pkcs8 = privateKey.export({ type: "pkcs8", format: "der" });
82127
82405
  const jwk = publicKey.export({ format: "jwk" });
82128
82406
  const cose = coseEs256(Buffer.from(jwk.x, "base64url"), Buffer.from(jwk.y, "base64url"));
82129
- const credentialId = randomBytes5(32);
82407
+ const credentialId = randomBytes6(32);
82130
82408
  const rpIdHash = createHash6("sha256").update(rpId).digest();
82131
82409
  const flags = Buffer.from([69]);
82132
82410
  const signCount = Buffer.alloc(4);
@@ -82151,7 +82429,7 @@ function createPasskeyRegistration(rpId) {
82151
82429
 
82152
82430
  // ../../packages/host-core/dist/vault/manager.js
82153
82431
  init_base_paths();
82154
- import { mkdirSync as mkdirSync21, readFileSync as readFileSync27, writeFileSync as writeFileSync19 } from "node:fs";
82432
+ import { mkdirSync as mkdirSync21, readFileSync as readFileSync29, writeFileSync as writeFileSync21 } from "node:fs";
82155
82433
  import { dirname as dirname20 } from "node:path";
82156
82434
 
82157
82435
  // ../../packages/vault-core/dist/crypto.js
@@ -82322,7 +82600,7 @@ function tombstone(id, updatedAt) {
82322
82600
  // ../../packages/vault-core/dist/authenticator.js
82323
82601
  var subtle2 = globalThis.crypto.subtle;
82324
82602
  var bs2 = (b) => b;
82325
- var DEFAULTS2 = { algorithm: "SHA1", digits: 6, period: 30, counter: 0 };
82603
+ var DEFAULTS3 = { algorithm: "SHA1", digits: 6, period: 30, counter: 0 };
82326
82604
  async function hmac(algorithm, keyBytes, msg) {
82327
82605
  const hash2 = algorithm === "SHA512" ? "SHA-512" : algorithm === "SHA256" ? "SHA-256" : "SHA-1";
82328
82606
  const key2 = await subtle2.importKey("raw", bs2(keyBytes), { name: "HMAC", hash: hash2 }, false, ["sign"]);
@@ -82354,7 +82632,7 @@ async function generateTotp(params, atMs = Date.now()) {
82354
82632
  return { code, secondsRemaining: period - seconds % period, period };
82355
82633
  }
82356
82634
  async function generateFromUri(uriOrSecret, atMs = Date.now()) {
82357
- const params = uriOrSecret.startsWith("otpauth://") ? parseOtpauthUri(uriOrSecret) : { ...DEFAULTS2, type: "totp", secret: uriOrSecret.replace(/\s+/g, "").toUpperCase() };
82635
+ const params = uriOrSecret.startsWith("otpauth://") ? parseOtpauthUri(uriOrSecret) : { ...DEFAULTS3, type: "totp", secret: uriOrSecret.replace(/\s+/g, "").toUpperCase() };
82358
82636
  return generateTotp(params, atMs);
82359
82637
  }
82360
82638
  function parseOtpauthUri(uri) {
@@ -82400,7 +82678,7 @@ var SETS = {
82400
82678
  symbol: "!@#$%^&*()-_=+[]{};:,.<>?"
82401
82679
  };
82402
82680
  var AMBIGUOUS = new Set("O0Il1");
82403
- function randomInt(maxExclusive) {
82681
+ function randomInt2(maxExclusive) {
82404
82682
  const limit = Math.floor(256 / maxExclusive) * maxExclusive;
82405
82683
  let x;
82406
82684
  do {
@@ -82409,9 +82687,9 @@ function randomInt(maxExclusive) {
82409
82687
  return x % maxExclusive;
82410
82688
  }
82411
82689
  function pick2(pool) {
82412
- return pool[randomInt(pool.length)];
82690
+ return pool[randomInt2(pool.length)];
82413
82691
  }
82414
- function generatePassword(opts = {}) {
82692
+ function generatePassword2(opts = {}) {
82415
82693
  const o = {
82416
82694
  length: 20,
82417
82695
  uppercase: true,
@@ -82452,7 +82730,7 @@ function generatePassword(opts = {}) {
82452
82730
  }
82453
82731
  function shuffle(arr) {
82454
82732
  for (let i = arr.length - 1; i > 0; i--) {
82455
- const j = randomInt(i + 1);
82733
+ const j = randomInt2(i + 1);
82456
82734
  [arr[i], arr[j]] = [arr[j], arr[i]];
82457
82735
  }
82458
82736
  return arr;
@@ -83173,7 +83451,7 @@ function makeDaemonVaultApi(getOrigin, getToken2) {
83173
83451
 
83174
83452
  // ../../packages/host-core/dist/vault/persistence.js
83175
83453
  init_base_paths();
83176
- import { existsSync as existsSync21, mkdirSync as mkdirSync19, readFileSync as readFileSync25, writeFileSync as writeFileSync17, unlinkSync as unlinkSync2 } from "node:fs";
83454
+ import { existsSync as existsSync22, mkdirSync as mkdirSync19, readFileSync as readFileSync27, writeFileSync as writeFileSync19, unlinkSync as unlinkSync3 } from "node:fs";
83177
83455
  import { dirname as dirname19 } from "node:path";
83178
83456
  function vaultStoreFile() {
83179
83457
  return homePath(".tesseract", "vault", "vault.json");
@@ -83185,9 +83463,9 @@ function makeVaultPersistence(file2 = vaultStoreFile, deviceKeyFile = vaultDevic
83185
83463
  return {
83186
83464
  load() {
83187
83465
  try {
83188
- if (!existsSync21(file2()))
83466
+ if (!existsSync22(file2()))
83189
83467
  return null;
83190
- const parsed = JSON.parse(readFileSync25(file2(), "utf8"));
83468
+ const parsed = JSON.parse(readFileSync27(file2(), "utf8"));
83191
83469
  if (!parsed || typeof parsed !== "object")
83192
83470
  return null;
83193
83471
  return { meta: parsed.meta ?? null, items: Array.isArray(parsed.items) ? parsed.items : [] };
@@ -83198,13 +83476,13 @@ function makeVaultPersistence(file2 = vaultStoreFile, deviceKeyFile = vaultDevic
83198
83476
  save(state2) {
83199
83477
  try {
83200
83478
  mkdirSync19(dirname19(file2()), { recursive: true, mode: 448 });
83201
- writeFileSync17(file2(), JSON.stringify(state2), { mode: 384 });
83479
+ writeFileSync19(file2(), JSON.stringify(state2), { mode: 384 });
83202
83480
  } catch {
83203
83481
  }
83204
83482
  },
83205
83483
  loadDeviceKey() {
83206
83484
  try {
83207
- return existsSync21(deviceKeyFile()) ? readFileSync25(deviceKeyFile(), "utf8").trim() || null : null;
83485
+ return existsSync22(deviceKeyFile()) ? readFileSync27(deviceKeyFile(), "utf8").trim() || null : null;
83208
83486
  } catch {
83209
83487
  return null;
83210
83488
  }
@@ -83212,14 +83490,14 @@ function makeVaultPersistence(file2 = vaultStoreFile, deviceKeyFile = vaultDevic
83212
83490
  saveDeviceKey(base64Key) {
83213
83491
  try {
83214
83492
  mkdirSync19(dirname19(deviceKeyFile()), { recursive: true, mode: 448 });
83215
- writeFileSync17(deviceKeyFile(), base64Key, { mode: 384 });
83493
+ writeFileSync19(deviceKeyFile(), base64Key, { mode: 384 });
83216
83494
  } catch {
83217
83495
  }
83218
83496
  },
83219
83497
  clearDeviceKey() {
83220
83498
  try {
83221
- if (existsSync21(deviceKeyFile()))
83222
- unlinkSync2(deviceKeyFile());
83499
+ if (existsSync22(deviceKeyFile()))
83500
+ unlinkSync3(deviceKeyFile());
83223
83501
  } catch {
83224
83502
  }
83225
83503
  }
@@ -83228,8 +83506,8 @@ function makeVaultPersistence(file2 = vaultStoreFile, deviceKeyFile = vaultDevic
83228
83506
 
83229
83507
  // ../../packages/host-core/dist/vault/biometric.js
83230
83508
  init_base_paths();
83231
- import { existsSync as existsSync22, mkdirSync as mkdirSync20, readFileSync as readFileSync26, rmSync as rmSync6, writeFileSync as writeFileSync18 } from "node:fs";
83232
- import { join as join44 } from "node:path";
83509
+ import { existsSync as existsSync23, mkdirSync as mkdirSync20, readFileSync as readFileSync28, rmSync as rmSync6, writeFileSync as writeFileSync20 } from "node:fs";
83510
+ import { join as join46 } from "node:path";
83233
83511
  async function electronBio() {
83234
83512
  if (!electronModule())
83235
83513
  return null;
@@ -83250,7 +83528,7 @@ function bioDir() {
83250
83528
  return d;
83251
83529
  }
83252
83530
  function bioFile(id) {
83253
- return join44(bioDir(), `${String(id).replace(/[^a-zA-Z0-9_-]/g, "").slice(0, 64)}.enc`);
83531
+ return join46(bioDir(), `${String(id).replace(/[^a-zA-Z0-9_-]/g, "").slice(0, 64)}.enc`);
83254
83532
  }
83255
83533
  async function biometricAvailable() {
83256
83534
  if (process.platform !== "darwin")
@@ -83265,24 +83543,24 @@ async function biometricAvailable() {
83265
83543
  }
83266
83544
  }
83267
83545
  function biometricEnrolled(id) {
83268
- return existsSync22(bioFile(id));
83546
+ return existsSync23(bioFile(id));
83269
83547
  }
83270
83548
  async function biometricEnroll(id, base64Key) {
83271
83549
  const bio = await electronBio();
83272
83550
  if (!bio)
83273
83551
  throw new Error("Touch ID is not available on this device.");
83274
83552
  await bio.systemPreferences.promptTouchID("enable Touch ID for your vault");
83275
- writeFileSync18(bioFile(id), bio.safeStorage.encryptString(base64Key), { mode: 384 });
83553
+ writeFileSync20(bioFile(id), bio.safeStorage.encryptString(base64Key), { mode: 384 });
83276
83554
  }
83277
83555
  async function biometricUnlock(id) {
83278
83556
  const f = bioFile(id);
83279
- if (!existsSync22(f))
83557
+ if (!existsSync23(f))
83280
83558
  throw new Error("Touch ID is not set up for this vault.");
83281
83559
  const bio = await electronBio();
83282
83560
  if (!bio)
83283
83561
  throw new Error("Touch ID is not available on this device.");
83284
83562
  await bio.systemPreferences.promptTouchID("unlock your vault");
83285
- return bio.safeStorage.decryptString(readFileSync26(f));
83563
+ return bio.safeStorage.decryptString(readFileSync28(f));
83286
83564
  }
83287
83565
  function biometricDisable(id) {
83288
83566
  try {
@@ -83312,7 +83590,7 @@ var VaultManager = class {
83312
83590
  // ── auto-lock ────────────────────────────────────────────────────────────────────
83313
83591
  loadAutoLock() {
83314
83592
  try {
83315
- const raw = JSON.parse(readFileSync27(settingsFile(), "utf8"));
83593
+ const raw = JSON.parse(readFileSync29(settingsFile(), "utf8"));
83316
83594
  return Number.isFinite(raw.autoLockMinutes) ? Math.max(0, raw.autoLockMinutes) : 0;
83317
83595
  } catch {
83318
83596
  return 0;
@@ -83321,7 +83599,7 @@ var VaultManager = class {
83321
83599
  saveAutoLock() {
83322
83600
  try {
83323
83601
  mkdirSync21(dirname20(settingsFile()), { recursive: true, mode: 448 });
83324
- writeFileSync19(settingsFile(), JSON.stringify({ autoLockMinutes: this.autoLockMinutes }), { mode: 384 });
83602
+ writeFileSync21(settingsFile(), JSON.stringify({ autoLockMinutes: this.autoLockMinutes }), { mode: 384 });
83325
83603
  } catch {
83326
83604
  }
83327
83605
  }
@@ -83600,7 +83878,7 @@ var VaultManager = class {
83600
83878
  }
83601
83879
  // ── misc ─────────────────────────────────────────────────────────────────────────
83602
83880
  generatePassword(opts) {
83603
- return generatePassword(opts);
83881
+ return generatePassword2(opts);
83604
83882
  }
83605
83883
  async syncNow() {
83606
83884
  await this.sync.ensureSynced();
@@ -83628,7 +83906,7 @@ function tokenMatches(got, want) {
83628
83906
  return false;
83629
83907
  const a = Buffer.from(got);
83630
83908
  const b = Buffer.from(want);
83631
- return a.length === b.length && timingSafeEqual4(a, b);
83909
+ return a.length === b.length && timingSafeEqual5(a, b);
83632
83910
  }
83633
83911
  function originAllowed(origin) {
83634
83912
  if (!origin)
@@ -83715,6 +83993,8 @@ async function startLocalServer(opts) {
83715
83993
  void handleVault(env.payload);
83716
83994
  else if (env.channel === "term")
83717
83995
  void handleTerm(env.payload);
83996
+ else if (env.channel === "rdeskadmin")
83997
+ void handleRdeskAdmin(env.payload);
83718
83998
  else if (env.channel === "voice")
83719
83999
  void handleVoice(env.payload);
83720
84000
  else if (env.channel === "present") {
@@ -83733,7 +84013,7 @@ async function startLocalServer(opts) {
83733
84013
  const rd = opts.getRdesk?.();
83734
84014
  if (rd && env.sessionId) {
83735
84015
  rdeskSessions.add(env.sessionId);
83736
- void rd.onClientMessage(env.sessionId, { clientId: loopbackOrigin, sameAccount: true, name: "This device" }, env.payload);
84016
+ void rd.onClientMessage(env.sessionId, { clientId: loopbackOrigin, name: "This device" }, env.payload);
83737
84017
  }
83738
84018
  } else if (env.channel === "signal") {
83739
84019
  const rd = opts.getRdesk?.();
@@ -83775,6 +84055,39 @@ async function startLocalServer(opts) {
83775
84055
  });
83776
84056
  ws.on("error", () => {
83777
84057
  });
84058
+ async function handleRdeskAdmin(msg) {
84059
+ const reply3 = (payload) => send3("rdeskadmin", { requestId: msg.requestId, ...payload });
84060
+ const state2 = () => ({
84061
+ prefs: readRdeskServerPrefs(),
84062
+ machineId: getMachinePublicId(),
84063
+ hasPassword: hasRdeskPassword(),
84064
+ canReveal: canRevealRdeskPassword(),
84065
+ sessions: opts.getRdeskSessions?.() ?? []
84066
+ });
84067
+ switch (msg.type) {
84068
+ case "rdesk:state":
84069
+ reply3({ type: "rdesk:state", ...state2() });
84070
+ break;
84071
+ case "rdesk:set":
84072
+ if (msg.patch)
84073
+ writeRdeskServerPrefs(msg.patch);
84074
+ reply3({ type: "rdesk:state", ...state2() });
84075
+ break;
84076
+ case "rdesk:reveal-password":
84077
+ reply3({ type: "rdesk:password", password: readRdeskPasswordPlain() });
84078
+ break;
84079
+ case "rdesk:regenerate-password":
84080
+ reply3({ type: "rdesk:state", password: regenerateRdeskPassword(), ...state2() });
84081
+ break;
84082
+ case "rdesk:disconnect":
84083
+ if (msg.sessionId)
84084
+ await opts.getRdesk?.()?.disconnect(msg.sessionId, "host");
84085
+ reply3({ type: "rdesk:state", ...state2() });
84086
+ break;
84087
+ default:
84088
+ reply3({ type: "rdesk:error", error: `unknown rdeskadmin message: ${String(msg.type)}` });
84089
+ }
84090
+ }
83778
84091
  async function handleAgent(msg) {
83779
84092
  const b = await getSharedBackend();
83780
84093
  switch (msg.type) {
@@ -84149,24 +84462,24 @@ async function startLocalServer(opts) {
84149
84462
 
84150
84463
  // ../../packages/host-core/dist/daemon-runtime.js
84151
84464
  init_base_paths();
84152
- import { readFileSync as readFileSync31, writeFileSync as writeFileSync26, existsSync as existsSync29, mkdirSync as mkdirSync27, unlinkSync as unlinkSync4, chmodSync as chmodSync6 } from "node:fs";
84465
+ import { readFileSync as readFileSync32, writeFileSync as writeFileSync27, existsSync as existsSync29, mkdirSync as mkdirSync27, unlinkSync as unlinkSync4, chmodSync as chmodSync6 } from "node:fs";
84153
84466
  import { execFileSync as execFileSync4 } from "node:child_process";
84154
84467
  import { join as join55 } from "node:path";
84155
84468
  import { randomBytes as randomBytes8 } from "node:crypto";
84156
84469
  import { hostname as hostname4 } from "node:os";
84157
84470
 
84158
84471
  // ../../packages/host-core/dist/device-identity.js
84159
- import { join as join45 } from "node:path";
84160
- import { readFileSync as readFileSync28, writeFileSync as writeFileSync20, existsSync as existsSync23, mkdirSync as mkdirSync22, renameSync as renameSync8, chmodSync as chmodSync3 } from "node:fs";
84472
+ import { join as join47 } from "node:path";
84473
+ import { readFileSync as readFileSync30, writeFileSync as writeFileSync22, existsSync as existsSync24, mkdirSync as mkdirSync22, renameSync as renameSync8, chmodSync as chmodSync3 } from "node:fs";
84161
84474
  var cached2 = null;
84162
84475
  function identityPath() {
84163
- return join45(daemonDir2(), "identity.json");
84476
+ return join47(daemonDir2(), "identity.json");
84164
84477
  }
84165
84478
  async function loadOrCreateDeviceIdentity() {
84166
84479
  if (cached2)
84167
84480
  return cached2;
84168
84481
  try {
84169
- const stored = JSON.parse(readFileSync28(identityPath(), "utf-8"));
84482
+ const stored = JSON.parse(readFileSync30(identityPath(), "utf-8"));
84170
84483
  if (stored?.privateJwk) {
84171
84484
  cached2 = await importIdentity(stored.privateJwk);
84172
84485
  return cached2;
@@ -84176,10 +84489,10 @@ async function loadOrCreateDeviceIdentity() {
84176
84489
  const kp = await generateIdentityKeyPair(true);
84177
84490
  const { privateJwk, publicRaw } = await exportIdentity(kp);
84178
84491
  const dir2 = daemonDir2();
84179
- if (!existsSync23(dir2))
84492
+ if (!existsSync24(dir2))
84180
84493
  mkdirSync22(dir2, { recursive: true, mode: 448 });
84181
84494
  const tmp = `${identityPath()}.tmp`;
84182
- writeFileSync20(tmp, JSON.stringify({ privateJwk, publicRaw, alg: "P256-ECDSA" }, null, 2), { mode: 384 });
84495
+ writeFileSync22(tmp, JSON.stringify({ privateJwk, publicRaw, alg: "P256-ECDSA" }, null, 2), { mode: 384 });
84183
84496
  renameSync8(tmp, identityPath());
84184
84497
  try {
84185
84498
  chmodSync3(identityPath(), 384);
@@ -84190,7 +84503,7 @@ async function loadOrCreateDeviceIdentity() {
84190
84503
  }
84191
84504
  var sealingCached = null;
84192
84505
  function sealingPath() {
84193
- return join45(daemonDir2(), "sealing.json");
84506
+ return join47(daemonDir2(), "sealing.json");
84194
84507
  }
84195
84508
  var EC_DH = { name: "ECDH", namedCurve: "P-256" };
84196
84509
  async function loadOrCreateSealingKey() {
@@ -84198,7 +84511,7 @@ async function loadOrCreateSealingKey() {
84198
84511
  return sealingCached;
84199
84512
  const { subtle: subtle5 } = globalThis.crypto;
84200
84513
  try {
84201
- const stored = JSON.parse(readFileSync28(sealingPath(), "utf-8"));
84514
+ const stored = JSON.parse(readFileSync30(sealingPath(), "utf-8"));
84202
84515
  if (stored?.privateJwk && stored.publicRaw) {
84203
84516
  const privateKey = await subtle5.importKey("jwk", stored.privateJwk, EC_DH, true, ["deriveBits"]);
84204
84517
  sealingCached = { privateKey, publicRaw: unb64u(stored.publicRaw) };
@@ -84210,10 +84523,10 @@ async function loadOrCreateSealingKey() {
84210
84523
  const publicRaw = new Uint8Array(await subtle5.exportKey("raw", kp.publicKey));
84211
84524
  const privateJwk = await subtle5.exportKey("jwk", kp.privateKey);
84212
84525
  const dir2 = daemonDir2();
84213
- if (!existsSync23(dir2))
84526
+ if (!existsSync24(dir2))
84214
84527
  mkdirSync22(dir2, { recursive: true, mode: 448 });
84215
84528
  const tmp = `${sealingPath()}.tmp`;
84216
- writeFileSync20(tmp, JSON.stringify({ privateJwk, publicRaw: b64u(publicRaw), alg: "P256-ECDH" }, null, 2), { mode: 384 });
84529
+ writeFileSync22(tmp, JSON.stringify({ privateJwk, publicRaw: b64u(publicRaw), alg: "P256-ECDH" }, null, 2), { mode: 384 });
84217
84530
  renameSync8(tmp, sealingPath());
84218
84531
  try {
84219
84532
  chmodSync3(sealingPath(), 384);
@@ -84276,10 +84589,10 @@ import { createSocket as createSocket2 } from "node:dgram";
84276
84589
  import { createRequire as createRequire4 } from "node:module";
84277
84590
 
84278
84591
  // ../../packages/host-core/dist/mesh/config.js
84279
- import { chmodSync as chmodSync4, existsSync as existsSync24, mkdirSync as mkdirSync23, readFileSync as readFileSync29, rmSync as rmSync7, writeFileSync as writeFileSync21 } from "node:fs";
84280
- import { join as join46 } from "node:path";
84592
+ import { chmodSync as chmodSync4, existsSync as existsSync25, mkdirSync as mkdirSync23, readFileSync as readFileSync31, rmSync as rmSync7, writeFileSync as writeFileSync23 } from "node:fs";
84593
+ import { join as join48 } from "node:path";
84281
84594
  function meshDir() {
84282
- return join46(daemonDir2(), "mesh");
84595
+ return join48(daemonDir2(), "mesh");
84283
84596
  }
84284
84597
  var FILES = {
84285
84598
  genesis: "genesis.bin",
@@ -84288,9 +84601,9 @@ var FILES = {
84288
84601
  bundle: "bundle.bin"
84289
84602
  };
84290
84603
  function readIfPresent(name) {
84291
- const p = join46(meshDir(), name);
84604
+ const p = join48(meshDir(), name);
84292
84605
  try {
84293
- return existsSync24(p) ? new Uint8Array(readFileSync29(p)) : void 0;
84606
+ return existsSync25(p) ? new Uint8Array(readFileSync31(p)) : void 0;
84294
84607
  } catch {
84295
84608
  return void 0;
84296
84609
  }
@@ -84307,8 +84620,8 @@ function loadMeshIdentity() {
84307
84620
  return { genesis, cert, staticPrivate, ...bundle ? { bundle } : {} };
84308
84621
  }
84309
84622
  function writeSecret(name, bytes2) {
84310
- const p = join46(meshDir(), name);
84311
- writeFileSync21(p, bytes2, { mode: 384 });
84623
+ const p = join48(meshDir(), name);
84624
+ writeFileSync23(p, bytes2, { mode: 384 });
84312
84625
  try {
84313
84626
  chmodSync4(p, 384);
84314
84627
  } catch {
@@ -84320,21 +84633,21 @@ function saveMeshIdentity(id) {
84320
84633
  chmodSync4(meshDir(), 448);
84321
84634
  } catch {
84322
84635
  }
84323
- writeFileSync21(join46(meshDir(), FILES.genesis), id.genesis);
84324
- writeFileSync21(join46(meshDir(), FILES.cert), id.cert);
84636
+ writeFileSync23(join48(meshDir(), FILES.genesis), id.genesis);
84637
+ writeFileSync23(join48(meshDir(), FILES.cert), id.cert);
84325
84638
  writeSecret(FILES.key, id.staticPrivate);
84326
84639
  if (id.bundle)
84327
- writeFileSync21(join46(meshDir(), FILES.bundle), id.bundle);
84640
+ writeFileSync23(join48(meshDir(), FILES.bundle), id.bundle);
84328
84641
  }
84329
84642
  var STATUS_REFRESH_MS = 5e3;
84330
84643
  var STATUS_STALE_MS = 2e4;
84331
84644
  function statusPath() {
84332
- return join46(meshDir(), "status.json");
84645
+ return join48(meshDir(), "status.json");
84333
84646
  }
84334
84647
  function writeStatus(status2) {
84335
84648
  try {
84336
84649
  mkdirSync23(meshDir(), { recursive: true, mode: 448 });
84337
- writeFileSync21(statusPath(), JSON.stringify({ ...status2, at: Date.now() }, null, 2));
84650
+ writeFileSync23(statusPath(), JSON.stringify({ ...status2, at: Date.now() }, null, 2));
84338
84651
  } catch {
84339
84652
  }
84340
84653
  }
@@ -84346,7 +84659,7 @@ function clearStatus() {
84346
84659
  }
84347
84660
  function readStatus() {
84348
84661
  try {
84349
- const raw = JSON.parse(readFileSync29(statusPath(), "utf8"));
84662
+ const raw = JSON.parse(readFileSync31(statusPath(), "utf8"));
84350
84663
  if (typeof raw.at !== "number")
84351
84664
  return null;
84352
84665
  return { ...raw, at: raw.at, stale: Date.now() - raw.at > STATUS_STALE_MS };
@@ -84357,7 +84670,7 @@ function readStatus() {
84357
84670
  function cacheBundle(bundle) {
84358
84671
  try {
84359
84672
  mkdirSync23(meshDir(), { recursive: true, mode: 448 });
84360
- writeFileSync21(join46(meshDir(), FILES.bundle), bundle);
84673
+ writeFileSync23(join48(meshDir(), FILES.bundle), bundle);
84361
84674
  } catch {
84362
84675
  }
84363
84676
  }
@@ -84970,7 +85283,7 @@ import { basename as basename11 } from "node:path";
84970
85283
  // ../../packages/host-core/dist/drops/paths.js
84971
85284
  init_base_paths();
84972
85285
  import { mkdir as mkdir11 } from "node:fs/promises";
84973
- import { join as join47 } from "node:path";
85286
+ import { join as join49 } from "node:path";
84974
85287
 
84975
85288
  // ../../packages/host-core/dist/drops/names.js
84976
85289
  var WINDOWS_RESERVED = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])$/i;
@@ -85059,7 +85372,7 @@ function basenameOf(p) {
85059
85372
  function resolveDropsRoot() {
85060
85373
  const root = resolveHostRoot();
85061
85374
  const preferred = dropsDir();
85062
- return isInsideRoot(preferred, root) ? preferred : join47(root, "Drops");
85375
+ return isInsideRoot(preferred, root) ? preferred : join49(root, "Drops");
85063
85376
  }
85064
85377
  async function ensureDropsRoot() {
85065
85378
  const dir2 = resolveDropsRoot();
@@ -85075,7 +85388,7 @@ async function ensureDropDir(opts) {
85075
85388
  parts.push(sanitizeDropName(opts.sender, "Unknown sender"));
85076
85389
  if (!opts.singleItem && opts.label)
85077
85390
  parts.push(sanitizeDropName(opts.label, "Files"));
85078
- const dir2 = join47(...parts);
85391
+ const dir2 = join49(...parts);
85079
85392
  await mkdir11(dir2, { recursive: true });
85080
85393
  return dir2;
85081
85394
  }
@@ -85218,7 +85531,7 @@ function createDropHandler(deps) {
85218
85531
  // ../../packages/host-core/dist/drops/blob-fetch.js
85219
85532
  import { createWriteStream as createWriteStream2 } from "node:fs";
85220
85533
  import { mkdir as mkdir12, rm as rm4, chmod as chmod3 } from "node:fs/promises";
85221
- import { dirname as dirname22, join as join48 } from "node:path";
85534
+ import { dirname as dirname22, join as join50 } from "node:path";
85222
85535
  import { Readable as Readable2 } from "node:stream";
85223
85536
  import { pipeline } from "node:stream/promises";
85224
85537
 
@@ -85390,7 +85703,7 @@ async function fetchBlobDrop(opts) {
85390
85703
  log7(`drop ${opts.dropId}: refusing directory "${item.rel}"`);
85391
85704
  continue;
85392
85705
  }
85393
- const abs = join48(opts.destDir, rel);
85706
+ const abs = join50(opts.destDir, rel);
85394
85707
  if (!isInsideRoot(abs, opts.destDir)) {
85395
85708
  log7(`drop ${opts.dropId}: refusing directory outside the destination`);
85396
85709
  continue;
@@ -85411,7 +85724,7 @@ async function fetchBlobDrop(opts) {
85411
85724
  log7(`drop ${opts.dropId}: refusing "${item.rel}"`);
85412
85725
  continue;
85413
85726
  }
85414
- let abs = join48(opts.destDir, rel);
85727
+ let abs = join50(opts.destDir, rel);
85415
85728
  if (!isInsideRoot(abs, opts.destDir)) {
85416
85729
  log7(`drop ${opts.dropId}: refusing a path outside the destination`);
85417
85730
  continue;
@@ -85654,7 +85967,7 @@ var VncEngine = class {
85654
85967
  const c = client;
85655
85968
  this.fbw = c.width ?? 1280;
85656
85969
  this.fbh = c.height ?? 800;
85657
- void this.pub?.startCanvas({ iceServers: [], forceRelay: this.ctx.forceRelay, quality: "auto", width: this.fbw, height: this.fbh, maxFps: 30 });
85970
+ void this.pub?.startCanvas({ iceServers: [], quality: "auto", width: this.fbw, height: this.fbh, maxFps: 30 });
85658
85971
  client.requestUpdate?.(false, 0, 0, this.fbw, this.fbh);
85659
85972
  });
85660
85973
  client.on("rect", (rect) => {
@@ -85756,38 +86069,47 @@ function registerRdpEngine() {
85756
86069
  }
85757
86070
 
85758
86071
  // ../../packages/host-core/dist/rdesk/audit.js
85759
- import { appendFileSync as appendFileSync2 } from "node:fs";
85760
- import { join as join49 } from "node:path";
86072
+ import { appendFile, stat as stat7, rename as rename3 } from "node:fs/promises";
86073
+ import { join as join51 } from "node:path";
86074
+ var MAX_BYTES = 2 * 1024 * 1024;
85761
86075
  function rdeskAuditPath() {
85762
- return join49(daemonDir2(), "rdesk-audit.jsonl");
86076
+ return join51(daemonDir2(), "rdesk-audit.jsonl");
85763
86077
  }
85764
- function appendRdeskAudit(e) {
86078
+ var queue = [];
86079
+ var draining = false;
86080
+ async function rotateIfNeeded(path6) {
85765
86081
  try {
85766
- appendFileSync2(rdeskAuditPath(), JSON.stringify(e) + "\n", { mode: 384 });
86082
+ const st = await stat7(path6);
86083
+ if (st.size < MAX_BYTES)
86084
+ return;
86085
+ await rename3(path6, `${path6}.1`);
85767
86086
  } catch {
85768
86087
  }
85769
86088
  }
85770
-
85771
- // ../../packages/host-core/dist/rdesk/password.js
85772
- import { scryptSync, randomBytes as randomBytes6, timingSafeEqual as timingSafeEqual5 } from "node:crypto";
85773
- import { readFileSync as readFileSync30, writeFileSync as writeFileSync22, existsSync as existsSync25, unlinkSync as unlinkSync3 } from "node:fs";
85774
- import { join as join50 } from "node:path";
85775
- function pwFile() {
85776
- return join50(daemonDir2(), "rdesk-password.json");
86089
+ async function drain() {
86090
+ if (draining)
86091
+ return;
86092
+ draining = true;
86093
+ try {
86094
+ while (queue.length) {
86095
+ const batch = queue.join("");
86096
+ queue = [];
86097
+ const path6 = rdeskAuditPath();
86098
+ await rotateIfNeeded(path6);
86099
+ await appendFile(path6, batch, { mode: 384 });
86100
+ }
86101
+ } catch {
86102
+ } finally {
86103
+ draining = false;
86104
+ if (queue.length)
86105
+ void drain();
86106
+ }
85777
86107
  }
85778
- function verifyRdeskPassword(password) {
85779
- if (!password)
85780
- return false;
85781
- const override = process.env.CCB_RDESK_PASSWORD;
85782
- if (override)
85783
- return password.length === override.length && timingSafeEqual5(Buffer.from(password), Buffer.from(override));
86108
+ function appendRdeskAudit(e) {
85784
86109
  try {
85785
- const { salt, hash: hash2 } = JSON.parse(readFileSync30(pwFile(), "utf8"));
85786
- const got = scryptSync(password, Buffer.from(salt, "hex"), 32);
85787
- const want = Buffer.from(hash2, "hex");
85788
- return got.length === want.length && timingSafeEqual5(got, want);
86110
+ queue.push(JSON.stringify(e) + "\n");
86111
+ void drain();
85789
86112
  } catch {
85790
- return false;
85791
86113
  }
85792
86114
  }
85793
86115
 
@@ -85923,9 +86245,9 @@ function bindBridge(i, log9) {
85923
86245
 
85924
86246
  // ../../packages/host-core/dist/vault/autofill-bridge.js
85925
86247
  import { randomBytes as randomBytes7 } from "node:crypto";
85926
- import { existsSync as existsSync26, mkdirSync as mkdirSync24, writeFileSync as writeFileSync23, rmSync as rmSync8 } from "node:fs";
86248
+ import { existsSync as existsSync26, mkdirSync as mkdirSync24, writeFileSync as writeFileSync24, rmSync as rmSync8 } from "node:fs";
85927
86249
  import { homedir as homedir6 } from "node:os";
85928
- import { join as join51 } from "node:path";
86250
+ import { join as join52 } from "node:path";
85929
86251
 
85930
86252
  // ../../packages/host-core/dist/vault/peer-verify.js
85931
86253
  import { execFile as execFile6 } from "node:child_process";
@@ -85987,10 +86309,10 @@ async function isTrustedAutofillPeer(remotePort) {
85987
86309
  var APPEX_BUNDLE_ID = "dev.tesseract.AutoFill";
85988
86310
  var BRIDGE_PORTS2 = [17872, 17873, 17874, 17875];
85989
86311
  function appexContainerData() {
85990
- return join51(homedir6(), "Library", "Containers", APPEX_BUNDLE_ID, "Data");
86312
+ return join52(homedir6(), "Library", "Containers", APPEX_BUNDLE_ID, "Data");
85991
86313
  }
85992
86314
  function bridgeInfoPath() {
85993
- return join51(appexContainerData(), "Library", "Application Support", "tesseract-autofill-bridge.json");
86315
+ return join52(appexContainerData(), "Library", "Application Support", "tesseract-autofill-bridge.json");
85994
86316
  }
85995
86317
  var infoLoggedOnce = false;
85996
86318
  function writeBridgeInfo(port, token3, log9) {
@@ -86005,9 +86327,9 @@ function writeBridgeInfo(port, token3, log9) {
86005
86327
  }
86006
86328
  return;
86007
86329
  }
86008
- const dir2 = join51(containerData, "Library", "Application Support");
86330
+ const dir2 = join52(containerData, "Library", "Application Support");
86009
86331
  mkdirSync24(dir2, { recursive: true });
86010
- writeFileSync23(bridgeInfoPath(), JSON.stringify({ port, token: token3, pid: process.pid, ts: Date.now() }), { mode: 384 });
86332
+ writeFileSync24(bridgeInfoPath(), JSON.stringify({ port, token: token3, pid: process.pid, ts: Date.now() }), { mode: 384 });
86011
86333
  if (!infoLoggedOnce) {
86012
86334
  infoLoggedOnce = true;
86013
86335
  log9("autofill bridge-info WROTE", port, "\u2192", dir2);
@@ -86163,7 +86485,7 @@ function startAutofillBridge(log9 = () => {
86163
86485
  }
86164
86486
 
86165
86487
  // ../../packages/host-core/dist/power-node.js
86166
- import { spawn as spawn9 } from "node:child_process";
86488
+ import { spawn as spawn10 } from "node:child_process";
86167
86489
  import { platform } from "node:os";
86168
86490
  function createNodePowerController() {
86169
86491
  const active = /* @__PURE__ */ new Set();
@@ -86171,9 +86493,9 @@ function createNodePowerController() {
86171
86493
  function spawnInhibitor() {
86172
86494
  try {
86173
86495
  if (platform() === "darwin")
86174
- return spawn9("caffeinate", ["-i"], { stdio: "ignore" });
86496
+ return spawn10("caffeinate", ["-i"], { stdio: "ignore" });
86175
86497
  if (platform() === "linux") {
86176
- return spawn9("systemd-inhibit", ["--what=sleep", "--why=tesseract agent running", "--mode=block", "sleep", "infinity"], { stdio: "ignore" });
86498
+ return spawn10("systemd-inhibit", ["--what=sleep", "--why=tesseract agent running", "--mode=block", "sleep", "infinity"], { stdio: "ignore" });
86177
86499
  }
86178
86500
  } catch {
86179
86501
  }
@@ -86221,9 +86543,9 @@ function createNodePowerController() {
86221
86543
 
86222
86544
  // ../../packages/host-core/dist/native-host.js
86223
86545
  import { execFileSync as execFileSync3 } from "node:child_process";
86224
- import { chmodSync as chmodSync5, existsSync as existsSync27, mkdirSync as mkdirSync25, writeFileSync as writeFileSync24 } from "node:fs";
86546
+ import { chmodSync as chmodSync5, existsSync as existsSync27, mkdirSync as mkdirSync25, writeFileSync as writeFileSync25 } from "node:fs";
86225
86547
  import { homedir as homedir7, platform as platform2 } from "node:os";
86226
- import { dirname as dirname23, join as join52, resolve as resolve10 } from "node:path";
86548
+ import { dirname as dirname23, join as join53, resolve as resolve10 } from "node:path";
86227
86549
  import { fileURLToPath as fileURLToPath3 } from "node:url";
86228
86550
  var HOST_NAME = "com.tesseract.companion";
86229
86551
  var EXTENSION_IDS = ["icpnghhpcmaonjckkpilpajblfehpplo", "giolfpookdhjfafcchaflhmigghaeffn"];
@@ -86232,18 +86554,18 @@ function browserProfileDirs() {
86232
86554
  const home = homedir7();
86233
86555
  const p = platform2();
86234
86556
  if (p === "darwin") {
86235
- const base = join52(home, "Library/Application Support");
86236
- return ["Google/Chrome", "Google/Chrome Beta", "Google/Chrome Dev", "Google/Chrome Canary", "Chromium", "Microsoft Edge", "BraveSoftware/Brave-Browser", "Vivaldi", "Arc"].map((b) => join52(base, b));
86557
+ const base = join53(home, "Library/Application Support");
86558
+ return ["Google/Chrome", "Google/Chrome Beta", "Google/Chrome Dev", "Google/Chrome Canary", "Chromium", "Microsoft Edge", "BraveSoftware/Brave-Browser", "Vivaldi", "Arc"].map((b) => join53(base, b));
86237
86559
  }
86238
86560
  if (p === "win32")
86239
86561
  return [];
86240
- const cfg2 = join52(home, ".config");
86241
- return ["google-chrome", "google-chrome-beta", "chromium", "microsoft-edge", "BraveSoftware/Brave-Browser", "vivaldi"].map((b) => join52(cfg2, b));
86562
+ const cfg2 = join53(home, ".config");
86563
+ return ["google-chrome", "google-chrome-beta", "chromium", "microsoft-edge", "BraveSoftware/Brave-Browser", "vivaldi"].map((b) => join53(cfg2, b));
86242
86564
  }
86243
86565
  function resolveBridge(log9) {
86244
86566
  const candidates = [
86245
86567
  process.env.CCB_COMPANION_HOST_JS,
86246
- process.resourcesPath ? join52(process.resourcesPath, "companion-host", BRIDGE_BASENAME) : void 0
86568
+ process.resourcesPath ? join53(process.resourcesPath, "companion-host", BRIDGE_BASENAME) : void 0
86247
86569
  ];
86248
86570
  try {
86249
86571
  const here = dirname23(fileURLToPath3(import.meta.url));
@@ -86259,14 +86581,14 @@ function resolveBridge(log9) {
86259
86581
  }
86260
86582
  function writeLauncher(daemonDir3, bridge, runtime) {
86261
86583
  const isWin = platform2() === "win32";
86262
- const launcher = join52(daemonDir3, isWin ? "companion-host.cmd" : "companion-host.sh");
86584
+ const launcher = join53(daemonDir3, isWin ? "companion-host.cmd" : "companion-host.sh");
86263
86585
  if (isWin) {
86264
- writeFileSync24(launcher, `@echo off\r
86586
+ writeFileSync25(launcher, `@echo off\r
86265
86587
  set ELECTRON_RUN_AS_NODE=1\r
86266
86588
  "${runtime}" "${bridge}" %*\r
86267
86589
  `);
86268
86590
  } else {
86269
- writeFileSync24(launcher, `#!/bin/sh
86591
+ writeFileSync25(launcher, `#!/bin/sh
86270
86592
  # Tesseract Companion native-messaging launcher (auto-written by the daemon).
86271
86593
  ELECTRON_RUN_AS_NODE=1 exec "${runtime}" "${bridge}" "$@"
86272
86594
  `);
@@ -86297,9 +86619,9 @@ function registerNativeHost(opts) {
86297
86619
  if (!existsSync27(profile))
86298
86620
  continue;
86299
86621
  try {
86300
- const dir2 = join52(profile, "NativeMessagingHosts");
86622
+ const dir2 = join53(profile, "NativeMessagingHosts");
86301
86623
  mkdirSync25(dir2, { recursive: true });
86302
- writeFileSync24(join52(dir2, `${HOST_NAME}.json`), manifestJson);
86624
+ writeFileSync25(join53(dir2, `${HOST_NAME}.json`), manifestJson);
86303
86625
  wrote++;
86304
86626
  } catch {
86305
86627
  }
@@ -86311,8 +86633,8 @@ function registerNativeHost(opts) {
86311
86633
  }
86312
86634
  function registerWindows(daemonDir3, manifestJson, log9) {
86313
86635
  try {
86314
- const manifestPath = join52(daemonDir3, `${HOST_NAME}.json`);
86315
- writeFileSync24(manifestPath, manifestJson);
86636
+ const manifestPath = join53(daemonDir3, `${HOST_NAME}.json`);
86637
+ writeFileSync25(manifestPath, manifestJson);
86316
86638
  for (const root of ["Software\\Google\\Chrome", "Software\\Microsoft\\Edge", "Software\\BraveSoftware\\Brave-Browser"]) {
86317
86639
  try {
86318
86640
  execFileSync3("reg", ["add", `HKCU\\${root}\\NativeMessagingHosts\\${HOST_NAME}`, "/ve", "/t", "REG_SZ", "/d", manifestPath, "/f"], { stdio: "ignore" });
@@ -86326,10 +86648,10 @@ function registerWindows(daemonDir3, manifestJson, log9) {
86326
86648
  }
86327
86649
 
86328
86650
  // ../../packages/host-core/dist/activity/activity-tracker.js
86329
- import { promises as fsp, writeFileSync as writeFileSync25, mkdirSync as mkdirSync26, existsSync as existsSync28 } from "node:fs";
86330
- import { join as join53 } from "node:path";
86651
+ import { promises as fsp, writeFileSync as writeFileSync26, mkdirSync as mkdirSync26, existsSync as existsSync28 } from "node:fs";
86652
+ import { join as join54 } from "node:path";
86331
86653
  function activityProfilePath() {
86332
- return join53(daemonDir2(), "activity-profile.json");
86654
+ return join54(daemonDir2(), "activity-profile.json");
86333
86655
  }
86334
86656
  var MAX_APPS = 300;
86335
86657
  var MAX_AGE_DAYS = 120;
@@ -86441,7 +86763,7 @@ function createActivityTracker(opts = {}) {
86441
86763
  pruneProfile(profile);
86442
86764
  profile.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
86443
86765
  mkdirSync26(daemonDir2(), { recursive: true, mode: 448 });
86444
- writeFileSync25(activityProfilePath(), JSON.stringify(profile), { mode: 384 });
86766
+ writeFileSync26(activityProfilePath(), JSON.stringify(profile), { mode: 384 });
86445
86767
  dirty = 0;
86446
86768
  }
86447
86769
  } catch (e) {
@@ -86455,9 +86777,6 @@ function createActivityTracker(opts = {}) {
86455
86777
  };
86456
86778
  }
86457
86779
 
86458
- // ../../packages/host-core/dist/rdesk/server-auth.js
86459
- var DEFAULT_API = (process.env.CCB_TESSERACT_API ?? "https://app.tesseract.dev").replace(/\/$/, "");
86460
-
86461
86780
  // ../../packages/host-core/dist/gui/rdesk-client-window.js
86462
86781
  var win = null;
86463
86782
  function getRdeskClientWebContents() {
@@ -86475,46 +86794,7 @@ function getRdeskClientWebContents() {
86475
86794
  }
86476
86795
 
86477
86796
  // ../../packages/host-core/dist/gui/rdesk-call-window.js
86478
- import { join as join54 } from "node:path";
86479
86797
  var win2 = null;
86480
- function showRdeskCallWindow(opts) {
86481
- const el2 = electronModule();
86482
- if (!el2?.BrowserWindow)
86483
- return;
86484
- if (win2 && !win2.isDestroyed()) {
86485
- try {
86486
- win2.focus();
86487
- } catch {
86488
- }
86489
- return;
86490
- }
86491
- const w = new el2.BrowserWindow({
86492
- width: 380,
86493
- height: 320,
86494
- resizable: true,
86495
- minimizable: true,
86496
- fullscreenable: false,
86497
- alwaysOnTop: true,
86498
- skipTaskbar: false,
86499
- title: "Remote desktop \u2014 call",
86500
- backgroundColor: "#15151a",
86501
- webPreferences: { contextIsolation: true, sandbox: false }
86502
- });
86503
- try {
86504
- w.setAlwaysOnTop(true, "floating");
86505
- } catch {
86506
- }
86507
- w.on("closed", () => {
86508
- win2 = null;
86509
- try {
86510
- opts.onClosed?.();
86511
- } catch {
86512
- }
86513
- });
86514
- const loadFile = w.loadFile;
86515
- void loadFile(join54(daemonUiDir(), "rdesk-call.html"), { query: { url: opts.url, token: opts.token, room: opts.room } }).catch((e) => console.error("[rdesk-call] loadFile FAILED", e?.message ?? e));
86516
- win2 = w;
86517
- }
86518
86798
  function closeRdeskCallWindow() {
86519
86799
  const w = win2;
86520
86800
  win2 = null;
@@ -86575,11 +86855,11 @@ async function runDaemon(opts = {}) {
86575
86855
  }
86576
86856
  for (let attempt = 0; attempt < 2; attempt++) {
86577
86857
  try {
86578
- writeFileSync26(f, String(process.pid), { flag: "wx" });
86858
+ writeFileSync27(f, String(process.pid), { flag: "wx" });
86579
86859
  return true;
86580
86860
  } catch {
86581
86861
  try {
86582
- const prev = Number(readFileSync31(f, "utf-8").trim());
86862
+ const prev = Number(readFileSync32(f, "utf-8").trim());
86583
86863
  if (!prev)
86584
86864
  return false;
86585
86865
  if (prev !== process.pid && isAlive2(prev))
@@ -86595,7 +86875,7 @@ async function runDaemon(opts = {}) {
86595
86875
  function releaseLock() {
86596
86876
  try {
86597
86877
  const f = pidfile();
86598
- if (existsSync29(f) && Number(readFileSync31(f, "utf-8").trim()) === process.pid)
86878
+ if (existsSync29(f) && Number(readFileSync32(f, "utf-8").trim()) === process.pid)
86599
86879
  unlinkSync4(f);
86600
86880
  } catch {
86601
86881
  }
@@ -86640,11 +86920,11 @@ async function runDaemon(opts = {}) {
86640
86920
  let lockPid = 0, statusPid = 0;
86641
86921
  const otherPids = [];
86642
86922
  try {
86643
- lockPid = Number(readFileSync31(pidfile(), "utf-8").trim());
86923
+ lockPid = Number(readFileSync32(pidfile(), "utf-8").trim());
86644
86924
  } catch {
86645
86925
  }
86646
86926
  try {
86647
- statusPid = Number(JSON.parse(readFileSync31(statusFile(), "utf-8")).pid ?? 0);
86927
+ statusPid = Number(JSON.parse(readFileSync32(statusFile(), "utf-8")).pid ?? 0);
86648
86928
  } catch {
86649
86929
  }
86650
86930
  for (const p of otherDaemonPids())
@@ -86661,7 +86941,7 @@ async function runDaemon(opts = {}) {
86661
86941
  }
86662
86942
  function writeStatus2(s) {
86663
86943
  try {
86664
- writeFileSync26(statusFile(), JSON.stringify({ pid: process.pid, version: VERSION2, execPath: process.execPath, updatedAt: (/* @__PURE__ */ new Date()).toISOString(), gui: daemonGuiTrayActive(), ...localInfo ?? {}, ...s }));
86944
+ writeFileSync27(statusFile(), JSON.stringify({ pid: process.pid, version: VERSION2, execPath: process.execPath, updatedAt: (/* @__PURE__ */ new Date()).toISOString(), gui: daemonGuiTrayActive(), ...localInfo ?? {}, ...s }));
86665
86945
  chmodSync6(statusFile(), 384);
86666
86946
  } catch {
86667
86947
  }
@@ -86675,7 +86955,7 @@ async function runDaemon(opts = {}) {
86675
86955
  }
86676
86956
  if (!acquireLock2()) {
86677
86957
  try {
86678
- writeFileSync26(pidfile(), String(process.pid));
86958
+ writeFileSync27(pidfile(), String(process.pid));
86679
86959
  } catch {
86680
86960
  }
86681
86961
  }
@@ -86845,7 +87125,7 @@ async function runDaemon(opts = {}) {
86845
87125
  process.env.CCB_HOST_DAEMON = "1";
86846
87126
  const localToken = randomBytes8(24).toString("hex");
86847
87127
  try {
86848
- localServer = await startLocalServer({ hostRoot: resolveHostRoot(), token: localToken, log: log9, getRdesk: () => rdeskManager, loopbackRdeskSend });
87128
+ localServer = await startLocalServer({ hostRoot: resolveHostRoot(), token: localToken, log: log9, getRdesk: () => rdeskManager, loopbackRdeskSend, getRdeskSessions: () => rdeskRef?.sessionInfos() ?? [] });
86849
87129
  localInfo = { localPort: localServer.port, localToken };
86850
87130
  } catch (e) {
86851
87131
  log9("local listener failed to start", e.message);
@@ -86975,7 +87255,7 @@ async function runDaemon(opts = {}) {
86975
87255
  let rdeskRef = null;
86976
87256
  const rdesk = new RdeskSessionManager({
86977
87257
  createEngine: createRemoteDesktopEngine,
86978
- consent: ({ viewerName }) => askRdeskConsent({ viewerName }),
87258
+ consent: ({ viewerName, grant }) => askRdeskConsent({ viewerName, grant }),
86979
87259
  // Unattended-access password (Phase 4): a scrypt hash in daemon settings, opt-in.
86980
87260
  checkPassword: (pw) => verifyRdeskPassword(pw),
86981
87261
  // Route to the local viewer over loopback when this session's viewer connected via
@@ -86994,8 +87274,20 @@ async function runDaemon(opts = {}) {
86994
87274
  else
86995
87275
  relayRdeskSignal(msg, { clientId }, sessionId);
86996
87276
  },
86997
- canServe: () => daemonGuiTrayActive(),
86998
- defaultAccess: "view",
87277
+ // Two independent conditions: the GUI/tray must be up (we need it to capture), AND the
87278
+ // user must not have switched sharing off. Read FRESH so the Settings toggle applies to
87279
+ // the next connect with no daemon restart. NOTE this is deliberately rdesk-scoped — the
87280
+ // `sharing`/`driving` flag in control-link gates fs/term/agent TOGETHER, so turning
87281
+ // screen sharing on must never be implemented there.
87282
+ canServe: () => daemonGuiReady() && rdeskSharingEnabled(),
87283
+ // Read per connect (not captured once) for the same reason.
87284
+ defaultAccess: () => readRdeskServerPrefs().defaultAccess,
87285
+ // Was persisted by the settings UI and then ignored — sessions always started at 'auto'.
87286
+ defaultQuality: () => readRdeskServerPrefs().quality,
87287
+ // Each viewer costs a capture+encode window; cap the count so a runaway or hostile
87288
+ // client can't spawn them until the machine falls over. Override for a genuine
87289
+ // multi-viewer use case (training/demo) once there's a setting for it.
87290
+ maxSessions: Number(process.env.CCB_RDESK_MAX_SESSIONS ?? "3") || 3,
86999
87291
  // Phase 3 host control plane: append every lifecycle event to the audit log, and
87000
87292
  // reflect live sessions in the tray ("Sharing this screen — N viewers" + Stop all).
87001
87293
  audit: (e) => appendRdeskAudit(e),
@@ -87009,33 +87301,23 @@ async function runDaemon(opts = {}) {
87009
87301
  // joined to the SAME LiveKit room (= the rdesk sessionId) the viewer is in, so the person at the
87010
87302
  // host can talk. Reuses the team-chat/huddle conferencing; the daemon mints its own join token
87011
87303
  // with the device token (identity `${sub}#host`, distinct from the viewer's).
87012
- onCall: ({ sessionId, on }) => {
87304
+ // Voice now rides the session's own peer connection (session-manager → engine.setAv),
87305
+ // so the host no longer mints a LiveKit token or opens an SFU call window for rdesk.
87306
+ // `onCall` is just the local surface: tell the person at this machine that the viewer
87307
+ // switched voice on. LiveKit stays exactly as it is for team huddles, which are 3+ way
87308
+ // and genuinely need an SFU.
87309
+ onCall: ({ sessionId, on, viewerName }) => {
87310
+ log9(`[rdesk call] ${viewerName ?? "viewer"} ${on ? "started" : "ended"} voice on ${sessionId}`);
87013
87311
  if (!on) {
87014
87312
  closeRdeskCallWindow();
87015
87313
  return;
87016
87314
  }
87017
- void (async () => {
87018
- try {
87019
- const r = await fetch(`${c.apiOrigin.replace(/\/$/, "")}/api/core/tesseract/conferencing/token`, {
87020
- method: "POST",
87021
- headers: { "content-type": "application/json", authorization: `Bearer ${accessToken}` },
87022
- body: JSON.stringify({ room: sessionId, identity: "host", name: hostname4() }),
87023
- signal: AbortSignal.timeout(1e4)
87024
- });
87025
- if (!r.ok) {
87026
- log9(`[rdesk call] token mint ${r.status} (conferencing configured?)`);
87027
- return;
87028
- }
87029
- const j = await r.json().catch(() => ({}));
87030
- if (!j.token || !j.url) {
87031
- log9("[rdesk call] token response missing token/url");
87032
- return;
87033
- }
87034
- showRdeskCallWindow({ url: j.url, token: j.token, room: sessionId });
87035
- } catch (e) {
87036
- log9("[rdesk call] token mint failed", e instanceof Error ? e.message : String(e));
87037
- }
87038
- })();
87315
+ try {
87316
+ const NotifCtor = electronModule()?.Notification;
87317
+ if (NotifCtor)
87318
+ new NotifCtor({ title: "Remote desktop", body: `${viewerName ?? "The viewer"} turned on voice.` }).show();
87319
+ } catch {
87320
+ }
87039
87321
  },
87040
87322
  // In-session chat from the viewer → surface it to the operator as a native notification.
87041
87323
  // On macOS the notification carries an inline REPLY box (hasReply) whose text is sent
@@ -87062,6 +87344,42 @@ async function runDaemon(opts = {}) {
87062
87344
  log: (m) => log9(m)
87063
87345
  });
87064
87346
  rdeskRef = rdesk;
87347
+ try {
87348
+ if (rdeskSharingEnabled()) {
87349
+ const generated = ensureRdeskPassword();
87350
+ const prefs = readRdeskServerPrefs();
87351
+ if (generated && !prefs.noticeShownAt) {
87352
+ const id = getMachinePublicId();
87353
+ log9(`rdesk: generated an unattended password; this machine is reachable as ${id ?? "(id pending)"}`);
87354
+ try {
87355
+ const NotifCtor = electronModule()?.Notification;
87356
+ if (NotifCtor) {
87357
+ const n = new NotifCtor({
87358
+ title: "Remote Desktop is on",
87359
+ body: id ? `This machine can be reached with ID ${id} and its password. Open Settings to see it or turn it off.` : "This machine can be reached remotely. Open Settings to see its ID and password, or turn it off."
87360
+ });
87361
+ n.on("click", () => {
87362
+ void openMainAppSettings();
87363
+ });
87364
+ n.show();
87365
+ }
87366
+ } catch {
87367
+ }
87368
+ writeRdeskServerPrefs({ noticeShownAt: Date.now() });
87369
+ }
87370
+ }
87371
+ } catch (e) {
87372
+ log9(`rdesk: password/notice setup failed: ${e instanceof Error ? e.message : e}`);
87373
+ }
87374
+ let rdeskWasEnabled = rdeskSharingEnabled();
87375
+ setInterval(() => {
87376
+ const on = rdeskSharingEnabled();
87377
+ if (rdeskWasEnabled && !on) {
87378
+ log9("rdesk: sharing turned off \u2014 disconnecting live sessions");
87379
+ void rdeskRef?.disconnectAll("revoked");
87380
+ }
87381
+ rdeskWasEnabled = on;
87382
+ }, 3e4).unref?.();
87065
87383
  rdeskManager = rdesk;
87066
87384
  const chatAgent = createChatAgentRunner({
87067
87385
  apiUrl: c.apiOrigin.replace(/\/$/, ""),
@@ -87104,7 +87422,7 @@ async function runDaemon(opts = {}) {
87104
87422
  // Remote-desktop host: a viewer drives an rdesk session on this machine.
87105
87423
  onRdesk: (payload, clientId, sessionId) => {
87106
87424
  if (rdesk && clientId)
87107
- void rdesk.onClientMessage(sessionId, { clientId, sameAccount: true }, payload);
87425
+ void rdesk.onClientMessage(sessionId, { clientId }, payload);
87108
87426
  },
87109
87427
  onRdeskSignal: (payload, _clientId, sessionId) => {
87110
87428
  rdesk?.onSignal(sessionId, payload);
@@ -87112,6 +87430,27 @@ async function runDaemon(opts = {}) {
87112
87430
  // Standalone CLIENT (viewer) receive-side: forward the host's rdesk control events, WebRTC
87113
87431
  // SDP/ICE signals, and session events to the client window. No client window ⇒ no-op, so
87114
87432
  // the daemon-as-HOST path above is unchanged.
87433
+ // Someone is asking to connect to this account's machines. The daemon can't run the
87434
+ // approval UI, but it CAN say so — otherwise, with the app closed, the request reaches an
87435
+ // online host that ignores it AND the push is suppressed for exactly that reason (the
87436
+ // fan-out only pushes when nothing is online), so nobody ever hears about it.
87437
+ onRdeskRequest: (m) => {
87438
+ if (m?.type !== "rdesk:request")
87439
+ return;
87440
+ const who = typeof m.fromName === "string" ? m.fromName : "Someone";
87441
+ log9(`[rdesk request] ${who} is asking to connect`);
87442
+ try {
87443
+ const NotifCtor = electronModule()?.Notification;
87444
+ if (!NotifCtor)
87445
+ return;
87446
+ const n = new NotifCtor({ title: "Remote desktop request", body: `${who} wants to connect to your computer. Open Tesseract to answer.` });
87447
+ n.on("click", () => {
87448
+ void openMainApp();
87449
+ });
87450
+ n.show();
87451
+ } catch {
87452
+ }
87453
+ },
87115
87454
  onSessionEvent: (msg) => {
87116
87455
  getRdeskClientWebContents()?.send("session:event", msg);
87117
87456
  },
@@ -87185,17 +87524,17 @@ var CHUNK_BYTES = 256 * 1024;
87185
87524
  import { platform as platform3 } from "node:os";
87186
87525
 
87187
87526
  // ../../packages/host-core/dist/service/launchd.js
87188
- import { writeFileSync as writeFileSync27, existsSync as existsSync30, mkdirSync as mkdirSync28, unlinkSync as unlinkSync5 } from "node:fs";
87527
+ import { writeFileSync as writeFileSync28, existsSync as existsSync30, mkdirSync as mkdirSync28, unlinkSync as unlinkSync5 } from "node:fs";
87189
87528
  import { dirname as dirname24, join as join56 } from "node:path";
87190
87529
  import { homedir as homedir8, userInfo } from "node:os";
87191
87530
 
87192
87531
  // ../../packages/host-core/dist/service/exec.js
87193
- import { execFile as execFile7, spawn as spawn10 } from "node:child_process";
87532
+ import { execFile as execFile7, spawn as spawn11 } from "node:child_process";
87194
87533
  import { promisify as promisify6 } from "node:util";
87195
87534
  var run3 = promisify6(execFile7);
87196
87535
  function runInherit(cmd, args) {
87197
87536
  return new Promise((resolve14) => {
87198
- const p = spawn10(cmd, args, { stdio: "inherit" });
87537
+ const p = spawn11(cmd, args, { stdio: "inherit" });
87199
87538
  p.on("error", () => resolve14());
87200
87539
  p.on("close", () => resolve14());
87201
87540
  });
@@ -87261,7 +87600,7 @@ ${env}
87261
87600
  mkdirSync28(workDirOf(spec), { recursive: true });
87262
87601
  mkdirSync28(dirname24(plistPath()), { recursive: true });
87263
87602
  mkdirSync28(dirname24(spec.logPath), { recursive: true });
87264
- writeFileSync27(plistPath(), buildPlist(spec));
87603
+ writeFileSync28(plistPath(), buildPlist(spec));
87265
87604
  await run3("launchctl", ["bootout", serviceTarget()]).catch(() => {
87266
87605
  });
87267
87606
  await run3("launchctl", ["bootstrap", domainTarget(), plistPath()]);
@@ -87311,7 +87650,7 @@ ${env}
87311
87650
  }
87312
87651
 
87313
87652
  // ../../packages/host-core/dist/service/systemd.js
87314
- import { writeFileSync as writeFileSync28, existsSync as existsSync31, mkdirSync as mkdirSync29, unlinkSync as unlinkSync6 } from "node:fs";
87653
+ import { writeFileSync as writeFileSync29, existsSync as existsSync31, mkdirSync as mkdirSync29, unlinkSync as unlinkSync6 } from "node:fs";
87315
87654
  import { dirname as dirname25, join as join57 } from "node:path";
87316
87655
  import { homedir as homedir9, userInfo as userInfo2 } from "node:os";
87317
87656
  function unitVal(v) {
@@ -87354,7 +87693,7 @@ WantedBy=default.target
87354
87693
  mkdirSync29(workDirOf(spec), { recursive: true });
87355
87694
  mkdirSync29(dirname25(unitPath()), { recursive: true });
87356
87695
  mkdirSync29(dirname25(spec.logPath), { recursive: true });
87357
- writeFileSync28(unitPath(), buildUnit(spec));
87696
+ writeFileSync29(unitPath(), buildUnit(spec));
87358
87697
  await run3("loginctl", ["enable-linger", userInfo2().username]).catch(() => {
87359
87698
  });
87360
87699
  await run3("systemctl", ["--user", "daemon-reload"]).catch(() => {
@@ -87397,7 +87736,7 @@ WantedBy=default.target
87397
87736
  }
87398
87737
 
87399
87738
  // ../../packages/host-core/dist/service/windows.js
87400
- import { writeFileSync as writeFileSync29, existsSync as existsSync32, mkdirSync as mkdirSync30, unlinkSync as unlinkSync7 } from "node:fs";
87739
+ import { writeFileSync as writeFileSync30, existsSync as existsSync32, mkdirSync as mkdirSync30, unlinkSync as unlinkSync7 } from "node:fs";
87401
87740
  import { dirname as dirname26, join as join58 } from "node:path";
87402
87741
  function makeWindowsTask(instanceId) {
87403
87742
  const task = `Fineguide\\TesseractHost${instanceId ? `-${instanceId}` : ""}`;
@@ -87421,7 +87760,7 @@ start "" /b ${cmd} >> "${oneLine2(spec.logPath).replace(/"/g, "")}" 2>&1\r
87421
87760
  mkdirSync30(spec.workingDirectory ?? daemonDir2(), { recursive: true });
87422
87761
  mkdirSync30(dirname26(spec.logPath), { recursive: true });
87423
87762
  mkdirSync30(dirname26(wrapperPath()), { recursive: true });
87424
- writeFileSync29(wrapperPath(), buildWrapper(spec));
87763
+ writeFileSync30(wrapperPath(), buildWrapper(spec));
87425
87764
  await run3("schtasks", ["/Create", "/TN", task, "/SC", "ONLOGON", "/RL", "LIMITED", "/F", "/TR", `"${wrapperPath()}"`]);
87426
87765
  await run3("schtasks", ["/Run", "/TN", task]).catch(() => {
87427
87766
  });
@@ -87709,7 +88048,7 @@ function whoami() {
87709
88048
  }
87710
88049
 
87711
88050
  // src/self-update.ts
87712
- import { spawn as spawn11 } from "node:child_process";
88051
+ import { spawn as spawn12 } from "node:child_process";
87713
88052
  import { createPublicKey, verify as cryptoVerify } from "node:crypto";
87714
88053
  var RESTART_EXIT_CODE = 75;
87715
88054
  var REGISTRY_BASE = "https://registry.npmjs.org/tesseract-agent";
@@ -87784,7 +88123,7 @@ function installVersion(target) {
87784
88123
  log8(`installing tesseract-agent@${target} \u2026`);
87785
88124
  let child;
87786
88125
  try {
87787
- child = spawn11(npm, ["install", "-g", `tesseract-agent@${target}`], { stdio: ["ignore", "inherit", "inherit"] });
88126
+ child = spawn12(npm, ["install", "-g", `tesseract-agent@${target}`], { stdio: ["ignore", "inherit", "inherit"] });
87788
88127
  } catch (e) {
87789
88128
  log8("npm spawn threw:", e instanceof Error ? e.message : String(e));
87790
88129
  resolve14(false);
@@ -87849,7 +88188,7 @@ function startSelfUpdater(current) {
87849
88188
  // src/service.ts
87850
88189
  import { join as join60 } from "node:path";
87851
88190
  import { platform as platform4 } from "node:os";
87852
- import { existsSync as existsSync34, readFileSync as readFileSync32 } from "node:fs";
88191
+ import { existsSync as existsSync34, readFileSync as readFileSync33 } from "node:fs";
87853
88192
  function controller(instanceId) {
87854
88193
  const c = getServiceController(instanceId);
87855
88194
  if (!c) {
@@ -87877,7 +88216,7 @@ function runningDaemonInfo() {
87877
88216
  const lock = join60(daemonDir2(), "daemon.lock");
87878
88217
  try {
87879
88218
  if (!existsSync34(lock)) return null;
87880
- const pid = Number(readFileSync32(lock, "utf-8").trim());
88219
+ const pid = Number(readFileSync33(lock, "utf-8").trim());
87881
88220
  if (!pid || !isAlive(pid)) return null;
87882
88221
  return { pid, device: readCredentials()?.deviceId };
87883
88222
  } catch {
@@ -87911,7 +88250,7 @@ async function logs(instanceId, follow) {
87911
88250
  }
87912
88251
 
87913
88252
  // src/net.ts
87914
- import { readFileSync as readFileSync34 } from "node:fs";
88253
+ import { readFileSync as readFileSync35 } from "node:fs";
87915
88254
  import { resolve as resolve12 } from "node:path";
87916
88255
  function usage() {
87917
88256
  console.log(`Usage:
@@ -87934,7 +88273,7 @@ function readOrDie(label, p) {
87934
88273
  process.exit(1);
87935
88274
  }
87936
88275
  try {
87937
- return new Uint8Array(readFileSync34(resolve12(p)));
88276
+ return new Uint8Array(readFileSync35(resolve12(p)));
87938
88277
  } catch (e) {
87939
88278
  console.error(`net enroll: could not read --${label} ${p}: ${e instanceof Error ? e.message : e}`);
87940
88279
  process.exit(1);
@@ -88037,7 +88376,7 @@ async function netCommand(rest, flags) {
88037
88376
  function pkgVersion() {
88038
88377
  try {
88039
88378
  const p = join63(dirname27(fileURLToPath4(import.meta.url)), "..", "package.json");
88040
- return JSON.parse(readFileSync35(p, "utf8")).version ?? "0.0.0";
88379
+ return JSON.parse(readFileSync36(p, "utf8")).version ?? "0.0.0";
88041
88380
  } catch {
88042
88381
  return "0.0.0";
88043
88382
  }
@@ -88093,7 +88432,7 @@ async function superviseDaemon(cliEntry) {
88093
88432
  let stopping3 = false;
88094
88433
  let child = null;
88095
88434
  const spawnWorker = () => {
88096
- const c = spawn12(process.execPath, [cliEntry, "__worker"], { stdio: "inherit", env: process.env });
88435
+ const c = spawn13(process.execPath, [cliEntry, "__worker"], { stdio: "inherit", env: process.env });
88097
88436
  child = c;
88098
88437
  let settled = false;
88099
88438
  const settle = (fn) => {
@@ -88242,7 +88581,7 @@ async function captureClaudeSubscription(flags) {
88242
88581
  console.log("Open the authorization URL in any browser; paste the returned code here if Claude asks for it.");
88243
88582
  try {
88244
88583
  const code = await new Promise((resolveCode, reject) => {
88245
- const child = spawn12(binary, args, {
88584
+ const child = spawn13(binary, args, {
88246
88585
  env: { ...process.env, CLAUDE_CONFIG_DIR: home },
88247
88586
  stdio: "inherit"
88248
88587
  });
@@ -88251,7 +88590,7 @@ async function captureClaudeSubscription(flags) {
88251
88590
  });
88252
88591
  if (code !== 0) throw new Error(`Claude sign-in did not complete (exit ${code}).`);
88253
88592
  const file2 = join63(home, ".credentials.json");
88254
- if (existsSync36(file2)) return readFileSync35(file2, "utf8");
88593
+ if (existsSync36(file2)) return readFileSync36(file2, "utf8");
88255
88594
  if (process.platform === "darwin") {
88256
88595
  const value = execFileSync5("security", [
88257
88596
  "find-generic-password",
@@ -88333,7 +88672,7 @@ async function runnerCommand(rest, flags) {
88333
88672
  process.exit(1);
88334
88673
  }
88335
88674
  const oauthFilePath = typeof flags["oauth-file"] === "string" ? resolve13(flags["oauth-file"]) : void 0;
88336
- const oauthAuthFile = oauthFilePath ? readFileSync35(oauthFilePath, "utf8") : void 0;
88675
+ const oauthAuthFile = oauthFilePath ? readFileSync36(oauthFilePath, "utf8") : void 0;
88337
88676
  if (oauthAuthFile) {
88338
88677
  try {
88339
88678
  JSON.parse(oauthAuthFile);