@midscene/computer 1.12.2 → 1.12.3-beta-20260828110230.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/lib/cli.js CHANGED
@@ -54,6 +54,7 @@ function _define_property(obj, key, value) {
54
54
  else obj[key] = value;
55
55
  return obj;
56
56
  }
57
+ const MOUSE_COORDINATE_TOLERANCE_PX = 5;
57
58
  class ComputerInputDriver {
58
59
  destroy() {
59
60
  if (this.destroyed) return;
@@ -69,6 +70,15 @@ class ComputerInputDriver {
69
70
  moveMouse(x, y) {
70
71
  this.getLibnutOrThrow('moveMouse').moveMouse(x, y);
71
72
  }
73
+ assertMousePosition(targetX, targetY, context) {
74
+ const current = this.getMousePos();
75
+ const drift = {
76
+ x: current.x - targetX,
77
+ y: current.y - targetY
78
+ };
79
+ 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})`);
80
+ return drift;
81
+ }
72
82
  focusActiveWindow() {
73
83
  const lib = this.getLibnutOrThrow('focusActiveWindow');
74
84
  if ('function' != typeof lib.getActiveWindow || 'function' != typeof lib.focusWindow) return false;
@@ -209,6 +219,122 @@ class ComputerInputDriver {
209
219
  this.pendingInputDelayWaits = new Set();
210
220
  }
211
221
  }
222
+ const WINDOWS_PHYSICAL_PIXEL_POWERSHELL_PREAMBLE = `
223
+ $midsceneAssemblyName = New-Object System.Reflection.AssemblyName('MidsceneDpiNative')
224
+ $midsceneAssembly = [System.AppDomain]::CurrentDomain.DefineDynamicAssembly(
225
+ $midsceneAssemblyName,
226
+ [System.Reflection.Emit.AssemblyBuilderAccess]::Run
227
+ )
228
+ $midsceneModule = $midsceneAssembly.DefineDynamicModule('MidsceneDpiNativeModule')
229
+ $midsceneType = $midsceneModule.DefineType(
230
+ 'MidsceneDpiNative.User32',
231
+ [System.Reflection.TypeAttributes]'Public, Class, Sealed, Abstract'
232
+ )
233
+ $midsceneMethodAttributes =
234
+ [System.Reflection.MethodAttributes]::Public -bor
235
+ [System.Reflection.MethodAttributes]::Static -bor
236
+ [System.Reflection.MethodAttributes]::PinvokeImpl
237
+ $midsceneSetDpiMethod = $midsceneType.DefinePInvokeMethod(
238
+ 'SetThreadDpiAwarenessContext',
239
+ 'user32.dll',
240
+ $midsceneMethodAttributes,
241
+ [System.Reflection.CallingConventions]::Standard,
242
+ [System.IntPtr],
243
+ [System.Type[]]@([System.IntPtr]),
244
+ [System.Runtime.InteropServices.CallingConvention]::Winapi,
245
+ [System.Runtime.InteropServices.CharSet]::None
246
+ )
247
+ $midsceneSetDpiMethod.SetImplementationFlags(
248
+ $midsceneSetDpiMethod.GetMethodImplementationFlags() -bor
249
+ [System.Reflection.MethodImplAttributes]::PreserveSig
250
+ )
251
+ $midsceneNativeMethods = $midsceneType.CreateType()
252
+ $midscenePerMonitorV2 = [System.IntPtr](-4)
253
+ $midscenePreviousDpiContext =
254
+ $midsceneNativeMethods::SetThreadDpiAwarenessContext($midscenePerMonitorV2)
255
+ if ($midscenePreviousDpiContext -eq [System.IntPtr]::Zero) {
256
+ throw 'Unable to enter the Per-Monitor V2 DPI awareness context.'
257
+ }
258
+ `.trim();
259
+ function windows_pointer_define_property(obj, key, value) {
260
+ if (key in obj) Object.defineProperty(obj, key, {
261
+ value: value,
262
+ enumerable: true,
263
+ configurable: true,
264
+ writable: true
265
+ });
266
+ else obj[key] = value;
267
+ return obj;
268
+ }
269
+ const WINDOWS_POINTER_TOLERANCE_PX = 5;
270
+ function assertFinitePoint(point, context) {
271
+ if (!Number.isFinite(point.x) || !Number.isFinite(point.y)) throw new Error(`${context} must contain finite coordinates, got (${point.x}, ${point.y})`);
272
+ }
273
+ function parseWindowsPointerPosition(output, context) {
274
+ const match = output.trim().match(/^(-?\d+),(-?\d+)$/);
275
+ if (!match) throw new Error(`${context} returned an invalid cursor position: ${JSON.stringify(output.trim())}`);
276
+ return {
277
+ x: Number(match[1]),
278
+ y: Number(match[2])
279
+ };
280
+ }
281
+ function windowsPointerPositionScript() {
282
+ return `
283
+ ${WINDOWS_PHYSICAL_PIXEL_POWERSHELL_PREAMBLE}
284
+ Add-Type -AssemblyName System.Windows.Forms, System.Drawing
285
+ $position = [System.Windows.Forms.Cursor]::Position
286
+ [Console]::Out.Write(('{0},{1}' -f $position.X, $position.Y))
287
+ `.trim();
288
+ }
289
+ function windowsPointerMoveScript(point, options) {
290
+ assertFinitePoint(point, 'Windows pointer target');
291
+ 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');
292
+ const targetX = Math.round(point.x);
293
+ const targetY = Math.round(point.y);
294
+ const smoothSteps = Math.max(1, Math.round(options?.smoothSteps ?? 1));
295
+ const smoothDelayMs = Math.max(0, Math.round(options?.smoothDelayMs ?? 0));
296
+ return `
297
+ ${WINDOWS_PHYSICAL_PIXEL_POWERSHELL_PREAMBLE}
298
+ Add-Type -AssemblyName System.Windows.Forms, System.Drawing
299
+ $targetX = ${targetX}
300
+ $targetY = ${targetY}
301
+ $smoothSteps = ${smoothSteps}
302
+ $smoothDelayMs = ${smoothDelayMs}
303
+ $start = [System.Windows.Forms.Cursor]::Position
304
+ for ($step = 1; $step -le $smoothSteps; $step += 1) {
305
+ $x = [int][Math]::Round($start.X + (($targetX - $start.X) * $step / [double]$smoothSteps))
306
+ $y = [int][Math]::Round($start.Y + (($targetY - $start.Y) * $step / [double]$smoothSteps))
307
+ [System.Windows.Forms.Cursor]::Position = [System.Drawing.Point]::new($x, $y)
308
+ if ($smoothDelayMs -gt 0 -and $step -lt $smoothSteps) {
309
+ Start-Sleep -Milliseconds $smoothDelayMs
310
+ }
311
+ }
312
+ [System.Windows.Forms.Cursor]::Position = [System.Drawing.Point]::new($targetX, $targetY)
313
+ $position = [System.Windows.Forms.Cursor]::Position
314
+ [Console]::Out.Write(('{0},{1}' -f $position.X, $position.Y))
315
+ `.trim();
316
+ }
317
+ class WindowsPointerDriver {
318
+ getPosition() {
319
+ return parseWindowsPointerPosition(this.options.runPowershell(windowsPointerPositionScript()), 'Windows pointer query');
320
+ }
321
+ moveTo(point, options) {
322
+ return parseWindowsPointerPosition(this.options.runPowershell(windowsPointerMoveScript(point, options)), 'Windows pointer move');
323
+ }
324
+ constructor(options){
325
+ windows_pointer_define_property(this, "options", void 0);
326
+ this.options = options;
327
+ }
328
+ }
329
+ function windowsPointerDrift(expected, actual) {
330
+ return {
331
+ x: actual.x - expected.x,
332
+ y: actual.y - expected.y
333
+ };
334
+ }
335
+ function windowsPointerIsWithinTolerance(drift) {
336
+ return Math.abs(drift.x) <= WINDOWS_POINTER_TOLERANCE_PX && Math.abs(drift.y) <= WINDOWS_POINTER_TOLERANCE_PX;
337
+ }
212
338
  const interrupt_namespaceObject = require("@midscene/shared/cli/interrupt");
213
339
  const debugXvfb = (0, logger_namespaceObject.getDebug)('computer:xvfb');
214
340
  const xvfbCleanupMonitorScript = String.raw`
@@ -487,19 +613,34 @@ function runPowershell(script) {
487
613
  windowsHide: true
488
614
  });
489
615
  }
