@xiuchang-midscene/android 1.6.0 → 2.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024-present Bytedance, Inc. and its affiliates.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/es/cli.mjs CHANGED
@@ -131,7 +131,7 @@ var __webpack_modules__ = {
131
131
  }
132
132
  }
133
133
  resolveServerBinPath() {
134
- const androidPkgJson = (0, node_module__rspack_import_1.createRequire)(import.meta.url).resolve('@midscene/android/package.json');
134
+ const androidPkgJson = (0, node_module__rspack_import_1.createRequire)(import.meta.url).resolve('@xiuchang-midscene/android/package.json');
135
135
  return node_path__rspack_import_2["default"].join(node_path__rspack_import_2["default"].dirname(androidPkgJson), 'bin', 'scrcpy-server');
136
136
  }
137
137
  getFfmpegPath() {
@@ -1473,7 +1473,7 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
1473
1473
  async ensureYadb() {
1474
1474
  if (!this.yadbPushed) {
1475
1475
  const adb = await this.getAdb();
1476
- const androidPkgJson = (0, external_node_module_.createRequire)(import.meta.url).resolve('@midscene/android/package.json');
1476
+ const androidPkgJson = (0, external_node_module_.createRequire)(import.meta.url).resolve('@xiuchang-midscene/android/package.json');
1477
1477
  const yadbBin = external_node_path_["default"].join(external_node_path_["default"].dirname(androidPkgJson), 'bin', 'yadb');
1478
1478
  await adb.push(yadbBin, '/data/local/tmp');
1479
1479
  this.yadbPushed = true;
@@ -1521,25 +1521,31 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
1521
1521
  IME_STRATEGY_ADB_KEYBOARD
1522
1522
  ];
1523
1523
  }
1524
- if (effectiveFallback?.length) for(let i = 0; i < effectiveFallback.length; i++){
1525
- const strategy = effectiveFallback[i];
1526
- const isLast = i === effectiveFallback.length - 1;
1527
- debugDevice(`[IME fallback] trying strategy: ${strategy} (${i + 1}/${effectiveFallback.length})`);
1528
- await this._typeWithStrategy(text, strategy, adb);
1529
- if (isLast) {
1530
- debugDevice('[IME fallback] all strategies tried');
1531
- break;
1532
- }
1533
- await sleep(300);
1534
- const success = await this.inputVerifyFn(text);
1535
- if (success) {
1536
- debugDevice(`[IME fallback] strategy '${strategy}' succeeded`);
1537
- break;
1524
+ if (effectiveFallback?.length) {
1525
+ const MAX_ROUNDS = 2;
1526
+ const totalAttempts = effectiveFallback.length * MAX_ROUNDS;
1527
+ let succeeded = false;
1528
+ for(let i = 0; i < totalAttempts; i++){
1529
+ const strategy = effectiveFallback[i % effectiveFallback.length];
1530
+ const isLast = i === totalAttempts - 1;
1531
+ debugDevice(`[IME fallback] trying strategy: ${strategy} (attempt ${i + 1}/${totalAttempts})`);
1532
+ await this._typeWithStrategy(text, strategy, adb);
1533
+ if (isLast) {
1534
+ debugDevice('[IME fallback] all attempts exhausted');
1535
+ break;
1536
+ }
1537
+ await sleep(800);
1538
+ const success = await this.inputVerifyFn(text);
1539
+ if (success) {
1540
+ debugDevice(`[IME fallback] strategy '${strategy}' succeeded`);
1541
+ succeeded = true;
1542
+ break;
1543
+ }
1544
+ debugDevice(`[IME fallback] strategy '${strategy}' failed, clearing field and trying next`);
1545
+ await adb.clearTextField(100);
1538
1546
  }
1539
- debugDevice(`[IME fallback] strategy '${strategy}' failed, clearing field and trying next`);
1540
- await adb.clearTextField(100);
1541
- }
1542
- else {
1547
+ debugDevice(`[IME fallback] done, succeeded=${succeeded}`);
1548
+ } else {
1543
1549
  const IME_STRATEGY = (this.options?.imeStrategy || globalConfigManager.getEnvConfigValue(MIDSCENE_ANDROID_IME_STRATEGY)) ?? IME_STRATEGY_YADB_FOR_NON_ASCII;
1544
1550
  await this._typeWithStrategy(text, IME_STRATEGY, adb);
1545
1551
  }
@@ -1916,7 +1922,7 @@ class AndroidAgent extends Agent {
1916
1922
  device.setAppNameMapping(this.appNameMapping);
1917
1923
  device.inputVerifyFn = async (text)=>{
1918
1924
  try {
1919
- return await this.aiBoolean(`the currently focused input field contains the text "${text}"`);
1925
+ return await this.aiBoolean(`the currently focused input field (text box) shows "${text}" as its actual typed value — ignore any IME candidate bar, autocomplete dropdown, or search suggestion list below the field; only check the input field itself`);
1920
1926
  } catch {
1921
1927
  return false;
1922
1928
  }
@@ -1994,7 +2000,7 @@ class AndroidMidsceneTools extends BaseMidsceneTools {
1994
2000
  const tools = new AndroidMidsceneTools();
1995
2001
  runToolsCLI(tools, 'midscene-android', {
1996
2002
  stripPrefix: 'android_',
1997
- version: "1.6.0"
2003
+ version: "2.0.0"
1998
2004
  }).catch((e)=>{
1999
2005
  if (!(e instanceof CLIError)) console.error(e);
2000
2006
  process.exit(e instanceof CLIError ? e.exitCode : 1);
package/dist/es/index.mjs CHANGED
@@ -130,7 +130,7 @@ var __webpack_modules__ = {
130
130
  }
131
131
  }
132
132
  resolveServerBinPath() {
133
- const androidPkgJson = (0, node_module__rspack_import_1.createRequire)(import.meta.url).resolve('@midscene/android/package.json');
133
+ const androidPkgJson = (0, node_module__rspack_import_1.createRequire)(import.meta.url).resolve('@xiuchang-midscene/android/package.json');
134
134
  return node_path__rspack_import_2["default"].join(node_path__rspack_import_2["default"].dirname(androidPkgJson), 'bin', 'scrcpy-server');
135
135
  }
136
136
  getFfmpegPath() {
@@ -1376,7 +1376,7 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
1376
1376
  async ensureYadb() {
1377
1377
  if (!this.yadbPushed) {
1378
1378
  const adb = await this.getAdb();
1379
- const androidPkgJson = (0, external_node_module_.createRequire)(import.meta.url).resolve('@midscene/android/package.json');
1379
+ const androidPkgJson = (0, external_node_module_.createRequire)(import.meta.url).resolve('@xiuchang-midscene/android/package.json');
1380
1380
  const yadbBin = external_node_path_["default"].join(external_node_path_["default"].dirname(androidPkgJson), 'bin', 'yadb');
1381
1381
  await adb.push(yadbBin, '/data/local/tmp');
1382
1382
  this.yadbPushed = true;
@@ -1424,25 +1424,31 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
1424
1424
  IME_STRATEGY_ADB_KEYBOARD
1425
1425
  ];
1426
1426
  }
1427
- if (effectiveFallback?.length) for(let i = 0; i < effectiveFallback.length; i++){
1428
- const strategy = effectiveFallback[i];
1429
- const isLast = i === effectiveFallback.length - 1;
1430
- debugDevice(`[IME fallback] trying strategy: ${strategy} (${i + 1}/${effectiveFallback.length})`);
1431
- await this._typeWithStrategy(text, strategy, adb);
1432
- if (isLast) {
1433
- debugDevice('[IME fallback] all strategies tried');
1434
- break;
1435
- }
1436
- await sleep(300);
1437
- const success = await this.inputVerifyFn(text);
1438
- if (success) {
1439
- debugDevice(`[IME fallback] strategy '${strategy}' succeeded`);
1440
- break;
1427
+ if (effectiveFallback?.length) {
1428
+ const MAX_ROUNDS = 2;
1429
+ const totalAttempts = effectiveFallback.length * MAX_ROUNDS;
1430
+ let succeeded = false;
1431
+ for(let i = 0; i < totalAttempts; i++){
1432
+ const strategy = effectiveFallback[i % effectiveFallback.length];
1433
+ const isLast = i === totalAttempts - 1;
1434
+ debugDevice(`[IME fallback] trying strategy: ${strategy} (attempt ${i + 1}/${totalAttempts})`);
1435
+ await this._typeWithStrategy(text, strategy, adb);
1436
+ if (isLast) {
1437
+ debugDevice('[IME fallback] all attempts exhausted');
1438
+ break;
1439
+ }
1440
+ await sleep(800);
1441
+ const success = await this.inputVerifyFn(text);
1442
+ if (success) {
1443
+ debugDevice(`[IME fallback] strategy '${strategy}' succeeded`);
1444
+ succeeded = true;
1445
+ break;
1446
+ }
1447
+ debugDevice(`[IME fallback] strategy '${strategy}' failed, clearing field and trying next`);
1448
+ await adb.clearTextField(100);
1441
1449
  }
1442
- debugDevice(`[IME fallback] strategy '${strategy}' failed, clearing field and trying next`);
1443
- await adb.clearTextField(100);
1444
- }
1445
- else {
1450
+ debugDevice(`[IME fallback] done, succeeded=${succeeded}`);
1451
+ } else {
1446
1452
  const IME_STRATEGY = (this.options?.imeStrategy || globalConfigManager.getEnvConfigValue(MIDSCENE_ANDROID_IME_STRATEGY)) ?? IME_STRATEGY_YADB_FOR_NON_ASCII;
1447
1453
  await this._typeWithStrategy(text, IME_STRATEGY, adb);
1448
1454
  }
@@ -1982,7 +1988,7 @@ class AndroidAgent extends Agent {
1982
1988
  device.setAppNameMapping(this.appNameMapping);
1983
1989
  device.inputVerifyFn = async (text)=>{
1984
1990
  try {
1985
- return await this.aiBoolean(`the currently focused input field contains the text "${text}"`);
1991
+ return await this.aiBoolean(`the currently focused input field (text box) shows "${text}" as its actual typed value — ignore any IME candidate bar, autocomplete dropdown, or search suggestion list below the field; only check the input field itself`);
1986
1992
  } catch {
1987
1993
  return false;
1988
1994
  }
@@ -130,7 +130,7 @@ var __webpack_modules__ = {
130
130
  }
131
131
  }
132
132
  resolveServerBinPath() {
133
- const androidPkgJson = (0, node_module__rspack_import_1.createRequire)(import.meta.url).resolve('@midscene/android/package.json');
133
+ const androidPkgJson = (0, node_module__rspack_import_1.createRequire)(import.meta.url).resolve('@xiuchang-midscene/android/package.json');
134
134
  return node_path__rspack_import_2["default"].join(node_path__rspack_import_2["default"].dirname(androidPkgJson), 'bin', 'scrcpy-server');
135
135
  }
136
136
  getFfmpegPath() {
@@ -1472,7 +1472,7 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
1472
1472
  async ensureYadb() {
1473
1473
  if (!this.yadbPushed) {
1474
1474
  const adb = await this.getAdb();
1475
- const androidPkgJson = (0, external_node_module_.createRequire)(import.meta.url).resolve('@midscene/android/package.json');
1475
+ const androidPkgJson = (0, external_node_module_.createRequire)(import.meta.url).resolve('@xiuchang-midscene/android/package.json');
1476
1476
  const yadbBin = external_node_path_["default"].join(external_node_path_["default"].dirname(androidPkgJson), 'bin', 'yadb');
1477
1477
  await adb.push(yadbBin, '/data/local/tmp');
1478
1478
  this.yadbPushed = true;
@@ -1520,25 +1520,31 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
1520
1520
  IME_STRATEGY_ADB_KEYBOARD
1521
1521
  ];
1522
1522
  }
1523
- if (effectiveFallback?.length) for(let i = 0; i < effectiveFallback.length; i++){
1524
- const strategy = effectiveFallback[i];
1525
- const isLast = i === effectiveFallback.length - 1;
1526
- debugDevice(`[IME fallback] trying strategy: ${strategy} (${i + 1}/${effectiveFallback.length})`);
1527
- await this._typeWithStrategy(text, strategy, adb);
1528
- if (isLast) {
1529
- debugDevice('[IME fallback] all strategies tried');
1530
- break;
1531
- }
1532
- await sleep(300);
1533
- const success = await this.inputVerifyFn(text);
1534
- if (success) {
1535
- debugDevice(`[IME fallback] strategy '${strategy}' succeeded`);
1536
- break;
1523
+ if (effectiveFallback?.length) {
1524
+ const MAX_ROUNDS = 2;
1525
+ const totalAttempts = effectiveFallback.length * MAX_ROUNDS;
1526
+ let succeeded = false;
1527
+ for(let i = 0; i < totalAttempts; i++){
1528
+ const strategy = effectiveFallback[i % effectiveFallback.length];
1529
+ const isLast = i === totalAttempts - 1;
1530
+ debugDevice(`[IME fallback] trying strategy: ${strategy} (attempt ${i + 1}/${totalAttempts})`);
1531
+ await this._typeWithStrategy(text, strategy, adb);
1532
+ if (isLast) {
1533
+ debugDevice('[IME fallback] all attempts exhausted');
1534
+ break;
1535
+ }
1536
+ await sleep(800);
1537
+ const success = await this.inputVerifyFn(text);
1538
+ if (success) {
1539
+ debugDevice(`[IME fallback] strategy '${strategy}' succeeded`);
1540
+ succeeded = true;
1541
+ break;
1542
+ }
1543
+ debugDevice(`[IME fallback] strategy '${strategy}' failed, clearing field and trying next`);
1544
+ await adb.clearTextField(100);
1537
1545
  }
1538
- debugDevice(`[IME fallback] strategy '${strategy}' failed, clearing field and trying next`);
1539
- await adb.clearTextField(100);
1540
- }
1541
- else {
1546
+ debugDevice(`[IME fallback] done, succeeded=${succeeded}`);
1547
+ } else {
1542
1548
  const IME_STRATEGY = (this.options?.imeStrategy || globalConfigManager.getEnvConfigValue(MIDSCENE_ANDROID_IME_STRATEGY)) ?? IME_STRATEGY_YADB_FOR_NON_ASCII;
1543
1549
  await this._typeWithStrategy(text, IME_STRATEGY, adb);
1544
1550
  }
@@ -1915,7 +1921,7 @@ class AndroidAgent extends Agent {
1915
1921
  device.setAppNameMapping(this.appNameMapping);
1916
1922
  device.inputVerifyFn = async (text)=>{
1917
1923
  try {
1918
- return await this.aiBoolean(`the currently focused input field contains the text "${text}"`);
1924
+ return await this.aiBoolean(`the currently focused input field (text box) shows "${text}" as its actual typed value — ignore any IME candidate bar, autocomplete dropdown, or search suggestion list below the field; only check the input field itself`);
1919
1925
  } catch {
1920
1926
  return false;
1921
1927
  }
@@ -1997,7 +2003,7 @@ class AndroidMCPServer extends BaseMCPServer {
1997
2003
  constructor(toolsManager){
1998
2004
  super({
1999
2005
  name: '@midscene/android-mcp',
2000
- version: "1.6.0",
2006
+ version: "2.0.0",
2001
2007
  description: 'Control the Android device using natural language commands'
2002
2008
  }, toolsManager);
2003
2009
  }
package/dist/lib/cli.js CHANGED
@@ -120,7 +120,7 @@ var __webpack_modules__ = {
120
120
  }
121
121
  }
122
122
  resolveServerBinPath() {
123
- const androidPkgJson = (0, node_module__rspack_import_1.createRequire)(__rslib_import_meta_url__).resolve('@midscene/android/package.json');
123
+ const androidPkgJson = (0, node_module__rspack_import_1.createRequire)(__rslib_import_meta_url__).resolve('@xiuchang-midscene/android/package.json');
124
124
  return node_path__rspack_import_2_default().join(node_path__rspack_import_2_default().dirname(androidPkgJson), 'bin', 'scrcpy-server');
125
125
  }
126
126
  getFfmpegPath() {
@@ -1488,7 +1488,7 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
1488
1488
  async ensureYadb() {
1489
1489
  if (!this.yadbPushed) {
1490
1490
  const adb = await this.getAdb();
1491
- const androidPkgJson = (0, external_node_module_.createRequire)(__rslib_import_meta_url__).resolve('@midscene/android/package.json');
1491
+ const androidPkgJson = (0, external_node_module_.createRequire)(__rslib_import_meta_url__).resolve('@xiuchang-midscene/android/package.json');
1492
1492
  const yadbBin = external_node_path_default().join(external_node_path_default().dirname(androidPkgJson), 'bin', 'yadb');
1493
1493
  await adb.push(yadbBin, '/data/local/tmp');
1494
1494
  this.yadbPushed = true;
@@ -1536,25 +1536,31 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
1536
1536
  IME_STRATEGY_ADB_KEYBOARD
1537
1537
  ];
1538
1538
  }
1539
- if (effectiveFallback?.length) for(let i = 0; i < effectiveFallback.length; i++){
1540
- const strategy = effectiveFallback[i];
1541
- const isLast = i === effectiveFallback.length - 1;
1542
- debugDevice(`[IME fallback] trying strategy: ${strategy} (${i + 1}/${effectiveFallback.length})`);
1543
- await this._typeWithStrategy(text, strategy, adb);
1544
- if (isLast) {
1545
- debugDevice('[IME fallback] all strategies tried');
1546
- break;
1547
- }
1548
- await (0, core_utils_namespaceObject.sleep)(300);
1549
- const success = await this.inputVerifyFn(text);
1550
- if (success) {
1551
- debugDevice(`[IME fallback] strategy '${strategy}' succeeded`);
1552
- break;
1539
+ if (effectiveFallback?.length) {
1540
+ const MAX_ROUNDS = 2;
1541
+ const totalAttempts = effectiveFallback.length * MAX_ROUNDS;
1542
+ let succeeded = false;
1543
+ for(let i = 0; i < totalAttempts; i++){
1544
+ const strategy = effectiveFallback[i % effectiveFallback.length];
1545
+ const isLast = i === totalAttempts - 1;
1546
+ debugDevice(`[IME fallback] trying strategy: ${strategy} (attempt ${i + 1}/${totalAttempts})`);
1547
+ await this._typeWithStrategy(text, strategy, adb);
1548
+ if (isLast) {
1549
+ debugDevice('[IME fallback] all attempts exhausted');
1550
+ break;
1551
+ }
1552
+ await (0, core_utils_namespaceObject.sleep)(800);
1553
+ const success = await this.inputVerifyFn(text);
1554
+ if (success) {
1555
+ debugDevice(`[IME fallback] strategy '${strategy}' succeeded`);
1556
+ succeeded = true;
1557
+ break;
1558
+ }
1559
+ debugDevice(`[IME fallback] strategy '${strategy}' failed, clearing field and trying next`);
1560
+ await adb.clearTextField(100);
1553
1561
  }
1554
- debugDevice(`[IME fallback] strategy '${strategy}' failed, clearing field and trying next`);
1555
- await adb.clearTextField(100);
1556
- }
1557
- else {
1562
+ debugDevice(`[IME fallback] done, succeeded=${succeeded}`);
1563
+ } else {
1558
1564
  const IME_STRATEGY = (this.options?.imeStrategy || env_namespaceObject.globalConfigManager.getEnvConfigValue(env_namespaceObject.MIDSCENE_ANDROID_IME_STRATEGY)) ?? IME_STRATEGY_YADB_FOR_NON_ASCII;
1559
1565
  await this._typeWithStrategy(text, IME_STRATEGY, adb);
1560
1566
  }
@@ -1931,7 +1937,7 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
1931
1937
  device.setAppNameMapping(this.appNameMapping);
1932
1938
  device.inputVerifyFn = async (text)=>{
1933
1939
  try {
1934
- return await this.aiBoolean(`the currently focused input field contains the text "${text}"`);
1940
+ return await this.aiBoolean(`the currently focused input field (text box) shows "${text}" as its actual typed value — ignore any IME candidate bar, autocomplete dropdown, or search suggestion list below the field; only check the input field itself`);
1935
1941
  } catch {
1936
1942
  return false;
1937
1943
  }
@@ -2009,7 +2015,7 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
2009
2015
  const tools = new AndroidMidsceneTools();
2010
2016
  (0, cli_namespaceObject.runToolsCLI)(tools, 'midscene-android', {
2011
2017
  stripPrefix: 'android_',
2012
- version: "1.6.0"
2018
+ version: "2.0.0"
2013
2019
  }).catch((e)=>{
2014
2020
  if (!(e instanceof cli_namespaceObject.CLIError)) console.error(e);
2015
2021
  process.exit(e instanceof cli_namespaceObject.CLIError ? e.exitCode : 1);
package/dist/lib/index.js CHANGED
@@ -120,7 +120,7 @@ var __webpack_modules__ = {
120
120
  }
121
121
  }
122
122
  resolveServerBinPath() {
123
- const androidPkgJson = (0, node_module__rspack_import_1.createRequire)(__rslib_import_meta_url__).resolve('@midscene/android/package.json');
123
+ const androidPkgJson = (0, node_module__rspack_import_1.createRequire)(__rslib_import_meta_url__).resolve('@xiuchang-midscene/android/package.json');
124
124
  return node_path__rspack_import_2_default().join(node_path__rspack_import_2_default().dirname(androidPkgJson), 'bin', 'scrcpy-server');
125
125
  }
126
126
  getFfmpegPath() {
@@ -1410,7 +1410,7 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
1410
1410
  async ensureYadb() {
1411
1411
  if (!this.yadbPushed) {
1412
1412
  const adb = await this.getAdb();
1413
- const androidPkgJson = (0, external_node_module_.createRequire)(__rslib_import_meta_url__).resolve('@midscene/android/package.json');
1413
+ const androidPkgJson = (0, external_node_module_.createRequire)(__rslib_import_meta_url__).resolve('@xiuchang-midscene/android/package.json');
1414
1414
  const yadbBin = external_node_path_default().join(external_node_path_default().dirname(androidPkgJson), 'bin', 'yadb');
1415
1415
  await adb.push(yadbBin, '/data/local/tmp');
1416
1416
  this.yadbPushed = true;
@@ -1458,25 +1458,31 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
1458
1458
  IME_STRATEGY_ADB_KEYBOARD
1459
1459
  ];
1460
1460
  }
1461
- if (effectiveFallback?.length) for(let i = 0; i < effectiveFallback.length; i++){
1462
- const strategy = effectiveFallback[i];
1463
- const isLast = i === effectiveFallback.length - 1;
1464
- debugDevice(`[IME fallback] trying strategy: ${strategy} (${i + 1}/${effectiveFallback.length})`);
1465
- await this._typeWithStrategy(text, strategy, adb);
1466
- if (isLast) {
1467
- debugDevice('[IME fallback] all strategies tried');
1468
- break;
1469
- }
1470
- await (0, utils_namespaceObject.sleep)(300);
1471
- const success = await this.inputVerifyFn(text);
1472
- if (success) {
1473
- debugDevice(`[IME fallback] strategy '${strategy}' succeeded`);
1474
- break;
1461
+ if (effectiveFallback?.length) {
1462
+ const MAX_ROUNDS = 2;
1463
+ const totalAttempts = effectiveFallback.length * MAX_ROUNDS;
1464
+ let succeeded = false;
1465
+ for(let i = 0; i < totalAttempts; i++){
1466
+ const strategy = effectiveFallback[i % effectiveFallback.length];
1467
+ const isLast = i === totalAttempts - 1;
1468
+ debugDevice(`[IME fallback] trying strategy: ${strategy} (attempt ${i + 1}/${totalAttempts})`);
1469
+ await this._typeWithStrategy(text, strategy, adb);
1470
+ if (isLast) {
1471
+ debugDevice('[IME fallback] all attempts exhausted');
1472
+ break;
1473
+ }
1474
+ await (0, utils_namespaceObject.sleep)(800);
1475
+ const success = await this.inputVerifyFn(text);
1476
+ if (success) {
1477
+ debugDevice(`[IME fallback] strategy '${strategy}' succeeded`);
1478
+ succeeded = true;
1479
+ break;
1480
+ }
1481
+ debugDevice(`[IME fallback] strategy '${strategy}' failed, clearing field and trying next`);
1482
+ await adb.clearTextField(100);
1475
1483
  }
1476
- debugDevice(`[IME fallback] strategy '${strategy}' failed, clearing field and trying next`);
1477
- await adb.clearTextField(100);
1478
- }
1479
- else {
1484
+ debugDevice(`[IME fallback] done, succeeded=${succeeded}`);
1485
+ } else {
1480
1486
  const IME_STRATEGY = (this.options?.imeStrategy || env_namespaceObject.globalConfigManager.getEnvConfigValue(env_namespaceObject.MIDSCENE_ANDROID_IME_STRATEGY)) ?? IME_STRATEGY_YADB_FOR_NON_ASCII;
1481
1487
  await this._typeWithStrategy(text, IME_STRATEGY, adb);
1482
1488
  }
@@ -2017,7 +2023,7 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
2017
2023
  device.setAppNameMapping(this.appNameMapping);
2018
2024
  device.inputVerifyFn = async (text)=>{
2019
2025
  try {
2020
- return await this.aiBoolean(`the currently focused input field contains the text "${text}"`);
2026
+ return await this.aiBoolean(`the currently focused input field (text box) shows "${text}" as its actual typed value — ignore any IME candidate bar, autocomplete dropdown, or search suggestion list below the field; only check the input field itself`);
2021
2027
  } catch {
2022
2028
  return false;
2023
2029
  }
@@ -120,7 +120,7 @@ var __webpack_modules__ = {
120
120
  }
121
121
  }
122
122
  resolveServerBinPath() {
123
- const androidPkgJson = (0, node_module__rspack_import_1.createRequire)(__rslib_import_meta_url__).resolve('@midscene/android/package.json');
123
+ const androidPkgJson = (0, node_module__rspack_import_1.createRequire)(__rslib_import_meta_url__).resolve('@xiuchang-midscene/android/package.json');
124
124
  return node_path__rspack_import_2_default().join(node_path__rspack_import_2_default().dirname(androidPkgJson), 'bin', 'scrcpy-server');
125
125
  }
126
126
  getFfmpegPath() {
@@ -1503,7 +1503,7 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
1503
1503
  async ensureYadb() {
1504
1504
  if (!this.yadbPushed) {
1505
1505
  const adb = await this.getAdb();
1506
- const androidPkgJson = (0, external_node_module_.createRequire)(__rslib_import_meta_url__).resolve('@midscene/android/package.json');
1506
+ const androidPkgJson = (0, external_node_module_.createRequire)(__rslib_import_meta_url__).resolve('@xiuchang-midscene/android/package.json');
1507
1507
  const yadbBin = external_node_path_default().join(external_node_path_default().dirname(androidPkgJson), 'bin', 'yadb');
1508
1508
  await adb.push(yadbBin, '/data/local/tmp');
1509
1509
  this.yadbPushed = true;
@@ -1551,25 +1551,31 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
1551
1551
  IME_STRATEGY_ADB_KEYBOARD
1552
1552
  ];
1553
1553
  }
1554
- if (effectiveFallback?.length) for(let i = 0; i < effectiveFallback.length; i++){
1555
- const strategy = effectiveFallback[i];
1556
- const isLast = i === effectiveFallback.length - 1;
1557
- debugDevice(`[IME fallback] trying strategy: ${strategy} (${i + 1}/${effectiveFallback.length})`);
1558
- await this._typeWithStrategy(text, strategy, adb);
1559
- if (isLast) {
1560
- debugDevice('[IME fallback] all strategies tried');
1561
- break;
1562
- }
1563
- await (0, core_utils_namespaceObject.sleep)(300);
1564
- const success = await this.inputVerifyFn(text);
1565
- if (success) {
1566
- debugDevice(`[IME fallback] strategy '${strategy}' succeeded`);
1567
- break;
1554
+ if (effectiveFallback?.length) {
1555
+ const MAX_ROUNDS = 2;
1556
+ const totalAttempts = effectiveFallback.length * MAX_ROUNDS;
1557
+ let succeeded = false;
1558
+ for(let i = 0; i < totalAttempts; i++){
1559
+ const strategy = effectiveFallback[i % effectiveFallback.length];
1560
+ const isLast = i === totalAttempts - 1;
1561
+ debugDevice(`[IME fallback] trying strategy: ${strategy} (attempt ${i + 1}/${totalAttempts})`);
1562
+ await this._typeWithStrategy(text, strategy, adb);
1563
+ if (isLast) {
1564
+ debugDevice('[IME fallback] all attempts exhausted');
1565
+ break;
1566
+ }
1567
+ await (0, core_utils_namespaceObject.sleep)(800);
1568
+ const success = await this.inputVerifyFn(text);
1569
+ if (success) {
1570
+ debugDevice(`[IME fallback] strategy '${strategy}' succeeded`);
1571
+ succeeded = true;
1572
+ break;
1573
+ }
1574
+ debugDevice(`[IME fallback] strategy '${strategy}' failed, clearing field and trying next`);
1575
+ await adb.clearTextField(100);
1568
1576
  }
1569
- debugDevice(`[IME fallback] strategy '${strategy}' failed, clearing field and trying next`);
1570
- await adb.clearTextField(100);
1571
- }
1572
- else {
1577
+ debugDevice(`[IME fallback] done, succeeded=${succeeded}`);
1578
+ } else {
1573
1579
  const IME_STRATEGY = (this.options?.imeStrategy || env_namespaceObject.globalConfigManager.getEnvConfigValue(env_namespaceObject.MIDSCENE_ANDROID_IME_STRATEGY)) ?? IME_STRATEGY_YADB_FOR_NON_ASCII;
1574
1580
  await this._typeWithStrategy(text, IME_STRATEGY, adb);
1575
1581
  }
@@ -1946,7 +1952,7 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
1946
1952
  device.setAppNameMapping(this.appNameMapping);
1947
1953
  device.inputVerifyFn = async (text)=>{
1948
1954
  try {
1949
- return await this.aiBoolean(`the currently focused input field contains the text "${text}"`);
1955
+ return await this.aiBoolean(`the currently focused input field (text box) shows "${text}" as its actual typed value — ignore any IME candidate bar, autocomplete dropdown, or search suggestion list below the field; only check the input field itself`);
1950
1956
  } catch {
1951
1957
  return false;
1952
1958
  }
@@ -2028,7 +2034,7 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
2028
2034
  constructor(toolsManager){
2029
2035
  super({
2030
2036
  name: '@midscene/android-mcp',
2031
- version: "1.6.0",
2037
+ version: "2.0.0",
2032
2038
  description: 'Control the Android device using natural language commands'
2033
2039
  }, toolsManager);
2034
2040
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xiuchang-midscene/android",
3
- "version": "1.6.0",
3
+ "version": "2.0.0",
4
4
  "description": "Android automation library for Midscene",
5
5
  "keywords": [
6
6
  "Android UI automation",
@@ -15,7 +15,11 @@
15
15
  "bin": {
16
16
  "midscene-android": "./bin/midscene-android"
17
17
  },
18
- "files": ["bin", "dist", "README.md"],
18
+ "files": [
19
+ "bin",
20
+ "dist",
21
+ "README.md"
22
+ ],
19
23
  "exports": {
20
24
  ".": {
21
25
  "types": "./dist/types/index.d.ts",
@@ -29,34 +33,21 @@
29
33
  },
30
34
  "./package.json": "./package.json"
31
35
  },
32
- "scripts": {
33
- "dev": "npm run build:watch",
34
- "prebuild": "node scripts/download-scrcpy-server.mjs && node scripts/download-yadb.mjs",
35
- "build": "rslib build",
36
- "build:watch": "rslib build --watch --no-clean",
37
- "prepack": "node scripts/download-scrcpy-server.mjs && node scripts/download-yadb.mjs",
38
- "playground": "DEBUG=midscene:* tsx demo/playground.ts",
39
- "test": "vitest --run",
40
- "test:u": "vitest --run -u",
41
- "test:ai": "AI_TEST_TYPE=android npm run test",
42
- "test:ai:cache": "MIDSCENE_CACHE=true AI_TEST_TYPE=android npm run test"
43
- },
44
36
  "dependencies": {
45
- "@midscene/core": "workspace:*",
46
- "@midscene/shared": "workspace:*",
47
37
  "@yume-chan/adb": "2.5.1",
48
38
  "@yume-chan/adb-scrcpy": "2.3.2",
49
39
  "@yume-chan/adb-server-node-tcp": "2.5.2",
50
40
  "@yume-chan/scrcpy": "2.3.0",
51
41
  "@yume-chan/stream-extra": "2.1.0",
52
42
  "appium-adb": "12.12.1",
53
- "sharp": "^0.34.3"
43
+ "sharp": "^0.34.3",
44
+ "@midscene/core": "1.6.0",
45
+ "@midscene/shared": "1.6.0"
54
46
  },
55
47
  "optionalDependencies": {
56
48
  "@ffmpeg-installer/ffmpeg": "^1.1.0"
57
49
  },
58
50
  "devDependencies": {
59
- "@midscene/playground": "workspace:*",
60
51
  "@rslib/core": "^0.18.3",
61
52
  "@types/node": "^18.0.0",
62
53
  "dotenv": "^16.4.5",
@@ -64,7 +55,19 @@
64
55
  "typescript": "^5.8.3",
65
56
  "tsx": "^4.19.2",
66
57
  "vitest": "3.0.5",
67
- "zod": "3.24.3"
58
+ "zod": "3.24.3",
59
+ "@midscene/playground": "1.6.0"
68
60
  },
69
- "license": "MIT"
70
- }
61
+ "license": "MIT",
62
+ "scripts": {
63
+ "dev": "npm run build:watch",
64
+ "prebuild": "node scripts/download-scrcpy-server.mjs && node scripts/download-yadb.mjs",
65
+ "build": "rslib build",
66
+ "build:watch": "rslib build --watch --no-clean",
67
+ "playground": "DEBUG=midscene:* tsx demo/playground.ts",
68
+ "test": "vitest --run",
69
+ "test:u": "vitest --run -u",
70
+ "test:ai": "AI_TEST_TYPE=android npm run test",
71
+ "test:ai:cache": "MIDSCENE_CACHE=true AI_TEST_TYPE=android npm run test"
72
+ }
73
+ }