@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/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,122 @@ class ComputerInputDriver {
184
194
  this.pendingInputDelayWaits = new Set();
185
195
  }
186
196
  }
197
+ const WINDOWS_PHYSICAL_PIXEL_POWERSHELL_PREAMBLE = `
198
+ $midsceneAssemblyName = New-Object System.Reflection.AssemblyName('MidsceneDpiNative')
199
+ $midsceneAssembly = [System.AppDomain]::CurrentDomain.DefineDynamicAssembly(
200
+ $midsceneAssemblyName,
201
+ [System.Reflection.Emit.AssemblyBuilderAccess]::Run
202
+ )
203
+ $midsceneModule = $midsceneAssembly.DefineDynamicModule('MidsceneDpiNativeModule')
204
+ $midsceneType = $midsceneModule.DefineType(
205
+ 'MidsceneDpiNative.User32',
206
+ [System.Reflection.TypeAttributes]'Public, Class, Sealed, Abstract'
207
+ )
208
+ $midsceneMethodAttributes =
209
+ [System.Reflection.MethodAttributes]::Public -bor
210
+ [System.Reflection.MethodAttributes]::Static -bor
211
+ [System.Reflection.MethodAttributes]::PinvokeImpl
212
+ $midsceneSetDpiMethod = $midsceneType.DefinePInvokeMethod(
213
+ 'SetThreadDpiAwarenessContext',
214
+ 'user32.dll',
215
+ $midsceneMethodAttributes,
216
+ [System.Reflection.CallingConventions]::Standard,
217
+ [System.IntPtr],
218
+ [System.Type[]]@([System.IntPtr]),
219
+ [System.Runtime.InteropServices.CallingConvention]::Winapi,
220
+ [System.Runtime.InteropServices.CharSet]::None
221
+ )
222
+ $midsceneSetDpiMethod.SetImplementationFlags(
223
+ $midsceneSetDpiMethod.GetMethodImplementationFlags() -bor
224
+ [System.Reflection.MethodImplAttributes]::PreserveSig
225
+ )
226
+ $midsceneNativeMethods = $midsceneType.CreateType()
227
+ $midscenePerMonitorV2 = [System.IntPtr](-4)
228
+ $midscenePreviousDpiContext =
229
+ $midsceneNativeMethods::SetThreadDpiAwarenessContext($midscenePerMonitorV2)
230
+ if ($midscenePreviousDpiContext -eq [System.IntPtr]::Zero) {
231
+ throw 'Unable to enter the Per-Monitor V2 DPI awareness context.'
232
+ }
233
+ `.trim();
234
+ function windows_pointer_define_property(obj, key, value) {
235
+ if (key in obj) Object.defineProperty(obj, key, {
236
+ value: value,
237
+ enumerable: true,
238
+ configurable: true,
239
+ writable: true
240
+ });
241
+ else obj[key] = value;
242
+ return obj;
243
+ }
244
+ const WINDOWS_POINTER_TOLERANCE_PX = 5;
245
+ function assertFinitePoint(point, context) {
246
+ if (!Number.isFinite(point.x) || !Number.isFinite(point.y)) throw new Error(`${context} must contain finite coordinates, got (${point.x}, ${point.y})`);
247
+ }
248
+ function parseWindowsPointerPosition(output, context) {
249
+ const match = output.trim().match(/^(-?\d+),(-?\d+)$/);
250
+ if (!match) throw new Error(`${context} returned an invalid cursor position: ${JSON.stringify(output.trim())}`);
251
+ return {
252
+ x: Number(match[1]),
253
+ y: Number(match[2])
254
+ };
255
+ }
256
+ function windowsPointerPositionScript() {
257
+ return `
258
+ ${WINDOWS_PHYSICAL_PIXEL_POWERSHELL_PREAMBLE}
259
+ Add-Type -AssemblyName System.Windows.Forms, System.Drawing
260
+ $position = [System.Windows.Forms.Cursor]::Position
261
+ [Console]::Out.Write(('{0},{1}' -f $position.X, $position.Y))
262
+ `.trim();
263
+ }
264
+ function windowsPointerMoveScript(point, options) {
265
+ assertFinitePoint(point, 'Windows pointer target');
266
+ 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');
267
+ const targetX = Math.round(point.x);
268
+ const targetY = Math.round(point.y);
269
+ const smoothSteps = Math.max(1, Math.round(options?.smoothSteps ?? 1));
270
+ const smoothDelayMs = Math.max(0, Math.round(options?.smoothDelayMs ?? 0));
271
+ return `
272
+ ${WINDOWS_PHYSICAL_PIXEL_POWERSHELL_PREAMBLE}
273
+ Add-Type -AssemblyName System.Windows.Forms, System.Drawing
274
+ $targetX = ${targetX}
275
+ $targetY = ${targetY}
276
+ $smoothSteps = ${smoothSteps}
277
+ $smoothDelayMs = ${smoothDelayMs}
278
+ $start = [System.Windows.Forms.Cursor]::Position
279
+ for ($step = 1; $step -le $smoothSteps; $step += 1) {
280
+ $x = [int][Math]::Round($start.X + (($targetX - $start.X) * $step / [double]$smoothSteps))
281
+ $y = [int][Math]::Round($start.Y + (($targetY - $start.Y) * $step / [double]$smoothSteps))
282
+ [System.Windows.Forms.Cursor]::Position = [System.Drawing.Point]::new($x, $y)
283
+ if ($smoothDelayMs -gt 0 -and $step -lt $smoothSteps) {
284
+ Start-Sleep -Milliseconds $smoothDelayMs
285
+ }
286
+ }
287
+ [System.Windows.Forms.Cursor]::Position = [System.Drawing.Point]::new($targetX, $targetY)
288
+ $position = [System.Windows.Forms.Cursor]::Position
289
+ [Console]::Out.Write(('{0},{1}' -f $position.X, $position.Y))
290
+ `.trim();
291
+ }
292
+ class WindowsPointerDriver {
293
+ getPosition() {
294
+ return parseWindowsPointerPosition(this.options.runPowershell(windowsPointerPositionScript()), 'Windows pointer query');
295
+ }
296
+ moveTo(point, options) {
297
+ return parseWindowsPointerPosition(this.options.runPowershell(windowsPointerMoveScript(point, options)), 'Windows pointer move');
298
+ }
299
+ constructor(options){
300
+ windows_pointer_define_property(this, "options", void 0);
301
+ this.options = options;
302
+ }
303
+ }
304
+ function windowsPointerDrift(expected, actual) {
305
+ return {
306
+ x: actual.x - expected.x,
307
+ y: actual.y - expected.y
308
+ };
309
+ }
310
+ function windowsPointerIsWithinTolerance(drift) {
311
+ return Math.abs(drift.x) <= WINDOWS_POINTER_TOLERANCE_PX && Math.abs(drift.y) <= WINDOWS_POINTER_TOLERANCE_PX;
312
+ }
187
313
  const debugXvfb = getDebug('computer:xvfb');
