@midscene/computer 1.12.7-beta-20260911063016.0 → 1.12.7

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
@@ -42,6 +42,73 @@ const utils_namespaceObject = require("@midscene/core/utils");
42
42
  const img_namespaceObject = require("@midscene/shared/img");
43
43
  const external_screenshot_desktop_namespaceObject = require("screenshot-desktop");
44
44
  var external_screenshot_desktop_default = /*#__PURE__*/ __webpack_require__.n(external_screenshot_desktop_namespaceObject);
45
+ const debugKeyboard = (0, logger_namespaceObject.getDebug)('computer:keyboard');
46
+ const APPLE_SCRIPT_KEY_CODES = {
47
+ return: 36,
48
+ enter: 36,
49
+ tab: 48,
50
+ space: 49,
51
+ backspace: 51,
52
+ delete: 51,
53
+ escape: 53,
54
+ forwarddelete: 117,
55
+ left: 123,
56
+ right: 124,
57
+ down: 125,
58
+ up: 126,
59
+ home: 115,
60
+ end: 119,
61
+ pageup: 116,
62
+ pagedown: 121,
63
+ f1: 122,
64
+ f2: 120,
65
+ f3: 99,
66
+ f4: 118,
67
+ f5: 96,
68
+ f6: 97,
69
+ f7: 98,
70
+ f8: 100,
71
+ f9: 101,
72
+ f10: 109,
73
+ f11: 103,
74
+ f12: 111
75
+ };
76
+ const APPLE_SCRIPT_MODIFIER_KEYS = {
77
+ command: 'command',
78
+ cmd: 'command',
79
+ control: 'control',
80
+ ctrl: 'control',
81
+ shift: 'shift',
82
+ alt: 'option',
83
+ option: 'option',
84
+ meta: 'command'
85
+ };
86
+ function buildKeyCommand(key) {
87
+ const keyCode = APPLE_SCRIPT_KEY_CODES[key.toLowerCase()];
88
+ if (void 0 !== keyCode) return `key code ${keyCode}`;
89
+ const escapedKey = key.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
90
+ return `keystroke "${escapedKey}"`;
91
+ }
92
+ function resolveModifierKeys(modifiers) {
93
+ return modifiers.map((modifier)=>APPLE_SCRIPT_MODIFIER_KEYS[modifier.toLowerCase()]).filter((modifier)=>void 0 !== modifier);
94
+ }
95
+ function buildAppleScriptKeyPress(key, modifiers = []) {
96
+ const modifierKeys = resolveModifierKeys(modifiers);
97
+ const modifierClause = modifierKeys.length ? ` using {${modifierKeys.map((modifier)=>`${modifier} down`).join(', ')}}` : '';
98
+ return `tell application "System Events" to ${buildKeyCommand(key)}${modifierClause}`;
99
+ }
100
+ function sendKeyViaAppleScript(key, modifiers = []) {
101
+ const script = buildAppleScriptKeyPress(key, modifiers);
102
+ debugKeyboard('sendKeyViaAppleScript', {
103
+ key,
104
+ modifiers,
105
+ script
106
+ });
107
+ (0, external_node_child_process_namespaceObject.execFileSync)("osascript", [
108
+ '-e',
109
+ script
110
+ ]);
111
+ }
45
112
  const external_node_assert_namespaceObject = require("node:assert");
46
113
  var external_node_assert_default = /*#__PURE__*/ __webpack_require__.n(external_node_assert_namespaceObject);
