@aiscene/android 1.6.3 → 1.6.6
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/bin/midscene-android +2 -0
- package/bin/yadb +0 -0
- package/dist/es/cli.mjs +58 -4
- package/dist/es/index.mjs +57 -3
- package/dist/es/mcp-server.mjs +58 -4
- package/dist/lib/cli.js +58 -4
- package/dist/lib/index.js +57 -3
- package/dist/lib/mcp-server.js +58 -4
- package/dist/types/index.d.ts +10 -0
- package/dist/types/mcp-server.d.ts +10 -0
- package/package.json +1 -1
- package/bin/AndroidManifest.xml +0 -0
- package/bin/META-INF/com/android/build/gradle/app-metadata.properties +0 -2
- package/bin/classes.dex +0 -0
- package/bin/resources.arsc +0 -0
package/bin/yadb
ADDED
|
Binary file
|
package/dist/es/cli.mjs
CHANGED
|
@@ -932,7 +932,61 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
932
932
|
async execYadb(keyboardContent) {
|
|
933
933
|
await this.ensureYadb();
|
|
934
934
|
const adb = await this.getAdb();
|
|
935
|
-
|
|
935
|
+
try {
|
|
936
|
+
const command = `app_process${this.getDisplayArg()} -Djava.class.path=/data/local/tmp/yadb /data/local/tmp com.ysbing.yadb.Main -keyboard "${keyboardContent}"`;
|
|
937
|
+
debugDevice(`Executing YADB input: "${keyboardContent}"`);
|
|
938
|
+
await adb.shell(command);
|
|
939
|
+
debugDevice(`YADB input completed: "${keyboardContent}"`);
|
|
940
|
+
} catch (error) {
|
|
941
|
+
const isAccessibilityConflict = error?.cause?.stderr?.includes('UiAutomationService') || error?.cause?.stderr?.includes('already registered') || error?.message?.includes('UiAutomationService');
|
|
942
|
+
if (isAccessibilityConflict) {
|
|
943
|
+
debugDevice("YADB failed due to AccessibilityService conflict (likely Appium running), falling back to clipboard method");
|
|
944
|
+
await this.inputViaClipboard(keyboardContent);
|
|
945
|
+
} else debugDevice(`YADB execution may have completed despite error: ${error}`);
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
async inputViaClipboard(text) {
|
|
949
|
+
const adb = await this.getAdb();
|
|
950
|
+
try {
|
|
951
|
+
debugDevice(`Inputting via clipboard: "${text}"`);
|
|
952
|
+
const escapedText = text.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
953
|
+
const setClipboardCmd = `
|
|
954
|
+
content insert --uri content://settings/system --bind name:s:clipboard_text --bind value:s:"${escapedText}"
|
|
955
|
+
`;
|
|
956
|
+
await adb.shell(setClipboardCmd);
|
|
957
|
+
await sleep(100);
|
|
958
|
+
await adb.shell('input keyevent KEYCODE_PASTE');
|
|
959
|
+
await sleep(100);
|
|
960
|
+
debugDevice(`Clipboard input completed via content provider: "${text}"`);
|
|
961
|
+
} catch (error1) {
|
|
962
|
+
debugDevice(`Content provider clipboard failed, trying clipper app: ${error1}`);
|
|
963
|
+
try {
|
|
964
|
+
const base64Text = Buffer.from(text, 'utf-8').toString('base64');
|
|
965
|
+
await adb.shell(`am broadcast -a clipper.set -e text "${base64Text}"`);
|
|
966
|
+
await sleep(100);
|
|
967
|
+
await adb.shell('input keyevent KEYCODE_PASTE');
|
|
968
|
+
await sleep(100);
|
|
969
|
+
debugDevice(`Clipboard input completed via clipper: "${text}"`);
|
|
970
|
+
} catch (error2) {
|
|
971
|
+
debugDevice(`All clipboard methods failed: ${error2}`);
|
|
972
|
+
const isPureAscii = /^[\x00-\x7F]*$/.test(text);
|
|
973
|
+
if (isPureAscii) {
|
|
974
|
+
debugDevice(`Using ADB inputText for ASCII text: "${text}"`);
|
|
975
|
+
await adb.inputText(text);
|
|
976
|
+
} else await this.inputCharByChar(text);
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
async inputCharByChar(text) {
|
|
981
|
+
const adb = await this.getAdb();
|
|
982
|
+
debugDevice(`Inputting character by character (slow method): "${text}"`);
|
|
983
|
+
const chars = Array.from(text);
|
|
984
|
+
for (const char of chars){
|
|
985
|
+
if (' ' === char) await adb.shell('input keyevent KEYCODE_SPACE');
|
|
986
|
+
else await adb.shell(`input text "${char}"`);
|
|
987
|
+
await sleep(50);
|
|
988
|
+
}
|
|
989
|
+
debugDevice("Character-by-character input completed");
|
|
936
990
|
}
|
|
937
991
|
async getElementsInfo() {
|
|
938
992
|
return [];
|
|
@@ -1413,8 +1467,8 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
1413
1467
|
if (!this.yadbPushed) {
|
|
1414
1468
|
const adb = await this.getAdb();
|
|
1415
1469
|
const androidPkgJson = (0, external_node_module_.createRequire)(import.meta.url).resolve('@aiscene/android/package.json');
|
|
1416
|
-
const
|
|
1417
|
-
await adb.push(
|
|
1470
|
+
const yadbDir = external_node_path_["default"].join(external_node_path_["default"].dirname(androidPkgJson), 'bin');
|
|
1471
|
+
await adb.push(yadbDir, '/data/local/tmp');
|
|
1418
1472
|
this.yadbPushed = true;
|
|
1419
1473
|
}
|
|
1420
1474
|
}
|
|
@@ -1865,7 +1919,7 @@ class AndroidMidsceneTools extends BaseMidsceneTools {
|
|
|
1865
1919
|
const tools = new AndroidMidsceneTools();
|
|
1866
1920
|
runToolsCLI(tools, 'midscene-android', {
|
|
1867
1921
|
stripPrefix: 'android_',
|
|
1868
|
-
version: "1.6.
|
|
1922
|
+
version: "1.6.6"
|
|
1869
1923
|
}).catch((e)=>{
|
|
1870
1924
|
if (!(e instanceof CLIError)) console.error(e);
|
|
1871
1925
|
process.exit(e instanceof CLIError ? e.exitCode : 1);
|
package/dist/es/index.mjs
CHANGED
|
@@ -835,7 +835,61 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
835
835
|
async execYadb(keyboardContent) {
|
|
836
836
|
await this.ensureYadb();
|
|
837
837
|
const adb = await this.getAdb();
|
|
838
|
-
|
|
838
|
+
try {
|
|
839
|
+
const command = `app_process${this.getDisplayArg()} -Djava.class.path=/data/local/tmp/yadb /data/local/tmp com.ysbing.yadb.Main -keyboard "${keyboardContent}"`;
|
|
840
|
+
debugDevice(`Executing YADB input: "${keyboardContent}"`);
|
|
841
|
+
await adb.shell(command);
|
|
842
|
+
debugDevice(`YADB input completed: "${keyboardContent}"`);
|
|
843
|
+
} catch (error) {
|
|
844
|
+
const isAccessibilityConflict = error?.cause?.stderr?.includes('UiAutomationService') || error?.cause?.stderr?.includes('already registered') || error?.message?.includes('UiAutomationService');
|
|
845
|
+
if (isAccessibilityConflict) {
|
|
846
|
+
debugDevice("YADB failed due to AccessibilityService conflict (likely Appium running), falling back to clipboard method");
|
|
847
|
+
await this.inputViaClipboard(keyboardContent);
|
|
848
|
+
} else debugDevice(`YADB execution may have completed despite error: ${error}`);
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
async inputViaClipboard(text) {
|
|
852
|
+
const adb = await this.getAdb();
|
|
853
|
+
try {
|
|
854
|
+
debugDevice(`Inputting via clipboard: "${text}"`);
|
|
855
|
+
const escapedText = text.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
856
|
+
const setClipboardCmd = `
|
|
857
|
+
content insert --uri content://settings/system --bind name:s:clipboard_text --bind value:s:"${escapedText}"
|
|
858
|
+
`;
|
|
859
|
+
await adb.shell(setClipboardCmd);
|
|
860
|
+
await sleep(100);
|
|
861
|
+
await adb.shell('input keyevent KEYCODE_PASTE');
|
|
862
|
+
await sleep(100);
|
|
863
|
+
debugDevice(`Clipboard input completed via content provider: "${text}"`);
|
|
864
|
+
} catch (error1) {
|
|
865
|
+
debugDevice(`Content provider clipboard failed, trying clipper app: ${error1}`);
|
|
866
|
+
try {
|
|
867
|
+
const base64Text = Buffer.from(text, 'utf-8').toString('base64');
|
|
868
|
+
await adb.shell(`am broadcast -a clipper.set -e text "${base64Text}"`);
|
|
869
|
+
await sleep(100);
|
|
870
|
+
await adb.shell('input keyevent KEYCODE_PASTE');
|
|
871
|
+
await sleep(100);
|
|
872
|
+
debugDevice(`Clipboard input completed via clipper: "${text}"`);
|
|
873
|
+
} catch (error2) {
|
|
874
|
+
debugDevice(`All clipboard methods failed: ${error2}`);
|
|
875
|
+
const isPureAscii = /^[\x00-\x7F]*$/.test(text);
|
|
876
|
+
if (isPureAscii) {
|
|
877
|
+
debugDevice(`Using ADB inputText for ASCII text: "${text}"`);
|
|
878
|
+
await adb.inputText(text);
|
|
879
|
+
} else await this.inputCharByChar(text);
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
async inputCharByChar(text) {
|
|
884
|
+
const adb = await this.getAdb();
|
|
885
|
+
debugDevice(`Inputting character by character (slow method): "${text}"`);
|
|
886
|
+
const chars = Array.from(text);
|
|
887
|
+
for (const char of chars){
|
|
888
|
+
if (' ' === char) await adb.shell('input keyevent KEYCODE_SPACE');
|
|
889
|
+
else await adb.shell(`input text "${char}"`);
|
|
890
|
+
await sleep(50);
|
|
891
|
+
}
|
|
892
|
+
debugDevice("Character-by-character input completed");
|
|
839
893
|
}
|
|
840
894
|
async getElementsInfo() {
|
|
841
895
|
return [];
|
|
@@ -1316,8 +1370,8 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
1316
1370
|
if (!this.yadbPushed) {
|
|
1317
1371
|
const adb = await this.getAdb();
|
|
1318
1372
|
const androidPkgJson = (0, external_node_module_.createRequire)(import.meta.url).resolve('@aiscene/android/package.json');
|
|
1319
|
-
const
|
|
1320
|
-
await adb.push(
|
|
1373
|
+
const yadbDir = external_node_path_["default"].join(external_node_path_["default"].dirname(androidPkgJson), 'bin');
|
|
1374
|
+
await adb.push(yadbDir, '/data/local/tmp');
|
|
1321
1375
|
this.yadbPushed = true;
|
|
1322
1376
|
}
|
|
1323
1377
|
}
|
package/dist/es/mcp-server.mjs
CHANGED
|
@@ -931,7 +931,61 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
931
931
|
async execYadb(keyboardContent) {
|
|
932
932
|
await this.ensureYadb();
|
|
933
933
|
const adb = await this.getAdb();
|
|
934
|
-
|
|
934
|
+
try {
|
|
935
|
+
const command = `app_process${this.getDisplayArg()} -Djava.class.path=/data/local/tmp/yadb /data/local/tmp com.ysbing.yadb.Main -keyboard "${keyboardContent}"`;
|
|
936
|
+
debugDevice(`Executing YADB input: "${keyboardContent}"`);
|
|
937
|
+
await adb.shell(command);
|
|
938
|
+
debugDevice(`YADB input completed: "${keyboardContent}"`);
|
|
939
|
+
} catch (error) {
|
|
940
|
+
const isAccessibilityConflict = error?.cause?.stderr?.includes('UiAutomationService') || error?.cause?.stderr?.includes('already registered') || error?.message?.includes('UiAutomationService');
|
|
941
|
+
if (isAccessibilityConflict) {
|
|
942
|
+
debugDevice("YADB failed due to AccessibilityService conflict (likely Appium running), falling back to clipboard method");
|
|
943
|
+
await this.inputViaClipboard(keyboardContent);
|
|
944
|
+
} else debugDevice(`YADB execution may have completed despite error: ${error}`);
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
async inputViaClipboard(text) {
|
|
948
|
+
const adb = await this.getAdb();
|
|
949
|
+
try {
|
|
950
|
+
debugDevice(`Inputting via clipboard: "${text}"`);
|
|
951
|
+
const escapedText = text.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
952
|
+
const setClipboardCmd = `
|
|
953
|
+
content insert --uri content://settings/system --bind name:s:clipboard_text --bind value:s:"${escapedText}"
|
|
954
|
+
`;
|
|
955
|
+
await adb.shell(setClipboardCmd);
|
|
956
|
+
await sleep(100);
|
|
957
|
+
await adb.shell('input keyevent KEYCODE_PASTE');
|
|
958
|
+
await sleep(100);
|
|
959
|
+
debugDevice(`Clipboard input completed via content provider: "${text}"`);
|
|
960
|
+
} catch (error1) {
|
|
961
|
+
debugDevice(`Content provider clipboard failed, trying clipper app: ${error1}`);
|
|
962
|
+
try {
|
|
963
|
+
const base64Text = Buffer.from(text, 'utf-8').toString('base64');
|
|
964
|
+
await adb.shell(`am broadcast -a clipper.set -e text "${base64Text}"`);
|
|
965
|
+
await sleep(100);
|
|
966
|
+
await adb.shell('input keyevent KEYCODE_PASTE');
|
|
967
|
+
await sleep(100);
|
|
968
|
+
debugDevice(`Clipboard input completed via clipper: "${text}"`);
|
|
969
|
+
} catch (error2) {
|
|
970
|
+
debugDevice(`All clipboard methods failed: ${error2}`);
|
|
971
|
+
const isPureAscii = /^[\x00-\x7F]*$/.test(text);
|
|
972
|
+
if (isPureAscii) {
|
|
973
|
+
debugDevice(`Using ADB inputText for ASCII text: "${text}"`);
|
|
974
|
+
await adb.inputText(text);
|
|
975
|
+
} else await this.inputCharByChar(text);
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
async inputCharByChar(text) {
|
|
980
|
+
const adb = await this.getAdb();
|
|
981
|
+
debugDevice(`Inputting character by character (slow method): "${text}"`);
|
|
982
|
+
const chars = Array.from(text);
|
|
983
|
+
for (const char of chars){
|
|
984
|
+
if (' ' === char) await adb.shell('input keyevent KEYCODE_SPACE');
|
|
985
|
+
else await adb.shell(`input text "${char}"`);
|
|
986
|
+
await sleep(50);
|
|
987
|
+
}
|
|
988
|
+
debugDevice("Character-by-character input completed");
|
|
935
989
|
}
|
|
936
990
|
async getElementsInfo() {
|
|
937
991
|
return [];
|
|
@@ -1412,8 +1466,8 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
1412
1466
|
if (!this.yadbPushed) {
|
|
1413
1467
|
const adb = await this.getAdb();
|
|
1414
1468
|
const androidPkgJson = (0, external_node_module_.createRequire)(import.meta.url).resolve('@aiscene/android/package.json');
|
|
1415
|
-
const
|
|
1416
|
-
await adb.push(
|
|
1469
|
+
const yadbDir = external_node_path_["default"].join(external_node_path_["default"].dirname(androidPkgJson), 'bin');
|
|
1470
|
+
await adb.push(yadbDir, '/data/local/tmp');
|
|
1417
1471
|
this.yadbPushed = true;
|
|
1418
1472
|
}
|
|
1419
1473
|
}
|
|
@@ -1868,7 +1922,7 @@ class AndroidMCPServer extends BaseMCPServer {
|
|
|
1868
1922
|
constructor(toolsManager){
|
|
1869
1923
|
super({
|
|
1870
1924
|
name: '@midscene/android-mcp',
|
|
1871
|
-
version: "1.6.
|
|
1925
|
+
version: "1.6.6",
|
|
1872
1926
|
description: 'Control the Android device using natural language commands'
|
|
1873
1927
|
}, toolsManager);
|
|
1874
1928
|
}
|
package/dist/lib/cli.js
CHANGED
|
@@ -947,7 +947,61 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
947
947
|
async execYadb(keyboardContent) {
|
|
948
948
|
await this.ensureYadb();
|
|
949
949
|
const adb = await this.getAdb();
|
|
950
|
-
|
|
950
|
+
try {
|
|
951
|
+
const command = `app_process${this.getDisplayArg()} -Djava.class.path=/data/local/tmp/yadb /data/local/tmp com.ysbing.yadb.Main -keyboard "${keyboardContent}"`;
|
|
952
|
+
debugDevice(`Executing YADB input: "${keyboardContent}"`);
|
|
953
|
+
await adb.shell(command);
|
|
954
|
+
debugDevice(`YADB input completed: "${keyboardContent}"`);
|
|
955
|
+
} catch (error) {
|
|
956
|
+
const isAccessibilityConflict = error?.cause?.stderr?.includes('UiAutomationService') || error?.cause?.stderr?.includes('already registered') || error?.message?.includes('UiAutomationService');
|
|
957
|
+
if (isAccessibilityConflict) {
|
|
958
|
+
debugDevice("YADB failed due to AccessibilityService conflict (likely Appium running), falling back to clipboard method");
|
|
959
|
+
await this.inputViaClipboard(keyboardContent);
|
|
960
|
+
} else debugDevice(`YADB execution may have completed despite error: ${error}`);
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
async inputViaClipboard(text) {
|
|
964
|
+
const adb = await this.getAdb();
|
|
965
|
+
try {
|
|
966
|
+
debugDevice(`Inputting via clipboard: "${text}"`);
|
|
967
|
+
const escapedText = text.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
968
|
+
const setClipboardCmd = `
|
|
969
|
+
content insert --uri content://settings/system --bind name:s:clipboard_text --bind value:s:"${escapedText}"
|
|
970
|
+
`;
|
|
971
|
+
await adb.shell(setClipboardCmd);
|
|
972
|
+
await (0, core_utils_namespaceObject.sleep)(100);
|
|
973
|
+
await adb.shell('input keyevent KEYCODE_PASTE');
|
|
974
|
+
await (0, core_utils_namespaceObject.sleep)(100);
|
|
975
|
+
debugDevice(`Clipboard input completed via content provider: "${text}"`);
|
|
976
|
+
} catch (error1) {
|
|
977
|
+
debugDevice(`Content provider clipboard failed, trying clipper app: ${error1}`);
|
|
978
|
+
try {
|
|
979
|
+
const base64Text = Buffer.from(text, 'utf-8').toString('base64');
|
|
980
|
+
await adb.shell(`am broadcast -a clipper.set -e text "${base64Text}"`);
|
|
981
|
+
await (0, core_utils_namespaceObject.sleep)(100);
|
|
982
|
+
await adb.shell('input keyevent KEYCODE_PASTE');
|
|
983
|
+
await (0, core_utils_namespaceObject.sleep)(100);
|
|
984
|
+
debugDevice(`Clipboard input completed via clipper: "${text}"`);
|
|
985
|
+
} catch (error2) {
|
|
986
|
+
debugDevice(`All clipboard methods failed: ${error2}`);
|
|
987
|
+
const isPureAscii = /^[\x00-\x7F]*$/.test(text);
|
|
988
|
+
if (isPureAscii) {
|
|
989
|
+
debugDevice(`Using ADB inputText for ASCII text: "${text}"`);
|
|
990
|
+
await adb.inputText(text);
|
|
991
|
+
} else await this.inputCharByChar(text);
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
async inputCharByChar(text) {
|
|
996
|
+
const adb = await this.getAdb();
|
|
997
|
+
debugDevice(`Inputting character by character (slow method): "${text}"`);
|
|
998
|
+
const chars = Array.from(text);
|
|
999
|
+
for (const char of chars){
|
|
1000
|
+
if (' ' === char) await adb.shell('input keyevent KEYCODE_SPACE');
|
|
1001
|
+
else await adb.shell(`input text "${char}"`);
|
|
1002
|
+
await (0, core_utils_namespaceObject.sleep)(50);
|
|
1003
|
+
}
|
|
1004
|
+
debugDevice("Character-by-character input completed");
|
|
951
1005
|
}
|
|
952
1006
|
async getElementsInfo() {
|
|
953
1007
|
return [];
|
|
@@ -1428,8 +1482,8 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
1428
1482
|
if (!this.yadbPushed) {
|
|
1429
1483
|
const adb = await this.getAdb();
|
|
1430
1484
|
const androidPkgJson = (0, external_node_module_.createRequire)(__rslib_import_meta_url__).resolve('@aiscene/android/package.json');
|
|
1431
|
-
const
|
|
1432
|
-
await adb.push(
|
|
1485
|
+
const yadbDir = external_node_path_default().join(external_node_path_default().dirname(androidPkgJson), 'bin');
|
|
1486
|
+
await adb.push(yadbDir, '/data/local/tmp');
|
|
1433
1487
|
this.yadbPushed = true;
|
|
1434
1488
|
}
|
|
1435
1489
|
}
|
|
@@ -1880,7 +1934,7 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
1880
1934
|
const tools = new AndroidMidsceneTools();
|
|
1881
1935
|
(0, cli_namespaceObject.runToolsCLI)(tools, 'midscene-android', {
|
|
1882
1936
|
stripPrefix: 'android_',
|
|
1883
|
-
version: "1.6.
|
|
1937
|
+
version: "1.6.6"
|
|
1884
1938
|
}).catch((e)=>{
|
|
1885
1939
|
if (!(e instanceof cli_namespaceObject.CLIError)) console.error(e);
|
|
1886
1940
|
process.exit(e instanceof cli_namespaceObject.CLIError ? e.exitCode : 1);
|
package/dist/lib/index.js
CHANGED
|
@@ -868,7 +868,61 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
868
868
|
async execYadb(keyboardContent) {
|
|
869
869
|
await this.ensureYadb();
|
|
870
870
|
const adb = await this.getAdb();
|
|
871
|
-
|
|
871
|
+
try {
|
|
872
|
+
const command = `app_process${this.getDisplayArg()} -Djava.class.path=/data/local/tmp/yadb /data/local/tmp com.ysbing.yadb.Main -keyboard "${keyboardContent}"`;
|
|
873
|
+
debugDevice(`Executing YADB input: "${keyboardContent}"`);
|
|
874
|
+
await adb.shell(command);
|
|
875
|
+
debugDevice(`YADB input completed: "${keyboardContent}"`);
|
|
876
|
+
} catch (error) {
|
|
877
|
+
const isAccessibilityConflict = error?.cause?.stderr?.includes('UiAutomationService') || error?.cause?.stderr?.includes('already registered') || error?.message?.includes('UiAutomationService');
|
|
878
|
+
if (isAccessibilityConflict) {
|
|
879
|
+
debugDevice("YADB failed due to AccessibilityService conflict (likely Appium running), falling back to clipboard method");
|
|
880
|
+
await this.inputViaClipboard(keyboardContent);
|
|
881
|
+
} else debugDevice(`YADB execution may have completed despite error: ${error}`);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
async inputViaClipboard(text) {
|
|
885
|
+
const adb = await this.getAdb();
|
|
886
|
+
try {
|
|
887
|
+
debugDevice(`Inputting via clipboard: "${text}"`);
|
|
888
|
+
const escapedText = text.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
889
|
+
const setClipboardCmd = `
|
|
890
|
+
content insert --uri content://settings/system --bind name:s:clipboard_text --bind value:s:"${escapedText}"
|
|
891
|
+
`;
|
|
892
|
+
await adb.shell(setClipboardCmd);
|
|
893
|
+
await (0, utils_namespaceObject.sleep)(100);
|
|
894
|
+
await adb.shell('input keyevent KEYCODE_PASTE');
|
|
895
|
+
await (0, utils_namespaceObject.sleep)(100);
|
|
896
|
+
debugDevice(`Clipboard input completed via content provider: "${text}"`);
|
|
897
|
+
} catch (error1) {
|
|
898
|
+
debugDevice(`Content provider clipboard failed, trying clipper app: ${error1}`);
|
|
899
|
+
try {
|
|
900
|
+
const base64Text = Buffer.from(text, 'utf-8').toString('base64');
|
|
901
|
+
await adb.shell(`am broadcast -a clipper.set -e text "${base64Text}"`);
|
|
902
|
+
await (0, utils_namespaceObject.sleep)(100);
|
|
903
|
+
await adb.shell('input keyevent KEYCODE_PASTE');
|
|
904
|
+
await (0, utils_namespaceObject.sleep)(100);
|
|
905
|
+
debugDevice(`Clipboard input completed via clipper: "${text}"`);
|
|
906
|
+
} catch (error2) {
|
|
907
|
+
debugDevice(`All clipboard methods failed: ${error2}`);
|
|
908
|
+
const isPureAscii = /^[\x00-\x7F]*$/.test(text);
|
|
909
|
+
if (isPureAscii) {
|
|
910
|
+
debugDevice(`Using ADB inputText for ASCII text: "${text}"`);
|
|
911
|
+
await adb.inputText(text);
|
|
912
|
+
} else await this.inputCharByChar(text);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
async inputCharByChar(text) {
|
|
917
|
+
const adb = await this.getAdb();
|
|
918
|
+
debugDevice(`Inputting character by character (slow method): "${text}"`);
|
|
919
|
+
const chars = Array.from(text);
|
|
920
|
+
for (const char of chars){
|
|
921
|
+
if (' ' === char) await adb.shell('input keyevent KEYCODE_SPACE');
|
|
922
|
+
else await adb.shell(`input text "${char}"`);
|
|
923
|
+
await (0, utils_namespaceObject.sleep)(50);
|
|
924
|
+
}
|
|
925
|
+
debugDevice("Character-by-character input completed");
|
|
872
926
|
}
|
|
873
927
|
async getElementsInfo() {
|
|
874
928
|
return [];
|
|
@@ -1349,8 +1403,8 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
1349
1403
|
if (!this.yadbPushed) {
|
|
1350
1404
|
const adb = await this.getAdb();
|
|
1351
1405
|
const androidPkgJson = (0, external_node_module_.createRequire)(__rslib_import_meta_url__).resolve('@aiscene/android/package.json');
|
|
1352
|
-
const
|
|
1353
|
-
await adb.push(
|
|
1406
|
+
const yadbDir = external_node_path_default().join(external_node_path_default().dirname(androidPkgJson), 'bin');
|
|
1407
|
+
await adb.push(yadbDir, '/data/local/tmp');
|
|
1354
1408
|
this.yadbPushed = true;
|
|
1355
1409
|
}
|
|
1356
1410
|
}
|
package/dist/lib/mcp-server.js
CHANGED
|
@@ -962,7 +962,61 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
962
962
|
async execYadb(keyboardContent) {
|
|
963
963
|
await this.ensureYadb();
|
|
964
964
|
const adb = await this.getAdb();
|
|
965
|
-
|
|
965
|
+
try {
|
|
966
|
+
const command = `app_process${this.getDisplayArg()} -Djava.class.path=/data/local/tmp/yadb /data/local/tmp com.ysbing.yadb.Main -keyboard "${keyboardContent}"`;
|
|
967
|
+
debugDevice(`Executing YADB input: "${keyboardContent}"`);
|
|
968
|
+
await adb.shell(command);
|
|
969
|
+
debugDevice(`YADB input completed: "${keyboardContent}"`);
|
|
970
|
+
} catch (error) {
|
|
971
|
+
const isAccessibilityConflict = error?.cause?.stderr?.includes('UiAutomationService') || error?.cause?.stderr?.includes('already registered') || error?.message?.includes('UiAutomationService');
|
|
972
|
+
if (isAccessibilityConflict) {
|
|
973
|
+
debugDevice("YADB failed due to AccessibilityService conflict (likely Appium running), falling back to clipboard method");
|
|
974
|
+
await this.inputViaClipboard(keyboardContent);
|
|
975
|
+
} else debugDevice(`YADB execution may have completed despite error: ${error}`);
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
async inputViaClipboard(text) {
|
|
979
|
+
const adb = await this.getAdb();
|
|
980
|
+
try {
|
|
981
|
+
debugDevice(`Inputting via clipboard: "${text}"`);
|
|
982
|
+
const escapedText = text.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
983
|
+
const setClipboardCmd = `
|
|
984
|
+
content insert --uri content://settings/system --bind name:s:clipboard_text --bind value:s:"${escapedText}"
|
|
985
|
+
`;
|
|
986
|
+
await adb.shell(setClipboardCmd);
|
|
987
|
+
await (0, core_utils_namespaceObject.sleep)(100);
|
|
988
|
+
await adb.shell('input keyevent KEYCODE_PASTE');
|
|
989
|
+
await (0, core_utils_namespaceObject.sleep)(100);
|
|
990
|
+
debugDevice(`Clipboard input completed via content provider: "${text}"`);
|
|
991
|
+
} catch (error1) {
|
|
992
|
+
debugDevice(`Content provider clipboard failed, trying clipper app: ${error1}`);
|
|
993
|
+
try {
|
|
994
|
+
const base64Text = Buffer.from(text, 'utf-8').toString('base64');
|
|
995
|
+
await adb.shell(`am broadcast -a clipper.set -e text "${base64Text}"`);
|
|
996
|
+
await (0, core_utils_namespaceObject.sleep)(100);
|
|
997
|
+
await adb.shell('input keyevent KEYCODE_PASTE');
|
|
998
|
+
await (0, core_utils_namespaceObject.sleep)(100);
|
|
999
|
+
debugDevice(`Clipboard input completed via clipper: "${text}"`);
|
|
1000
|
+
} catch (error2) {
|
|
1001
|
+
debugDevice(`All clipboard methods failed: ${error2}`);
|
|
1002
|
+
const isPureAscii = /^[\x00-\x7F]*$/.test(text);
|
|
1003
|
+
if (isPureAscii) {
|
|
1004
|
+
debugDevice(`Using ADB inputText for ASCII text: "${text}"`);
|
|
1005
|
+
await adb.inputText(text);
|
|
1006
|
+
} else await this.inputCharByChar(text);
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
async inputCharByChar(text) {
|
|
1011
|
+
const adb = await this.getAdb();
|
|
1012
|
+
debugDevice(`Inputting character by character (slow method): "${text}"`);
|
|
1013
|
+
const chars = Array.from(text);
|
|
1014
|
+
for (const char of chars){
|
|
1015
|
+
if (' ' === char) await adb.shell('input keyevent KEYCODE_SPACE');
|
|
1016
|
+
else await adb.shell(`input text "${char}"`);
|
|
1017
|
+
await (0, core_utils_namespaceObject.sleep)(50);
|
|
1018
|
+
}
|
|
1019
|
+
debugDevice("Character-by-character input completed");
|
|
966
1020
|
}
|
|
967
1021
|
async getElementsInfo() {
|
|
968
1022
|
return [];
|
|
@@ -1443,8 +1497,8 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
1443
1497
|
if (!this.yadbPushed) {
|
|
1444
1498
|
const adb = await this.getAdb();
|
|
1445
1499
|
const androidPkgJson = (0, external_node_module_.createRequire)(__rslib_import_meta_url__).resolve('@aiscene/android/package.json');
|
|
1446
|
-
const
|
|
1447
|
-
await adb.push(
|
|
1500
|
+
const yadbDir = external_node_path_default().join(external_node_path_default().dirname(androidPkgJson), 'bin');
|
|
1501
|
+
await adb.push(yadbDir, '/data/local/tmp');
|
|
1448
1502
|
this.yadbPushed = true;
|
|
1449
1503
|
}
|
|
1450
1504
|
}
|
|
@@ -1899,7 +1953,7 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
1899
1953
|
constructor(toolsManager){
|
|
1900
1954
|
super({
|
|
1901
1955
|
name: '@midscene/android-mcp',
|
|
1902
|
-
version: "1.6.
|
|
1956
|
+
version: "1.6.6",
|
|
1903
1957
|
description: 'Control the Android device using natural language commands'
|
|
1904
1958
|
}, toolsManager);
|
|
1905
1959
|
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -109,6 +109,16 @@ export declare class AndroidDevice implements AbstractInterface {
|
|
|
109
109
|
private resolvePackageName;
|
|
110
110
|
launch(uri: string): Promise<AndroidDevice>;
|
|
111
111
|
execYadb(keyboardContent: string): Promise<void>;
|
|
112
|
+
/**
|
|
113
|
+
* 通过剪贴板输入文本(备用方案,当YADB不可用时)
|
|
114
|
+
* 支持中文等非ASCII字符
|
|
115
|
+
*/
|
|
116
|
+
private inputViaClipboard;
|
|
117
|
+
/**
|
|
118
|
+
* 逐字符输入文本(最后备用方案)
|
|
119
|
+
* 通过模拟按键输入,支持中文(如果有对应输入法)
|
|
120
|
+
*/
|
|
121
|
+
private inputCharByChar;
|
|
112
122
|
getElementsInfo(): Promise<ElementInfo[]>;
|
|
113
123
|
getElementsNodeTree(): Promise<any>;
|
|
114
124
|
getScreenSize(): Promise<{
|
|
@@ -108,6 +108,16 @@ declare class AndroidDevice implements AbstractInterface {
|
|
|
108
108
|
private resolvePackageName;
|
|
109
109
|
launch(uri: string): Promise<AndroidDevice>;
|
|
110
110
|
execYadb(keyboardContent: string): Promise<void>;
|
|
111
|
+
/**
|
|
112
|
+
* 通过剪贴板输入文本(备用方案,当YADB不可用时)
|
|
113
|
+
* 支持中文等非ASCII字符
|
|
114
|
+
*/
|
|
115
|
+
private inputViaClipboard;
|
|
116
|
+
/**
|
|
117
|
+
* 逐字符输入文本(最后备用方案)
|
|
118
|
+
* 通过模拟按键输入,支持中文(如果有对应输入法)
|
|
119
|
+
*/
|
|
120
|
+
private inputCharByChar;
|
|
111
121
|
getElementsInfo(): Promise<ElementInfo[]>;
|
|
112
122
|
getElementsNodeTree(): Promise<any>;
|
|
113
123
|
getScreenSize(): Promise<{
|
package/package.json
CHANGED
package/bin/AndroidManifest.xml
DELETED
|
Binary file
|
package/bin/classes.dex
DELETED
|
Binary file
|
package/bin/resources.arsc
DELETED
|
Binary file
|