@midscene/computer 1.12.2 → 1.12.3-beta-20260828085408.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 +239 -75
- package/dist/es/index.mjs +239 -75
- package/dist/lib/cli.js +239 -75
- package/dist/lib/index.js +239 -75
- package/dist/types/index.d.ts +7 -4
- package/package.json +3 -3
package/dist/es/cli.mjs
CHANGED
|
@@ -29,6 +29,7 @@ function _define_property(obj, key, value) {
|
|
|
29
29
|
else obj[key] = value;
|
|
30
30
|
return obj;
|
|
31
31
|
}
|
|
32
|
+
const MOUSE_COORDINATE_TOLERANCE_PX = 5;
|
|
32
33
|
class ComputerInputDriver {
|
|
33
34
|
destroy() {
|
|
34
35
|
if (this.destroyed) return;
|
|
@@ -44,6 +45,15 @@ class ComputerInputDriver {
|
|
|
44
45
|
moveMouse(x, y) {
|
|
45
46
|
this.getLibnutOrThrow('moveMouse').moveMouse(x, y);
|
|
46
47
|
}
|
|
48
|
+
assertMousePosition(targetX, targetY, context) {
|
|
49
|
+
const current = this.getMousePos();
|
|
50
|
+
const drift = {
|
|
51
|
+
x: current.x - targetX,
|
|
52
|
+
y: current.y - targetY
|
|
53
|
+
};
|
|
54
|
+
if (Math.abs(drift.x) > MOUSE_COORDINATE_TOLERANCE_PX || Math.abs(drift.y) > MOUSE_COORDINATE_TOLERANCE_PX) throw new Error(`${context}: expected (${targetX}, ${targetY}), got (${current.x}, ${current.y}), drift=(${drift.x}, ${drift.y})`);
|
|
55
|
+
return drift;
|
|
56
|
+
}
|
|
47
57
|
focusActiveWindow() {
|
|
48
58
|
const lib = this.getLibnutOrThrow('focusActiveWindow');
|
|
49
59
|
if ('function' != typeof lib.getActiveWindow || 'function' != typeof lib.focusWindow) return false;
|
|
@@ -184,6 +194,83 @@ class ComputerInputDriver {
|
|
|
184
194
|
this.pendingInputDelayWaits = new Set();
|
|
185
195
|
}
|
|
186
196
|
}
|
|
197
|
+
function windows_pointer_define_property(obj, key, value) {
|
|
198
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
199
|
+
value: value,
|
|
200
|
+
enumerable: true,
|
|
201
|
+
configurable: true,
|
|
202
|
+
writable: true
|
|
203
|
+
});
|
|
204
|
+
else obj[key] = value;
|
|
205
|
+
return obj;
|
|
206
|
+
}
|
|
207
|
+
const WINDOWS_POINTER_TOLERANCE_PX = 5;
|
|
208
|
+
function assertFinitePoint(point, context) {
|
|
209
|
+
if (!Number.isFinite(point.x) || !Number.isFinite(point.y)) throw new Error(`${context} must contain finite coordinates, got (${point.x}, ${point.y})`);
|
|
210
|
+
}
|
|
211
|
+
function parseWindowsPointerPosition(output, context) {
|
|
212
|
+
const match = output.trim().match(/^(-?\d+),(-?\d+)$/);
|
|
213
|
+
if (!match) throw new Error(`${context} returned an invalid cursor position: ${JSON.stringify(output.trim())}`);
|
|
214
|
+
return {
|
|
215
|
+
x: Number(match[1]),
|
|
216
|
+
y: Number(match[2])
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
function windowsPointerPositionScript() {
|
|
220
|
+
return `
|
|
221
|
+
Add-Type -AssemblyName System.Windows.Forms, System.Drawing
|
|
222
|
+
$position = [System.Windows.Forms.Cursor]::Position
|
|
223
|
+
[Console]::Out.Write(('{0},{1}' -f $position.X, $position.Y))
|
|
224
|
+
`.trim();
|
|
225
|
+
}
|
|
226
|
+
function windowsPointerMoveScript(point, options) {
|
|
227
|
+
assertFinitePoint(point, 'Windows pointer target');
|
|
228
|
+
if (options?.smoothSteps !== void 0 && !Number.isFinite(options.smoothSteps) || options?.smoothDelayMs !== void 0 && !Number.isFinite(options.smoothDelayMs)) throw new Error('Windows pointer smoothing options must be finite numbers');
|
|
229
|
+
const targetX = Math.round(point.x);
|
|
230
|
+
const targetY = Math.round(point.y);
|
|
231
|
+
const smoothSteps = Math.max(1, Math.round(options?.smoothSteps ?? 1));
|
|
232
|
+
const smoothDelayMs = Math.max(0, Math.round(options?.smoothDelayMs ?? 0));
|
|
233
|
+
return `
|
|
234
|
+
Add-Type -AssemblyName System.Windows.Forms, System.Drawing
|
|
235
|
+
$targetX = ${targetX}
|
|
236
|
+
$targetY = ${targetY}
|
|
237
|
+
$smoothSteps = ${smoothSteps}
|
|
238
|
+
$smoothDelayMs = ${smoothDelayMs}
|
|
239
|
+
$start = [System.Windows.Forms.Cursor]::Position
|
|
240
|
+
for ($step = 1; $step -le $smoothSteps; $step += 1) {
|
|
241
|
+
$x = [int][Math]::Round($start.X + (($targetX - $start.X) * $step / [double]$smoothSteps))
|
|
242
|
+
$y = [int][Math]::Round($start.Y + (($targetY - $start.Y) * $step / [double]$smoothSteps))
|
|
243
|
+
[System.Windows.Forms.Cursor]::Position = [System.Drawing.Point]::new($x, $y)
|
|
244
|
+
if ($smoothDelayMs -gt 0 -and $step -lt $smoothSteps) {
|
|
245
|
+
Start-Sleep -Milliseconds $smoothDelayMs
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
[System.Windows.Forms.Cursor]::Position = [System.Drawing.Point]::new($targetX, $targetY)
|
|
249
|
+
$position = [System.Windows.Forms.Cursor]::Position
|
|
250
|
+
[Console]::Out.Write(('{0},{1}' -f $position.X, $position.Y))
|
|
251
|
+
`.trim();
|
|
252
|
+
}
|
|
253
|
+
class WindowsPointerDriver {
|
|
254
|
+
getPosition() {
|
|
255
|
+
return parseWindowsPointerPosition(this.options.runPowershell(windowsPointerPositionScript()), 'Windows pointer query');
|
|
256
|
+
}
|
|
257
|
+
moveTo(point, options) {
|
|
258
|
+
return parseWindowsPointerPosition(this.options.runPowershell(windowsPointerMoveScript(point, options)), 'Windows pointer move');
|
|
259
|
+
}
|
|
260
|
+
constructor(options){
|
|
261
|
+
windows_pointer_define_property(this, "options", void 0);
|
|
262
|
+
this.options = options;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
function windowsPointerDrift(expected, actual) {
|
|
266
|
+
return {
|
|
267
|
+
x: actual.x - expected.x,
|
|
268
|
+
y: actual.y - expected.y
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
function windowsPointerIsWithinTolerance(drift) {
|
|
272
|
+
return Math.abs(drift.x) <= WINDOWS_POINTER_TOLERANCE_PX && Math.abs(drift.y) <= WINDOWS_POINTER_TOLERANCE_PX;
|
|
273
|
+
}
|
|
187
274
|
const debugXvfb = getDebug('computer:xvfb');
|
|
188
275
|
const xvfbCleanupMonitorScript = String.raw`
|
|
189
276
|
const parentPid = Number(process.argv[1]);
|
|
@@ -461,19 +548,33 @@ function runPowershell(script) {
|
|
|
461
548
|
windowsHide: true
|
|
462
549
|
});
|
|
463
550
|
}
|
|
464
|
-
function
|
|
551
|
+
function readWindowsDisplayGeometries() {
|
|
465
552
|
const script = `
|
|
466
553
|
Add-Type -AssemblyName System.Windows.Forms
|
|
467
554
|
$s = [System.Windows.Forms.Screen]::AllScreens | ForEach-Object {
|
|
468
|
-
|
|
555
|
+
$b = $_.Bounds
|
|
556
|
+
[PSCustomObject]@{
|
|
557
|
+
id = $_.DeviceName
|
|
558
|
+
name = $_.DeviceName
|
|
559
|
+
primary = $_.Primary
|
|
560
|
+
bounds = [PSCustomObject]@{ x = $b.X; y = $b.Y; width = $b.Width; height = $b.Height }
|
|
561
|
+
}
|
|
469
562
|
}
|
|
470
563
|
ConvertTo-Json @($s) -Compress
|
|
471
564
|
`.trim();
|
|
472
|
-
const
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
565
|
+
const output = runPowershell(script).trim();
|
|
566
|
+
if (!output) throw new Error('Windows display enumeration returned no data');
|
|
567
|
+
const parsed = JSON.parse(output);
|
|
568
|
+
if (!Array.isArray(parsed)) throw new Error('Windows display enumeration returned invalid data');
|
|
569
|
+
const displays = parsed.filter(isWindowsDisplayGeometry);
|
|
570
|
+
if (displays.length !== parsed.length || 0 === displays.length) throw new Error('Windows display enumeration returned invalid geometry');
|
|
571
|
+
return displays;
|
|
572
|
+
}
|
|
573
|
+
function listWindowsDisplays(geometries = readWindowsDisplayGeometries()) {
|
|
574
|
+
return geometries.map((display)=>({
|
|
575
|
+
id: display.id,
|
|
576
|
+
name: display.name,
|
|
577
|
+
primary: display.primary
|
|
477
578
|
}));
|
|
478
579
|
}
|
|
479
580
|
let device_libnut = null;
|
|
@@ -575,6 +676,16 @@ function getDisplayInfoBinary() {
|
|
|
575
676
|
function isFiniteNumber(value) {
|
|
576
677
|
return 'number' == typeof value && Number.isFinite(value);
|
|
577
678
|
}
|
|
679
|
+
function isDisplayBounds(value) {
|
|
680
|
+
if (!value || 'object' != typeof value) return false;
|
|
681
|
+
const bounds = value;
|
|
682
|
+
return isFiniteNumber(bounds.x) && isFiniteNumber(bounds.y) && isFiniteNumber(bounds.width) && isFiniteNumber(bounds.height) && bounds.width > 0 && bounds.height > 0;
|
|
683
|
+
}
|
|
684
|
+
function isWindowsDisplayGeometry(value) {
|
|
685
|
+
if (!value || 'object' != typeof value) return false;
|
|
686
|
+
const candidate = value;
|
|
687
|
+
return 'string' == typeof candidate.id && candidate.id.length > 0 && 'string' == typeof candidate.name && 'boolean' == typeof candidate.primary && isDisplayBounds(candidate.bounds);
|
|
688
|
+
}
|
|
578
689
|
function isDarwinDisplayGeometry(value) {
|
|
579
690
|
if (!value || 'object' != typeof value) return false;
|
|
580
691
|
const candidate = value;
|
|
@@ -617,9 +728,11 @@ function readDarwinFrontmostApplication() {
|
|
|
617
728
|
return;
|
|
618
729
|
}
|
|
619
730
|
}
|
|
620
|
-
async function pressMouseAtGlobalPoint(inputDriver, targetX, targetY, holdDuration, reason) {
|
|
621
|
-
|
|
622
|
-
|
|
731
|
+
async function pressMouseAtGlobalPoint(inputDriver, targetX, targetY, current, holdDuration, reason) {
|
|
732
|
+
const drift = {
|
|
733
|
+
x: current.x - targetX,
|
|
734
|
+
y: current.y - targetY
|
|
735
|
+
};
|
|
623
736
|
debugComputerInput('tap mouse moved %o', {
|
|
624
737
|
reason,
|
|
625
738
|
target: {
|
|
@@ -627,10 +740,7 @@ async function pressMouseAtGlobalPoint(inputDriver, targetX, targetY, holdDurati
|
|
|
627
740
|
y: targetY
|
|
628
741
|
},
|
|
629
742
|
current,
|
|
630
|
-
drift
|
|
631
|
-
x: current.x - targetX,
|
|
632
|
-
y: current.y - targetY
|
|
633
|
-
}
|
|
743
|
+
drift
|
|
634
744
|
});
|
|
635
745
|
await inputDriver.withMouseButton('left', async ()=>{
|
|
636
746
|
debugComputerInput('tap mouse down %o', {
|
|
@@ -649,9 +759,18 @@ function resolveDarwinDisplayGeometryFromList(displayId, displays) {
|
|
|
649
759
|
if (void 0 === displayId || '' === displayId) return displays.find((display)=>display.primary) || displays.find((display)=>0 === display.screenIndex) || displays[0];
|
|
650
760
|
return displays.find((display)=>display.screenIndex === screenIndex) || displays.find((display)=>display.cgDisplayId === screenIndex);
|
|
651
761
|
}
|
|
652
|
-
function
|
|
653
|
-
if (
|
|
654
|
-
|
|
762
|
+
function resolveWindowsDisplayGeometryFromList(displayId, displays) {
|
|
763
|
+
if (!displays.length) return;
|
|
764
|
+
if (void 0 === displayId || '' === displayId) return displays.find((display)=>display.primary) || displays[0];
|
|
765
|
+
return displays.find((display)=>display.id === displayId);
|
|
766
|
+
}
|
|
767
|
+
function resolveDisplayGeometry(displayId, windowsDisplays) {
|
|
768
|
+
if ('darwin' === process.platform) return resolveDarwinDisplayGeometryFromList(displayId, readDarwinDisplayGeometries());
|
|
769
|
+
if ('win32' === process.platform) {
|
|
770
|
+
const geometry = resolveWindowsDisplayGeometryFromList(displayId, windowsDisplays ?? readWindowsDisplayGeometries());
|
|
771
|
+
if (!geometry) throw new Error(displayId ? `Requested Windows display not found: ${displayId}` : 'No Windows displays were detected');
|
|
772
|
+
return geometry;
|
|
773
|
+
}
|
|
655
774
|
}
|
|
656
775
|
function mapDisplayLocalPointToGlobal(point, geometry) {
|
|
657
776
|
if (!geometry) return point;
|
|
@@ -737,6 +856,30 @@ function normalizePrimaryKey(key) {
|
|
|
737
856
|
return KEY_NAME_MAP[lowerKey] || lowerKey;
|
|
738
857
|
}
|
|
739
858
|
class ComputerDevice {
|
|
859
|
+
async moveGlobalPointer(point, context, smooth) {
|
|
860
|
+
if (this.destroyed) throw new Error('ComputerDevice has been destroyed');
|
|
861
|
+
const target = {
|
|
862
|
+
x: Math.round(point.x),
|
|
863
|
+
y: Math.round(point.y)
|
|
864
|
+
};
|
|
865
|
+
if ('win32' === process.platform) {
|
|
866
|
+
const actual = this.windowsPointerDriver.moveTo(target, {
|
|
867
|
+
smoothSteps: smooth?.smoothSteps,
|
|
868
|
+
smoothDelayMs: smooth?.smoothDelay
|
|
869
|
+
});
|
|
870
|
+
const drift = windowsPointerDrift(target, actual);
|
|
871
|
+
if (!windowsPointerIsWithinTolerance(drift)) throw new Error(`${context}: expected (${target.x}, ${target.y}), got (${actual.x}, ${actual.y}), drift=(${drift.x}, ${drift.y})`);
|
|
872
|
+
await this.inputDriver.delay(CLICK_SETTLE_DELAY);
|
|
873
|
+
return actual;
|
|
874
|
+
}
|
|
875
|
+
if (smooth) await this.inputDriver.smoothMoveMouse(target.x, target.y, smooth.smoothSteps, smooth.smoothDelay);
|
|
876
|
+
else this.inputDriver.moveMouse(target.x, target.y);
|
|
877
|
+
await this.inputDriver.delay(CLICK_SETTLE_DELAY);
|
|
878
|
+
return target;
|
|
879
|
+
}
|
|
880
|
+
moveDisplayPointer(point, context, smooth) {
|
|
881
|
+
return this.moveGlobalPointer(this.toGlobalPoint(point), context, smooth);
|
|
882
|
+
}
|
|
740
883
|
async focusKeyboardTarget(element, delayMs) {
|
|
741
884
|
const [x, y] = element.center;
|
|
742
885
|
if ('darwin' === process.platform) await this.inputPrimitives.pointer.tap({
|
|
@@ -744,11 +887,10 @@ class ComputerDevice {
|
|
|
744
887
|
y
|
|
745
888
|
});
|
|
746
889
|
else {
|
|
747
|
-
|
|
890
|
+
await this.moveDisplayPointer({
|
|
748
891
|
x,
|
|
749
892
|
y
|
|
750
|
-
});
|
|
751
|
-
this.inputDriver.moveMouse(Math.round(point.x), Math.round(point.y));
|
|
893
|
+
}, 'Mouse did not reach the keyboard focus target');
|
|
752
894
|
this.inputDriver.mouseClick('left');
|
|
753
895
|
}
|
|
754
896
|
await this.inputDriver.delay(delayMs);
|
|
@@ -767,7 +909,7 @@ class ComputerDevice {
|
|
|
767
909
|
}));
|
|
768
910
|
} catch (error) {
|
|
769
911
|
debugDevice(`Failed to list displays: ${error}`);
|
|
770
|
-
|
|
912
|
+
throw new Error(`Failed to list displays: ${error}`);
|
|
771
913
|
}
|
|
772
914
|
}
|
|
773
915
|
async connect() {
|
|
@@ -796,9 +938,10 @@ class ComputerDevice {
|
|
|
796
938
|
}
|
|
797
939
|
}
|
|
798
940
|
device_libnut = await getLibnut();
|
|
799
|
-
|
|
941
|
+
const windowsDisplayGeometries = 'win32' === process.platform ? readWindowsDisplayGeometries() : void 0;
|
|
942
|
+
this.displayGeometry = resolveDisplayGeometry(this.displayId, windowsDisplayGeometries);
|
|
800
943
|
const size = await this.size();
|
|
801
|
-
const displays = await ComputerDevice.listDisplays();
|
|
944
|
+
const displays = windowsDisplayGeometries ? listWindowsDisplays(windowsDisplayGeometries) : await ComputerDevice.listDisplays();
|
|
802
945
|
const headlessInfo = this.xvfbInstance ? `\nHeadless: true (Xvfb on ${this.xvfbInstance.display})` : '';
|
|
803
946
|
this.description = `
|
|
804
947
|
Type: Computer
|
|
@@ -808,7 +951,7 @@ Screen Size: ${size.width}x${size.height}
|
|
|
808
951
|
Available Displays: ${displays.length > 0 ? displays.map((d)=>d.name).join(', ') : 'Unknown'}${headlessInfo}
|
|
809
952
|
`;
|
|
810
953
|
debugDevice('Computer device connected', this.description);
|
|
811
|
-
await this.healthCheck();
|
|
954
|
+
await this.healthCheck(displays);
|
|
812
955
|
} catch (error) {
|
|
813
956
|
if (this.xvfbInstance) {
|
|
814
957
|
if (!this.options?.keepXvfbAliveUntilProcessExit) this.xvfbInstance.stop();
|
|
@@ -827,9 +970,9 @@ Available Displays: ${displays.length > 0 ? displays.map((d)=>d.name).join(', ')
|
|
|
827
970
|
throw new Error(`Unable to connect to computer device: ${error}`);
|
|
828
971
|
}
|
|
829
972
|
}
|
|
830
|
-
async healthCheck() {
|
|
973
|
+
async healthCheck(displays) {
|
|
831
974
|
console.log('[HealthCheck] Starting health check...');
|
|
832
|
-
console.log("[HealthCheck] @midscene/computer v1.12.
|
|
975
|
+
console.log("[HealthCheck] @midscene/computer v1.12.3-beta-20260828085408.0");
|
|
833
976
|
console.log('[HealthCheck] Taking screenshot...');
|
|
834
977
|
const screenshotTimeout = 15000;
|
|
835
978
|
let timeoutId;
|
|
@@ -841,23 +984,38 @@ Available Displays: ${displays.length > 0 ? displays.map((d)=>d.name).join(', ')
|
|
|
841
984
|
timeoutPromise
|
|
842
985
|
]);
|
|
843
986
|
console.log(`[HealthCheck] Screenshot succeeded (length=${base64.length})`);
|
|
844
|
-
console.log('[HealthCheck]
|
|
845
|
-
const startPos = this.inputDriver.getMousePos();
|
|
987
|
+
console.log('[HealthCheck] Verifying mouse control...');
|
|
988
|
+
const startPos = 'win32' === process.platform ? this.windowsPointerDriver.getPosition() : this.inputDriver.getMousePos();
|
|
846
989
|
console.log(`[HealthCheck] Current mouse position: (${startPos.x}, ${startPos.y})`);
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
990
|
+
if ('win32' === process.platform) {
|
|
991
|
+
if (!this.displayGeometry) throw new Error('Windows display geometry is unavailable');
|
|
992
|
+
const bounds = this.displayGeometry.bounds;
|
|
993
|
+
const target = {
|
|
994
|
+
x: Math.round(bounds.x + bounds.width / 2),
|
|
995
|
+
y: Math.round(bounds.y + bounds.height / 2)
|
|
996
|
+
};
|
|
997
|
+
try {
|
|
998
|
+
const actual = this.windowsPointerDriver.moveTo(target);
|
|
999
|
+
const drift = windowsPointerDrift(target, actual);
|
|
1000
|
+
if (!windowsPointerIsWithinTolerance(drift)) throw new Error(`Windows screenshot-space pointer verification: expected (${target.x}, ${target.y}), got (${actual.x}, ${actual.y}), drift=(${drift.x}, ${drift.y})`);
|
|
1001
|
+
} finally{
|
|
1002
|
+
this.windowsPointerDriver.moveTo(startPos);
|
|
1003
|
+
}
|
|
1004
|
+
console.log(`[HealthCheck] Mouse verified in screenshot-space display bounds (${bounds.x}, ${bounds.y}, ${bounds.width}, ${bounds.height})`);
|
|
1005
|
+
} else {
|
|
1006
|
+
const offsetX = Math.floor(40 * Math.random()) + 10;
|
|
1007
|
+
const offsetY = Math.floor(40 * Math.random()) + 10;
|
|
1008
|
+
const targetX = startPos.x + offsetX;
|
|
1009
|
+
const targetY = startPos.y + offsetY;
|
|
1010
|
+
console.log(`[HealthCheck] Moving mouse to (${targetX}, ${targetY})...`);
|
|
1011
|
+
try {
|
|
1012
|
+
this.inputDriver.moveMouse(targetX, targetY);
|
|
1013
|
+
await sleep(CLICK_SETTLE_DELAY);
|
|
1014
|
+
this.inputDriver.assertMousePosition(targetX, targetY, 'Mouse health check failed');
|
|
1015
|
+
} finally{
|
|
1016
|
+
this.inputDriver.moveMouse(startPos.x, startPos.y);
|
|
1017
|
+
console.log(`[HealthCheck] Mouse restored to (${startPos.x}, ${startPos.y})`);
|
|
1018
|
+
}
|
|
861
1019
|
}
|
|
862
1020
|
if ('win32' === process.platform && !this.isRunningAsAdmin()) {
|
|
863
1021
|
const hint = [
|
|
@@ -867,10 +1025,7 @@ Available Displays: ${displays.length > 0 ? displays.map((d)=>d.name).join(', ')
|
|
|
867
1025
|
].join(' ');
|
|
868
1026
|
warnDevice(`[HealthCheck] ${hint}`);
|
|
869
1027
|
}
|
|
870
|
-
this.inputDriver.moveMouse(startPos.x, startPos.y);
|
|
871
|
-
console.log(`[HealthCheck] Mouse restored to (${startPos.x}, ${startPos.y})`);
|
|
872
1028
|
console.log('[HealthCheck] Listing monitors...');
|
|
873
|
-
const displays = await ComputerDevice.listDisplays();
|
|
874
1029
|
if (displays.length > 0) {
|
|
875
1030
|
console.log(`[HealthCheck] Found ${displays.length} monitor(s):`);
|
|
876
1031
|
for (const display of displays){
|
|
@@ -1053,10 +1208,14 @@ $g.Dispose(); $bmp.Dispose(); $ms.Dispose()
|
|
|
1053
1208
|
resolveUntargetedScrollPoint(screenSize) {
|
|
1054
1209
|
if ('win32' === process.platform) {
|
|
1055
1210
|
const activeWindowRect = this.inputDriver.getActiveWindowRect();
|
|
1056
|
-
if (activeWindowRect)
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1211
|
+
if (activeWindowRect) {
|
|
1212
|
+
const activeWindowCenter = {
|
|
1213
|
+
x: activeWindowRect.x + activeWindowRect.width / 2,
|
|
1214
|
+
y: activeWindowRect.y + activeWindowRect.height / 2
|
|
1215
|
+
};
|
|
1216
|
+
const bounds = this.displayGeometry?.bounds;
|
|
1217
|
+
if (!bounds || activeWindowCenter.x >= bounds.x && activeWindowCenter.x < bounds.x + bounds.width && activeWindowCenter.y >= bounds.y && activeWindowCenter.y < bounds.y + bounds.height) return activeWindowCenter;
|
|
1218
|
+
}
|
|
1060
1219
|
}
|
|
1061
1220
|
return this.toGlobalPoint({
|
|
1062
1221
|
x: screenSize.width / 2,
|
|
@@ -1067,17 +1226,16 @@ $g.Dispose(); $bmp.Dispose(); $ms.Dispose()
|
|
|
1067
1226
|
if (param.locate) {
|
|
1068
1227
|
const element = param.locate;
|
|
1069
1228
|
const [x, y] = element.center;
|
|
1070
|
-
|
|
1229
|
+
await this.moveDisplayPointer({
|
|
1071
1230
|
x,
|
|
1072
1231
|
y
|
|
1073
|
-
});
|
|
1074
|
-
this.inputDriver.moveMouse(Math.round(point.x), Math.round(point.y));
|
|
1232
|
+
}, 'Mouse did not reach the scroll target');
|
|
1075
1233
|
return;
|
|
1076
1234
|
}
|
|
1077
1235
|
const screenSize = await this.size();
|
|
1078
1236
|
if ('win32' === process.platform && this.inputDriver.focusActiveWindow()) await this.inputDriver.delay(CLICK_FOCUS_SETTLE_DELAY);
|
|
1079
1237
|
const point = this.resolveUntargetedScrollPoint(screenSize);
|
|
1080
|
-
this.
|
|
1238
|
+
await this.moveGlobalPointer(point, 'Mouse did not reach the scroll viewport');
|
|
1081
1239
|
await this.inputDriver.delay(MOUSE_MOVE_EFFECT_WAIT);
|
|
1082
1240
|
return screenSize;
|
|
1083
1241
|
}
|
|
@@ -1202,6 +1360,9 @@ $g.Dispose(); $bmp.Dispose(); $ms.Dispose()
|
|
|
1202
1360
|
runPhasedScroll,
|
|
1203
1361
|
debug: (message)=>debugDevice(message)
|
|
1204
1362
|
}));
|
|
1363
|
+
device_define_property(this, "windowsPointerDriver", new WindowsPointerDriver({
|
|
1364
|
+
runPowershell
|
|
1365
|
+
}));
|
|
1205
1366
|
device_define_property(this, "useAppleScript", void 0);
|
|
1206
1367
|
device_define_property(this, "adminCheckCache", void 0);
|
|
1207
1368
|
device_define_property(this, "uri", void 0);
|
|
@@ -1226,15 +1387,17 @@ $g.Dispose(); $bmp.Dispose(); $ms.Dispose()
|
|
|
1226
1387
|
},
|
|
1227
1388
|
holdDuration,
|
|
1228
1389
|
displayId: this.displayId,
|
|
1229
|
-
displayGeometry: this.displayGeometry
|
|
1230
|
-
screenIndex: this.displayGeometry.screenIndex,
|
|
1231
|
-
cgDisplayId: this.displayGeometry.cgDisplayId,
|
|
1232
|
-
bounds: this.displayGeometry.bounds
|
|
1233
|
-
} : void 0
|
|
1390
|
+
displayGeometry: this.displayGeometry
|
|
1234
1391
|
});
|
|
1235
1392
|
const frontmostBefore = 'darwin' === process.platform ? readDarwinFrontmostApplication() : void 0;
|
|
1236
|
-
await this.
|
|
1237
|
-
|
|
1393
|
+
const current = await this.moveGlobalPointer({
|
|
1394
|
+
x: targetX,
|
|
1395
|
+
y: targetY
|
|
1396
|
+
}, 'Mouse did not reach the tap target', {
|
|
1397
|
+
smoothSteps: SMOOTH_MOVE_STEPS_TAP,
|
|
1398
|
+
smoothDelay: SMOOTH_MOVE_DELAY_TAP
|
|
1399
|
+
});
|
|
1400
|
+
await pressMouseAtGlobalPoint(this.inputDriver, targetX, targetY, current, holdDuration, 'primary');
|
|
1238
1401
|
if (frontmostBefore && 'darwin' === process.platform) {
|
|
1239
1402
|
await sleep(CLICK_FOCUS_SETTLE_DELAY);
|
|
1240
1403
|
const frontmostAfter = readDarwinFrontmostApplication();
|
|
@@ -1245,42 +1408,43 @@ $g.Dispose(); $bmp.Dispose(); $ms.Dispose()
|
|
|
1245
1408
|
focusChanged
|
|
1246
1409
|
});
|
|
1247
1410
|
if (focusChanged) {
|
|
1248
|
-
this.
|
|
1249
|
-
|
|
1411
|
+
const followUpCurrent = await this.moveGlobalPointer({
|
|
1412
|
+
x: targetX,
|
|
1413
|
+
y: targetY
|
|
1414
|
+
}, 'Mouse did not reach the focus follow-up target');
|
|
1415
|
+
await pressMouseAtGlobalPoint(this.inputDriver, targetX, targetY, followUpCurrent, holdDuration, 'focus-follow-up');
|
|
1250
1416
|
}
|
|
1251
1417
|
}
|
|
1252
1418
|
},
|
|
1253
1419
|
doubleClick: async ({ x, y })=>{
|
|
1254
|
-
|
|
1420
|
+
await this.moveDisplayPointer({
|
|
1255
1421
|
x,
|
|
1256
1422
|
y
|
|
1257
|
-
});
|
|
1258
|
-
this.inputDriver.moveMouse(Math.round(target.x), Math.round(target.y));
|
|
1423
|
+
}, 'Mouse did not reach the double-click target');
|
|
1259
1424
|
this.inputDriver.mouseClick('left', true);
|
|
1260
1425
|
},
|
|
1261
1426
|
rightClick: async ({ x, y })=>{
|
|
1262
|
-
|
|
1427
|
+
await this.moveDisplayPointer({
|
|
1263
1428
|
x,
|
|
1264
1429
|
y
|
|
1265
|
-
});
|
|
1266
|
-
this.inputDriver.moveMouse(Math.round(target.x), Math.round(target.y));
|
|
1430
|
+
}, 'Mouse did not reach the right-click target');
|
|
1267
1431
|
this.inputDriver.mouseClick('right');
|
|
1268
1432
|
},
|
|
1269
1433
|
hover: async ({ x, y })=>{
|
|
1270
|
-
|
|
1434
|
+
await this.moveDisplayPointer({
|
|
1271
1435
|
x,
|
|
1272
1436
|
y
|
|
1437
|
+
}, 'Mouse did not reach the hover target', {
|
|
1438
|
+
smoothSteps: SMOOTH_MOVE_STEPS_MOUSE_MOVE,
|
|
1439
|
+
smoothDelay: SMOOTH_MOVE_DELAY_MOUSE_MOVE
|
|
1273
1440
|
});
|
|
1274
|
-
await this.inputDriver.smoothMoveMouse(Math.round(target.x), Math.round(target.y), SMOOTH_MOVE_STEPS_MOUSE_MOVE, SMOOTH_MOVE_DELAY_MOUSE_MOVE);
|
|
1275
1441
|
await this.inputDriver.delay(MOUSE_MOVE_EFFECT_WAIT);
|
|
1276
1442
|
},
|
|
1277
1443
|
dragAndDrop: async (from, to)=>{
|
|
1278
|
-
|
|
1279
|
-
const globalTo = this.toGlobalPoint(to);
|
|
1280
|
-
this.inputDriver.moveMouse(Math.round(globalFrom.x), Math.round(globalFrom.y));
|
|
1444
|
+
await this.moveDisplayPointer(from, 'Mouse did not reach the drag start target');
|
|
1281
1445
|
await this.inputDriver.withMouseButton('left', async ()=>{
|
|
1282
1446
|
await this.inputDriver.delay(100);
|
|
1283
|
-
this.
|
|
1447
|
+
await this.moveDisplayPointer(to, 'Mouse did not reach the drag end target');
|
|
1284
1448
|
await this.inputDriver.delay(100);
|
|
1285
1449
|
});
|
|
1286
1450
|
}
|
|
@@ -2238,7 +2402,7 @@ const tools = new ComputerMidsceneTools({
|
|
|
2238
2402
|
});
|
|
2239
2403
|
runToolsCLI(tools, 'midscene-computer', {
|
|
2240
2404
|
stripPrefix: 'computer_',
|
|
2241
|
-
version: "1.12.
|
|
2405
|
+
version: "1.12.3-beta-20260828085408.0",
|
|
2242
2406
|
extraCommands: createReportCliCommands()
|
|
2243
2407
|
}).catch((e)=>{
|
|
2244
2408
|
process.exit(reportCLIError(e));
|