188
314
  const xvfbCleanupMonitorScript = String.raw`
189
315
  const parentPid = Number(process.argv[1]);
@@ -461,19 +587,34 @@ function runPowershell(script) {
461
587
  windowsHide: true
462
588
  });
463
589
  }
464
- function listWindowsDisplays() {
590
+ function readWindowsDisplayGeometries() {
465
591
  const script = `
592
+ ${WINDOWS_PHYSICAL_PIXEL_POWERSHELL_PREAMBLE}
466
593
  Add-Type -AssemblyName System.Windows.Forms
467
594
  $s = [System.Windows.Forms.Screen]::AllScreens | ForEach-Object {
468
- [PSCustomObject]@{ id = $_.DeviceName; name = $_.DeviceName; primary = $_.Primary }
595
+ $b = $_.Bounds
596
+ [PSCustomObject]@{
597
+ id = $_.DeviceName
598
+ name = $_.DeviceName
599
+ primary = $_.Primary
600
+ bounds = [PSCustomObject]@{ x = $b.X; y = $b.Y; width = $b.Width; height = $b.Height }
601
+ }
469
602
  }
470
603
  ConvertTo-Json @($s) -Compress
471
604
  `.trim();
472
- const parsed = JSON.parse(runPowershell(script).trim());
473
- return parsed.map((d)=>({
474
- id: String(d.id),
475
- name: d.name || String(d.id),
476
- primary: d.primary || false
605
+ const output = runPowershell(script).trim();
606
+ if (!output) throw new Error('Windows display enumeration returned no data');
607
+ const parsed = JSON.parse(output);
608
+ if (!Array.isArray(parsed)) throw new Error('Windows display enumeration returned invalid data');
609
+ const displays = parsed.filter(isWindowsDisplayGeometry);
610
+ if (displays.length !== parsed.length || 0 === displays.length) throw new Error('Windows display enumeration returned invalid geometry');
611
+ return displays;
612
+ }
613
+ function listWindowsDisplays(geometries = readWindowsDisplayGeometries()) {
614
+ return geometries.map((display)=>({
615
+ id: display.id,
616
+ name: display.name,
617
+ primary: display.primary
477
618
  }));
478
619
  }
479
620
  let device_libnut = null;
@@ -575,6 +716,16 @@ function getDisplayInfoBinary() {
575
716
  function isFiniteNumber(value) {
576
717
  return 'number' == typeof value && Number.isFinite(value);
577
718
  }
719
+ function isDisplayBounds(value) {
720
+ if (!value || 'object' != typeof value) return false;
721
+ const bounds = value;
722
+ return isFiniteNumber(bounds.x) && isFiniteNumber(bounds.y) && isFiniteNumber(bounds.width) && isFiniteNumber(bounds.height) && bounds.width > 0 && bounds.height > 0;
723
+ }
724
+ function isWindowsDisplayGeometry(value) {
725
+ if (!value || 'object' != typeof value) return false;
726
+ const candidate = value;
727
+ return 'string' == typeof candidate.id && candidate.id.length > 0 && 'string' == typeof candidate.name && 'boolean' == typeof candidate.primary && isDisplayBounds(candidate.bounds);
728
+ }
578
729
  function isDarwinDisplayGeometry(value) {
579
730
  if (!value || 'object' != typeof value) return false;
580
731
  const candidate = value;
@@ -617,9 +768,11 @@ function readDarwinFrontmostApplication() {
617
768
  return;
618
769
  }
619
770
  }
620
- async function pressMouseAtGlobalPoint(inputDriver, targetX, targetY, holdDuration, reason) {
621
- await inputDriver.delay(CLICK_SETTLE_DELAY);
622
- const current = inputDriver.getMousePos();
771
+ async function pressMouseAtGlobalPoint(inputDriver, targetX, targetY, current, holdDuration, reason) {
772
+ const drift = {
773
+ x: current.x - targetX,
774
+ y: current.y - targetY
775
+ };
623
776
  debugComputerInput('tap mouse moved %o', {
624
777
  reason,
625
778
  target: {
@@ -627,10 +780,7 @@ async function pressMouseAtGlobalPoint(inputDriver, targetX, targetY, holdDurati
627
780
  y: targetY
628
781
  },
629
782
  current,
630
- drift: {
631
- x: current.x - targetX,
632
- y: current.y - targetY
633
- }
783
+ drift
634
784
  });
635
785
  await inputDriver.withMouseButton('left', async ()=>{
636
786
  debugComputerInput('tap mouse down %o', {
@@ -649,9 +799,18 @@ function resolveDarwinDisplayGeometryFromList(displayId, displays) {
649
799
  if (void 0 === displayId || '' === displayId) return displays.find((display)=>display.primary) || displays.find((display)=>0 === display.screenIndex) || displays[0];
650
800
  return displays.find((display)=>display.screenIndex === screenIndex) || displays.find((display)=>display.cgDisplayId === screenIndex);
651
801
  }
652
- function resolveDisplayGeometry(displayId) {
653
- if ('darwin' !== process.platform) return;
654
- return resolveDarwinDisplayGeometryFromList(displayId, readDarwinDisplayGeometries());
802
+ function resolveWindowsDisplayGeometryFromList(displayId, displays) {
803
+ if (!displays.length) return;
804
+ if (void 0 === displayId || '' === displayId) return displays.find((display)=>display.primary) || displays[0];
805
+ return displays.find((display)=>display.id === displayId);
806
+ }
807
+ function resolveDisplayGeometry(displayId, windowsDisplays) {
808
+ if ('darwin' === process.platform) return resolveDarwinDisplayGeometryFromList(displayId, readDarwinDisplayGeometries());
809
+ if ('win32' === process.platform) {
810
+ const geometry = resolveWindowsDisplayGeometryFromList(displayId, windowsDisplays ?? readWindowsDisplayGeometries());
811
+ if (!geometry) throw new Error(displayId ? `Requested Windows display not found: ${displayId}` : 'No Windows displays were detected');
812
+ return geometry;
813
+ }
655
814
  }
656
815
  function mapDisplayLocalPointToGlobal(point, geometry) {
657
816
  if (!geometry) return point;
@@ -737,6 +896,30 @@ function normalizePrimaryKey(key) {
737
896
  return KEY_NAME_MAP[lowerKey] || lowerKey;
738
897
  }
739
898
  class ComputerDevice {
899
+ async moveGlobalPointer(point, context, smooth) {
900
+ if (this.destroyed) throw new Error('ComputerDevice has been destroyed');
901
+ const target = {
902
+ x: Math.round(point.x),
903
+ y: Math.round(point.y)
904
+ };
905
+ if ('win32' === process.platform) {
906
+ const actual = this.windowsPointerDriver.moveTo(target, {
907
+ smoothSteps: smooth?.smoothSteps,
908
+ smoothDelayMs: smooth?.smoothDelay
909
+ });
910
+ const drift = windowsPointerDrift(target, actual);
911
+ if (!windowsPointerIsWithinTolerance(drift)) throw new Error(`${context}: expected (${target.x}, ${target.y}), got (${actual.x}, ${actual.y}), drift=(${drift.x}, ${drift.y})`);
912
+ await this.inputDriver.delay(CLICK_SETTLE_DELAY);
913
+ return actual;
914
+ }
915
+ if (smooth) await this.inputDriver.smoothMoveMouse(target.x, target.y, smooth.smoothSteps, smooth.smoothDelay);
916
+ else this.inputDriver.moveMouse(target.x, target.y);
917
+ await this.inputDriver.delay(CLICK_SETTLE_DELAY);
918
+ return target;
919
+ }
920
+ moveDisplayPointer(point, context, smooth) {
921
+ return this.moveGlobalPointer(this.toGlobalPoint(point), context, smooth);
922
+ }
740
923
  async focusKeyboardTarget(element, delayMs) {
741
924
  const [x, y] = element.center;
742
925
  if ('darwin' === process.platform) await this.inputPrimitives.pointer.tap({
@@ -744,11 +927,10 @@ class ComputerDevice {
744
927
  y
745
928
  });
746
929
  else {
747
- const point = this.toGlobalPoint({
930
+ await this.moveDisplayPointer({
748
931
  x,
749
932
  y
750
- });
751
- this.inputDriver.moveMouse(Math.round(point.x), Math.round(point.y));
933
+ }, 'Mouse did not reach the keyboard focus target');
752
934
  this.inputDriver.mouseClick('left');
753
935
  }
754
936
  await this.inputDriver.delay(delayMs);
@@ -767,7 +949,7 @@ class ComputerDevice {
767
949
  }));
768
950
  } catch (error) {
769
951
  debugDevice(`Failed to list displays: ${error}`);
770
- return [];
952
+ throw new Error(`Failed to list displays: ${error}`);
771
953
  }
772
954
  }
773
955
  async connect() {
@@ -796,9 +978,10 @@ class ComputerDevice {
796
978
  }
797
979
  }
798
980
  device_libnut = await getLibnut();
799
- this.displayGeometry = resolveDisplayGeometry(this.displayId);
981
+ const windowsDisplayGeometries = 'win32' === process.platform ? readWindowsDisplayGeometries() : void 0;
982
+ this.displayGeometry = resolveDisplayGeometry(this.displayId, windowsDisplayGeometries);
800
983
  const size = await this.size();
801
- const displays = await ComputerDevice.listDisplays();
984
+ const displays = windowsDisplayGeometries ? listWindowsDisplays(windowsDisplayGeometries) : await ComputerDevice.listDisplays();
802
985
  const headlessInfo = this.xvfbInstance ? `\nHeadless: true (Xvfb on ${this.xvfbInstance.display})` : '';
803
986
  this.description = `
