whipdesk 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -0
- package/dist/agent.cjs +267 -69
- package/dist/mobile-web/assets/index-CaqF5am7.css +1 -0
- package/dist/mobile-web/assets/index-D-eJUnpy.js +2 -0
- package/dist/mobile-web/assets/index.esm-DZJXVPeZ.js +33 -0
- package/dist/mobile-web/index.html +2 -2
- package/package.json +1 -1
- package/dist/mobile-web/assets/index-C_O8Rc7r.css +0 -1
- package/dist/mobile-web/assets/index-_PAhuG6R.js +0 -2
- package/dist/mobile-web/assets/index.esm-wogdVWd2.js +0 -30
package/dist/agent.cjs
CHANGED
|
@@ -27641,7 +27641,7 @@ var require_websocket_server = __commonJS({
|
|
|
27641
27641
|
|
|
27642
27642
|
// src/index.ts
|
|
27643
27643
|
var import_node_readline2 = require("node:readline");
|
|
27644
|
-
var
|
|
27644
|
+
var import_node_os9 = require("node:os");
|
|
27645
27645
|
|
|
27646
27646
|
// src/config.ts
|
|
27647
27647
|
var import_node_crypto = require("node:crypto");
|
|
@@ -27693,7 +27693,7 @@ function isPackaged() {
|
|
|
27693
27693
|
}
|
|
27694
27694
|
|
|
27695
27695
|
// src/version.ts
|
|
27696
|
-
var AGENT_VERSION = "0.1.
|
|
27696
|
+
var AGENT_VERSION = "0.1.3";
|
|
27697
27697
|
|
|
27698
27698
|
// src/config.ts
|
|
27699
27699
|
var here = (0, import_node_path2.dirname)((0, import_node_url2.fileURLToPath)(__whipdesk_meta_url));
|
|
@@ -28274,12 +28274,20 @@ async function readNsScreens() {
|
|
|
28274
28274
|
return null;
|
|
28275
28275
|
}
|
|
28276
28276
|
}
|
|
28277
|
+
function friendlyDisplayName(raw, index) {
|
|
28278
|
+
const s = String(raw ?? "").trim();
|
|
28279
|
+
if (!s) return `Display ${index + 1}`;
|
|
28280
|
+
const m = /DISPLAY(\d+)/i.exec(s);
|
|
28281
|
+
if (m) return `Display ${m[1]}`;
|
|
28282
|
+
if (/^\\\\/.test(s)) return `Display ${index + 1}`;
|
|
28283
|
+
return s;
|
|
28284
|
+
}
|
|
28277
28285
|
async function listBaseDisplays() {
|
|
28278
28286
|
try {
|
|
28279
28287
|
const displays = await import_screenshot_desktop.default.listDisplays();
|
|
28280
28288
|
return displays.map((d, index) => ({
|
|
28281
28289
|
id: typeof d.id === "number" ? d.id : index,
|
|
28282
|
-
name:
|
|
28290
|
+
name: friendlyDisplayName(d.name, index),
|
|
28283
28291
|
primary: Boolean(d.primary) || index === 0
|
|
28284
28292
|
}));
|
|
28285
28293
|
} catch (error) {
|
|
@@ -28446,7 +28454,7 @@ function buildWelcome(ctx) {
|
|
|
28446
28454
|
return {
|
|
28447
28455
|
type: "welcome",
|
|
28448
28456
|
protocol: PROTOCOL_VERSION,
|
|
28449
|
-
agent: { version: AGENT_VERSION, platform: (0, import_node_os2.platform)(), hostname: (0, import_node_os2.hostname)() },
|
|
28457
|
+
agent: { version: AGENT_VERSION, platform: (0, import_node_os2.platform)(), hostname: (0, import_node_os2.hostname)(), hdr: ctx.hdrActive || void 0 },
|
|
28450
28458
|
screen: ctx.screen,
|
|
28451
28459
|
capture: { fps: ctx.config.fps, quality: ctx.config.quality, maxWidth: ctx.config.maxWidth },
|
|
28452
28460
|
capabilities: {
|
|
@@ -28541,11 +28549,96 @@ async function dispatch(ctx, msg, controller) {
|
|
|
28541
28549
|
}
|
|
28542
28550
|
|
|
28543
28551
|
// src/capture/encoder.ts
|
|
28544
|
-
var
|
|
28552
|
+
var import_node_child_process3 = require("node:child_process");
|
|
28545
28553
|
var import_node_dgram = require("node:dgram");
|
|
28546
28554
|
var import_node_util2 = require("node:util");
|
|
28547
28555
|
var import_ffmpeg_static = __toESM(require("ffmpeg-static"), 1);
|
|
28548
|
-
|
|
28556
|
+
|
|
28557
|
+
// src/capture/hdr-win.ts
|
|
28558
|
+
var import_node_child_process2 = require("node:child_process");
|
|
28559
|
+
var cached = null;
|
|
28560
|
+
var inflight = null;
|
|
28561
|
+
function invalidateHdrCache() {
|
|
28562
|
+
cached = null;
|
|
28563
|
+
}
|
|
28564
|
+
async function windowsHdrState(maxAgeMs = 15e3) {
|
|
28565
|
+
if (process.platform !== "win32") return null;
|
|
28566
|
+
if (cached && Date.now() - cached.at < maxAgeMs) return cached.state;
|
|
28567
|
+
if (inflight) return inflight;
|
|
28568
|
+
inflight = probe().then((state) => {
|
|
28569
|
+
if (state) cached = { state, at: Date.now() };
|
|
28570
|
+
return state;
|
|
28571
|
+
}).finally(() => {
|
|
28572
|
+
inflight = null;
|
|
28573
|
+
});
|
|
28574
|
+
return inflight;
|
|
28575
|
+
}
|
|
28576
|
+
async function probe() {
|
|
28577
|
+
return new Promise((resolve) => {
|
|
28578
|
+
(0, import_node_child_process2.execFile)(
|
|
28579
|
+
"powershell",
|
|
28580
|
+
["-NoProfile", "-NonInteractive", "-WindowStyle", "Hidden", "-Command", PROBE_PS],
|
|
28581
|
+
{ timeout: 8e3, windowsHide: true },
|
|
28582
|
+
(err, stdout) => {
|
|
28583
|
+
if (err) {
|
|
28584
|
+
log.debug("hdr probe failed:", err.message);
|
|
28585
|
+
return resolve(null);
|
|
28586
|
+
}
|
|
28587
|
+
let supported = false;
|
|
28588
|
+
let active = false;
|
|
28589
|
+
let sawAny = false;
|
|
28590
|
+
for (const line of String(stdout).split(/\r?\n/)) {
|
|
28591
|
+
const m = /^([01]) ([01])$/.exec(line.trim());
|
|
28592
|
+
if (!m) continue;
|
|
28593
|
+
sawAny = true;
|
|
28594
|
+
if (m[1] === "1") supported = true;
|
|
28595
|
+
if (m[2] === "1") active = true;
|
|
28596
|
+
}
|
|
28597
|
+
resolve(sawAny ? { supported, active } : null);
|
|
28598
|
+
}
|
|
28599
|
+
);
|
|
28600
|
+
});
|
|
28601
|
+
}
|
|
28602
|
+
var PROBE_PS = `
|
|
28603
|
+
$src = @'
|
|
28604
|
+
using System;
|
|
28605
|
+
using System.Runtime.InteropServices;
|
|
28606
|
+
public static class WdHdr {
|
|
28607
|
+
[StructLayout(LayoutKind.Sequential)] public struct LUID { public uint Low; public int High; }
|
|
28608
|
+
[StructLayout(LayoutKind.Sequential)] public struct PATH_SOURCE { public LUID adapterId; public uint id; public uint modeInfoIdx; public uint statusFlags; }
|
|
28609
|
+
[StructLayout(LayoutKind.Sequential)] public struct RATIONAL { public uint num; public uint den; }
|
|
28610
|
+
[StructLayout(LayoutKind.Sequential)] public struct PATH_TARGET { public LUID adapterId; public uint id; public uint modeInfoIdx; public uint outputTechnology; public uint rotation; public uint scaling; public RATIONAL refreshRate; public uint scanLineOrdering; public int targetAvailable; public uint statusFlags; }
|
|
28611
|
+
[StructLayout(LayoutKind.Sequential)] public struct PATH_INFO { public PATH_SOURCE sourceInfo; public PATH_TARGET targetInfo; public uint flags; }
|
|
28612
|
+
[StructLayout(LayoutKind.Sequential, Size = 64)] public struct MODE_INFO { public uint infoType; public uint id; public LUID adapterId; }
|
|
28613
|
+
[StructLayout(LayoutKind.Sequential)] public struct DEVICE_INFO_HEADER { public uint type; public uint size; public LUID adapterId; public uint id; }
|
|
28614
|
+
[StructLayout(LayoutKind.Sequential)] public struct GET_ADVANCED_COLOR_INFO { public DEVICE_INFO_HEADER header; public uint value; public uint colorEncoding; public uint bitsPerColorChannel; }
|
|
28615
|
+
[DllImport("user32.dll")] public static extern int GetDisplayConfigBufferSizes(uint flags, out uint numPaths, out uint numModes);
|
|
28616
|
+
[DllImport("user32.dll")] public static extern int QueryDisplayConfig(uint flags, ref uint numPaths, [Out] PATH_INFO[] paths, ref uint numModes, [Out] MODE_INFO[] modes, IntPtr topologyId);
|
|
28617
|
+
[DllImport("user32.dll")] public static extern int DisplayConfigGetDeviceInfo(ref GET_ADVANCED_COLOR_INFO info);
|
|
28618
|
+
public static void Probe() {
|
|
28619
|
+
uint np, nm;
|
|
28620
|
+
if (GetDisplayConfigBufferSizes(2, out np, out nm) != 0) return;
|
|
28621
|
+
var paths = new PATH_INFO[np];
|
|
28622
|
+
var modes = new MODE_INFO[nm];
|
|
28623
|
+
if (QueryDisplayConfig(2, ref np, paths, ref nm, modes, IntPtr.Zero) != 0) return;
|
|
28624
|
+
for (int i = 0; i < np; i++) {
|
|
28625
|
+
var q = new GET_ADVANCED_COLOR_INFO();
|
|
28626
|
+
q.header.type = 9;
|
|
28627
|
+
q.header.size = (uint)Marshal.SizeOf(typeof(GET_ADVANCED_COLOR_INFO));
|
|
28628
|
+
q.header.adapterId = paths[i].targetInfo.adapterId;
|
|
28629
|
+
q.header.id = paths[i].targetInfo.id;
|
|
28630
|
+
if (DisplayConfigGetDeviceInfo(ref q) == 0)
|
|
28631
|
+
Console.WriteLine(((q.value & 1) != 0 ? "1" : "0") + " " + ((q.value & 2) != 0 ? "1" : "0"));
|
|
28632
|
+
}
|
|
28633
|
+
}
|
|
28634
|
+
}
|
|
28635
|
+
'@
|
|
28636
|
+
Add-Type -TypeDefinition $src -Language CSharp
|
|
28637
|
+
[WdHdr]::Probe()
|
|
28638
|
+
`.trim();
|
|
28639
|
+
|
|
28640
|
+
// src/capture/encoder.ts
|
|
28641
|
+
var exec2 = (0, import_node_util2.promisify)(import_node_child_process3.execFile);
|
|
28549
28642
|
var VIDEO_PAYLOAD_TYPE = 96;
|
|
28550
28643
|
var VIDEO_CLOCK_RATE = 9e4;
|
|
28551
28644
|
var encoderName;
|
|
@@ -28597,29 +28690,55 @@ async function avScreenIndex(displayId) {
|
|
|
28597
28690
|
const map = await avScreenMapPromise;
|
|
28598
28691
|
return map.get(displayId) ?? map.get(0) ?? null;
|
|
28599
28692
|
}
|
|
28600
|
-
|
|
28693
|
+
var HDR_TONEMAP = ",tonemap=tonemap=hable:desat=0:peak=10,zscale=tin=linear:pin=bt709:t=bt709:p=bt709:m=bt709:r=tv,format=yuv420p";
|
|
28694
|
+
async function captureDeviceFor(displayIndex, fps, winFallback) {
|
|
28601
28695
|
const r = String(Math.max(1, Math.min(60, Math.round(fps))));
|
|
28696
|
+
const ts2 = ["-fflags", "+genpts", "-use_wallclock_as_timestamps", "1"];
|
|
28602
28697
|
if (process.platform === "darwin") {
|
|
28603
28698
|
const idx = await avScreenIndex(displayIndex);
|
|
28604
28699
|
if (idx == null) return null;
|
|
28605
|
-
return
|
|
28700
|
+
return {
|
|
28701
|
+
inputArgs: [...ts2, "-f", "avfoundation", "-capture_cursor", "1", "-pixel_format", "nv12", "-framerate", r, "-i", `${idx}:none`],
|
|
28702
|
+
hwPrefix: "",
|
|
28703
|
+
postFilter: ""
|
|
28704
|
+
};
|
|
28606
28705
|
}
|
|
28607
28706
|
if (process.platform === "win32") {
|
|
28608
|
-
|
|
28707
|
+
if (winFallback) {
|
|
28708
|
+
return { inputArgs: [...ts2, "-f", "gdigrab", "-framerate", r, "-i", "desktop"], hwPrefix: "", postFilter: "" };
|
|
28709
|
+
}
|
|
28710
|
+
const idx = Math.max(0, Math.round(displayIndex));
|
|
28711
|
+
const hdr = (await windowsHdrState())?.active === true;
|
|
28712
|
+
if (hdr) {
|
|
28713
|
+
return {
|
|
28714
|
+
inputArgs: ["-f", "lavfi", "-i", `ddagrab=output_idx=${idx}:framerate=${r}:draw_mouse=1:output_fmt=rgbaf16`],
|
|
28715
|
+
// gbrpf32le BEFORE the split/crop/scale: swscale handles planar-float scaling everywhere,
|
|
28716
|
+
// while direct rgbaf16 scaling is spottier across builds. Tone-mapping itself stays
|
|
28717
|
+
// per-branch AFTER the scale (postFilter) so the heavy math runs at ≤maxWidth, not 4K.
|
|
28718
|
+
hwPrefix: "hwdownload,format=rgbaf16,format=gbrpf32le,",
|
|
28719
|
+
postFilter: HDR_TONEMAP,
|
|
28720
|
+
winHdr: true
|
|
28721
|
+
};
|
|
28722
|
+
}
|
|
28723
|
+
return {
|
|
28724
|
+
inputArgs: ["-f", "lavfi", "-i", `ddagrab=output_idx=${idx}:framerate=${r}:draw_mouse=1`],
|
|
28725
|
+
hwPrefix: "hwdownload,format=bgra,",
|
|
28726
|
+
postFilter: ""
|
|
28727
|
+
};
|
|
28609
28728
|
}
|
|
28610
28729
|
if (process.platform === "linux") {
|
|
28611
|
-
return ["-f", "x11grab", "-framerate", r, "-i", process.env.DISPLAY || ":0.0"];
|
|
28730
|
+
return { inputArgs: [...ts2, "-f", "x11grab", "-framerate", r, "-i", process.env.DISPLAY || ":0.0"], hwPrefix: "", postFilter: "" };
|
|
28612
28731
|
}
|
|
28613
28732
|
return null;
|
|
28614
28733
|
}
|
|
28615
28734
|
function videoFilter(crop, maxWidth) {
|
|
28616
|
-
const scale = `scale=min(${Math.round(maxWidth)}\\,iw):-2`;
|
|
28735
|
+
const scale = `scale=trunc(min(${Math.round(maxWidth)}\\,iw)/2)*2:-2`;
|
|
28617
28736
|
if (isFull(crop)) return scale;
|
|
28618
28737
|
const c = crop;
|
|
28619
28738
|
return `crop=iw*${c.w.toFixed(4)}:ih*${c.h.toFixed(4)}:iw*${c.x.toFixed(4)}:ih*${c.y.toFixed(4)},${scale}`;
|
|
28620
28739
|
}
|
|
28621
28740
|
function overviewFilter(ov) {
|
|
28622
|
-
return `scale=min(${Math.round(ov.width)}\\,iw):-2,fps=${Math.max(1, Math.round(ov.fps))}`;
|
|
28741
|
+
return `scale=trunc(min(${Math.round(ov.width)}\\,iw)/2)*2:-2,fps=${Math.max(1, Math.round(ov.fps))}`;
|
|
28623
28742
|
}
|
|
28624
28743
|
function sameCrop(a, b) {
|
|
28625
28744
|
if (isFull(a) && isFull(b)) return true;
|
|
@@ -28663,6 +28782,8 @@ var ScreenCaptureSession = class _ScreenCaptureSession {
|
|
|
28663
28782
|
restarting = false;
|
|
28664
28783
|
/** Config the running ffmpeg was built from; lets a restart detect that `cfg` moved during it. */
|
|
28665
28784
|
appliedCfg = null;
|
|
28785
|
+
/** Windows only: set once ddagrab fails to produce a frame, switching capture to gdigrab. */
|
|
28786
|
+
winCaptureFallback = false;
|
|
28666
28787
|
/** Don't declare a not-yet-producing capture dead until it's had this long to start up. */
|
|
28667
28788
|
static FIRST_FRAME_GRACE_MS = 6e3;
|
|
28668
28789
|
/** Once it HAS produced frames, restart if it goes silent this long (display sleep, etc.). */
|
|
@@ -28702,7 +28823,7 @@ var ScreenCaptureSession = class _ScreenCaptureSession {
|
|
|
28702
28823
|
if (this.stopped) return false;
|
|
28703
28824
|
const cfg = this.cfg;
|
|
28704
28825
|
const enc = await pickH264Encoder();
|
|
28705
|
-
const capture = await captureDeviceFor(cfg.displayIndex, cfg.fps);
|
|
28826
|
+
const capture = await captureDeviceFor(cfg.displayIndex, cfg.fps, this.winCaptureFallback);
|
|
28706
28827
|
if (!enc || !import_ffmpeg_static.default || !capture) {
|
|
28707
28828
|
if (!capture) log.warn("no direct screen-capture device on this platform \u2014 screen sharing unavailable");
|
|
28708
28829
|
this.fail();
|
|
@@ -28739,7 +28860,7 @@ var ScreenCaptureSession = class _ScreenCaptureSession {
|
|
|
28739
28860
|
const port = sock.address().port;
|
|
28740
28861
|
const portOv = sockOv ? sockOv.address().port : 0;
|
|
28741
28862
|
const preset = enc === "libx264" ? ["-preset", "ultrafast", "-tune", "zerolatency"] : ["-realtime", "1"];
|
|
28742
|
-
const input = ["-hide_banner", "-loglevel", "error",
|
|
28863
|
+
const input = ["-hide_banner", "-loglevel", "error", ...capture.inputArgs, "-an"];
|
|
28743
28864
|
const h264Out = (kbps, gop, ssrc, outPort) => [
|
|
28744
28865
|
"-fps_mode",
|
|
28745
28866
|
"vfr",
|
|
@@ -28766,26 +28887,30 @@ var ScreenCaptureSession = class _ScreenCaptureSession {
|
|
|
28766
28887
|
String(VIDEO_PAYLOAD_TYPE),
|
|
28767
28888
|
"-ssrc",
|
|
28768
28889
|
ssrc,
|
|
28890
|
+
// buffer_size = SO_SNDBUF: keyframe bursts must not overflow the send side either (the
|
|
28891
|
+
// receive side is widened in bindUdp — see the black-bottom-of-keyframe note there).
|
|
28769
28892
|
"-f",
|
|
28770
28893
|
"rtp",
|
|
28771
|
-
`rtp://127.0.0.1:${outPort}?pkt_size=1200`
|
|
28894
|
+
`rtp://127.0.0.1:${outPort}?pkt_size=1200&buffer_size=1048576`
|
|
28772
28895
|
];
|
|
28773
28896
|
const mainGop = Math.max(10, Math.round(cfg.fps));
|
|
28774
28897
|
const args = ov ? [
|
|
28775
28898
|
...input,
|
|
28776
28899
|
// The full captured frame fans into two encodes: [m] the sharp crop, [o] the small overview.
|
|
28900
|
+
// hwPrefix (ddagrab) downloads once, before the split, so both branches get RAM frames.
|
|
28901
|
+
// postFilter (HDR tone map) runs per-branch AFTER the scale — cheap at output size.
|
|
28777
28902
|
"-filter_complex",
|
|
28778
|
-
`[0:v]split=2[m][o];[m]${videoFilter(cfg.crop, cfg.maxWidth)}[mainout];[o]${overviewFilter(ov)}[ovout]`,
|
|
28903
|
+
`[0:v]${capture.hwPrefix}split=2[m][o];[m]${videoFilter(cfg.crop, cfg.maxWidth)}${capture.postFilter}[mainout];[o]${overviewFilter(ov)}${capture.postFilter}[ovout]`,
|
|
28779
28904
|
"-map",
|
|
28780
28905
|
"[mainout]",
|
|
28781
28906
|
...h264Out(cfg.kbps, mainGop, "1", port),
|
|
28782
28907
|
"-map",
|
|
28783
28908
|
"[ovout]",
|
|
28784
28909
|
...h264Out(ov.kbps, Math.max(1, Math.round(ov.fps)), "2", portOv)
|
|
28785
|
-
] : [...input, "-vf", videoFilter(cfg.crop, cfg.maxWidth)
|
|
28910
|
+
] : [...input, "-vf", `${capture.hwPrefix}${videoFilter(cfg.crop, cfg.maxWidth)}${capture.postFilter}`, ...h264Out(cfg.kbps, mainGop, "1", port)];
|
|
28786
28911
|
let proc;
|
|
28787
28912
|
try {
|
|
28788
|
-
proc = (0,
|
|
28913
|
+
proc = (0, import_node_child_process3.spawn)(import_ffmpeg_static.default, args, { stdio: ["ignore", "ignore", "pipe"] });
|
|
28789
28914
|
} catch (error) {
|
|
28790
28915
|
log.warn("video encoder failed to start:", error.message);
|
|
28791
28916
|
closeQuietly(sock);
|
|
@@ -28826,17 +28951,26 @@ var ScreenCaptureSession = class _ScreenCaptureSession {
|
|
|
28826
28951
|
for (const line of d.toString().split("\n")) {
|
|
28827
28952
|
const t = line.trim();
|
|
28828
28953
|
if (!t || /^objc\[|NSKVONotifying|not linked into application/.test(t)) continue;
|
|
28954
|
+
if (process.platform === "win32" && /AcquireNextFrame failed|Output parameters changed|Requested output format unavailable/i.test(t)) {
|
|
28955
|
+
invalidateHdrCache();
|
|
28956
|
+
}
|
|
28829
28957
|
log.debug("ffmpeg:", t.slice(0, 200));
|
|
28830
28958
|
}
|
|
28831
28959
|
});
|
|
28832
28960
|
proc.on("error", (e) => log.warn("video encoder process error:", e.message));
|
|
28833
28961
|
proc.on("exit", (code, sig) => {
|
|
28834
|
-
if (this.proc
|
|
28962
|
+
if (this.proc !== proc || this.stopped) return;
|
|
28963
|
+
log.debug(`ffmpeg exited unexpectedly (${code ?? sig ?? "?"})`);
|
|
28964
|
+
const delay2 = Date.now() - this.spawnAt < 2e3 ? 2e3 : 400;
|
|
28965
|
+
const t = setTimeout(() => {
|
|
28966
|
+
if (this.proc === proc && !this.stopped) void this.restart();
|
|
28967
|
+
}, delay2);
|
|
28968
|
+
t.unref?.();
|
|
28835
28969
|
});
|
|
28836
28970
|
this.startHealthMonitor();
|
|
28837
28971
|
const cropDesc = isFull(cfg.crop) ? "full desktop" : `zoomed ${cfg.crop.x.toFixed(2)},${cfg.crop.y.toFixed(2)} ${cfg.crop.w.toFixed(2)}x${cfg.crop.h.toFixed(2)}`;
|
|
28838
28972
|
log.debug(
|
|
28839
|
-
`video: H.264 capture started (${enc}, ${kbpsArg(cfg.kbps)}@${cfg.fps}fps, ${cropDesc}${ov ? " + overview" : ""})`
|
|
28973
|
+
`video: H.264 capture started (${enc}, ${kbpsArg(cfg.kbps)}@${cfg.fps}fps, ${cropDesc}${ov ? " + overview" : ""}${capture.winHdr ? ", HDR desktop tone-mapped to SDR" : ""})`
|
|
28840
28974
|
);
|
|
28841
28975
|
return true;
|
|
28842
28976
|
}
|
|
@@ -28861,6 +28995,12 @@ var ScreenCaptureSession = class _ScreenCaptureSession {
|
|
|
28861
28995
|
return;
|
|
28862
28996
|
}
|
|
28863
28997
|
if (now - this.spawnAt < _ScreenCaptureSession.FIRST_FRAME_GRACE_MS) return;
|
|
28998
|
+
if (process.platform === "win32" && !this.winCaptureFallback) {
|
|
28999
|
+
this.winCaptureFallback = true;
|
|
29000
|
+
log.debug("screen capture: ddagrab produced no frame \u2014 falling back to gdigrab");
|
|
29001
|
+
void this.restart();
|
|
29002
|
+
return;
|
|
29003
|
+
}
|
|
28864
29004
|
if (++this.deadRestarts > _ScreenCaptureSession.MAX_DEAD_RESTARTS) {
|
|
28865
29005
|
this.kill();
|
|
28866
29006
|
this.fail();
|
|
@@ -28973,6 +29113,10 @@ function bindUdp() {
|
|
|
28973
29113
|
sock.once("error", reject);
|
|
28974
29114
|
sock.bind(0, "127.0.0.1", () => {
|
|
28975
29115
|
sock.removeListener("error", reject);
|
|
29116
|
+
try {
|
|
29117
|
+
sock.setRecvBufferSize(4 * 1024 * 1024);
|
|
29118
|
+
} catch {
|
|
29119
|
+
}
|
|
28976
29120
|
resolve(sock);
|
|
28977
29121
|
});
|
|
28978
29122
|
});
|
|
@@ -29251,9 +29395,9 @@ async function answerWebRtcOffer(ctx, offerSdp, onClosed, opts = {}) {
|
|
|
29251
29395
|
}
|
|
29252
29396
|
};
|
|
29253
29397
|
(isOverview ? overviewSinks : sinks).push(sink);
|
|
29254
|
-
const
|
|
29255
|
-
if (authenticated)
|
|
29256
|
-
else pendingAttach.push(
|
|
29398
|
+
const run3 = () => void (isOverview ? ctx.video.attachOverview(sink) : ctx.video.attach(sink));
|
|
29399
|
+
if (authenticated) run3();
|
|
29400
|
+
else pendingAttach.push(run3);
|
|
29257
29401
|
} catch (error) {
|
|
29258
29402
|
log.warn(`video track ${i} setup failed:`, error.message);
|
|
29259
29403
|
}
|
|
@@ -29445,6 +29589,9 @@ function printSetupReminder() {
|
|
|
29445
29589
|
console.log(" Setup / permissions:");
|
|
29446
29590
|
for (const line of lines) console.log(line);
|
|
29447
29591
|
console.log("");
|
|
29592
|
+
console.log(" Troubleshooting: relaunch with --verbose (e.g. `whipdesk --verbose`) for detailed");
|
|
29593
|
+
console.log(" capture/network logs \u2014 attach them when reporting an issue.");
|
|
29594
|
+
console.log("");
|
|
29448
29595
|
}
|
|
29449
29596
|
|
|
29450
29597
|
// src/server.ts
|
|
@@ -29509,12 +29656,12 @@ var ScreenCapturer = class {
|
|
|
29509
29656
|
};
|
|
29510
29657
|
|
|
29511
29658
|
// src/input/applescript.ts
|
|
29512
|
-
var
|
|
29659
|
+
var import_node_child_process4 = require("node:child_process");
|
|
29513
29660
|
var import_node_util3 = require("node:util");
|
|
29514
|
-
var exec3 = (0, import_node_util3.promisify)(
|
|
29661
|
+
var exec3 = (0, import_node_util3.promisify)(import_node_child_process4.execFile);
|
|
29515
29662
|
async function createAppleScriptBackend() {
|
|
29516
29663
|
if (process.platform !== "darwin") return null;
|
|
29517
|
-
const
|
|
29664
|
+
const run3 = (script) => exec3("osascript", ["-e", script]).then(() => void 0);
|
|
29518
29665
|
return {
|
|
29519
29666
|
name: "applescript (keyboard-only)",
|
|
29520
29667
|
canMouse: false,
|
|
@@ -29535,17 +29682,17 @@ async function createAppleScriptBackend() {
|
|
|
29535
29682
|
async scroll() {
|
|
29536
29683
|
},
|
|
29537
29684
|
async typeText(text, submit) {
|
|
29538
|
-
if (text) await
|
|
29539
|
-
if (submit) await
|
|
29685
|
+
if (text) await run3(`tell application "System Events" to keystroke ${asAppleString(text)}`);
|
|
29686
|
+
if (submit) await run3(`tell application "System Events" to key code 36`);
|
|
29540
29687
|
},
|
|
29541
29688
|
async keyTap(name, modifiers = []) {
|
|
29542
29689
|
const using = modifiers.map(modifierClause).filter((m) => m !== null).join(", ");
|
|
29543
29690
|
const usingClause = using ? ` using {${using}}` : "";
|
|
29544
29691
|
const code = KEY_CODES[name.toLowerCase()];
|
|
29545
29692
|
if (code !== void 0) {
|
|
29546
|
-
await
|
|
29693
|
+
await run3(`tell application "System Events" to key code ${code}${usingClause}`);
|
|
29547
29694
|
} else if (name.length === 1) {
|
|
29548
|
-
await
|
|
29695
|
+
await run3(`tell application "System Events" to keystroke ${asAppleString(name)}${usingClause}`);
|
|
29549
29696
|
}
|
|
29550
29697
|
}
|
|
29551
29698
|
};
|
|
@@ -29909,7 +30056,7 @@ function meanDiff(a, b) {
|
|
|
29909
30056
|
}
|
|
29910
30057
|
|
|
29911
30058
|
// src/presence.ts
|
|
29912
|
-
var
|
|
30059
|
+
var import_node_child_process5 = require("node:child_process");
|
|
29913
30060
|
var Presence = class {
|
|
29914
30061
|
watchers = 0;
|
|
29915
30062
|
start() {
|
|
@@ -29919,7 +30066,7 @@ var Presence = class {
|
|
|
29919
30066
|
if (process.platform !== "darwin") return;
|
|
29920
30067
|
const escape2 = (s) => s.replace(/[\\"]/g, "\\$&");
|
|
29921
30068
|
const script = `display notification "${escape2(body)}" with title "${escape2(title)}"`;
|
|
29922
|
-
(0,
|
|
30069
|
+
(0, import_node_child_process5.execFile)("osascript", ["-e", script], () => void 0);
|
|
29923
30070
|
}
|
|
29924
30071
|
setTitle(text) {
|
|
29925
30072
|
if (process.stdout.isTTY) process.stdout.write(`\x1B]2;${text}\x07`);
|
|
@@ -29951,7 +30098,7 @@ var Presence = class {
|
|
|
29951
30098
|
};
|
|
29952
30099
|
|
|
29953
30100
|
// src/power/keep-awake.ts
|
|
29954
|
-
var
|
|
30101
|
+
var import_node_child_process6 = require("node:child_process");
|
|
29955
30102
|
var import_node_os4 = require("node:os");
|
|
29956
30103
|
var KeepAwake = class {
|
|
29957
30104
|
child = null;
|
|
@@ -29994,15 +30141,15 @@ var KeepAwake = class {
|
|
|
29994
30141
|
spawnBlocker() {
|
|
29995
30142
|
switch ((0, import_node_os4.platform)()) {
|
|
29996
30143
|
case "darwin":
|
|
29997
|
-
return (0,
|
|
30144
|
+
return (0, import_node_child_process6.spawn)("caffeinate", ["-i", "-w", String(process.pid)], { stdio: "ignore" });
|
|
29998
30145
|
case "win32":
|
|
29999
|
-
return (0,
|
|
30146
|
+
return (0, import_node_child_process6.spawn)(
|
|
30000
30147
|
"powershell",
|
|
30001
30148
|
["-NoProfile", "-NonInteractive", "-WindowStyle", "Hidden", "-Command", WINDOWS_KEEP_AWAKE],
|
|
30002
30149
|
{ stdio: "ignore", windowsHide: true }
|
|
30003
30150
|
);
|
|
30004
30151
|
case "linux":
|
|
30005
|
-
return (0,
|
|
30152
|
+
return (0, import_node_child_process6.spawn)(
|
|
30006
30153
|
"systemd-inhibit",
|
|
30007
30154
|
[
|
|
30008
30155
|
"--what=sleep:idle",
|
|
@@ -30027,7 +30174,7 @@ var WINDOWS_KEEP_AWAKE = [
|
|
|
30027
30174
|
].join(" ");
|
|
30028
30175
|
|
|
30029
30176
|
// src/power/wake-display.ts
|
|
30030
|
-
var
|
|
30177
|
+
var import_node_child_process7 = require("node:child_process");
|
|
30031
30178
|
var import_node_os5 = require("node:os");
|
|
30032
30179
|
var DisplayWake = class {
|
|
30033
30180
|
hold = null;
|
|
@@ -30048,21 +30195,22 @@ var DisplayWake = class {
|
|
|
30048
30195
|
this.stopHold();
|
|
30049
30196
|
}
|
|
30050
30197
|
}
|
|
30051
|
-
/** Turn the display on right now (best-effort, never throws).
|
|
30052
|
-
|
|
30198
|
+
/** Turn the display on right now (best-effort, never throws). `holdSeconds` keeps it on that
|
|
30199
|
+
* long on macOS (scheduled actions need the panel up for the whole wake→click→type sequence). */
|
|
30200
|
+
wake(holdSeconds = 5) {
|
|
30053
30201
|
try {
|
|
30054
30202
|
switch ((0, import_node_os5.platform)()) {
|
|
30055
30203
|
case "darwin":
|
|
30056
|
-
(0,
|
|
30204
|
+
(0, import_node_child_process7.spawn)("caffeinate", ["-u", "-t", String(holdSeconds)], { stdio: "ignore" }).unref();
|
|
30057
30205
|
break;
|
|
30058
30206
|
case "win32":
|
|
30059
|
-
(0,
|
|
30207
|
+
(0, import_node_child_process7.spawn)("powershell", ["-NoProfile", "-NonInteractive", "-WindowStyle", "Hidden", "-Command", WINDOWS_WAKE], {
|
|
30060
30208
|
stdio: "ignore",
|
|
30061
30209
|
windowsHide: true
|
|
30062
30210
|
}).unref();
|
|
30063
30211
|
break;
|
|
30064
30212
|
case "linux":
|
|
30065
|
-
(0,
|
|
30213
|
+
(0, import_node_child_process7.spawn)("sh", ["-c", "xset s reset; xset dpms force on"], { stdio: "ignore" }).unref();
|
|
30066
30214
|
break;
|
|
30067
30215
|
}
|
|
30068
30216
|
} catch (e) {
|
|
@@ -30073,10 +30221,10 @@ var DisplayWake = class {
|
|
|
30073
30221
|
try {
|
|
30074
30222
|
switch ((0, import_node_os5.platform)()) {
|
|
30075
30223
|
case "darwin":
|
|
30076
|
-
this.hold = (0,
|
|
30224
|
+
this.hold = (0, import_node_child_process7.spawn)("caffeinate", ["-d", "-w", String(process.pid)], { stdio: "ignore" });
|
|
30077
30225
|
break;
|
|
30078
30226
|
case "win32":
|
|
30079
|
-
this.hold = (0,
|
|
30227
|
+
this.hold = (0, import_node_child_process7.spawn)(
|
|
30080
30228
|
"powershell",
|
|
30081
30229
|
["-NoProfile", "-NonInteractive", "-WindowStyle", "Hidden", "-Command", WINDOWS_HOLD],
|
|
30082
30230
|
{ stdio: "ignore", windowsHide: true }
|
|
@@ -30128,6 +30276,37 @@ var WINDOWS_HOLD = [
|
|
|
30128
30276
|
"while ($true) { Start-Sleep -Seconds 3600 }"
|
|
30129
30277
|
].join(" ");
|
|
30130
30278
|
|
|
30279
|
+
// src/power/session-lock.ts
|
|
30280
|
+
var import_node_child_process8 = require("node:child_process");
|
|
30281
|
+
var import_node_os6 = require("node:os");
|
|
30282
|
+
async function isSessionLocked() {
|
|
30283
|
+
try {
|
|
30284
|
+
switch ((0, import_node_os6.platform)()) {
|
|
30285
|
+
case "darwin": {
|
|
30286
|
+
const out = await run("ioreg", ["-n", "Root", "-d1"]);
|
|
30287
|
+
const m = /"IOConsoleLocked"\s*=\s*(Yes|No)/.exec(out);
|
|
30288
|
+
return m ? m[1] === "Yes" : null;
|
|
30289
|
+
}
|
|
30290
|
+
case "win32": {
|
|
30291
|
+
const out = await run("tasklist", ["/FI", "IMAGENAME eq LogonUI.exe", "/NH"]);
|
|
30292
|
+
return /LogonUI\.exe/i.test(out);
|
|
30293
|
+
}
|
|
30294
|
+
default:
|
|
30295
|
+
return null;
|
|
30296
|
+
}
|
|
30297
|
+
} catch {
|
|
30298
|
+
return null;
|
|
30299
|
+
}
|
|
30300
|
+
}
|
|
30301
|
+
function run(cmd, args) {
|
|
30302
|
+
return new Promise((resolve, reject) => {
|
|
30303
|
+
(0, import_node_child_process8.execFile)(cmd, args, { timeout: 4e3, windowsHide: true }, (err, stdout) => {
|
|
30304
|
+
if (err) reject(err);
|
|
30305
|
+
else resolve(String(stdout));
|
|
30306
|
+
});
|
|
30307
|
+
});
|
|
30308
|
+
}
|
|
30309
|
+
|
|
30131
30310
|
// src/security/setup.ts
|
|
30132
30311
|
var import_node_readline = require("node:readline");
|
|
30133
30312
|
|
|
@@ -30486,9 +30665,9 @@ var import_node_path9 = require("node:path");
|
|
|
30486
30665
|
|
|
30487
30666
|
// src/monitor/agents.ts
|
|
30488
30667
|
var import_node_fs7 = require("node:fs");
|
|
30489
|
-
var
|
|
30668
|
+
var import_node_os7 = require("node:os");
|
|
30490
30669
|
var import_node_path7 = require("node:path");
|
|
30491
|
-
var home = (0,
|
|
30670
|
+
var home = (0, import_node_os7.homedir)();
|
|
30492
30671
|
var has = (tokens, name) => tokens.includes(name);
|
|
30493
30672
|
function claudeProjectDir(cwd) {
|
|
30494
30673
|
return (0, import_node_path7.join)(home, ".claude", "projects", cwd.replace(/[/\\.]/g, "-"));
|
|
@@ -30549,8 +30728,8 @@ function readLastJsonlLine(path) {
|
|
|
30549
30728
|
}
|
|
30550
30729
|
function vsCodeUserDirs() {
|
|
30551
30730
|
const variants = ["Code", "Code - Insiders", "VSCodium"];
|
|
30552
|
-
if ((0,
|
|
30553
|
-
if ((0,
|
|
30731
|
+
if ((0, import_node_os7.platform)() === "darwin") return variants.map((v) => (0, import_node_path7.join)(home, "Library", "Application Support", v, "User"));
|
|
30732
|
+
if ((0, import_node_os7.platform)() === "win32") {
|
|
30554
30733
|
const appData = process.env.APPDATA || (0, import_node_path7.join)(home, "AppData", "Roaming");
|
|
30555
30734
|
return variants.map((v) => (0, import_node_path7.join)(appData, v, "User"));
|
|
30556
30735
|
}
|
|
@@ -30592,11 +30771,12 @@ function vsCodeCopilotInstalled() {
|
|
|
30592
30771
|
} catch {
|
|
30593
30772
|
}
|
|
30594
30773
|
}
|
|
30774
|
+
if (!value) value = vsCodeChatSessionDirs().length > 0;
|
|
30595
30775
|
copilotInstalledCache = { value, at: now };
|
|
30596
30776
|
return value;
|
|
30597
30777
|
}
|
|
30598
30778
|
function isVsCodeExtensionHost(cmd) {
|
|
30599
|
-
return cmd.includes("--type=extensionhost") || cmd.includes("extensionhostprocess");
|
|
30779
|
+
return cmd.includes("--type=extensionhost") || cmd.includes("extensionhostprocess") || cmd.includes("node.mojom.nodeservice") && cmd.includes("--inspect-port");
|
|
30600
30780
|
}
|
|
30601
30781
|
var AGENTS = [
|
|
30602
30782
|
{
|
|
@@ -30679,11 +30859,11 @@ function agentLabel(kind) {
|
|
|
30679
30859
|
}
|
|
30680
30860
|
|
|
30681
30861
|
// src/monitor/processes.ts
|
|
30682
|
-
var
|
|
30683
|
-
var
|
|
30684
|
-
function
|
|
30862
|
+
var import_node_child_process9 = require("node:child_process");
|
|
30863
|
+
var import_node_os8 = require("node:os");
|
|
30864
|
+
function run2(cmd, args, timeoutMs = 5e3) {
|
|
30685
30865
|
return new Promise((resolve) => {
|
|
30686
|
-
(0,
|
|
30866
|
+
(0, import_node_child_process9.execFile)(cmd, args, { timeout: timeoutMs, maxBuffer: 16 * 1024 * 1024, windowsHide: true }, (err, stdout) => {
|
|
30687
30867
|
resolve(err && !stdout ? "" : stdout.toString());
|
|
30688
30868
|
});
|
|
30689
30869
|
});
|
|
@@ -30698,7 +30878,7 @@ function tokenize(command) {
|
|
|
30698
30878
|
return out;
|
|
30699
30879
|
}
|
|
30700
30880
|
async function listUnix() {
|
|
30701
|
-
const out = await
|
|
30881
|
+
const out = await run2("ps", ["-axww", "-o", "pid=,ppid=,pcpu=,tty=,command="]);
|
|
30702
30882
|
const procs = [];
|
|
30703
30883
|
for (const line of out.split("\n")) {
|
|
30704
30884
|
const m = line.match(/^\s*(\d+)\s+(\d+)\s+([\d.]+)\s+(\S+)\s+(.*)$/);
|
|
@@ -30718,7 +30898,7 @@ async function listUnix() {
|
|
|
30718
30898
|
}
|
|
30719
30899
|
async function listWindows() {
|
|
30720
30900
|
const script = "Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId,CommandLine,Name | ConvertTo-Csv -NoTypeInformation";
|
|
30721
|
-
const out = await
|
|
30901
|
+
const out = await run2("powershell.exe", ["-NoProfile", "-NonInteractive", "-Command", script], 9e3);
|
|
30722
30902
|
const procs = [];
|
|
30723
30903
|
for (const line of out.split(/\r?\n/)) {
|
|
30724
30904
|
const m = line.match(/^"(\d+)","(\d*)",(.*)$/);
|
|
@@ -30742,16 +30922,16 @@ async function listWindows() {
|
|
|
30742
30922
|
return procs;
|
|
30743
30923
|
}
|
|
30744
30924
|
function listProcesses() {
|
|
30745
|
-
return (0,
|
|
30925
|
+
return (0, import_node_os8.platform)() === "win32" ? listWindows() : listUnix();
|
|
30746
30926
|
}
|
|
30747
30927
|
async function processCwd(pid) {
|
|
30748
|
-
const os = (0,
|
|
30928
|
+
const os = (0, import_node_os8.platform)();
|
|
30749
30929
|
if (os === "win32") return "";
|
|
30750
30930
|
if (os === "linux") {
|
|
30751
|
-
const out2 = await
|
|
30931
|
+
const out2 = await run2("readlink", [`/proc/${pid}/cwd`], 2e3);
|
|
30752
30932
|
return out2.trim();
|
|
30753
30933
|
}
|
|
30754
|
-
const out = await
|
|
30934
|
+
const out = await run2("lsof", ["-a", "-p", String(pid), "-d", "cwd", "-Fn"], 3e3);
|
|
30755
30935
|
for (const line of out.split("\n")) if (line.startsWith("n")) return line.slice(1).trim();
|
|
30756
30936
|
return "";
|
|
30757
30937
|
}
|
|
@@ -30928,8 +31108,8 @@ var SessionMonitor = class {
|
|
|
30928
31108
|
this.timer.unref?.();
|
|
30929
31109
|
}
|
|
30930
31110
|
async cwdFor(pid) {
|
|
30931
|
-
const
|
|
30932
|
-
if (
|
|
31111
|
+
const cached2 = this.cwdCache.get(pid);
|
|
31112
|
+
if (cached2 !== void 0) return cached2;
|
|
30933
31113
|
const cwd = await processCwd(pid).catch(() => "");
|
|
30934
31114
|
this.cwdCache.set(pid, cwd);
|
|
30935
31115
|
return cwd;
|
|
@@ -31325,6 +31505,14 @@ async function startAgent() {
|
|
|
31325
31505
|
if (a) {
|
|
31326
31506
|
try {
|
|
31327
31507
|
log.info(`timer "${t.label || id}" firing${a.kind ? ` (${a.kind})` : ""}`);
|
|
31508
|
+
displayWake.wake(30);
|
|
31509
|
+
await delay(3e3);
|
|
31510
|
+
const locked = await isSessionLocked();
|
|
31511
|
+
if (locked === true) {
|
|
31512
|
+
throw new Error(
|
|
31513
|
+
"the screen is locked, so the input would go to the lock screen instead of your app. For unattended scheduled work, set the machine to not require a password immediately after display sleep."
|
|
31514
|
+
);
|
|
31515
|
+
}
|
|
31328
31516
|
if (typeof a.x === "number" && typeof a.y === "number") {
|
|
31329
31517
|
const pinnedId = t.displayId;
|
|
31330
31518
|
let restoreInput = null;
|
|
@@ -31359,8 +31547,8 @@ async function startAgent() {
|
|
|
31359
31547
|
const message = error.message ?? String(error);
|
|
31360
31548
|
log.warn("timer action failed:", message);
|
|
31361
31549
|
hub.emit({
|
|
31362
|
-
title: t.label || "
|
|
31363
|
-
body: `
|
|
31550
|
+
title: t.label || "Scheduled work",
|
|
31551
|
+
body: `Time's up, but the scheduled action failed: ${message.slice(0, 120)}`,
|
|
31364
31552
|
level: "error",
|
|
31365
31553
|
source: "timer"
|
|
31366
31554
|
});
|
|
@@ -31368,8 +31556,8 @@ async function startAgent() {
|
|
|
31368
31556
|
}
|
|
31369
31557
|
}
|
|
31370
31558
|
hub.emit({
|
|
31371
|
-
title: t.label || "
|
|
31372
|
-
body: a ? "
|
|
31559
|
+
title: t.label || "Scheduled work",
|
|
31560
|
+
body: a ? "Time's up \u2014 scheduled action done." : "Time's up.",
|
|
31373
31561
|
level: "success",
|
|
31374
31562
|
source: "timer"
|
|
31375
31563
|
});
|
|
@@ -31377,8 +31565,8 @@ async function startAgent() {
|
|
|
31377
31565
|
for (const t of loadTimers(config.stateDir)) {
|
|
31378
31566
|
if (t.fireAtMs <= Date.now()) {
|
|
31379
31567
|
hub.emit({
|
|
31380
|
-
title: t.label || "
|
|
31381
|
-
body: "This
|
|
31568
|
+
title: t.label || "Scheduled work",
|
|
31569
|
+
body: "This came due while WhipDesk was not running \u2014 its action was NOT performed.",
|
|
31382
31570
|
level: "error",
|
|
31383
31571
|
source: "timer"
|
|
31384
31572
|
});
|
|
@@ -31450,6 +31638,7 @@ async function startAgent() {
|
|
|
31450
31638
|
displays,
|
|
31451
31639
|
videoAvailable,
|
|
31452
31640
|
video,
|
|
31641
|
+
hdrActive: false,
|
|
31453
31642
|
get activeDisplay() {
|
|
31454
31643
|
return activeDisplay;
|
|
31455
31644
|
},
|
|
@@ -31457,6 +31646,7 @@ async function startAgent() {
|
|
|
31457
31646
|
addController(controller) {
|
|
31458
31647
|
controllers.add(controller);
|
|
31459
31648
|
log.info(`controller connected (${controllers.size} active)`);
|
|
31649
|
+
void windowsHdrState().then((s) => ctx.hdrActive = s?.active === true);
|
|
31460
31650
|
displayWake.setActive(true);
|
|
31461
31651
|
for (const c of controllers) c.send({ type: "presence", watchers: controllers.size });
|
|
31462
31652
|
controller.send({ type: "watchers", regions: regionWatcher.list() });
|
|
@@ -31672,6 +31862,14 @@ async function startAgent() {
|
|
|
31672
31862
|
}
|
|
31673
31863
|
log.info(`listening on :${config.port} (capture: ${capturer.backend}, input: ${input.name})`);
|
|
31674
31864
|
log.info(pin.isSet ? "connection PIN: required" : "connection PIN: NONE (set one in a terminal)");
|
|
31865
|
+
void windowsHdrState().then((s) => {
|
|
31866
|
+
ctx.hdrActive = s?.active === true;
|
|
31867
|
+
if (s?.active) {
|
|
31868
|
+
log.warn(
|
|
31869
|
+
"HDR is ON on this desktop \u2014 the remote stream is tone-mapped to SDR and colors may look washed out. Turn HDR off if it bothers you."
|
|
31870
|
+
);
|
|
31871
|
+
}
|
|
31872
|
+
});
|
|
31675
31873
|
return { server, config, presence, keepAwake, ctx };
|
|
31676
31874
|
}
|
|
31677
31875
|
function isLevel(value) {
|
|
@@ -31735,8 +31933,8 @@ async function main() {
|
|
|
31735
31933
|
registry = await startDeviceRegistry({
|
|
31736
31934
|
rtdb,
|
|
31737
31935
|
identity,
|
|
31738
|
-
name: (0,
|
|
31739
|
-
platform: (0,
|
|
31936
|
+
name: (0, import_node_os9.hostname)(),
|
|
31937
|
+
platform: (0, import_node_os9.platform)(),
|
|
31740
31938
|
version: AGENT_VERSION,
|
|
31741
31939
|
getLan: () => ({ ip: getLanIp(), port: config.port, token: config.token })
|
|
31742
31940
|
});
|