47
114
  function _define_property(obj, key, value) {
@@ -116,6 +183,41 @@ class ComputerInputDriver {
116
183
  if (void 0 !== modifiers) lib.keyTap(key, modifiers);
117
184
  else lib.keyTap(key);
118
185
  }
186
+ keyToggle(key, state, modifiers) {
187
+ const lib = this.getLibnutOrThrow('keyToggle');
188
+ if (void 0 !== modifiers) lib.keyToggle(key, state, modifiers);
189
+ else lib.keyToggle(key, state);
190
+ }
191
+ async keyTapWithModifierDelay(key, modifiers, delayMs) {
192
+ const uniqueModifiers = [
193
+ ...new Set(modifiers)
194
+ ];
195
+ if (0 === uniqueModifiers.length) return void this.keyTap(key);
196
+ if (0 === delayMs) return void this.keyTap(key, uniqueModifiers);
197
+ const pressedModifiers = [];
198
+ let failure;
199
+ try {
200
+ for (const modifier of uniqueModifiers){
201
+ this.keyToggle(modifier, 'down');
202
+ pressedModifiers.push(modifier);
203
+ await this.delay(delayMs);
204
+ }
205
+ this.keyTap(key);
206
+ await this.delay(delayMs);
207
+ } catch (error) {
208
+ failure = error;
209
+ }
210
+ for (const modifier of pressedModifiers.reverse()){
211
+ const releaseFailure = this.tryReleaseKey(modifier);
212
+ if (void 0 === failure && void 0 !== releaseFailure) failure = releaseFailure;
213
+ if (void 0 === failure) try {
214
+ await this.delay(delayMs);
215
+ } catch (error) {
216
+ failure = error;
217
+ }
218
+ }
219
+ if (void 0 !== failure) throw failure;
220
+ }
119
221
  typeString(text) {
120
222
  this.getLibnutOrThrow('typeString').typeString(text);
121
223
  }
@@ -188,6 +290,20 @@ class ComputerInputDriver {
188
290
  this.options.debug(`Failed to release mouse button ${button}: ${error}`);
189
291
  }
190
292
  }
293
+ tryReleaseKey(key) {
294
+ try {
295
+ const libnut = this.options.getLibnut();
296
+ external_node_assert_default()(libnut, 'libnut not initialized');
297
+ libnut.keyToggle(key, 'up');
298
+ return;
299
+ } catch (error) {
300
+ const releaseError = new Error(`Failed to release modifier key "${key}"`, {
301
+ cause: error
302
+ });
303
+ this.options.debug(`${releaseError.message}: ${error}`);
304
+ return releaseError;
305
+ }
306
+ }
191
307
  rejectPendingInputDelays() {
192
308
  const error = this.createDestroyedError('in-flight input');
193
309
  for (const waitRef of this.pendingInputDelayWaits){
@@ -205,6 +321,96 @@ class ComputerInputDriver {
205
321
  this.pendingInputDelayWaits = new Set();
206
322
  }
207
323
  }
324
+ const US_SHIFTED_CHARACTER_KEYS = new Map([
325
+ [
326
+ '~',
327
+ '`'
328
+ ],
329
+ [
330
+ '!',
331
+ '1'
332
+ ],
333
+ [
334
+ '@',
335
+ '2'
336
+ ],
337
+ [
338
+ '#',
339
+ '3'
340
+ ],
341
+ [
342
+ '$',
343
+ '4'
344
+ ],
345
+ [
346
+ '%',
347
+ '5'
348
+ ],
349
+ [
350
+ '^',
351
+ '6'
352
+ ],
353
+ [
354
+ '&',
355
+ '7'
356
+ ],
357
+ [
358
+ '*',
359
+ '8'
360
+ ],
361
+ [
362
+ '(',
363
+ '9'
364
+ ],
365
+ [
366
+ ')',
367
+ '0'
368
+ ],
369
+ [
370
+ '_',
371
+ '-'
372
+ ],
373
+ [
374
+ '+',
375
+ '='
376
+ ],
377
+ [
378
+ '{',
379
+ '['
380
+ ],
381
+ [
382
+ '}',
383
+ ']'
384
+ ],
385
+ [
386
+ '|',
387
+ '\\'
388
+ ],
389
+ [
390
+ ':',
391
+ ';'
392
+ ],
393
+ [
394
+ '"',
395
+ "'"
396
+ ],
397
+ [
398
+ '<',
399
+ ','
400
+ ],
401
+ [
402
+ '>',
403
+ '.'
404
+ ],
405
+ [
406
+ '?',
407
+ '/'
408
+ ]
409
+ ]);
410
+ function resolveShiftedKey(character, keyboardLayout) {
411
+ if (/^[A-Z]$/.test(character)) return character.toLowerCase();
412
+ return 'en-US' === keyboardLayout ? US_SHIFTED_CHARACTER_KEYS.get(character) : void 0;
413
+ }
208
414
  const POWERSHELL_TIMEOUT_MS = 15000;
209
415
  const POWERSHELL_MAX_BUFFER = 67108864;
210
416
  const WINDOWS_PHYSICAL_PIXEL_POWERSHELL_PREAMBLE = `
@@ -569,92 +775,6 @@ const LIBNUT_FALLBACK_PIXELS_PER_DETENT = 100;
569
775
  const LIBNUT_FALLBACK_TICK_DELAY_MS = 30;
570
776
  const LIBNUT_FALLBACK_MAX_DETENTS = 200;
571
777
  const LIBNUT_FALLBACK_DETENT_AMOUNT = 'win32' === process.platform ? 120 : 1;
572
- const LINUX_SHIFTED_CHARACTER_KEYS = new Map([
573
- [
574
- '~',
575
- '`'
576
- ],
577
- [
578
- '!',
579
- '1'
580
- ],
581
- [
582
- '@',
583
- '2'
584
- ],
585
- [
586
- '#',
587
- '3'
588
- ],
589
- [
590
- '$',
591
- '4'
592
- ],
593
- [
594
- '%',
595
- '5'
596
- ],
597
- [
598
- '^',
599
- '6'
600
- ],
601
- [
602
- '&',
603
- '7'
604
- ],
605
- [
606
- '*',
607
- '8'
608
- ],
609
- [
610
- '(',
611
- '9'
612
- ],
613
- [
614
- ')',
615
- '0'
616
- ],
617
- [
618
- '_',
619
- '-'
620
- ],
621
- [
622
- '+',
623
- '='
624
- ],
625
- [
626
- '{',
627
- '['
628
- ],
629
- [
630
- '}',
631
- ']'
632
- ],
633
- [
634
- '|',
635
- '\\'
636
- ],
637
- [
638
- ':',
639
- ';'
640
- ],
641
- [
642
- '"',
643
- "'"
644
- ],
645
- [
646
- '<',
647
- ','
648
- ],
649
- [
650
- '>',
651
- '.'
652
- ],
653
- [
654
- '?',
655
- '/'
656
- ]
657
- ]);
658
778
  const LIBNUT_FALLBACK_EDGE_DETENTS = Math.min(LIBNUT_FALLBACK_MAX_DETENTS, Math.max(1, Math.ceil(EDGE_SCROLL_TOTAL_PX / LIBNUT_FALLBACK_PIXELS_PER_DETENT)));