804
987
  Type: Computer
@@ -808,7 +991,7 @@ Screen Size: ${size.width}x${size.height}
808
991
  Available Displays: ${displays.length > 0 ? displays.map((d)=>d.name).join(', ') : 'Unknown'}${headlessInfo}
809
992
  `;
810
993
  debugDevice('Computer device connected', this.description);
811
- await this.healthCheck();
994
+ await this.healthCheck(displays);
812
995
  } catch (error) {
813
996
  if (this.xvfbInstance) {
814
997
  if (!this.options?.keepXvfbAliveUntilProcessExit) this.xvfbInstance.stop();
@@ -827,9 +1010,9 @@ Available Displays: ${displays.length > 0 ? displays.map((d)=>d.name).join(', ')
827
1010
  throw new Error(`Unable to connect to computer device: ${error}`);
828
1011
  }
829
1012
  }
830
- async healthCheck() {
1013
+ async healthCheck(displays) {
831
1014
  console.log('[HealthCheck] Starting health check...');
832
- console.log("[HealthCheck] @midscene/computer v1.12.2");
1015
+ console.log("[HealthCheck] @midscene/computer v1.12.3-beta-20260828110230.0");
833
1016
  console.log('[HealthCheck] Taking screenshot...');
834
1017
  const screenshotTimeout = 15000;
835
1018
  let timeoutId;
@@ -841,23 +1024,38 @@ Available Displays: ${displays.length > 0 ? displays.map((d)=>d.name).join(', ')
841
1024
  timeoutPromise
842
1025
  ]);
843
1026
  console.log(`[HealthCheck] Screenshot succeeded (length=${base64.length})`);
844
- console.log('[HealthCheck] Moving mouse...');
845
- const startPos = this.inputDriver.getMousePos();
1027
+ console.log('[HealthCheck] Verifying mouse control...');
1028
+ const startPos = 'win32' === process.platform ? this.windowsPointerDriver.getPosition() : this.inputDriver.getMousePos();
846
1029
  console.log(`[HealthCheck] Current mouse position: (${startPos.x}, ${startPos.y})`);
847
- const offsetX = Math.floor(40 * Math.random()) + 10;
848
- const offsetY = Math.floor(40 * Math.random()) + 10;
849
- const targetX = startPos.x + offsetX;
850
- const targetY = startPos.y + offsetY;
851
- console.log(`[HealthCheck] Moving mouse to (${targetX}, ${targetY})...`);
852
- this.inputDriver.moveMouse(targetX, targetY);
853
- await sleep(50);
854
- const movedPos = this.inputDriver.getMousePos();
855
- console.log(`[HealthCheck] Mouse position after move: (${movedPos.x}, ${movedPos.y})`);
856
- const deltaX = Math.abs(movedPos.x - targetX);
857
- const deltaY = Math.abs(movedPos.y - targetY);
858
- if (deltaX > 5 || deltaY > 5) {
859
- const msg = `[HealthCheck] WARNING: Mouse control may not be working. Expected (${targetX}, ${targetY}), got (${movedPos.x}, ${movedPos.y}), delta=(${deltaX}, ${deltaY})`;
860
- warnDevice(msg);
1030
+ if ('win32' === process.platform) {
1031
+ if (!this.displayGeometry) throw new Error('Windows display geometry is unavailable');
1032
+ const bounds = this.displayGeometry.bounds;
1033
+ const target = {
1034
+ x: Math.round(bounds.x + bounds.width / 2),
1035
+ y: Math.round(bounds.y + bounds.height / 2)
1036
+ };
1037
+ try {
1038
+ const actual = this.windowsPointerDriver.moveTo(target);
1039
+ const drift = windowsPointerDrift(target, actual);
1040
+ 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})`);
1041
+ } finally{
1042
+ this.windowsPointerDriver.moveTo(startPos);
1043
+ }
1044
+ console.log(`[HealthCheck] Mouse verified in screenshot-space display bounds (${bounds.x}, ${bounds.y}, ${bounds.width}, ${bounds.height})`);
1045
+ } else {
1046
+ const offsetX = Math.floor(40 * Math.random()) + 10;
1047
+ const offsetY = Math.floor(40 * Math.random()) + 10;
1048
+ const targetX = startPos.x + offsetX;
1049
+ const targetY = startPos.y + offsetY;
1050
+ console.log(`[HealthCheck] Moving mouse to (${targetX}, ${targetY})...`);
1051
+ try {
1052
+ this.inputDriver.moveMouse(targetX, targetY);
1053
+ await sleep(CLICK_SETTLE_DELAY);
1054
+ this.inputDriver.assertMousePosition(targetX, targetY, 'Mouse health check failed');
1055
+ } finally{
1056
+ this.inputDriver.moveMouse(startPos.x, startPos.y);
1057
+ console.log(`[HealthCheck] Mouse restored to (${startPos.x}, ${startPos.y})`);
1058
+ }
861
1059
  }
