@midscene/computer 1.10.0 → 1.10.1-beta-20260624112700.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 +74 -2
- package/dist/es/index.mjs +74 -2
- package/dist/lib/cli.js +74 -2
- package/dist/lib/index.js +74 -2
- package/dist/types/index.d.ts +14 -0
- package/package.json +3 -3
package/dist/es/cli.mjs
CHANGED
|
@@ -372,6 +372,42 @@ function sendKeyViaAppleScript(key, modifiers = []) {
|
|
|
372
372
|
script
|
|
373
373
|
]);
|
|
374
374
|
}
|
|
375
|
+
const POWERSHELL_TIMEOUT_MS = 15000;
|
|
376
|
+
const POWERSHELL_MAX_BUFFER = 67108864;
|
|
377
|
+
function escapePowershellSingleQuoted(value) {
|
|
378
|
+
return value.replace(/'/g, "''");
|
|
379
|
+
}
|
|
380
|
+
function runPowershell(script) {
|
|
381
|
+
const encoded = Buffer.from(script, 'utf16le').toString('base64');
|
|
382
|
+
return execFileSync('powershell.exe', [
|
|
383
|
+
'-NoProfile',
|
|
384
|
+
'-NonInteractive',
|
|
385
|
+
'-ExecutionPolicy',
|
|
386
|
+
'Bypass',
|
|
387
|
+
'-EncodedCommand',
|
|
388
|
+
encoded
|
|
389
|
+
], {
|
|
390
|
+
encoding: 'utf8',
|
|
391
|
+
timeout: POWERSHELL_TIMEOUT_MS,
|
|
392
|
+
maxBuffer: POWERSHELL_MAX_BUFFER,
|
|
393
|
+
windowsHide: true
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
function listWindowsDisplays() {
|
|
397
|
+
const script = `
|
|
398
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
399
|
+
$s = [System.Windows.Forms.Screen]::AllScreens | ForEach-Object {
|
|
400
|
+
[PSCustomObject]@{ id = $_.DeviceName; name = $_.DeviceName; primary = $_.Primary }
|
|
401
|
+
}
|
|
402
|
+
ConvertTo-Json @($s) -Compress
|
|
403
|
+
`.trim();
|
|
404
|
+
const parsed = JSON.parse(runPowershell(script).trim());
|
|
405
|
+
return parsed.map((d)=>({
|
|
406
|
+
id: String(d.id),
|
|
407
|
+
name: d.name || String(d.id),
|
|
408
|
+
primary: d.primary || false
|
|
409
|
+
}));
|
|
410
|
+
}
|
|
375
411
|
let device_libnut = null;
|
|
376
412
|
let libnutLoadError = null;
|
|
377
413
|
async function getLibnut() {
|
|
@@ -638,6 +674,7 @@ class ComputerDevice {
|
|
|
638
674
|
}
|
|
639
675
|
static async listDisplays() {
|
|
640
676
|
try {
|
|
677
|
+
if ('win32' === process.platform) return listWindowsDisplays();
|
|
641
678
|
const displays = await screenshot_desktop.listDisplays();
|
|
642
679
|
return displays.map((d)=>({
|
|
643
680
|
id: String(d.id),
|
|
@@ -695,7 +732,7 @@ Available Displays: ${displays.length > 0 ? displays.map((d)=>d.name).join(', ')
|
|
|
695
732
|
}
|
|
696
733
|
async healthCheck() {
|
|
697
734
|
console.log('[HealthCheck] Starting health check...');
|
|
698
|
-
console.log("[HealthCheck] @midscene/computer v1.10.0");
|
|
735
|
+
console.log("[HealthCheck] @midscene/computer v1.10.1-beta-20260624112700.0");
|
|
699
736
|
console.log('[HealthCheck] Taking screenshot...');
|
|
700
737
|
const screenshotTimeout = 15000;
|
|
701
738
|
let timeoutId;
|
|
@@ -764,6 +801,7 @@ Available Displays: ${displays.length > 0 ? displays.map((d)=>d.name).join(', ')
|
|
|
764
801
|
debugDevice('Taking screenshot', {
|
|
765
802
|
displayId: this.displayId
|
|
766
803
|
});
|
|
804
|
+
if ('win32' === process.platform) return this.screenshotViaPowershell();
|
|
767
805
|
const options = {
|
|
768
806
|
format: 'png'
|
|
769
807
|
};
|
|
@@ -797,6 +835,40 @@ Please follow these steps:
|
|
|
797
835
|
Original error: ${lastRawMessage}`);
|
|
798
836
|
throw new Error(`Failed to take screenshot: ${lastRawMessage}`);
|
|
799
837
|
}
|
|
838
|
+
screenshotViaPowershell() {
|
|
839
|
+
const deviceName = this.displayId ? String(this.displayId) : '';
|
|
840
|
+
const target = deviceName ? `[System.Windows.Forms.Screen]::AllScreens | Where-Object { $_.DeviceName -eq '${escapePowershellSingleQuoted(deviceName)}' } | Select-Object -First 1` : '$null';
|
|
841
|
+
const script = `
|
|
842
|
+
$ErrorActionPreference = 'Stop'
|
|
843
|
+
Add-Type -AssemblyName System.Windows.Forms, System.Drawing
|
|
844
|
+
Add-Type @"
|
|
845
|
+
using System;
|
|
846
|
+
using System.Runtime.InteropServices;
|
|
847
|
+
public class NutDpi { [DllImport("user32.dll")] public static extern bool SetProcessDPIAware(); }
|
|
848
|
+
"@
|
|
849
|
+
[NutDpi]::SetProcessDPIAware() | Out-Null
|
|
850
|
+
$screen = ${target}
|
|
851
|
+
if (-not $screen) { $screen = [System.Windows.Forms.Screen]::PrimaryScreen }
|
|
852
|
+
$b = $screen.Bounds
|
|
853
|
+
$bmp = New-Object System.Drawing.Bitmap($b.Width, $b.Height)
|
|
854
|
+
$g = [System.Drawing.Graphics]::FromImage($bmp)
|
|
855
|
+
$g.CopyFromScreen($b.X, $b.Y, 0, 0, $bmp.Size)
|
|
856
|
+
$ms = New-Object System.IO.MemoryStream
|
|
857
|
+
$bmp.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png)
|
|
858
|
+
[Console]::Out.Write([Convert]::ToBase64String($ms.ToArray()))
|
|
859
|
+
$g.Dispose(); $bmp.Dispose(); $ms.Dispose()
|
|
860
|
+
`.trim();
|
|
861
|
+
let stdout;
|
|
862
|
+
try {
|
|
863
|
+
stdout = runPowershell(script);
|
|
864
|
+
} catch (error) {
|
|
865
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
866
|
+
throw new Error(`Failed to take screenshot on Windows: ${message}`);
|
|
867
|
+
}
|
|
868
|
+
const body = stdout.trim();
|
|
869
|
+
if (!body) throw new Error('Failed to take screenshot on Windows: PowerShell returned no image data');
|
|
870
|
+
return createImgBase64ByFormat('png', body);
|
|
871
|
+
}
|
|
800
872
|
async size() {
|
|
801
873
|
if (this.displayGeometry) return {
|
|
802
874
|
width: Math.round(this.displayGeometry.bounds.width),
|
|
@@ -2058,7 +2130,7 @@ class ComputerMidsceneTools extends BaseMidsceneTools {
|
|
|
2058
2130
|
const tools = new ComputerMidsceneTools();
|
|
2059
2131
|
runToolsCLI(tools, 'midscene-computer', {
|
|
2060
2132
|
stripPrefix: 'computer_',
|
|
2061
|
-
version: "1.10.0",
|
|
2133
|
+
version: "1.10.1-beta-20260624112700.0",
|
|
2062
2134
|
extraCommands: createReportCliCommands()
|
|
2063
2135
|
}).catch((e)=>{
|
|
2064
2136
|
process.exit(reportCLIError(e));
|
package/dist/es/index.mjs
CHANGED
|
@@ -372,6 +372,42 @@ function sendKeyViaAppleScript(key, modifiers = []) {
|
|
|
372
372
|
script
|
|
373
373
|
]);
|
|
374
374
|
}
|
|
375
|
+
const POWERSHELL_TIMEOUT_MS = 15000;
|
|
376
|
+
const POWERSHELL_MAX_BUFFER = 67108864;
|
|
377
|
+
function escapePowershellSingleQuoted(value) {
|
|
378
|
+
return value.replace(/'/g, "''");
|
|
379
|
+
}
|
|
380
|
+
function runPowershell(script) {
|
|
381
|
+
const encoded = Buffer.from(script, 'utf16le').toString('base64');
|
|
382
|
+
return execFileSync('powershell.exe', [
|
|
383
|
+
'-NoProfile',
|
|
384
|
+
'-NonInteractive',
|
|
385
|
+
'-ExecutionPolicy',
|
|
386
|
+
'Bypass',
|
|
387
|
+
'-EncodedCommand',
|
|
388
|
+
encoded
|
|
389
|
+
], {
|
|
390
|
+
encoding: 'utf8',
|
|
391
|
+
timeout: POWERSHELL_TIMEOUT_MS,
|
|
392
|
+
maxBuffer: POWERSHELL_MAX_BUFFER,
|
|
393
|
+
windowsHide: true
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
function listWindowsDisplays() {
|
|
397
|
+
const script = `
|
|
398
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
399
|
+
$s = [System.Windows.Forms.Screen]::AllScreens | ForEach-Object {
|
|
400
|
+
[PSCustomObject]@{ id = $_.DeviceName; name = $_.DeviceName; primary = $_.Primary }
|
|
401
|
+
}
|
|
402
|
+
ConvertTo-Json @($s) -Compress
|
|
403
|
+
`.trim();
|
|
404
|
+
const parsed = JSON.parse(runPowershell(script).trim());
|
|
405
|
+
return parsed.map((d)=>({
|
|
406
|
+
id: String(d.id),
|
|
407
|
+
name: d.name || String(d.id),
|
|
408
|
+
primary: d.primary || false
|
|
409
|
+
}));
|
|
410
|
+
}
|
|
375
411
|
let device_libnut = null;
|
|
376
412
|
let libnutLoadError = null;
|
|
377
413
|
async function getLibnut() {
|
|
@@ -638,6 +674,7 @@ class ComputerDevice {
|
|
|
638
674
|
}
|
|
639
675
|
static async listDisplays() {
|
|
640
676
|
try {
|
|
677
|
+
if ('win32' === process.platform) return listWindowsDisplays();
|
|
641
678
|
const displays = await screenshot_desktop.listDisplays();
|
|
642
679
|
return displays.map((d)=>({
|
|
643
680
|
id: String(d.id),
|
|
@@ -695,7 +732,7 @@ Available Displays: ${displays.length > 0 ? displays.map((d)=>d.name).join(', ')
|
|
|
695
732
|
}
|
|
696
733
|
async healthCheck() {
|
|
697
734
|
console.log('[HealthCheck] Starting health check...');
|
|
698
|
-
console.log("[HealthCheck] @midscene/computer v1.10.0");
|
|
735
|
+
console.log("[HealthCheck] @midscene/computer v1.10.1-beta-20260624112700.0");
|
|
699
736
|
console.log('[HealthCheck] Taking screenshot...');
|
|
700
737
|
const screenshotTimeout = 15000;
|
|
701
738
|
let timeoutId;
|
|
@@ -764,6 +801,7 @@ Available Displays: ${displays.length > 0 ? displays.map((d)=>d.name).join(', ')
|
|
|
764
801
|
debugDevice('Taking screenshot', {
|
|
765
802
|
displayId: this.displayId
|
|
766
803
|
});
|
|
804
|
+
if ('win32' === process.platform) return this.screenshotViaPowershell();
|
|
767
805
|
const options = {
|
|
768
806
|
format: 'png'
|
|
769
807
|
};
|
|
@@ -797,6 +835,40 @@ Please follow these steps:
|
|
|
797
835
|
Original error: ${lastRawMessage}`);
|
|
798
836
|
throw new Error(`Failed to take screenshot: ${lastRawMessage}`);
|
|
799
837
|
}
|
|
838
|
+
screenshotViaPowershell() {
|
|
839
|
+
const deviceName = this.displayId ? String(this.displayId) : '';
|
|
840
|
+
const target = deviceName ? `[System.Windows.Forms.Screen]::AllScreens | Where-Object { $_.DeviceName -eq '${escapePowershellSingleQuoted(deviceName)}' } | Select-Object -First 1` : '$null';
|
|
841
|
+
const script = `
|
|
842
|
+
$ErrorActionPreference = 'Stop'
|
|
843
|
+
Add-Type -AssemblyName System.Windows.Forms, System.Drawing
|
|
844
|
+
Add-Type @"
|
|
845
|
+
using System;
|
|
846
|
+
using System.Runtime.InteropServices;
|
|
847
|
+
public class NutDpi { [DllImport("user32.dll")] public static extern bool SetProcessDPIAware(); }
|
|
848
|
+
"@
|
|
849
|
+
[NutDpi]::SetProcessDPIAware() | Out-Null
|
|
850
|
+
$screen = ${target}
|
|
851
|
+
if (-not $screen) { $screen = [System.Windows.Forms.Screen]::PrimaryScreen }
|
|
852
|
+
$b = $screen.Bounds
|
|
853
|
+
$bmp = New-Object System.Drawing.Bitmap($b.Width, $b.Height)
|
|
854
|
+
$g = [System.Drawing.Graphics]::FromImage($bmp)
|
|
855
|
+
$g.CopyFromScreen($b.X, $b.Y, 0, 0, $bmp.Size)
|
|
856
|
+
$ms = New-Object System.IO.MemoryStream
|
|
857
|
+
$bmp.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png)
|
|
858
|
+
[Console]::Out.Write([Convert]::ToBase64String($ms.ToArray()))
|
|
859
|
+
$g.Dispose(); $bmp.Dispose(); $ms.Dispose()
|
|
860
|
+
`.trim();
|
|
861
|
+
let stdout;
|
|
862
|
+
try {
|
|
863
|
+
stdout = runPowershell(script);
|
|
864
|
+
} catch (error) {
|
|
865
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
866
|
+
throw new Error(`Failed to take screenshot on Windows: ${message}`);
|
|
867
|
+
}
|
|
868
|
+
const body = stdout.trim();
|
|
869
|
+
if (!body) throw new Error('Failed to take screenshot on Windows: PowerShell returned no image data');
|
|
870
|
+
return createImgBase64ByFormat('png', body);
|
|
871
|
+
}
|
|
800
872
|
async size() {
|
|
801
873
|
if (this.displayGeometry) return {
|
|
802
874
|
width: Math.round(this.displayGeometry.bounds.width),
|
|
@@ -2099,7 +2171,7 @@ class ComputerMidsceneTools extends BaseMidsceneTools {
|
|
|
2099
2171
|
}
|
|
2100
2172
|
}
|
|
2101
2173
|
function version() {
|
|
2102
|
-
const currentVersion = "1.10.0";
|
|
2174
|
+
const currentVersion = "1.10.1-beta-20260624112700.0";
|
|
2103
2175
|
console.log(`@midscene/computer v${currentVersion}`);
|
|
2104
2176
|
return currentVersion;
|
|
2105
2177
|
}
|
package/dist/lib/cli.js
CHANGED
|
@@ -398,6 +398,42 @@ function sendKeyViaAppleScript(key, modifiers = []) {
|
|
|
398
398
|
script
|
|
399
399
|
]);
|
|
400
400
|
}
|
|
401
|
+
const POWERSHELL_TIMEOUT_MS = 15000;
|
|
402
|
+
const POWERSHELL_MAX_BUFFER = 67108864;
|
|
403
|
+
function escapePowershellSingleQuoted(value) {
|
|
404
|
+
return value.replace(/'/g, "''");
|
|
405
|
+
}
|
|
406
|
+
function runPowershell(script) {
|
|
407
|
+
const encoded = Buffer.from(script, 'utf16le').toString('base64');
|
|
408
|
+
return (0, external_node_child_process_namespaceObject.execFileSync)('powershell.exe', [
|
|
409
|
+
'-NoProfile',
|
|
410
|
+
'-NonInteractive',
|
|
411
|
+
'-ExecutionPolicy',
|
|
412
|
+
'Bypass',
|
|
413
|
+
'-EncodedCommand',
|
|
414
|
+
encoded
|
|
415
|
+
], {
|
|
416
|
+
encoding: 'utf8',
|
|
417
|
+
timeout: POWERSHELL_TIMEOUT_MS,
|
|
418
|
+
maxBuffer: POWERSHELL_MAX_BUFFER,
|
|
419
|
+
windowsHide: true
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
function listWindowsDisplays() {
|
|
423
|
+
const script = `
|
|
424
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
425
|
+
$s = [System.Windows.Forms.Screen]::AllScreens | ForEach-Object {
|
|
426
|
+
[PSCustomObject]@{ id = $_.DeviceName; name = $_.DeviceName; primary = $_.Primary }
|
|
427
|
+
}
|
|
428
|
+
ConvertTo-Json @($s) -Compress
|
|
429
|
+
`.trim();
|
|
430
|
+
const parsed = JSON.parse(runPowershell(script).trim());
|
|
431
|
+
return parsed.map((d)=>({
|
|
432
|
+
id: String(d.id),
|
|
433
|
+
name: d.name || String(d.id),
|
|
434
|
+
primary: d.primary || false
|
|
435
|
+
}));
|
|
436
|
+
}
|
|
401
437
|
let device_libnut = null;
|
|
402
438
|
let libnutLoadError = null;
|
|
403
439
|
async function getLibnut() {
|
|
@@ -664,6 +700,7 @@ class ComputerDevice {
|
|
|
664
700
|
}
|
|
665
701
|
static async listDisplays() {
|
|
666
702
|
try {
|
|
703
|
+
if ('win32' === process.platform) return listWindowsDisplays();
|
|
667
704
|
const displays = await external_screenshot_desktop_default().listDisplays();
|
|
668
705
|
return displays.map((d)=>({
|
|
669
706
|
id: String(d.id),
|
|
@@ -721,7 +758,7 @@ Available Displays: ${displays.length > 0 ? displays.map((d)=>d.name).join(', ')
|
|
|
721
758
|
}
|
|
722
759
|
async healthCheck() {
|
|
723
760
|
console.log('[HealthCheck] Starting health check...');
|
|
724
|
-
console.log("[HealthCheck] @midscene/computer v1.10.0");
|
|
761
|
+
console.log("[HealthCheck] @midscene/computer v1.10.1-beta-20260624112700.0");
|
|
725
762
|
console.log('[HealthCheck] Taking screenshot...');
|
|
726
763
|
const screenshotTimeout = 15000;
|
|
727
764
|
let timeoutId;
|
|
@@ -790,6 +827,7 @@ Available Displays: ${displays.length > 0 ? displays.map((d)=>d.name).join(', ')
|
|
|
790
827
|
debugDevice('Taking screenshot', {
|
|
791
828
|
displayId: this.displayId
|
|
792
829
|
});
|
|
830
|
+
if ('win32' === process.platform) return this.screenshotViaPowershell();
|
|
793
831
|
const options = {
|
|
794
832
|
format: 'png'
|
|
795
833
|
};
|
|
@@ -823,6 +861,40 @@ Please follow these steps:
|
|
|
823
861
|
Original error: ${lastRawMessage}`);
|
|
824
862
|
throw new Error(`Failed to take screenshot: ${lastRawMessage}`);
|
|
825
863
|
}
|
|
864
|
+
screenshotViaPowershell() {
|
|
865
|
+
const deviceName = this.displayId ? String(this.displayId) : '';
|
|
866
|
+
const target = deviceName ? `[System.Windows.Forms.Screen]::AllScreens | Where-Object { $_.DeviceName -eq '${escapePowershellSingleQuoted(deviceName)}' } | Select-Object -First 1` : '$null';
|
|
867
|
+
const script = `
|
|
868
|
+
$ErrorActionPreference = 'Stop'
|
|
869
|
+
Add-Type -AssemblyName System.Windows.Forms, System.Drawing
|
|
870
|
+
Add-Type @"
|
|
871
|
+
using System;
|
|
872
|
+
using System.Runtime.InteropServices;
|
|
873
|
+
public class NutDpi { [DllImport("user32.dll")] public static extern bool SetProcessDPIAware(); }
|
|
874
|
+
"@
|
|
875
|
+
[NutDpi]::SetProcessDPIAware() | Out-Null
|
|
876
|
+
$screen = ${target}
|
|
877
|
+
if (-not $screen) { $screen = [System.Windows.Forms.Screen]::PrimaryScreen }
|
|
878
|
+
$b = $screen.Bounds
|
|
879
|
+
$bmp = New-Object System.Drawing.Bitmap($b.Width, $b.Height)
|
|
880
|
+
$g = [System.Drawing.Graphics]::FromImage($bmp)
|
|
881
|
+
$g.CopyFromScreen($b.X, $b.Y, 0, 0, $bmp.Size)
|
|
882
|
+
$ms = New-Object System.IO.MemoryStream
|
|
883
|
+
$bmp.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png)
|
|
884
|
+
[Console]::Out.Write([Convert]::ToBase64String($ms.ToArray()))
|
|
885
|
+
$g.Dispose(); $bmp.Dispose(); $ms.Dispose()
|
|
886
|
+
`.trim();
|
|
887
|
+
let stdout;
|
|
888
|
+
try {
|
|
889
|
+
stdout = runPowershell(script);
|
|
890
|
+
} catch (error) {
|
|
891
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
892
|
+
throw new Error(`Failed to take screenshot on Windows: ${message}`);
|
|
893
|
+
}
|
|
894
|
+
const body = stdout.trim();
|
|
895
|
+
if (!body) throw new Error('Failed to take screenshot on Windows: PowerShell returned no image data');
|
|
896
|
+
return (0, img_namespaceObject.createImgBase64ByFormat)('png', body);
|
|
897
|
+
}
|
|
826
898
|
async size() {
|
|
827
899
|
if (this.displayGeometry) return {
|
|
828
900
|
width: Math.round(this.displayGeometry.bounds.width),
|
|
@@ -2085,7 +2157,7 @@ class ComputerMidsceneTools extends base_tools_namespaceObject.BaseMidsceneTools
|
|
|
2085
2157
|
const tools = new ComputerMidsceneTools();
|
|
2086
2158
|
(0, cli_namespaceObject.runToolsCLI)(tools, 'midscene-computer', {
|
|
2087
2159
|
stripPrefix: 'computer_',
|
|
2088
|
-
version: "1.10.0",
|
|
2160
|
+
version: "1.10.1-beta-20260624112700.0",
|
|
2089
2161
|
extraCommands: (0, core_namespaceObject.createReportCliCommands)()
|
|
2090
2162
|
}).catch((e)=>{
|
|
2091
2163
|
process.exit((0, cli_namespaceObject.reportCLIError)(e));
|
package/dist/lib/index.js
CHANGED
|
@@ -424,6 +424,42 @@ function sendKeyViaAppleScript(key, modifiers = []) {
|
|
|
424
424
|
script
|
|
425
425
|
]);
|
|
426
426
|
}
|
|
427
|
+
const POWERSHELL_TIMEOUT_MS = 15000;
|
|
428
|
+
const POWERSHELL_MAX_BUFFER = 67108864;
|
|
429
|
+
function escapePowershellSingleQuoted(value) {
|
|
430
|
+
return value.replace(/'/g, "''");
|
|
431
|
+
}
|
|
432
|
+
function runPowershell(script) {
|
|
433
|
+
const encoded = Buffer.from(script, 'utf16le').toString('base64');
|
|
434
|
+
return (0, external_node_child_process_namespaceObject.execFileSync)('powershell.exe', [
|
|
435
|
+
'-NoProfile',
|
|
436
|
+
'-NonInteractive',
|
|
437
|
+
'-ExecutionPolicy',
|
|
438
|
+
'Bypass',
|
|
439
|
+
'-EncodedCommand',
|
|
440
|
+
encoded
|
|
441
|
+
], {
|
|
442
|
+
encoding: 'utf8',
|
|
443
|
+
timeout: POWERSHELL_TIMEOUT_MS,
|
|
444
|
+
maxBuffer: POWERSHELL_MAX_BUFFER,
|
|
445
|
+
windowsHide: true
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
function listWindowsDisplays() {
|
|
449
|
+
const script = `
|
|
450
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
451
|
+
$s = [System.Windows.Forms.Screen]::AllScreens | ForEach-Object {
|
|
452
|
+
[PSCustomObject]@{ id = $_.DeviceName; name = $_.DeviceName; primary = $_.Primary }
|
|
453
|
+
}
|
|
454
|
+
ConvertTo-Json @($s) -Compress
|
|
455
|
+
`.trim();
|
|
456
|
+
const parsed = JSON.parse(runPowershell(script).trim());
|
|
457
|
+
return parsed.map((d)=>({
|
|
458
|
+
id: String(d.id),
|
|
459
|
+
name: d.name || String(d.id),
|
|
460
|
+
primary: d.primary || false
|
|
461
|
+
}));
|
|
462
|
+
}
|
|
427
463
|
let device_libnut = null;
|
|
428
464
|
let libnutLoadError = null;
|
|
429
465
|
async function getLibnut() {
|
|
@@ -690,6 +726,7 @@ class ComputerDevice {
|
|
|
690
726
|
}
|
|
691
727
|
static async listDisplays() {
|
|
692
728
|
try {
|
|
729
|
+
if ('win32' === process.platform) return listWindowsDisplays();
|
|
693
730
|
const displays = await external_screenshot_desktop_default().listDisplays();
|
|
694
731
|
return displays.map((d)=>({
|
|
695
732
|
id: String(d.id),
|
|
@@ -747,7 +784,7 @@ Available Displays: ${displays.length > 0 ? displays.map((d)=>d.name).join(', ')
|
|
|
747
784
|
}
|
|
748
785
|
async healthCheck() {
|
|
749
786
|
console.log('[HealthCheck] Starting health check...');
|
|
750
|
-
console.log("[HealthCheck] @midscene/computer v1.10.0");
|
|
787
|
+
console.log("[HealthCheck] @midscene/computer v1.10.1-beta-20260624112700.0");
|
|
751
788
|
console.log('[HealthCheck] Taking screenshot...');
|
|
752
789
|
const screenshotTimeout = 15000;
|
|
753
790
|
let timeoutId;
|
|
@@ -816,6 +853,7 @@ Available Displays: ${displays.length > 0 ? displays.map((d)=>d.name).join(', ')
|
|
|
816
853
|
debugDevice('Taking screenshot', {
|
|
817
854
|
displayId: this.displayId
|
|
818
855
|
});
|
|
856
|
+
if ('win32' === process.platform) return this.screenshotViaPowershell();
|
|
819
857
|
const options = {
|
|
820
858
|
format: 'png'
|
|
821
859
|
};
|
|
@@ -849,6 +887,40 @@ Please follow these steps:
|
|
|
849
887
|
Original error: ${lastRawMessage}`);
|
|
850
888
|
throw new Error(`Failed to take screenshot: ${lastRawMessage}`);
|
|
851
889
|
}
|
|
890
|
+
screenshotViaPowershell() {
|
|
891
|
+
const deviceName = this.displayId ? String(this.displayId) : '';
|
|
892
|
+
const target = deviceName ? `[System.Windows.Forms.Screen]::AllScreens | Where-Object { $_.DeviceName -eq '${escapePowershellSingleQuoted(deviceName)}' } | Select-Object -First 1` : '$null';
|
|
893
|
+
const script = `
|
|
894
|
+
$ErrorActionPreference = 'Stop'
|
|
895
|
+
Add-Type -AssemblyName System.Windows.Forms, System.Drawing
|
|
896
|
+
Add-Type @"
|
|
897
|
+
using System;
|
|
898
|
+
using System.Runtime.InteropServices;
|
|
899
|
+
public class NutDpi { [DllImport("user32.dll")] public static extern bool SetProcessDPIAware(); }
|
|
900
|
+
"@
|
|
901
|
+
[NutDpi]::SetProcessDPIAware() | Out-Null
|
|
902
|
+
$screen = ${target}
|
|
903
|
+
if (-not $screen) { $screen = [System.Windows.Forms.Screen]::PrimaryScreen }
|
|
904
|
+
$b = $screen.Bounds
|
|
905
|
+
$bmp = New-Object System.Drawing.Bitmap($b.Width, $b.Height)
|
|
906
|
+
$g = [System.Drawing.Graphics]::FromImage($bmp)
|
|
907
|
+
$g.CopyFromScreen($b.X, $b.Y, 0, 0, $bmp.Size)
|
|
908
|
+
$ms = New-Object System.IO.MemoryStream
|
|
909
|
+
$bmp.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png)
|
|
910
|
+
[Console]::Out.Write([Convert]::ToBase64String($ms.ToArray()))
|
|
911
|
+
$g.Dispose(); $bmp.Dispose(); $ms.Dispose()
|
|
912
|
+
`.trim();
|
|
913
|
+
let stdout;
|
|
914
|
+
try {
|
|
915
|
+
stdout = runPowershell(script);
|
|
916
|
+
} catch (error) {
|
|
917
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
918
|
+
throw new Error(`Failed to take screenshot on Windows: ${message}`);
|
|
919
|
+
}
|
|
920
|
+
const body = stdout.trim();
|
|
921
|
+
if (!body) throw new Error('Failed to take screenshot on Windows: PowerShell returned no image data');
|
|
922
|
+
return (0, img_namespaceObject.createImgBase64ByFormat)('png', body);
|
|
923
|
+
}
|
|
852
924
|
async size() {
|
|
853
925
|
if (this.displayGeometry) return {
|
|
854
926
|
width: Math.round(this.displayGeometry.bounds.width),
|
|
@@ -2157,7 +2229,7 @@ class ComputerMidsceneTools extends base_tools_namespaceObject.BaseMidsceneTools
|
|
|
2157
2229
|
}
|
|
2158
2230
|
const env_namespaceObject = require("@midscene/shared/env");
|
|
2159
2231
|
function version() {
|
|
2160
|
-
const currentVersion = "1.10.0";
|
|
2232
|
+
const currentVersion = "1.10.1-beta-20260624112700.0";
|
|
2161
2233
|
console.log(`@midscene/computer v${currentVersion}`);
|
|
2162
2234
|
return currentVersion;
|
|
2163
2235
|
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -101,6 +101,20 @@ export declare class ComputerDevice implements AbstractInterface {
|
|
|
101
101
|
*/
|
|
102
102
|
private isRunningAsAdmin;
|
|
103
103
|
screenshotBase64(): Promise<string>;
|
|
104
|
+
/**
|
|
105
|
+
* Windows screenshot path that bypasses screenshot-desktop's polyglot .bat
|
|
106
|
+
* (see screenshotBase64 for the rationale). Captures via PowerShell +
|
|
107
|
+
* System.Drawing, which honors `displayId` by matching the monitor's
|
|
108
|
+
* DeviceName and captures in virtual-desktop coordinates, so secondary
|
|
109
|
+
* displays — including those at negative offsets — are supported.
|
|
110
|
+
*
|
|
111
|
+
* Note: the script makes the process system-DPI-aware (SetProcessDPIAware)
|
|
112
|
+
* so captures return physical pixels. On mixed-DPI multi-monitor setups a
|
|
113
|
+
* non-primary monitor may still be off by its per-monitor scale factor;
|
|
114
|
+
* system DPI awareness covers the common single-scale and uniform-scale
|
|
115
|
+
* cases that #2150 reports.
|
|
116
|
+
*/
|
|
117
|
+
private screenshotViaPowershell;
|
|
104
118
|
size(): Promise<Size>;
|
|
105
119
|
private toGlobalPoint;
|
|
106
120
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@midscene/computer",
|
|
3
|
-
"version": "1.10.0",
|
|
3
|
+
"version": "1.10.1-beta-20260624112700.0",
|
|
4
4
|
"description": "Midscene.js Computer Desktop Automation",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
"@computer-use/libnut": "^4.2.0",
|
|
34
34
|
"clipboardy": "^4.0.0",
|
|
35
35
|
"screenshot-desktop": "^1.15.3",
|
|
36
|
-
"@midscene/shared": "1.10.0",
|
|
37
|
-
"@midscene/core": "1.10.0"
|
|
36
|
+
"@midscene/shared": "1.10.1-beta-20260624112700.0",
|
|
37
|
+
"@midscene/core": "1.10.1-beta-20260624112700.0"
|
|
38
38
|
},
|
|
39
39
|
"optionalDependencies": {
|
|
40
40
|
"node-mac-permissions": "2.5.0"
|