659
779
  const DEFAULT_SCROLL_VIEWPORT_RATIO = 0.7;
660
780
  const EDGE_SCROLL_SPEC = {
@@ -691,67 +811,6 @@ const EDGE_SCROLL_SPEC = {
691
811
  ]
692
812
  }
693
813
  };
694
- const APPLESCRIPT_KEY_CODE_MAP = {
695
- return: 36,
696
- enter: 36,
697
- tab: 48,
698
- space: 49,
699
- backspace: 51,
700
- delete: 51,
701
- escape: 53,
702
- forwarddelete: 117,
703
- left: 123,
704
- right: 124,
705
- down: 125,
706
- up: 126,
707
- home: 115,
708
- end: 119,
709
- pageup: 116,
710
- pagedown: 121,
711
- f1: 122,
712
- f2: 120,
713
- f3: 99,
714
- f4: 118,
715
- f5: 96,
716
- f6: 97,
717
- f7: 98,
718
- f8: 100,
719
- f9: 101,
720
- f10: 109,
721
- f11: 103,
722
- f12: 111
723
- };
724
- const APPLESCRIPT_MODIFIER_MAP = {
725
- command: 'command down',
726
- cmd: 'command down',
727
- control: 'control down',
728
- ctrl: 'control down',
729
- shift: 'shift down',
730
- alt: 'option down',
731
- option: 'option down',
732
- meta: 'command down'
733
- };
734
- function sendKeyViaAppleScript(key, modifiers = []) {
735
- const lowerKey = key.toLowerCase();
736
- const keyCode = APPLESCRIPT_KEY_CODE_MAP[lowerKey];
737
- const modifierParts = modifiers.map((m)=>APPLESCRIPT_MODIFIER_MAP[m.toLowerCase()]).filter(Boolean);
738
- const modifierStr = modifierParts.length > 0 ? ` using {${modifierParts.join(', ')}}` : '';
739
- let script;
740
- if (void 0 !== keyCode) script = `tell application "System Events" to key code ${keyCode}${modifierStr}`;
741
- else {
742
- const escapedKey = key.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
743
- script = `tell application "System Events" to keystroke "${escapedKey}"${modifierStr}`;
744
- }
745
- debugDevice('sendKeyViaAppleScript', {
746
- key,
747
- modifiers,
748
- script
749
- });
750
- (0, external_node_child_process_namespaceObject.execFileSync)("osascript", [
751
- '-e',
752
- script
753
- ]);
754
- }
755
814
  function escapePowershellSingleQuoted(value) {
756
815
  return value.replace(/'/g, "''");
757
816
  }
@@ -1179,7 +1238,7 @@ Available Displays: ${displays.length > 0 ? displays.map((d)=>d.name).join(', ')
1179
1238
  }
1180
1239
  async healthCheck(displays) {
1181
1240
  console.log('[HealthCheck] Starting health check...');
1182
- console.log("[HealthCheck] @midscene/computer v1.12.7-beta-20260911063016.0");
1241
+ console.log("[HealthCheck] @midscene/computer v1.12.7");
1183
1242
  console.log('[HealthCheck] Taking screenshot...');
1184
1243
  const screenshotTimeout = 15000;
1185
1244
  let timeoutId;
@@ -1356,9 +1415,9 @@ $g.Dispose(); $bmp.Dispose(); $ms.Dispose()
1356
1415
  ]);