862
1060
  if ('win32' === process.platform && !this.isRunningAsAdmin()) {
863
1061
  const hint = [
@@ -867,10 +1065,7 @@ Available Displays: ${displays.length > 0 ? displays.map((d)=>d.name).join(', ')
867
1065
  ].join(' ');
868
1066
  warnDevice(`[HealthCheck] ${hint}`);
869
1067
  }
870
- this.inputDriver.moveMouse(startPos.x, startPos.y);
871
- console.log(`[HealthCheck] Mouse restored to (${startPos.x}, ${startPos.y})`);
872
1068
  console.log('[HealthCheck] Listing monitors...');
873
- const displays = await ComputerDevice.listDisplays();
874
1069
  if (displays.length > 0) {
875
1070
  console.log(`[HealthCheck] Found ${displays.length} monitor(s):`);
876
1071
  for (const display of displays){
@@ -939,6 +1134,7 @@ $screen = [System.Windows.Forms.Screen]::AllScreens | Where-Object { $_.DeviceNa
939
1134
  if (-not $screen) { throw "Requested display not found: $dn" }` : '$screen = [System.Windows.Forms.Screen]::PrimaryScreen';
940
1135
  const script = `
941
1136
  $ErrorActionPreference = 'Stop'
1137
+ ${WINDOWS_PHYSICAL_PIXEL_POWERSHELL_PREAMBLE}
942
1138
  Add-Type -AssemblyName System.Windows.Forms, System.Drawing
943
1139
  ${selectScreen}
944
1140
  $b = $screen.Bounds
@@ -1053,10 +1249,14 @@ $g.Dispose(); $bmp.Dispose(); $ms.Dispose()
1053
1249
  resolveUntargetedScrollPoint(screenSize) {
1054
1250
  if ('win32' === process.platform) {
1055
1251
  const activeWindowRect = this.inputDriver.getActiveWindowRect();
1056
- if (activeWindowRect) return {
1057
- x: activeWindowRect.x + activeWindowRect.width / 2,
1058
- y: activeWindowRect.y + activeWindowRect.height / 2
1059
- };
1252
+ if (activeWindowRect) {
1253
+ const activeWindowCenter = {
1254
+ x: activeWindowRect.x + activeWindowRect.width / 2,
1255
+ y: activeWindowRect.y + activeWindowRect.height / 2
1256
+ };
1257
+ const bounds = this.displayGeometry?.bounds;
1258
+ if (!bounds || activeWindowCenter.x >= bounds.x && activeWindowCenter.x < bounds.x + bounds.width && activeWindowCenter.y >= bounds.y && activeWindowCenter.y < bounds.y + bounds.height) return activeWindowCenter;
1259
+ }
1060
1260
  }
1061
1261
  return this.toGlobalPoint({
1062
1262
  x: screenSize.width / 2,
@@ -1067,17 +1267,16 @@ $g.Dispose(); $bmp.Dispose(); $ms.Dispose()
1067
1267
  if (param.locate) {
1068
1268
  const element = param.locate;
1069
1269
  const [x, y] = element.center;
1070
- const point = this.toGlobalPoint({
1270
+ await this.moveDisplayPointer({
1071
1271
  x,
1072
1272
  y
1073
- });
1074
- this.inputDriver.moveMouse(Math.round(point.x), Math.round(point.y));
1273
+ }, 'Mouse did not reach the scroll target');
1075
1274
  return;
1076
1275
  }
1077
1276
  const screenSize = await this.size();
1078
1277
  if ('win32' === process.platform && this.inputDriver.focusActiveWindow()) await this.inputDriver.delay(CLICK_FOCUS_SETTLE_DELAY);
1079
1278
  const point = this.resolveUntargetedScrollPoint(screenSize);
1080
- this.inputDriver.moveMouse(Math.round(point.x), Math.round(point.y));
1279
+ await this.moveGlobalPointer(point, 'Mouse did not reach the scroll viewport');
1081
1280
  await this.inputDriver.delay(MOUSE_MOVE_EFFECT_WAIT);
1082
1281
  return screenSize;
1083
1282
  }
@@ -1202,6 +1401,9 @@ $g.Dispose(); $bmp.Dispose(); $ms.Dispose()
1202
1401
  runPhasedScroll,
1203
1402
  debug: (message)=>debugDevice(message)
1204
1403
  }));
1404
+ device_define_property(this, "windowsPointerDriver", new WindowsPointerDriver({
1405
+ runPowershell
1406
+ }));
1205
1407
  device_define_property(this, "useAppleScript", void 0);
1206
1408
  device_define_property(this, "adminCheckCache", void 0);
1207
1409
  device_define_property(this, "uri", void 0);
@@ -1226,15 +1428,17 @@ $g.Dispose(); $bmp.Dispose(); $ms.Dispose()
1226
1428
  },
1227
1429
  holdDuration,
1228
1430
  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
1431
+ displayGeometry: this.displayGeometry
1234
1432
  });
