@midscene/computer 1.13.1-beta-20260921034220.0 → 1.13.1-beta-20260921075813.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/cli.mjs +54 -161
- package/dist/es/index.mjs +54 -161
- package/dist/lib/cli.js +54 -161
- package/dist/lib/index.js +54 -161
- package/dist/types/index.d.ts +0 -3
- package/package.json +3 -3
package/dist/es/cli.mjs
CHANGED
|
@@ -461,8 +461,12 @@ if ($midscenePreviousDpiContext -eq [System.IntPtr]::Zero) {
|
|
|
461
461
|
throw 'Unable to enter the Per-Monitor V2 DPI awareness context.'
|
|
462
462
|
}
|
|
463
463
|
`.trim();
|
|
464
|
-
function
|
|
465
|
-
const
|
|
464
|
+
function runWindowsPhysicalPixelPowershell(script) {
|
|
465
|
+
const physicalPixelScript = `$ProgressPreference = 'SilentlyContinue'
|
|
466
|
+
$ErrorActionPreference = 'Stop'
|
|
467
|
+
${WINDOWS_PHYSICAL_PIXEL_POWERSHELL_PREAMBLE}
|
|
468
|
+
${script}`;
|
|
469
|
+
const encoded = Buffer.from(physicalPixelScript, 'utf16le').toString('base64');
|
|
466
470
|
return execFileSync('powershell.exe', [
|
|
467
471
|
'-NoProfile',
|
|
468
472
|
'-NonInteractive',
|
|
@@ -475,130 +479,6 @@ function runWindowsPowershellScript(script) {
|
|
|
475
479
|
windowsHide: true
|
|
476
480
|
});
|
|
477
481
|
}
|
|
478
|
-
function runWindowsPowershellCommand(script) {
|
|
479
|
-
return execFileSync('powershell.exe', [
|
|
480
|
-
'-NoProfile',
|
|
481
|
-
'-Command',
|
|
482
|
-
script
|
|
483
|
-
], {
|
|
484
|
-
encoding: 'utf8',
|
|
485
|
-
timeout: POWERSHELL_TIMEOUT_MS,
|
|
486
|
-
maxBuffer: POWERSHELL_MAX_BUFFER,
|
|
487
|
-
windowsHide: true
|
|
488
|
-
});
|
|
489
|
-
}
|
|
490
|
-
function runWindowsPowershell(script) {
|
|
491
|
-
return runWindowsPowershellCommand(`$ProgressPreference = 'SilentlyContinue'
|
|
492
|
-
$ErrorActionPreference = 'Stop'
|
|
493
|
-
${script}`);
|
|
494
|
-
}
|
|
495
|
-
function runWindowsPhysicalPixelPowershell(script) {
|
|
496
|
-
const physicalPixelScript = `$ProgressPreference = 'SilentlyContinue'
|
|
497
|
-
$ErrorActionPreference = 'Stop'
|
|
498
|
-
${WINDOWS_PHYSICAL_PIXEL_POWERSHELL_PREAMBLE}
|
|
499
|
-
${script}`;
|
|
500
|
-
return runWindowsPowershellScript(physicalPixelScript);
|
|
501
|
-
}
|
|
502
|
-
class WindowsDisplayEnumerationEmptyError extends Error {
|
|
503
|
-
}
|
|
504
|
-
function isFiniteNumber(value) {
|
|
505
|
-
return 'number' == typeof value && Number.isFinite(value);
|
|
506
|
-
}
|
|
507
|
-
function isWindowsDisplayGeometry(value) {
|
|
508
|
-
if (!value || 'object' != typeof value) return false;
|
|
509
|
-
const candidate = value;
|
|
510
|
-
const bounds = candidate.bounds;
|
|
511
|
-
return 'string' == typeof candidate.id && candidate.id.length > 0 && 'string' == typeof candidate.name && 'boolean' == typeof candidate.primary && !!bounds && isFiniteNumber(bounds.x) && isFiniteNumber(bounds.y) && isFiniteNumber(bounds.width) && isFiniteNumber(bounds.height) && bounds.width > 0 && bounds.height > 0;
|
|
512
|
-
}
|
|
513
|
-
function readWindowsDisplayGeometriesWith(runPowershell, context) {
|
|
514
|
-
const serializeScreens = 'legacy' === context ? 'ConvertTo-Json -InputObject $screens -Compress' : '[Console]::Out.Write((ConvertTo-Json -InputObject $screens -Compress))';
|
|
515
|
-
const script = `
|
|
516
|
-
Add-Type -AssemblyName System.Windows.Forms
|
|
517
|
-
$screens = @([System.Windows.Forms.Screen]::AllScreens | ForEach-Object {
|
|
518
|
-
$bounds = $_.Bounds
|
|
519
|
-
[PSCustomObject]@{
|
|
520
|
-
id = $_.DeviceName
|
|
521
|
-
name = $_.DeviceName
|
|
522
|
-
primary = $_.Primary
|
|
523
|
-
bounds = [PSCustomObject]@{
|
|
524
|
-
x = $bounds.X
|
|
525
|
-
y = $bounds.Y
|
|
526
|
-
width = $bounds.Width
|
|
527
|
-
height = $bounds.Height
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
})
|
|
531
|
-
${serializeScreens}
|
|
532
|
-
`.trim();
|
|
533
|
-
const output = runPowershell(script).trim();
|
|
534
|
-
if (!output) throw new WindowsDisplayEnumerationEmptyError(`Windows ${context} display enumeration returned no data`);
|
|
535
|
-
const parsed = JSON.parse(output);
|
|
536
|
-
if (!Array.isArray(parsed)) throw new Error('Windows display enumeration returned invalid data');
|
|
537
|
-
const displays = parsed.filter(isWindowsDisplayGeometry);
|
|
538
|
-
if (displays.length !== parsed.length) throw new Error('Windows display enumeration returned invalid geometry');
|
|
539
|
-
if (0 === displays.length) throw new WindowsDisplayEnumerationEmptyError(`Windows ${context} display enumeration returned no displays`);
|
|
540
|
-
return displays;
|
|
541
|
-
}
|
|
542
|
-
function readWindowsDisplayGeometries() {
|
|
543
|
-
return readWindowsDisplayGeometriesWith(runWindowsPhysicalPixelPowershell, 'physical');
|
|
544
|
-
}
|
|
545
|
-
function discoverWindowsDisplays(runners = {
|
|
546
|
-
physical: runWindowsPhysicalPixelPowershell,
|
|
547
|
-
legacy: runWindowsPowershell
|
|
548
|
-
}) {
|
|
549
|
-
try {
|
|
550
|
-
return {
|
|
551
|
-
geometries: readWindowsDisplayGeometriesWith(runners.physical, 'physical'),
|
|
552
|
-
coordinateMode: 'physical'
|
|
553
|
-
};
|
|
554
|
-
} catch (error) {
|
|
555
|
-
if (!(error instanceof WindowsDisplayEnumerationEmptyError)) throw error;
|
|
556
|
-
const geometries = readWindowsDisplayGeometriesWith(runners.legacy, 'legacy');
|
|
557
|
-
return {
|
|
558
|
-
geometries,
|
|
559
|
-
coordinateMode: 'legacy'
|
|
560
|
-
};
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
function readPngDimensions(dataUri) {
|
|
564
|
-
const prefix = 'data:image/png;base64,';
|
|
565
|
-
if (!dataUri.startsWith(prefix)) throw new Error('Screenshot is not a PNG data URI');
|
|
566
|
-
const buffer = Buffer.from(dataUri.slice(prefix.length), 'base64');
|
|
567
|
-
const pngSignature = Buffer.from([
|
|
568
|
-
137,
|
|
569
|
-
80,
|
|
570
|
-
78,
|
|
571
|
-
71,
|
|
572
|
-
13,
|
|
573
|
-
10,
|
|
574
|
-
26,
|
|
575
|
-
10
|
|
576
|
-
]);
|
|
577
|
-
if (buffer.length < 24 || !buffer.subarray(0, pngSignature.length).equals(pngSignature)) throw new Error('Screenshot returned invalid PNG data');
|
|
578
|
-
const width = buffer.readUInt32BE(16);
|
|
579
|
-
const height = buffer.readUInt32BE(20);
|
|
580
|
-
if (width <= 0 || height <= 0) throw new Error('Screenshot returned invalid PNG dimensions');
|
|
581
|
-
return {
|
|
582
|
-
width,
|
|
583
|
-
height
|
|
584
|
-
};
|
|
585
|
-
}
|
|
586
|
-
function assertLegacyWindowsCoordinateCompatibility({ geometry, screenshotBase64, inputSize }) {
|
|
587
|
-
if (!geometry?.primary) throw new Error('Windows legacy coordinate compatibility mode only supports the primary display');
|
|
588
|
-
if (0 !== geometry.bounds.x || 0 !== geometry.bounds.y) throw new Error(`Windows legacy coordinate compatibility mode requires a primary display origin of (0, 0), got (${geometry.bounds.x}, ${geometry.bounds.y})`);
|
|
589
|
-
const screenshotSize = readPngDimensions(screenshotBase64);
|
|
590
|
-
const displaySize = {
|
|
591
|
-
width: Math.round(geometry.bounds.width),
|
|
592
|
-
height: Math.round(geometry.bounds.height)
|
|
593
|
-
};
|
|
594
|
-
const sizesMatch = screenshotSize.width === displaySize.width && screenshotSize.height === displaySize.height && inputSize.width === displaySize.width && inputSize.height === displaySize.height;
|
|
595
|
-
if (!sizesMatch) throw new Error(`Windows legacy coordinate compatibility check failed: display=${displaySize.width}x${displaySize.height}, screenshot=${screenshotSize.width}x${screenshotSize.height}, input=${inputSize.width}x${inputSize.height}. Refusing to continue because clicks may use incorrect coordinates.`);
|
|
596
|
-
}
|
|
597
|
-
function resolveWindowsDisplayGeometryFromList(displayId, displays) {
|
|
598
|
-
if (!displays.length) return;
|
|
599
|
-
if (void 0 === displayId || '' === displayId) return displays.find((display)=>display.primary) || displays[0];
|
|
600
|
-
return displays.find((display)=>display.id === displayId);
|
|
601
|
-
}
|
|
602
482
|
function windows_pointer_define_property(obj, key, value) {
|
|
603
483
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
604
484
|
value: value,
|
|
@@ -916,6 +796,28 @@ const EDGE_SCROLL_SPEC = {
|
|
|
916
796
|
function escapePowershellSingleQuoted(value) {
|
|
917
797
|
return value.replace(/'/g, "''");
|
|
918
798
|
}
|
|
799
|
+
function readWindowsDisplayGeometries() {
|
|
800
|
+
const script = `
|
|
801
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
802
|
+
$s = [System.Windows.Forms.Screen]::AllScreens | ForEach-Object {
|
|
803
|
+
$b = $_.Bounds
|
|
804
|
+
[PSCustomObject]@{
|
|
805
|
+
id = $_.DeviceName
|
|
806
|
+
name = $_.DeviceName
|
|
807
|
+
primary = $_.Primary
|
|
808
|
+
bounds = [PSCustomObject]@{ x = $b.X; y = $b.Y; width = $b.Width; height = $b.Height }
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
ConvertTo-Json @($s) -Compress
|
|
812
|
+
`.trim();
|
|
813
|
+
const output = runWindowsPhysicalPixelPowershell(script).trim();
|
|
814
|
+
if (!output) throw new Error('Windows display enumeration returned no data');
|
|
815
|
+
const parsed = JSON.parse(output);
|
|
816
|
+
if (!Array.isArray(parsed)) throw new Error('Windows display enumeration returned invalid data');
|
|
817
|
+
const displays = parsed.filter(isWindowsDisplayGeometry);
|
|
818
|
+
if (displays.length !== parsed.length || 0 === displays.length) throw new Error('Windows display enumeration returned invalid geometry');
|
|
819
|
+
return displays;
|
|
820
|
+
}
|
|
919
821
|
function listWindowsDisplays(geometries = readWindowsDisplayGeometries()) {
|
|
920
822
|
return geometries.map((display)=>({
|
|
921
823
|
id: display.id,
|
|
@@ -1019,13 +921,23 @@ function getDisplayInfoBinary() {
|
|
|
1019
921
|
displayInfoBinaryPath = binPath;
|
|
1020
922
|
return binPath;
|
|
1021
923
|
}
|
|
1022
|
-
function
|
|
924
|
+
function isFiniteNumber(value) {
|
|
1023
925
|
return 'number' == typeof value && Number.isFinite(value);
|
|
1024
926
|
}
|
|
927
|
+
function isDisplayBounds(value) {
|
|
928
|
+
if (!value || 'object' != typeof value) return false;
|
|
929
|
+
const bounds = value;
|
|
930
|
+
return isFiniteNumber(bounds.x) && isFiniteNumber(bounds.y) && isFiniteNumber(bounds.width) && isFiniteNumber(bounds.height) && bounds.width > 0 && bounds.height > 0;
|
|
931
|
+
}
|
|
932
|
+
function isWindowsDisplayGeometry(value) {
|
|
933
|
+
if (!value || 'object' != typeof value) return false;
|
|
934
|
+
const candidate = value;
|
|
935
|
+
return 'string' == typeof candidate.id && candidate.id.length > 0 && 'string' == typeof candidate.name && 'boolean' == typeof candidate.primary && isDisplayBounds(candidate.bounds);
|
|
936
|
+
}
|
|
1025
937
|
function isDarwinDisplayGeometry(value) {
|
|
1026
938
|
if (!value || 'object' != typeof value) return false;
|
|
1027
939
|
const candidate = value;
|
|
1028
|
-
return Number.isInteger(candidate.screenIndex) && Number.isInteger(candidate.cgDisplayId) && 'boolean' == typeof candidate.primary && !!candidate.bounds &&
|
|
940
|
+
return Number.isInteger(candidate.screenIndex) && Number.isInteger(candidate.cgDisplayId) && 'boolean' == typeof candidate.primary && !!candidate.bounds && isFiniteNumber(candidate.bounds.x) && isFiniteNumber(candidate.bounds.y) && isFiniteNumber(candidate.bounds.width) && isFiniteNumber(candidate.bounds.height);
|
|
1029
941
|
}
|
|
1030
942
|
function readDarwinDisplayGeometries() {
|
|
1031
943
|
const bin = getDisplayInfoBinary();
|
|
@@ -1095,6 +1007,11 @@ function resolveDarwinDisplayGeometryFromList(displayId, displays) {
|
|
|
1095
1007
|
if (void 0 === displayId || '' === displayId) return displays.find((display)=>display.primary) || displays.find((display)=>0 === display.screenIndex) || displays[0];
|
|
1096
1008
|
return displays.find((display)=>display.screenIndex === screenIndex) || displays.find((display)=>display.cgDisplayId === screenIndex);
|
|
1097
1009
|
}
|
|
1010
|
+
function resolveWindowsDisplayGeometryFromList(displayId, displays) {
|
|
1011
|
+
if (!displays.length) return;
|
|
1012
|
+
if (void 0 === displayId || '' === displayId) return displays.find((display)=>display.primary) || displays[0];
|
|
1013
|
+
return displays.find((display)=>display.id === displayId);
|
|
1014
|
+
}
|
|
1098
1015
|
function resolveDisplayGeometry(displayId, windowsDisplays) {
|
|
1099
1016
|
if ('darwin' === process.platform) return resolveDarwinDisplayGeometryFromList(displayId, readDarwinDisplayGeometries());
|
|
1100
1017
|
if ('win32' === process.platform) {
|
|
@@ -1187,24 +1104,13 @@ function normalizePrimaryKey(key) {
|
|
|
1187
1104
|
return KEY_NAME_MAP[lowerKey] || lowerKey;
|
|
1188
1105
|
}
|
|
1189
1106
|
class ComputerDevice {
|
|
1190
|
-
usesPhysicalWindowsCoordinates() {
|
|
1191
|
-
return 'win32' === process.platform && 'physical' === this.windowsCoordinateContext.mode;
|
|
1192
|
-
}
|
|
1193
|
-
assertLegacyWindowsCoordinateCompatibility(screenshotBase64) {
|
|
1194
|
-
if ('win32' !== process.platform || 'legacy' !== this.windowsCoordinateContext.mode) return;
|
|
1195
|
-
assertLegacyWindowsCoordinateCompatibility({
|
|
1196
|
-
geometry: this.displayGeometry,
|
|
1197
|
-
screenshotBase64,
|
|
1198
|
-
inputSize: this.inputDriver.getScreenSize()
|
|
1199
|
-
});
|
|
1200
|
-
}
|
|
1201
1107
|
async moveGlobalPointer(point, context, smooth) {
|
|
1202
1108
|
if (this.destroyed) throw new Error('ComputerDevice has been destroyed');
|
|
1203
1109
|
const target = {
|
|
1204
1110
|
x: Math.round(point.x),
|
|
1205
1111
|
y: Math.round(point.y)
|
|
1206
1112
|
};
|
|
1207
|
-
if (
|
|
1113
|
+
if ('win32' === process.platform) {
|
|
1208
1114
|
const actual = this.windowsPointerDriver.moveTo(target, {
|
|
1209
1115
|
smoothSteps: smooth?.smoothSteps,
|
|
1210
1116
|
smoothDelayMs: smooth?.smoothDelay
|
|
@@ -1256,7 +1162,7 @@ class ComputerDevice {
|
|
|
1256
1162
|
}
|
|
1257
1163
|
static async listDisplays() {
|
|
1258
1164
|
try {
|
|
1259
|
-
if ('win32' === process.platform) return listWindowsDisplays(
|
|
1165
|
+
if ('win32' === process.platform) return listWindowsDisplays();
|
|
1260
1166
|
const displays = await screenshot_desktop.listDisplays();
|
|
1261
1167
|
return displays.map((d)=>({
|
|
1262
1168
|
id: String(d.id),
|
|
@@ -1294,14 +1200,7 @@ class ComputerDevice {
|
|
|
1294
1200
|
}
|
|
1295
1201
|
}
|
|
1296
1202
|
device_libnut = await getLibnut();
|
|
1297
|
-
const
|
|
1298
|
-
const windowsDisplayGeometries = windowsDisplayDiscovery?.geometries;
|
|
1299
|
-
if (windowsDisplayDiscovery?.coordinateMode === 'legacy') warnDevice('Windows display enumeration returned no data in the Per-Monitor V2 DPI context; using the legacy Windows coordinate path for this device connection.');
|
|
1300
|
-
this.windowsCoordinateContext = windowsDisplayDiscovery?.coordinateMode === 'legacy' ? {
|
|
1301
|
-
mode: 'legacy'
|
|
1302
|
-
} : {
|
|
1303
|
-
mode: 'physical'
|
|
1304
|
-
};
|
|
1203
|
+
const windowsDisplayGeometries = 'win32' === process.platform ? readWindowsDisplayGeometries() : void 0;
|
|
1305
1204
|
this.displayGeometry = resolveDisplayGeometry(this.displayId, windowsDisplayGeometries);
|
|
1306
1205
|
const size = await this.size();
|
|
1307
1206
|
const displays = windowsDisplayGeometries ? listWindowsDisplays(windowsDisplayGeometries) : await ComputerDevice.listDisplays();
|
|
@@ -1312,7 +1211,6 @@ Platform: ${process.platform}
|
|
|
1312
1211
|
Display: ${this.displayId || 'Primary'}
|
|
1313
1212
|
Screen Size: ${size.width}x${size.height}
|
|
1314
1213
|
Available Displays: ${displays.length > 0 ? displays.map((d)=>d.name).join(', ') : 'Unknown'}${headlessInfo}
|
|
1315
|
-
${'win32' === process.platform ? `Windows Coordinate Mode: ${this.windowsCoordinateContext.mode}` : ''}
|
|
1316
1214
|
`;
|
|
1317
1215
|
debugDevice('Computer device connected', this.description);
|
|
1318
1216
|
await this.healthCheck(displays);
|
|
@@ -1336,7 +1234,7 @@ ${'win32' === process.platform ? `Windows Coordinate Mode: ${this.windowsCoordin
|
|
|
1336
1234
|
}
|
|
1337
1235
|
async healthCheck(displays) {
|
|
1338
1236
|
console.log('[HealthCheck] Starting health check...');
|
|
1339
|
-
console.log("[HealthCheck] @midscene/computer v1.13.1-beta-
|
|
1237
|
+
console.log("[HealthCheck] @midscene/computer v1.13.1-beta-20260921075813.0");
|
|
1340
1238
|
console.log('[HealthCheck] Taking screenshot...');
|
|
1341
1239
|
const screenshotTimeout = 15000;
|
|
1342
1240
|
let timeoutId;
|
|
@@ -1348,12 +1246,10 @@ ${'win32' === process.platform ? `Windows Coordinate Mode: ${this.windowsCoordin
|
|
|
1348
1246
|
timeoutPromise
|
|
1349
1247
|
]);
|
|
1350
1248
|
console.log(`[HealthCheck] Screenshot succeeded (length=${base64.length})`);
|
|
1351
|
-
this.assertLegacyWindowsCoordinateCompatibility(base64);
|
|
1352
1249
|
console.log('[HealthCheck] Verifying mouse control...');
|
|
1353
|
-
const
|
|
1354
|
-
const startPos = usePhysicalWindowsCoordinates ? this.windowsPointerDriver.getPosition() : this.inputDriver.getMousePos();
|
|
1250
|
+
const startPos = 'win32' === process.platform ? this.windowsPointerDriver.getPosition() : this.inputDriver.getMousePos();
|
|
1355
1251
|
console.log(`[HealthCheck] Current mouse position: (${startPos.x}, ${startPos.y})`);
|
|
1356
|
-
if (
|
|
1252
|
+
if ('win32' === process.platform) {
|
|
1357
1253
|
if (!this.displayGeometry) throw new Error('Windows display geometry is unavailable');
|
|
1358
1254
|
const bounds = this.displayGeometry.bounds;
|
|
1359
1255
|
const target = {
|
|
@@ -1419,7 +1315,7 @@ ${'win32' === process.platform ? `Windows Coordinate Mode: ${this.windowsCoordin
|
|
|
1419
1315
|
debugDevice('Taking screenshot', {
|
|
1420
1316
|
displayId: this.displayId
|
|
1421
1317
|
});
|
|
1422
|
-
if (
|
|
1318
|
+
if ('win32' === process.platform) return this.screenshotViaPowershell();
|
|
1423
1319
|
const options = {
|
|
1424
1320
|
format: 'png'
|
|
1425
1321
|
};
|
|
@@ -1582,7 +1478,7 @@ $g.Dispose(); $bmp.Dispose(); $ms.Dispose()
|
|
|
1582
1478
|
this.inputDriver.sendKey(key, modifiers);
|
|
1583
1479
|
}
|
|
1584
1480
|
resolveUntargetedScrollPoint(screenSize) {
|
|
1585
|
-
if (
|
|
1481
|
+
if ('win32' === process.platform) {
|
|
1586
1482
|
const activeWindowRect = this.windowsPointerDriver.getActiveWindowRect();
|
|
1587
1483
|
if (activeWindowRect) {
|
|
1588
1484
|
const activeWindowCenter = {
|
|
@@ -1726,9 +1622,6 @@ $g.Dispose(); $bmp.Dispose(); $ms.Dispose()
|
|
|
1726
1622
|
device_define_property(this, "options", void 0);
|
|
1727
1623
|
device_define_property(this, "displayId", void 0);
|
|
1728
1624
|
device_define_property(this, "displayGeometry", void 0);
|
|
1729
|
-
device_define_property(this, "windowsCoordinateContext", {
|
|
1730
|
-
mode: 'physical'
|
|
1731
|
-
});
|
|
1732
1625
|
device_define_property(this, "description", void 0);
|
|
1733
1626
|
device_define_property(this, "destroyed", false);
|
|
1734
1627
|
device_define_property(this, "xvfbInstance", void 0);
|
|
@@ -2830,7 +2723,7 @@ const tools = new ComputerMidsceneTools({
|
|
|
2830
2723
|
});
|
|
2831
2724
|
runToolsCLI(tools, 'midscene-computer', {
|
|
2832
2725
|
stripPrefix: 'computer_',
|
|
2833
|
-
version: "1.13.1-beta-
|
|
2726
|
+
version: "1.13.1-beta-20260921075813.0",
|
|
2834
2727
|
extraCommands: createReportCliCommands()
|
|
2835
2728
|
}).catch((e)=>{
|
|
2836
2729
|
process.exit(reportCLIError(e));
|
package/dist/es/index.mjs
CHANGED
|
@@ -461,8 +461,12 @@ if ($midscenePreviousDpiContext -eq [System.IntPtr]::Zero) {
|
|
|
461
461
|
throw 'Unable to enter the Per-Monitor V2 DPI awareness context.'
|
|
462
462
|
}
|
|
463
463
|
`.trim();
|
|
464
|
-
function
|
|
465
|
-
const
|
|
464
|
+
function runWindowsPhysicalPixelPowershell(script) {
|
|
465
|
+
const physicalPixelScript = `$ProgressPreference = 'SilentlyContinue'
|
|
466
|
+
$ErrorActionPreference = 'Stop'
|
|
467
|
+
${WINDOWS_PHYSICAL_PIXEL_POWERSHELL_PREAMBLE}
|
|
468
|
+
${script}`;
|
|
469
|
+
const encoded = Buffer.from(physicalPixelScript, 'utf16le').toString('base64');
|
|
466
470
|
return execFileSync('powershell.exe', [
|
|
467
471
|
'-NoProfile',
|
|
468
472
|
'-NonInteractive',
|
|
@@ -475,130 +479,6 @@ function runWindowsPowershellScript(script) {
|
|
|
475
479
|
windowsHide: true
|
|
476
480
|
});
|
|
477
481
|
}
|
|
478
|
-
function runWindowsPowershellCommand(script) {
|
|
479
|
-
return execFileSync('powershell.exe', [
|
|
480
|
-
'-NoProfile',
|
|
481
|
-
'-Command',
|
|
482
|
-
script
|
|
483
|
-
], {
|
|
484
|
-
encoding: 'utf8',
|
|
485
|
-
timeout: POWERSHELL_TIMEOUT_MS,
|
|
486
|
-
maxBuffer: POWERSHELL_MAX_BUFFER,
|
|
487
|
-
windowsHide: true
|
|
488
|
-
});
|
|
489
|
-
}
|
|
490
|
-
function runWindowsPowershell(script) {
|
|
491
|
-
return runWindowsPowershellCommand(`$ProgressPreference = 'SilentlyContinue'
|
|
492
|
-
$ErrorActionPreference = 'Stop'
|
|
493
|
-
${script}`);
|
|
494
|
-
}
|
|
495
|
-
function runWindowsPhysicalPixelPowershell(script) {
|
|
496
|
-
const physicalPixelScript = `$ProgressPreference = 'SilentlyContinue'
|
|
497
|
-
$ErrorActionPreference = 'Stop'
|
|
498
|
-
${WINDOWS_PHYSICAL_PIXEL_POWERSHELL_PREAMBLE}
|
|
499
|
-
${script}`;
|
|
500
|
-
return runWindowsPowershellScript(physicalPixelScript);
|
|
501
|
-
}
|
|
502
|
-
class WindowsDisplayEnumerationEmptyError extends Error {
|
|
503
|
-
}
|
|
504
|
-
function isFiniteNumber(value) {
|
|
505
|
-
return 'number' == typeof value && Number.isFinite(value);
|
|
506
|
-
}
|
|
507
|
-
function isWindowsDisplayGeometry(value) {
|
|
508
|
-
if (!value || 'object' != typeof value) return false;
|
|
509
|
-
const candidate = value;
|
|
510
|
-
const bounds = candidate.bounds;
|
|
511
|
-
return 'string' == typeof candidate.id && candidate.id.length > 0 && 'string' == typeof candidate.name && 'boolean' == typeof candidate.primary && !!bounds && isFiniteNumber(bounds.x) && isFiniteNumber(bounds.y) && isFiniteNumber(bounds.width) && isFiniteNumber(bounds.height) && bounds.width > 0 && bounds.height > 0;
|
|
512
|
-
}
|
|
513
|
-
function readWindowsDisplayGeometriesWith(runPowershell, context) {
|
|
514
|
-
const serializeScreens = 'legacy' === context ? 'ConvertTo-Json -InputObject $screens -Compress' : '[Console]::Out.Write((ConvertTo-Json -InputObject $screens -Compress))';
|
|
515
|
-
const script = `
|
|
516
|
-
Add-Type -AssemblyName System.Windows.Forms
|
|
517
|
-
$screens = @([System.Windows.Forms.Screen]::AllScreens | ForEach-Object {
|
|
518
|
-
$bounds = $_.Bounds
|
|
519
|
-
[PSCustomObject]@{
|
|
520
|
-
id = $_.DeviceName
|
|
521
|
-
name = $_.DeviceName
|
|
522
|
-
primary = $_.Primary
|
|
523
|
-
bounds = [PSCustomObject]@{
|
|
524
|
-
x = $bounds.X
|
|
525
|
-
y = $bounds.Y
|
|
526
|
-
width = $bounds.Width
|
|
527
|
-
height = $bounds.Height
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
})
|
|
531
|
-
${serializeScreens}
|
|
532
|
-
`.trim();
|
|
533
|
-
const output = runPowershell(script).trim();
|
|
534
|
-
if (!output) throw new WindowsDisplayEnumerationEmptyError(`Windows ${context} display enumeration returned no data`);
|
|
535
|
-
const parsed = JSON.parse(output);
|
|
536
|
-
if (!Array.isArray(parsed)) throw new Error('Windows display enumeration returned invalid data');
|
|
537
|
-
const displays = parsed.filter(isWindowsDisplayGeometry);
|
|
538
|
-
if (displays.length !== parsed.length) throw new Error('Windows display enumeration returned invalid geometry');
|
|
539
|
-
if (0 === displays.length) throw new WindowsDisplayEnumerationEmptyError(`Windows ${context} display enumeration returned no displays`);
|
|
540
|
-
return displays;
|
|
541
|
-
}
|
|
542
|
-
function readWindowsDisplayGeometries() {
|
|
543
|
-
return readWindowsDisplayGeometriesWith(runWindowsPhysicalPixelPowershell, 'physical');
|
|
544
|
-
}
|
|
545
|
-
function discoverWindowsDisplays(runners = {
|
|
546
|
-
physical: runWindowsPhysicalPixelPowershell,
|
|
547
|
-
legacy: runWindowsPowershell
|
|
548
|
-
}) {
|
|
549
|
-
try {
|
|
550
|
-
return {
|
|
551
|
-
geometries: readWindowsDisplayGeometriesWith(runners.physical, 'physical'),
|
|
552
|
-
coordinateMode: 'physical'
|
|
553
|
-
};
|
|
554
|
-
} catch (error) {
|
|
555
|
-
if (!(error instanceof WindowsDisplayEnumerationEmptyError)) throw error;
|
|
556
|
-
const geometries = readWindowsDisplayGeometriesWith(runners.legacy, 'legacy');
|
|
557
|
-
return {
|
|
558
|
-
geometries,
|
|
559
|
-
coordinateMode: 'legacy'
|
|
560
|
-
};
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
function readPngDimensions(dataUri) {
|
|
564
|
-
const prefix = 'data:image/png;base64,';
|
|
565
|
-
if (!dataUri.startsWith(prefix)) throw new Error('Screenshot is not a PNG data URI');
|
|
566
|
-
const buffer = Buffer.from(dataUri.slice(prefix.length), 'base64');
|
|
567
|
-
const pngSignature = Buffer.from([
|
|
568
|
-
137,
|
|
569
|
-
80,
|
|
570
|
-
78,
|
|
571
|
-
71,
|
|
572
|
-
13,
|
|
573
|
-
10,
|
|
574
|
-
26,
|
|
575
|
-
10
|
|
576
|
-
]);
|
|
577
|
-
if (buffer.length < 24 || !buffer.subarray(0, pngSignature.length).equals(pngSignature)) throw new Error('Screenshot returned invalid PNG data');
|
|
578
|
-
const width = buffer.readUInt32BE(16);
|
|
579
|
-
const height = buffer.readUInt32BE(20);
|
|
580
|
-
if (width <= 0 || height <= 0) throw new Error('Screenshot returned invalid PNG dimensions');
|
|
581
|
-
return {
|
|
582
|
-
width,
|
|
583
|
-
height
|
|
584
|
-
};
|
|
585
|
-
}
|
|
586
|
-
function assertLegacyWindowsCoordinateCompatibility({ geometry, screenshotBase64, inputSize }) {
|
|
587
|
-
if (!geometry?.primary) throw new Error('Windows legacy coordinate compatibility mode only supports the primary display');
|
|
588
|
-
if (0 !== geometry.bounds.x || 0 !== geometry.bounds.y) throw new Error(`Windows legacy coordinate compatibility mode requires a primary display origin of (0, 0), got (${geometry.bounds.x}, ${geometry.bounds.y})`);
|
|
589
|
-
const screenshotSize = readPngDimensions(screenshotBase64);
|
|
590
|
-
const displaySize = {
|
|
591
|
-
width: Math.round(geometry.bounds.width),
|
|
592
|
-
height: Math.round(geometry.bounds.height)
|
|
593
|
-
};
|
|
594
|
-
const sizesMatch = screenshotSize.width === displaySize.width && screenshotSize.height === displaySize.height && inputSize.width === displaySize.width && inputSize.height === displaySize.height;
|
|
595
|
-
if (!sizesMatch) throw new Error(`Windows legacy coordinate compatibility check failed: display=${displaySize.width}x${displaySize.height}, screenshot=${screenshotSize.width}x${screenshotSize.height}, input=${inputSize.width}x${inputSize.height}. Refusing to continue because clicks may use incorrect coordinates.`);
|
|
596
|
-
}
|
|
597
|
-
function resolveWindowsDisplayGeometryFromList(displayId, displays) {
|
|
598
|
-
if (!displays.length) return;
|
|
599
|
-
if (void 0 === displayId || '' === displayId) return displays.find((display)=>display.primary) || displays[0];
|
|
600
|
-
return displays.find((display)=>display.id === displayId);
|
|
601
|
-
}
|
|
602
482
|
function windows_pointer_define_property(obj, key, value) {
|
|
603
483
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
604
484
|
value: value,
|
|
@@ -916,6 +796,28 @@ const EDGE_SCROLL_SPEC = {
|
|
|
916
796
|
function escapePowershellSingleQuoted(value) {
|
|
917
797
|
return value.replace(/'/g, "''");
|
|
918
798
|
}
|
|
799
|
+
function readWindowsDisplayGeometries() {
|
|
800
|
+
const script = `
|
|
801
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
802
|
+
$s = [System.Windows.Forms.Screen]::AllScreens | ForEach-Object {
|
|
803
|
+
$b = $_.Bounds
|
|
804
|
+
[PSCustomObject]@{
|
|
805
|
+
id = $_.DeviceName
|
|
806
|
+
name = $_.DeviceName
|
|
807
|
+
primary = $_.Primary
|
|
808
|
+
bounds = [PSCustomObject]@{ x = $b.X; y = $b.Y; width = $b.Width; height = $b.Height }
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
ConvertTo-Json @($s) -Compress
|
|
812
|
+
`.trim();
|
|
813
|
+
const output = runWindowsPhysicalPixelPowershell(script).trim();
|
|
814
|
+
if (!output) throw new Error('Windows display enumeration returned no data');
|
|
815
|
+
const parsed = JSON.parse(output);
|
|
816
|
+
if (!Array.isArray(parsed)) throw new Error('Windows display enumeration returned invalid data');
|
|
817
|
+
const displays = parsed.filter(isWindowsDisplayGeometry);
|
|
818
|
+
if (displays.length !== parsed.length || 0 === displays.length) throw new Error('Windows display enumeration returned invalid geometry');
|
|
819
|
+
return displays;
|
|
820
|
+
}
|
|
919
821
|
function listWindowsDisplays(geometries = readWindowsDisplayGeometries()) {
|
|
920
822
|
return geometries.map((display)=>({
|
|
921
823
|
id: display.id,
|
|
@@ -1019,13 +921,23 @@ function getDisplayInfoBinary() {
|
|
|
1019
921
|
displayInfoBinaryPath = binPath;
|
|
1020
922
|
return binPath;
|
|
1021
923
|
}
|
|
1022
|
-
function
|
|
924
|
+
function isFiniteNumber(value) {
|
|
1023
925
|
return 'number' == typeof value && Number.isFinite(value);
|
|
1024
926
|
}
|
|
927
|
+
function isDisplayBounds(value) {
|
|
928
|
+
if (!value || 'object' != typeof value) return false;
|
|
929
|
+
const bounds = value;
|
|
930
|
+
return isFiniteNumber(bounds.x) && isFiniteNumber(bounds.y) && isFiniteNumber(bounds.width) && isFiniteNumber(bounds.height) && bounds.width > 0 && bounds.height > 0;
|
|
931
|
+
}
|
|
932
|
+
function isWindowsDisplayGeometry(value) {
|
|
933
|
+
if (!value || 'object' != typeof value) return false;
|
|
934
|
+
const candidate = value;
|
|
935
|
+
return 'string' == typeof candidate.id && candidate.id.length > 0 && 'string' == typeof candidate.name && 'boolean' == typeof candidate.primary && isDisplayBounds(candidate.bounds);
|
|
936
|
+
}
|
|
1025
937
|
function isDarwinDisplayGeometry(value) {
|
|
1026
938
|
if (!value || 'object' != typeof value) return false;
|
|
1027
939
|
const candidate = value;
|
|
1028
|
-
return Number.isInteger(candidate.screenIndex) && Number.isInteger(candidate.cgDisplayId) && 'boolean' == typeof candidate.primary && !!candidate.bounds &&
|
|
940
|
+
return Number.isInteger(candidate.screenIndex) && Number.isInteger(candidate.cgDisplayId) && 'boolean' == typeof candidate.primary && !!candidate.bounds && isFiniteNumber(candidate.bounds.x) && isFiniteNumber(candidate.bounds.y) && isFiniteNumber(candidate.bounds.width) && isFiniteNumber(candidate.bounds.height);
|
|
1029
941
|
}
|
|
1030
942
|
function readDarwinDisplayGeometries() {
|
|
1031
943
|
const bin = getDisplayInfoBinary();
|
|
@@ -1095,6 +1007,11 @@ function resolveDarwinDisplayGeometryFromList(displayId, displays) {
|
|
|
1095
1007
|
if (void 0 === displayId || '' === displayId) return displays.find((display)=>display.primary) || displays.find((display)=>0 === display.screenIndex) || displays[0];
|
|
1096
1008
|
return displays.find((display)=>display.screenIndex === screenIndex) || displays.find((display)=>display.cgDisplayId === screenIndex);
|
|
1097
1009
|
}
|
|
1010
|
+
function resolveWindowsDisplayGeometryFromList(displayId, displays) {
|
|
1011
|
+
if (!displays.length) return;
|
|
1012
|
+
if (void 0 === displayId || '' === displayId) return displays.find((display)=>display.primary) || displays[0];
|
|
1013
|
+
return displays.find((display)=>display.id === displayId);
|
|
1014
|
+
}
|
|
1098
1015
|
function resolveDisplayGeometry(displayId, windowsDisplays) {
|
|
1099
1016
|
if ('darwin' === process.platform) return resolveDarwinDisplayGeometryFromList(displayId, readDarwinDisplayGeometries());
|
|
1100
1017
|
if ('win32' === process.platform) {
|
|
@@ -1187,24 +1104,13 @@ function normalizePrimaryKey(key) {
|
|
|
1187
1104
|
return KEY_NAME_MAP[lowerKey] || lowerKey;
|
|
1188
1105
|
}
|
|
1189
1106
|
class ComputerDevice {
|
|
1190
|
-
usesPhysicalWindowsCoordinates() {
|
|
1191
|
-
return 'win32' === process.platform && 'physical' === this.windowsCoordinateContext.mode;
|
|
1192
|
-
}
|
|
1193
|
-
assertLegacyWindowsCoordinateCompatibility(screenshotBase64) {
|
|
1194
|
-
if ('win32' !== process.platform || 'legacy' !== this.windowsCoordinateContext.mode) return;
|
|
1195
|
-
assertLegacyWindowsCoordinateCompatibility({
|
|
1196
|
-
geometry: this.displayGeometry,
|
|
1197
|
-
screenshotBase64,
|
|
1198
|
-
inputSize: this.inputDriver.getScreenSize()
|
|
1199
|
-
});
|
|
1200
|
-
}
|
|
1201
1107
|
async moveGlobalPointer(point, context, smooth) {
|
|
1202
1108
|
if (this.destroyed) throw new Error('ComputerDevice has been destroyed');
|
|
1203
1109
|
const target = {
|
|
1204
1110
|
x: Math.round(point.x),
|
|
1205
1111
|
y: Math.round(point.y)
|
|
1206
1112
|
};
|
|
1207
|
-
if (
|
|
1113
|
+
if ('win32' === process.platform) {
|
|
1208
1114
|
const actual = this.windowsPointerDriver.moveTo(target, {
|
|
1209
1115
|
smoothSteps: smooth?.smoothSteps,
|
|
1210
1116
|
smoothDelayMs: smooth?.smoothDelay
|
|
@@ -1256,7 +1162,7 @@ class ComputerDevice {
|
|
|
1256
1162
|
}
|
|
1257
1163
|
static async listDisplays() {
|
|
1258
1164
|
try {
|
|
1259
|
-
if ('win32' === process.platform) return listWindowsDisplays(
|
|
1165
|
+
if ('win32' === process.platform) return listWindowsDisplays();
|
|
1260
1166
|
const displays = await screenshot_desktop.listDisplays();
|
|
1261
1167
|
return displays.map((d)=>({
|
|
1262
1168
|
id: String(d.id),
|
|
@@ -1294,14 +1200,7 @@ class ComputerDevice {
|
|
|
1294
1200
|
}
|
|
1295
1201
|
}
|
|
1296
1202
|
device_libnut = await getLibnut();
|
|
1297
|
-
const
|
|
1298
|
-
const windowsDisplayGeometries = windowsDisplayDiscovery?.geometries;
|
|
1299
|
-
if (windowsDisplayDiscovery?.coordinateMode === 'legacy') warnDevice('Windows display enumeration returned no data in the Per-Monitor V2 DPI context; using the legacy Windows coordinate path for this device connection.');
|
|
1300
|
-
this.windowsCoordinateContext = windowsDisplayDiscovery?.coordinateMode === 'legacy' ? {
|
|
1301
|
-
mode: 'legacy'
|
|
1302
|
-
} : {
|
|
1303
|
-
mode: 'physical'
|
|
1304
|
-
};
|
|
1203
|
+
const windowsDisplayGeometries = 'win32' === process.platform ? readWindowsDisplayGeometries() : void 0;
|
|
1305
1204
|
this.displayGeometry = resolveDisplayGeometry(this.displayId, windowsDisplayGeometries);
|
|
1306
1205
|
const size = await this.size();
|
|
1307
1206
|
const displays = windowsDisplayGeometries ? listWindowsDisplays(windowsDisplayGeometries) : await ComputerDevice.listDisplays();
|
|
@@ -1312,7 +1211,6 @@ Platform: ${process.platform}
|
|
|
1312
1211
|
Display: ${this.displayId || 'Primary'}
|
|
1313
1212
|
Screen Size: ${size.width}x${size.height}
|
|
1314
1213
|
Available Displays: ${displays.length > 0 ? displays.map((d)=>d.name).join(', ') : 'Unknown'}${headlessInfo}
|
|
1315
|
-
${'win32' === process.platform ? `Windows Coordinate Mode: ${this.windowsCoordinateContext.mode}` : ''}
|
|
1316
1214
|
`;
|
|
1317
1215
|
debugDevice('Computer device connected', this.description);
|
|
1318
1216
|
await this.healthCheck(displays);
|
|
@@ -1336,7 +1234,7 @@ ${'win32' === process.platform ? `Windows Coordinate Mode: ${this.windowsCoordin
|
|
|
1336
1234
|
}
|
|
1337
1235
|
async healthCheck(displays) {
|
|
1338
1236
|
console.log('[HealthCheck] Starting health check...');
|
|
1339
|
-
console.log("[HealthCheck] @midscene/computer v1.13.1-beta-
|
|
1237
|
+
console.log("[HealthCheck] @midscene/computer v1.13.1-beta-20260921075813.0");
|
|
1340
1238
|
console.log('[HealthCheck] Taking screenshot...');
|
|
1341
1239
|
const screenshotTimeout = 15000;
|
|
1342
1240
|
let timeoutId;
|
|
@@ -1348,12 +1246,10 @@ ${'win32' === process.platform ? `Windows Coordinate Mode: ${this.windowsCoordin
|
|
|
1348
1246
|
timeoutPromise
|
|
1349
1247
|
]);
|
|
1350
1248
|
console.log(`[HealthCheck] Screenshot succeeded (length=${base64.length})`);
|
|
1351
|
-
this.assertLegacyWindowsCoordinateCompatibility(base64);
|
|
1352
1249
|
console.log('[HealthCheck] Verifying mouse control...');
|
|
1353
|
-
const
|
|
1354
|
-
const startPos = usePhysicalWindowsCoordinates ? this.windowsPointerDriver.getPosition() : this.inputDriver.getMousePos();
|
|
1250
|
+
const startPos = 'win32' === process.platform ? this.windowsPointerDriver.getPosition() : this.inputDriver.getMousePos();
|
|
1355
1251
|
console.log(`[HealthCheck] Current mouse position: (${startPos.x}, ${startPos.y})`);
|
|
1356
|
-
if (
|
|
1252
|
+
if ('win32' === process.platform) {
|
|
1357
1253
|
if (!this.displayGeometry) throw new Error('Windows display geometry is unavailable');
|
|
1358
1254
|
const bounds = this.displayGeometry.bounds;
|
|
1359
1255
|
const target = {
|
|
@@ -1419,7 +1315,7 @@ ${'win32' === process.platform ? `Windows Coordinate Mode: ${this.windowsCoordin
|
|
|
1419
1315
|
debugDevice('Taking screenshot', {
|
|
1420
1316
|
displayId: this.displayId
|
|
1421
1317
|
});
|
|
1422
|
-
if (
|
|
1318
|
+
if ('win32' === process.platform) return this.screenshotViaPowershell();
|
|
1423
1319
|
const options = {
|
|
1424
1320
|
format: 'png'
|
|
1425
1321
|
};
|
|
@@ -1582,7 +1478,7 @@ $g.Dispose(); $bmp.Dispose(); $ms.Dispose()
|
|
|
1582
1478
|
this.inputDriver.sendKey(key, modifiers);
|
|
1583
1479
|
}
|
|
1584
1480
|
resolveUntargetedScrollPoint(screenSize) {
|
|
1585
|
-
if (
|
|
1481
|
+
if ('win32' === process.platform) {
|
|
1586
1482
|
const activeWindowRect = this.windowsPointerDriver.getActiveWindowRect();
|
|
1587
1483
|
if (activeWindowRect) {
|
|
1588
1484
|
const activeWindowCenter = {
|
|
@@ -1726,9 +1622,6 @@ $g.Dispose(); $bmp.Dispose(); $ms.Dispose()
|
|
|
1726
1622
|
device_define_property(this, "options", void 0);
|
|
1727
1623
|
device_define_property(this, "displayId", void 0);
|
|
1728
1624
|
device_define_property(this, "displayGeometry", void 0);
|
|
1729
|
-
device_define_property(this, "windowsCoordinateContext", {
|
|
1730
|
-
mode: 'physical'
|
|
1731
|
-
});
|
|
1732
1625
|
device_define_property(this, "description", void 0);
|
|
1733
1626
|
device_define_property(this, "destroyed", false);
|
|
1734
1627
|
device_define_property(this, "xvfbInstance", void 0);
|
|
@@ -2869,7 +2762,7 @@ class ComputerMidsceneTools extends BaseMidsceneTools {
|
|
|
2869
2762
|
}
|
|
2870
2763
|
}
|
|
2871
2764
|
function version() {
|
|
2872
|
-
const currentVersion = "1.13.1-beta-
|
|
2765
|
+
const currentVersion = "1.13.1-beta-20260921075813.0";
|
|
2873
2766
|
console.log(`@midscene/computer v${currentVersion}`);
|
|
2874
2767
|
return currentVersion;
|
|
2875
2768
|
}
|