1357
1416
  else {
1358
1417
  const modifier = 'darwin' === process.platform ? 'command' : 'control';
1359
- this.inputDriver.keyTap('v', [
1418
+ await this.inputDriver.keyTapWithModifierDelay('v', [
1360
1419
  modifier
1361
- ]);
1420
+ ], this.options?.keyboardModifierDelay ?? 0);
1362
1421
  }
1363
1422
  await this.inputDriver.delay(100);
1364
1423
  } finally{
@@ -1373,12 +1432,16 @@ $g.Dispose(); $bmp.Dispose(); $ms.Dispose()
1373
1432
  }
1374
1433
  async typeStringWithDelay(text, keyboardTypeDelay) {
1375
1434
  await (0, device_namespaceObject.sendTextSequentially)(text.replace(/\r\n?/g, '\n'), {
1376
- sendCharacter: (character)=>{
1377
- const linuxShiftedKey = 'linux' === process.platform ? LINUX_SHIFTED_CHARACTER_KEYS.get(character) : void 0;
1435
+ sendCharacter: async (character)=>{
1436
+ const linuxShiftedKey = 'linux' === process.platform ? US_SHIFTED_CHARACTER_KEYS.get(character) : void 0;
1437
+ const pacedShiftedKey = !this.useAppleScript && (this.options?.keyboardModifierDelay ?? 0) > 0 ? resolveShiftedKey(character, this.options?.keyboardLayout) : void 0;
1378
1438
  if ('\n' === character) this.inputDriver.sendKey('return');
1379
1439
  else if ('\t' === character) this.inputDriver.sendKey('tab');
1380
1440
  else if (' ' === character) this.inputDriver.sendKey('space');
1381
1441
  else if (this.useAppleScript) this.inputDriver.sendKeyViaAppleScript(character);
1442
+ else if (void 0 !== pacedShiftedKey) await this.inputDriver.keyTapWithModifierDelay(pacedShiftedKey, [
1443
+ 'shift'
1444
+ ], this.options?.keyboardModifierDelay ?? 0);
1382
1445
  else if (void 0 !== linuxShiftedKey) this.inputDriver.keyTap(linuxShiftedKey, [
1383
1446
  'shift'
1384
1447
  ]);
@@ -1399,9 +1462,9 @@ $g.Dispose(); $bmp.Dispose(); $ms.Dispose()
1399
1462
  return;
1400
1463
  }
1401
1464
  const modifier = 'darwin' === process.platform ? 'command' : 'control';
1402
- this.inputDriver.keyTap('a', [
1465
+ await this.inputDriver.keyTapWithModifierDelay('a', [
1403
1466
  modifier
1404
- ]);
1467
+ ], this.options?.keyboardModifierDelay ?? 0);
1405
1468
  await this.inputDriver.delay(50);
1406
1469
  this.inputDriver.keyTap('backspace');
1407
1470
  }
@@ -1415,6 +1478,7 @@ $g.Dispose(); $bmp.Dispose(); $ms.Dispose()
1415
1478
  modifiers,
1416
1479
  driver: this.useAppleScript ? "applescript" : 'libnut'
1417
1480
  });
1481
+ if (!this.useAppleScript && modifiers.length > 0 && (this.options?.keyboardModifierDelay ?? 0) > 0) return void await this.inputDriver.keyTapWithModifierDelay(key, modifiers, this.options?.keyboardModifierDelay ?? 0);
1418
1482
  this.inputDriver.sendKey(key, modifiers);
1419
1483
  }