1235
1433
  const frontmostBefore = 'darwin' === process.platform ? readDarwinFrontmostApplication() : void 0;
1236
- await this.inputDriver.smoothMoveMouse(targetX, targetY, SMOOTH_MOVE_STEPS_TAP, SMOOTH_MOVE_DELAY_TAP);
1237
- await pressMouseAtGlobalPoint(this.inputDriver, targetX, targetY, holdDuration, 'primary');
1434
+ const current = await this.moveGlobalPointer({
1435
+ x: targetX,
1436
+ y: targetY
1437
+ }, 'Mouse did not reach the tap target', {
1438
+ smoothSteps: SMOOTH_MOVE_STEPS_TAP,
1439
+ smoothDelay: SMOOTH_MOVE_DELAY_TAP
1440
+ });
1441
+ await pressMouseAtGlobalPoint(this.inputDriver, targetX, targetY, current, holdDuration, 'primary');
1238
1442
  if (frontmostBefore && 'darwin' === process.platform) {
1239
1443
  await sleep(CLICK_FOCUS_SETTLE_DELAY);
1240
1444
  const frontmostAfter = readDarwinFrontmostApplication();
@@ -1245,42 +1449,43 @@ $g.Dispose(); $bmp.Dispose(); $ms.Dispose()
1245
1449
  focusChanged
1246
1450
  });