490
- function listWindowsDisplays() {
616
+ function readWindowsDisplayGeometries() {
491
617
  const script = `
618
+ ${WINDOWS_PHYSICAL_PIXEL_POWERSHELL_PREAMBLE}
492
619
  Add-Type -AssemblyName System.Windows.Forms
493
620
  $s = [System.Windows.Forms.Screen]::AllScreens | ForEach-Object {
494
- [PSCustomObject]@{ id = $_.DeviceName; name = $_.DeviceName; primary = $_.Primary }
621
+ $b = $_.Bounds
622
+ [PSCustomObject]@{
623
+ id = $_.DeviceName
624
+ name = $_.DeviceName
625
+ primary = $_.Primary
626
+ bounds = [PSCustomObject]@{ x = $b.X; y = $b.Y; width = $b.Width; height = $b.Height }
627
+ }
495
628
  }
496
629
  ConvertTo-Json @($s) -Compress
497
630
  `.trim();
498
- const parsed = JSON.parse(runPowershell(script).trim());
499
- return parsed.map((d)=>({
500
- id: String(d.id),
501
- name: d.name || String(d.id),
502
- primary: d.primary || false
631
+ const output = runPowershell(script).trim();
632
+ if (!output) throw new Error('Windows display enumeration returned no data');
633
+ const parsed = JSON.parse(output);
634
+ if (!Array.isArray(parsed)) throw new Error('Windows display enumeration returned invalid data');
635
+ const displays = parsed.filter(isWindowsDisplayGeometry);
636
+ if (displays.length !== parsed.length || 0 === displays.length) throw new Error('Windows display enumeration returned invalid geometry');
637
+ return displays;
638
+ }
639
+ function listWindowsDisplays(geometries = readWindowsDisplayGeometries()) {
640
+ return geometries.map((display)=>({
641
+ id: display.id,
642
+ name: display.name,
643
+ primary: display.primary
503
644
  }));
504
645
  }
505
646
  let device_libnut = null;
@@ -601,6 +742,16 @@ function getDisplayInfoBinary() {
601
742
  function isFiniteNumber(value) {
602
743
  return 'number' == typeof value && Number.isFinite(value);
603
744
  }
745
+ function isDisplayBounds(value) {
746
+ if (!value || 'object' != typeof value) return false;
747
+ const bounds = value;
748
+ return isFiniteNumber(bounds.x) && isFiniteNumber(bounds.y) && isFiniteNumber(bounds.width) && isFiniteNumber(bounds.height) && bounds.width > 0 && bounds.height > 0;
749
+ }
750
+ function isWindowsDisplayGeometry(value) {
751
+ if (!value || 'object' != typeof value) return false;
752
+ const candidate = value;
753
+ return 'string' == typeof candidate.id && candidate.id.length > 0 && 'string' == typeof candidate.name && 'boolean' == typeof candidate.primary && isDisplayBounds(candidate.bounds);
754
+ }
604
755
  function isDarwinDisplayGeometry(value) {
605
756
  if (!value || 'object' != typeof value) return false;
606
757
  const candidate = value;
@@ -643,9 +794,11 @@ function readDarwinFrontmostApplication() {
643
794
  return;
644
795
  }
645
796
  }
646
- async function pressMouseAtGlobalPoint(inputDriver, targetX, targetY, holdDuration, reason) {
647
- await inputDriver.delay(CLICK_SETTLE_DELAY);
648
- const current = inputDriver.getMousePos();
797
+ async function pressMouseAtGlobalPoint(inputDriver, targetX, targetY, current, holdDuration, reason) {
798
+ const drift = {
799
+ x: current.x - targetX,
800
+ y: current.y - targetY
801
+ };
649
802
  debugComputerInput('tap mouse moved %o', {
650
803
  reason,
651
804
  target: {
@@ -653,10 +806,7 @@ async function pressMouseAtGlobalPoint(inputDriver, targetX, targetY, holdDurati
653
806
  y: targetY
654
807
  },
655
808
  current,
656
- drift: {
657
- x: current.x - targetX,
658
- y: current.y - targetY
659
- }
809
+ drift
660
810
  });
661
811
  await inputDriver.withMouseButton('left', async ()=>{
662
812
  debugComputerInput('tap mouse down %o', {
@@ -675,9 +825,18 @@ function resolveDarwinDisplayGeometryFromList(displayId, displays) {
675
825
  if (void 0 === displayId || '' === displayId) return displays.find((display)=>display.primary) || displays.find((display)=>0 === display.screenIndex) || displays[0];
676
826
  return displays.find((display)=>display.screenIndex === screenIndex) || displays.find((display)=>display.cgDisplayId === screenIndex);
677
827
  }
678
- function resolveDisplayGeometry(displayId) {
679
- if ('darwin' !== process.platform) return;
680
- return resolveDarwinDisplayGeometryFromList(displayId, readDarwinDisplayGeometries());
828
+ function resolveWindowsDisplayGeometryFromList(displayId, displays) {
829
+ if (!displays.length) return;
830
+ if (void 0 === displayId || '' === displayId) return displays.find((display)=>display.primary) || displays[0];
831
+ return displays.find((display)=>display.id === displayId);
832
+ }
833
+ function resolveDisplayGeometry(displayId, windowsDisplays) {
834
+ if ('darwin' === process.platform) return resolveDarwinDisplayGeometryFromList(displayId, readDarwinDisplayGeometries());
835
+ if ('win32' === process.platform) {
836
+ const geometry = resolveWindowsDisplayGeometryFromList(displayId, windowsDisplays ?? readWindowsDisplayGeometries());
837
+ if (!geometry) throw new Error(displayId ? `Requested Windows display not found: ${displayId}` : 'No Windows displays were detected');
838
+ return geometry;
839
+ }
681
840
  }
682
841
  function mapDisplayLocalPointToGlobal(point, geometry) {
683
842
  if (!geometry) return point;
@@ -763,6 +922,30 @@ function normalizePrimaryKey(key) {
763
922
  return KEY_NAME_MAP[lowerKey] || lowerKey;
764
923
  }
765
924
  class ComputerDevice {
925
+ async moveGlobalPointer(point, context, smooth) {
926
+ if (this.destroyed) throw new Error('ComputerDevice has been destroyed');
927
+ const target = {
928
+ x: Math.round(point.x),
929
+ y: Math.round(point.y)
930
+ };
931
+ if ('win32' === process.platform) {
932
+ const actual = this.windowsPointerDriver.moveTo(target, {
933
+ smoothSteps: smooth?.smoothSteps,
934
+ smoothDelayMs: smooth?.smoothDelay
935
+ });
936
+ const drift = windowsPointerDrift(target, actual);
937
+ if (!windowsPointerIsWithinTolerance(drift)) throw new Error(`${context}: expected (${target.x}, ${target.y}), got (${actual.x}, ${actual.y}), drift=(${drift.x}, ${drift.y})`);
938
+ await this.inputDriver.delay(CLICK_SETTLE_DELAY);
939
+ return actual;
940
+ }
941
+ if (smooth) await this.inputDriver.smoothMoveMouse(target.x, target.y, smooth.smoothSteps, smooth.smoothDelay);
942
+ else this.inputDriver.moveMouse(target.x, target.y);
943
+ await this.inputDriver.delay(CLICK_SETTLE_DELAY);
944
+ return target;
945
+ }
946
+ moveDisplayPointer(point, context, smooth) {
947
+ return this.moveGlobalPointer(this.toGlobalPoint(point), context, smooth);
948
+ }
766
949
  async focusKeyboardTarget(element, delayMs) {
767
950
  const [x, y] = element.center;
768
951
  if ('darwin' === process.platform) await this.inputPrimitives.pointer.tap({
@@ -770,11 +953,10 @@ class ComputerDevice {
770
953
  y
771
954
  });
772
955
  else {
773
- const point = this.toGlobalPoint({
956
+ await this.moveDisplayPointer({
774
957
  x,
775
958
  y
776
- });
777
- this.inputDriver.moveMouse(Math.round(point.x), Math.round(point.y));
959
+ }, 'Mouse did not reach the keyboard focus target');
778
960
  this.inputDriver.mouseClick('left');
779
961
  }
780
962
  await this.inputDriver.delay(delayMs);
@@ -793,7 +975,7 @@ class ComputerDevice {
793
975
  }));
794
976
  } catch (error) {
795
977
  debugDevice(`Failed to list displays: ${error}`);
796
- return [];
978
+ throw new Error(`Failed to list displays: ${error}`);
797
979
  }
798
980
  }
799
981
  async connect() {
@@ -822,9 +1004,10 @@ class ComputerDevice {
822
1004
  }
823
1005
  }
824
1006
  device_libnut = await getLibnut();
825
- this.displayGeometry = resolveDisplayGeometry(this.displayId);
1007
+ const windowsDisplayGeometries = 'win32' === process.platform ? readWindowsDisplayGeometries() : void 0;
1008
+ this.displayGeometry = resolveDisplayGeometry(this.displayId, windowsDisplayGeometries);
826
1009
  const size = await this.size();
827
- const displays = await ComputerDevice.listDisplays();
1010
+ const displays = windowsDisplayGeometries ? listWindowsDisplays(windowsDisplayGeometries) : await ComputerDevice.listDisplays();
828
1011
  const headlessInfo = this.xvfbInstance ? `\nHeadless: true (Xvfb on ${this.xvfbInstance.display})` : '';
829
1012
  this.description = `
830
1013
  Type: Computer
@@ -834,7 +1017,7 @@ Screen Size: ${size.width}x${size.height}
834
1017
  Available Displays: ${displays.length > 0 ? displays.map((d)=>d.name).join(', ') : 'Unknown'}${headlessInfo}
835
1018
  `;
836
1019
  debugDevice('Computer device connected', this.description);
837
- await this.healthCheck();
1020
+ await this.healthCheck(displays);
838
1021
  } catch (error) {
839
1022
  if (this.xvfbInstance) {
840
1023
  if (!this.options?.keepXvfbAliveUntilProcessExit) this.xvfbInstance.stop();
@@ -853,9 +1036,9 @@ Available Displays: ${displays.length > 0 ? displays.map((d)=>d.name).join(', ')
853
1036
  throw new Error(`Unable to connect to computer device: ${error}`);
854
1037
  }
855
1038
  }
856
- async healthCheck() {
1039
+ async healthCheck(displays) {
857
1040
  console.log('[HealthCheck] Starting health check...');
858
- console.log("[HealthCheck] @midscene/computer v1.12.2");
1041
+ console.log("[HealthCheck] @midscene/computer v1.12.3-beta-20260828110230.0");
859
1042
  console.log('[HealthCheck] Taking screenshot...');
860
1043
  const screenshotTimeout = 15000;
861
1044
  let timeoutId;
@@ -867,23 +1050,38 @@ Available Displays: ${displays.length > 0 ? displays.map((d)=>d.name).join(', ')
867
1050
  timeoutPromise
868
1051
  ]);
869
1052
  console.log(`[HealthCheck] Screenshot succeeded (length=${base64.length})`);
870
- console.log('[HealthCheck] Moving mouse...');
871
- const startPos = this.inputDriver.getMousePos();
1053
+ console.log('[HealthCheck] Verifying mouse control...');
1054
+ const startPos = 'win32' === process.platform ? this.windowsPointerDriver.getPosition() : this.inputDriver.getMousePos();
872
1055
  console.log(`[HealthCheck] Current mouse position: (${startPos.x}, ${startPos.y})`);
873
- const offsetX = Math.floor(40 * Math.random()) + 10;
874
- const offsetY = Math.floor(40 * Math.random()) + 10;
875
- const targetX = startPos.x + offsetX;
876
- const targetY = startPos.y + offsetY;
877
- console.log(`[HealthCheck] Moving mouse to (${targetX}, ${targetY})...`);
878
- this.inputDriver.moveMouse(targetX, targetY);
879
- await (0, utils_namespaceObject.sleep)(50);
880
- const movedPos = this.inputDriver.getMousePos();
881
- console.log(`[HealthCheck] Mouse position after move: (${movedPos.x}, ${movedPos.y})`);
882
- const deltaX = Math.abs(movedPos.x - targetX);
883
- const deltaY = Math.abs(movedPos.y - targetY);
884
- if (deltaX > 5 || deltaY > 5) {
885
- const msg = `[HealthCheck] WARNING: Mouse control may not be working. Expected (${targetX}, ${targetY}), got (${movedPos.x}, ${movedPos.y}), delta=(${deltaX}, ${deltaY})`;
886
- warnDevice(msg);
1056
+ if ('win32' === process.platform) {
1057
+ if (!this.displayGeometry) throw new Error('Windows display geometry is unavailable');
1058
+ const bounds = this.displayGeometry.bounds;
1059
+ const target = {
1060
+ x: Math.round(bounds.x + bounds.width / 2),
1061
+ y: Math.round(bounds.y + bounds.height / 2)
1062
+ };
1063
+ try {
1064
+ const actual = this.windowsPointerDriver.moveTo(target);
1065
+ const drift = windowsPointerDrift(target, actual);
1066
+ 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})`);
1067
+ } finally{
1068
+ this.windowsPointerDriver.moveTo(startPos);
1069
+ }
1070
+ console.log(`[HealthCheck] Mouse verified in screenshot-space display bounds (${bounds.x}, ${bounds.y}, ${bounds.width}, ${bounds.height})`);
1071
+ } else {
1072
+ const offsetX = Math.floor(40 * Math.random()) + 10;
1073
+ const offsetY = Math.floor(40 * Math.random()) + 10;
1074
+ const targetX = startPos.x + offsetX;
1075
+ const targetY = startPos.y + offsetY;
1076
+ console.log(`[HealthCheck] Moving mouse to (${targetX}, ${targetY})...`);
1077
+ try {
1078
+ this.inputDriver.moveMouse(targetX, targetY);
1079
+ await (0, utils_namespaceObject.sleep)(CLICK_SETTLE_DELAY);
1080
+ this.inputDriver.assertMousePosition(targetX, targetY, 'Mouse health check failed');
1081
+ } finally{
1082
+ this.inputDriver.moveMouse(startPos.x, startPos.y);
1083
+ console.log(`[HealthCheck] Mouse restored to (${startPos.x}, ${startPos.y})`);
1084
+ }
887
1085
  }
888
1086
  if ('win32' === process.platform && !this.isRunningAsAdmin()) {
889
1087
  const hint = [
@@ -893,10 +1091,7 @@ Available Displays: ${displays.length > 0 ? displays.map((d)=>d.name).join(', ')
893
1091
  ].join(' ');
894
1092
  warnDevice(`[HealthCheck] ${hint}`);
895
1093
  }
896
- this.inputDriver.moveMouse(startPos.x, startPos.y);
897
- console.log(`[HealthCheck] Mouse restored to (${startPos.x}, ${startPos.y})`);
898
1094
  console.log('[HealthCheck] Listing monitors...');
899
- const displays = await ComputerDevice.listDisplays();
900
1095
  if (displays.length > 0) {
901
1096
  console.log(`[HealthCheck] Found ${displays.length} monitor(s):`);
902
1097
  for (const display of displays){
@@ -965,6 +1160,7 @@ $screen = [System.Windows.Forms.Screen]::AllScreens | Where-Object { $_.DeviceNa
965
1160
  if (-not $screen) { throw "Requested display not found: $dn" }` : '$screen = [System.Windows.Forms.Screen]::PrimaryScreen';
966
1161
  const script = `
967
1162
  $ErrorActionPreference = 'Stop'
1163
+ ${WINDOWS_PHYSICAL_PIXEL_POWERSHELL_PREAMBLE}
968
1164
  Add-Type -AssemblyName System.Windows.Forms, System.Drawing
969
1165
  ${selectScreen}
970
1166
  $b = $screen.Bounds
@@ -1079,10 +1275,14 @@ $g.Dispose(); $bmp.Dispose(); $ms.Dispose()
1079
1275
  resolveUntargetedScrollPoint(screenSize) {
1080
1276
  if ('win32' === process.platform) {
1081
1277
  const activeWindowRect = this.inputDriver.getActiveWindowRect();
1082
- if (activeWindowRect) return {
1083
- x: activeWindowRect.x + activeWindowRect.width / 2,
1084
- y: activeWindowRect.y + activeWindowRect.height / 2
1085
- };
1278
+ if (activeWindowRect) {
1279
+ const activeWindowCenter = {
1280
+ x: activeWindowRect.x + activeWindowRect.width / 2,
1281
+ y: activeWindowRect.y + activeWindowRect.height / 2
1282
+ };
1283
+ const bounds = this.displayGeometry?.bounds;
1284
+ if (!bounds || activeWindowCenter.x >= bounds.x && activeWindowCenter.x < bounds.x + bounds.width && activeWindowCenter.y >= bounds.y && activeWindowCenter.y < bounds.y + bounds.height) return activeWindowCenter;
1285
+ }
1086
1286
  }
1087
1287
  return this.toGlobalPoint({
1088
1288
  x: screenSize.width / 2,
@@ -1093,17 +1293,16 @@ $g.Dispose(); $bmp.Dispose(); $ms.Dispose()
1093
1293
  if (param.locate) {
1094
1294
  const element = param.locate;
1095
1295
  const [x, y] = element.center;
1096
- const point = this.toGlobalPoint({
1296
+ await this.moveDisplayPointer({
1097
1297
  x,
1098
1298
  y
1099
- });
1100
- this.inputDriver.moveMouse(Math.round(point.x), Math.round(point.y));
1299
+ }, 'Mouse did not reach the scroll target');
1101
1300
  return;
1102
1301
  }
1103
1302
  const screenSize = await this.size();
1104
1303
  if ('win32' === process.platform && this.inputDriver.focusActiveWindow()) await this.inputDriver.delay(CLICK_FOCUS_SETTLE_DELAY);
1105
1304
  const point = this.resolveUntargetedScrollPoint(screenSize);
1106
- this.inputDriver.moveMouse(Math.round(point.x), Math.round(point.y));
1305
+ await this.moveGlobalPointer(point, 'Mouse did not reach the scroll viewport');
1107
1306
  await this.inputDriver.delay(MOUSE_MOVE_EFFECT_WAIT);
1108
1307
  return screenSize;
1109
1308
  }
@@ -1228,6 +1427,9 @@ $g.Dispose(); $bmp.Dispose(); $ms.Dispose()
1228
1427
  runPhasedScroll,
1229
1428
  debug: (message)=>debugDevice(message)
1230
1429
  }));
1430
+ device_define_property(this, "windowsPointerDriver", new WindowsPointerDriver({
1431
+ runPowershell
1432
+ }));
1231
1433
  device_define_property(this, "useAppleScript", void 0);
1232
1434
  device_define_property(this, "adminCheckCache", void 0);
1233
1435
  device_define_property(this, "uri", void 0);
@@ -1252,15 +1454,17 @@ $g.Dispose(); $bmp.Dispose(); $ms.Dispose()
1252
1454
  },
1253
1455
  holdDuration,
1254
1456
  displayId: this.displayId,
1255
- displayGeometry: this.displayGeometry ? {
1256
- screenIndex: this.displayGeometry.screenIndex,
1257
- cgDisplayId: this.displayGeometry.cgDisplayId,
1258
- bounds: this.displayGeometry.bounds
1259
- } : void 0
1457
+ displayGeometry: this.displayGeometry
1260
1458
  });
1261
1459
  const frontmostBefore = 'darwin' === process.platform ? readDarwinFrontmostApplication() : void 0;
1262
- await this.inputDriver.smoothMoveMouse(targetX, targetY, SMOOTH_MOVE_STEPS_TAP, SMOOTH_MOVE_DELAY_TAP);
1263
- await pressMouseAtGlobalPoint(this.inputDriver, targetX, targetY, holdDuration, 'primary');
1460
+ const current = await this.moveGlobalPointer({
1461
+ x: targetX,
1462
+ y: targetY
1463
+ }, 'Mouse did not reach the tap target', {
1464
+ smoothSteps: SMOOTH_MOVE_STEPS_TAP,
1465
+ smoothDelay: SMOOTH_MOVE_DELAY_TAP
1466
+ });
1467
+ await pressMouseAtGlobalPoint(this.inputDriver, targetX, targetY, current, holdDuration, 'primary');
1264
1468
  if (frontmostBefore && 'darwin' === process.platform) {
1265
1469
  await (0, utils_namespaceObject.sleep)(CLICK_FOCUS_SETTLE_DELAY);
1266
1470
  const frontmostAfter = readDarwinFrontmostApplication();
@@ -1271,42 +1475,43 @@ $g.Dispose(); $bmp.Dispose(); $ms.Dispose()
1271
1475
  focusChanged
1272
1476
  });
1273
1477
  if (focusChanged) {
1274
- this.inputDriver.moveMouse(targetX, targetY);
1275
- await pressMouseAtGlobalPoint(this.inputDriver, targetX, targetY, holdDuration, 'focus-follow-up');
1478
+ const followUpCurrent = await this.moveGlobalPointer({
1479
+ x: targetX,
1480
+ y: targetY
1481
+ }, 'Mouse did not reach the focus follow-up target');
1482
+ await pressMouseAtGlobalPoint(this.inputDriver, targetX, targetY, followUpCurrent, holdDuration, 'focus-follow-up');
1276
1483
  }
1277
1484
  }
1278
1485
  },
1279
1486
  doubleClick: async ({ x, y })=>{
1280
- const target = this.toGlobalPoint({
1487
+ await this.moveDisplayPointer({
1281
1488
  x,
1282
1489
  y
1283
- });
1284
- this.inputDriver.moveMouse(Math.round(target.x), Math.round(target.y));
1490
+ }, 'Mouse did not reach the double-click target');
1285
1491
  this.inputDriver.mouseClick('left', true);
1286
1492
  },
1287
1493
  rightClick: async ({ x, y })=>{
1288
- const target = this.toGlobalPoint({
1494
+ await this.moveDisplayPointer({
1289
1495
  x,
1290
1496
  y
1291
- });
1292
- this.inputDriver.moveMouse(Math.round(target.x), Math.round(target.y));
1497
+ }, 'Mouse did not reach the right-click target');
1293
1498
  this.inputDriver.mouseClick('right');
1294
1499
  },
1295
1500
  hover: async ({ x, y })=>{
1296
- const target = this.toGlobalPoint({
1501
+ await this.moveDisplayPointer({
1297
1502
  x,
1298
1503
  y
1504
+ }, 'Mouse did not reach the hover target', {
1505
+ smoothSteps: SMOOTH_MOVE_STEPS_MOUSE_MOVE,
1506
+ smoothDelay: SMOOTH_MOVE_DELAY_MOUSE_MOVE
1299
1507
  });
1300
- await this.inputDriver.smoothMoveMouse(Math.round(target.x), Math.round(target.y), SMOOTH_MOVE_STEPS_MOUSE_MOVE, SMOOTH_MOVE_DELAY_MOUSE_MOVE);
1301
1508
  await this.inputDriver.delay(MOUSE_MOVE_EFFECT_WAIT);
1302
1509
  },
1303
1510
  dragAndDrop: async (from, to)=>{
1304
- const globalFrom = this.toGlobalPoint(from);
1305
- const globalTo = this.toGlobalPoint(to);
1306
- this.inputDriver.moveMouse(Math.round(globalFrom.x), Math.round(globalFrom.y));
1511
+ await this.moveDisplayPointer(from, 'Mouse did not reach the drag start target');
1307
1512
  await this.inputDriver.withMouseButton('left', async ()=>{
1308
1513
  await this.inputDriver.delay(100);
1309
- this.inputDriver.moveMouse(Math.round(globalTo.x), Math.round(globalTo.y));
1514
+ await this.moveDisplayPointer(to, 'Mouse did not reach the drag end target');
1310
1515
  await this.inputDriver.delay(100);
1311
1516
  });
1312
1517
  }
@@ -2265,7 +2470,7 @@ const tools = new ComputerMidsceneTools({
2265
2470
  });
2266
2471
  (0, cli_namespaceObject.runToolsCLI)(tools, 'midscene-computer', {
2267
2472
  stripPrefix: 'computer_',
2268
- version: "1.12.2",
2473
+ version: "1.12.3-beta-20260828110230.0",
2269
2474
  extraCommands: (0, core_namespaceObject.createReportCliCommands)()
2270
2475
  }).catch((e)=>{
2271
2476
  process.exit((0, cli_namespaceObject.reportCLIError)(e));