1420
1484
  resolveUntargetedScrollPoint(screenSize) {
@@ -1568,7 +1632,7 @@ $g.Dispose(); $bmp.Dispose(); $ms.Dispose()
1568
1632
  device_define_property(this, "inputDriver", new ComputerInputDriver({
1569
1633
  getLibnut: ()=>device_libnut,
1570
1634
  useAppleScript: ()=>this.useAppleScript,
1571
- sendKeyViaAppleScript,
1635
+ sendKeyViaAppleScript: sendKeyViaAppleScript,
1572
1636
  runPhasedScroll,
1573
1637
  debug: (message)=>debugDevice(message)
1574
1638
  }));
@@ -1694,6 +1758,8 @@ $g.Dispose(); $bmp.Dispose(); $ms.Dispose()
1694
1758
  }
1695
1759
  }
1696
1760
  });
1761
+ if (options?.keyboardModifierDelay !== void 0 && (!Number.isFinite(options.keyboardModifierDelay) || options.keyboardModifierDelay < 0)) throw new Error('keyboardModifierDelay must be a finite non-negative number');
1762
+ if (options?.keyboardLayout !== void 0 && 'en-US' !== options.keyboardLayout) throw new Error('keyboardLayout must be "en-US" when specified');
1697
1763
  this.options = options;
1698
1764
  this.displayId = options?.displayId;
1699
1765
  this.useAppleScript = 'darwin' === process.platform && options?.keyboardDriver !== 'libnut';