1247
1451
  if (focusChanged) {
1248
- this.inputDriver.moveMouse(targetX, targetY);
1249
- await pressMouseAtGlobalPoint(this.inputDriver, targetX, targetY, holdDuration, 'focus-follow-up');
1452
+ const followUpCurrent = await this.moveGlobalPointer({
1453
+ x: targetX,
1454
+ y: targetY
1455
+ }, 'Mouse did not reach the focus follow-up target');
1456
+ await pressMouseAtGlobalPoint(this.inputDriver, targetX, targetY, followUpCurrent, holdDuration, 'focus-follow-up');
1250
1457
  }
1251
1458
  }
1252
1459
  },
1253
1460
  doubleClick: async ({ x, y })=>{
1254
- const target = this.toGlobalPoint({
1461
+ await this.moveDisplayPointer({
1255
1462
  x,
1256
1463
  y
1257
- });
1258
- this.inputDriver.moveMouse(Math.round(target.x), Math.round(target.y));
1464
+ }, 'Mouse did not reach the double-click target');
1259
1465
  this.inputDriver.mouseClick('left', true);
1260
1466
  },
1261
1467
  rightClick: async ({ x, y })=>{
1262
- const target = this.toGlobalPoint({
1468
+ await this.moveDisplayPointer({
1263
1469
  x,
1264
1470
  y
1265
- });
1266
- this.inputDriver.moveMouse(Math.round(target.x), Math.round(target.y));
1471
+ }, 'Mouse did not reach the right-click target');
1267
1472
  this.inputDriver.mouseClick('right');
1268
1473
  },