@@ -2397,6 +2463,8 @@ function createLocalComputerDevice(opts) {
2397
2463
  keyboardTypeDelay: opts?.keyboardTypeDelay,
2398
2464
  inputStrategy: opts?.inputStrategy,
2399
2465
  keyboardDriver: opts?.keyboardDriver,
2466
+ keyboardModifierDelay: opts?.keyboardModifierDelay,
2467
+ keyboardLayout: opts?.keyboardLayout,
2400
2468
  headless: opts?.headless,
2401
2469
  xvfbResolution: opts?.xvfbResolution,
2402
2470
  keepXvfbAliveUntilProcessExit: opts?.keepXvfbAliveUntilProcessExit
@@ -2454,6 +2522,10 @@ const computerInitArgShape = {
2454
2522
  headless: core_namespaceObject.z.boolean().optional().describe('Start virtual display via Xvfb (Linux local mode only). Ignored when host is set.'),
2455
2523
  keyboardTypeDelay: core_namespaceObject.z.number().finite().nonnegative().optional().describe('Finite non-negative delay in milliseconds between keystrokes. In "legacy" mode, positive values enable key-by-key input; zero or omitted uses clipboard input.'),
2456
2524
  inputStrategy: core_namespaceObject.z["enum"](device_namespaceObject.inputStrategies).optional().describe('Text input strategy. "legacy" (default) preserves current Computer behavior, "sequential" sends one Unicode code point at a time, and "bulk" uses one backend input operation. "bulk" requires keyboardTypeDelay to be omitted or set to 0.'),
2525
+ keyboardModifierDelay: core_namespaceObject.z.number().finite().nonnegative().optional().describe('Finite non-negative delay in milliseconds after each modifier transition and the main key for local libnut keyboard events. Positive values pace shortcuts and the implicit Shift used by uppercase letters during sequential input. Layout-dependent punctuation also requires keyboardLayout. Ignored in RDP mode and by the macOS AppleScript driver.'),
2526
+ keyboardLayout: core_namespaceObject.z["enum"]([
2527
+ 'en-US'
2528
+ ]).optional().describe('Keyboard layout used to resolve layout-dependent shifted punctuation to base keys during sequential local libnut input. Currently only "en-US" is supported. Ignored in RDP mode and by the macOS AppleScript driver.'),
2457
2529
  host: core_namespaceObject.z.string().optional().describe('RDP host (FQDN or IP). Set this to switch into RDP mode.'),
2458
2530
  port: core_namespaceObject.z.number().optional().describe('RDP port (default 3389). Requires host.'),
2459
2531
  username: core_namespaceObject.z.string().optional().describe('RDP username. Requires host.'),
@@ -2470,7 +2542,7 @@ const computerInitArgShape = {
2470
2542
  function adaptComputerInitArgs(extracted) {
2471
2543
  if (!extracted || 0 === Object.keys(extracted).length) return;
2472
2544
  if (extracted.host) {
2473
- const { displayId: _d, headless: _h, ...rdpFields } = extracted;
2545
+ const { displayId: _d, headless: _h, keyboardModifierDelay: _s, keyboardLayout: _l, ...rdpFields } = extracted;
2474
2546
  const host = normalizeRdpHost(extracted.host);
2475
2547
  return {
2476
2548
  mode: 'rdp',
@@ -2484,6 +2556,8 @@ function adaptComputerInitArgs(extracted) {
2484
2556
  headless: extracted.headless,
2485
2557
  keyboardTypeDelay: extracted.keyboardTypeDelay,
2486
2558
  inputStrategy: extracted.inputStrategy,
2559
+ keyboardModifierDelay: extracted.keyboardModifierDelay,
2560
+ keyboardLayout: extracted.keyboardLayout,
2487
2561
  ...(0, agent_behavior_init_args_namespaceObject.extractAgentBehaviorInitArgs)(extracted) ?? {}
2488
2562
  };
2489
2563
  }
@@ -2535,6 +2609,8 @@ class ComputerMidsceneTools extends base_tools_namespaceObject.BaseMidsceneTools
2535
2609
  const headless = opts?.mode === 'local' ? opts.headless : void 0;
2536
2610
  const keyboardTypeDelay = opts?.keyboardTypeDelay;
2537
2611
  const inputStrategy = opts?.inputStrategy;
2612
+ const keyboardModifierDelay = opts?.mode === 'local' ? opts.keyboardModifierDelay : void 0;
2613
+ const keyboardLayout = opts?.mode === 'local' ? opts.keyboardLayout : void 0;
2538
2614
  agent_tools_debug('Creating Computer agent with displayId:', displayId || 'primary');
2539
2615
  const agentOpts = {
2540
2616
  ...displayId ? {
@@ -2549,6 +2625,12 @@ class ComputerMidsceneTools extends base_tools_namespaceObject.BaseMidsceneTools
2549
2625
  ...void 0 !== inputStrategy ? {
2550
2626
  inputStrategy
2551
2627
  } : {},
2628
+ ...void 0 !== keyboardModifierDelay ? {
2629
+ keyboardModifierDelay
2630
+ } : {},
2631
+ ...void 0 !== keyboardLayout ? {
2632
+ keyboardLayout
2633
+ } : {},
2552
2634
  ...this.options.keepXvfbAliveUntilProcessExit ? {
2553
2635
  keepXvfbAliveUntilProcessExit: true
2554
2636
  } : {},
@@ -2633,7 +2715,7 @@ const tools = new ComputerMidsceneTools({
2633
2715
  });
2634
2716
  (0, cli_namespaceObject.runToolsCLI)(tools, 'midscene-computer', {
2635
2717
  stripPrefix: 'computer_',
2636
- version: "1.12.7-beta-20260911063016.0",
2718
+ version: "1.12.7",
2637
2719
  extraCommands: (0, core_namespaceObject.createReportCliCommands)()
2638
2720
  }).catch((e)=>{
2639
2721
  process.exit((0, cli_namespaceObject.reportCLIError)(e));