1269
1474
  hover: async ({ x, y })=>{
1270
- const target = this.toGlobalPoint({
1475
+ await this.moveDisplayPointer({
1271
1476
  x,
1272
1477
  y
1478
+ }, 'Mouse did not reach the hover target', {
1479
+ smoothSteps: SMOOTH_MOVE_STEPS_MOUSE_MOVE,
1480
+ smoothDelay: SMOOTH_MOVE_DELAY_MOUSE_MOVE
1273
1481
  });
1274
- await this.inputDriver.smoothMoveMouse(Math.round(target.x), Math.round(target.y), SMOOTH_MOVE_STEPS_MOUSE_MOVE, SMOOTH_MOVE_DELAY_MOUSE_MOVE);
1275
1482
  await this.inputDriver.delay(MOUSE_MOVE_EFFECT_WAIT);
1276
1483
  },
1277
1484
  dragAndDrop: async (from, to)=>{
1278
- const globalFrom = this.toGlobalPoint(from);
1279
- const globalTo = this.toGlobalPoint(to);
1280
- this.inputDriver.moveMouse(Math.round(globalFrom.x), Math.round(globalFrom.y));
1485
+ await this.moveDisplayPointer(from, 'Mouse did not reach the drag start target');
1281
1486
  await this.inputDriver.withMouseButton('left', async ()=>{
1282
1487
  await this.inputDriver.delay(100);
1283
- this.inputDriver.moveMouse(Math.round(globalTo.x), Math.round(globalTo.y));
1488
+ await this.moveDisplayPointer(to, 'Mouse did not reach the drag end target');
1284
1489
  await this.inputDriver.delay(100);
1285
1490
  });
1286
1491
  }
@@ -2238,7 +2443,7 @@ const tools = new ComputerMidsceneTools({
2238
2443
  });
2239
2444
  runToolsCLI(tools, 'midscene-computer', {
2240
2445
  stripPrefix: 'computer_',
2241
- version: "1.12.2",
2446
+ version: "1.12.3-beta-20260828110230.0",
2242
2447
  extraCommands: createReportCliCommands()
2243
2448
  }).catch((e)=>{
2244
2449
  process.exit(reportCLIError(e));