@aiscene/android 1.6.4 → 1.6.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/bin/midscene-android +2 -0
- package/bin/yadb +0 -0
- package/dist/es/cli.mjs +61 -2
- package/dist/es/index.mjs +60 -1
- package/dist/es/mcp-server.mjs +61 -2
- package/dist/lib/cli.js +61 -2
- package/dist/lib/index.js +60 -1
- package/dist/lib/mcp-server.js +61 -2
- 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,66 @@ ${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
|
+
const inputPromise = adb.shell(command);
|
|
939
|
+
const timeoutPromise = new Promise((_, reject)=>setTimeout(()=>reject(new Error('YADB timeout')), 1000));
|
|
940
|
+
await Promise.race([
|
|
941
|
+
inputPromise,
|
|
942
|
+
timeoutPromise
|
|
943
|
+
]);
|
|
944
|
+
debugDevice(`YADB input completed: "${keyboardContent}"`);
|
|
945
|
+
} catch (error) {
|
|
946
|
+
const isAccessibilityConflict = error?.cause?.stderr?.includes('UiAutomationService') || error?.cause?.stderr?.includes('already registered') || error?.message?.includes('UiAutomationService');
|
|
947
|
+
if (isAccessibilityConflict) {
|
|
948
|
+
debugDevice("YADB failed due to AccessibilityService conflict (likely Appium running), falling back to clipboard method");
|
|
949
|
+
await this.inputViaClipboard(keyboardContent);
|
|
950
|
+
} else 'YADB timeout' === error.message ? debugDevice(`YADB timed out after 2s, assuming input succeeded: "${keyboardContent}"`) : debugDevice(`YADB execution may have completed despite error: ${error}`);
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
async inputViaClipboard(text) {
|
|
954
|
+
const adb = await this.getAdb();
|
|
955
|
+
try {
|
|
956
|
+
debugDevice(`Inputting via clipboard: "${text}"`);
|
|
957
|
+
const escapedText = text.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
958
|
+
const setClipboardCmd = `
|
|
959
|
+
content insert --uri content://settings/system --bind name:s:clipboard_text --bind value:s:"${escapedText}"
|
|
960
|
+
`;
|
|
961
|
+
await adb.shell(setClipboardCmd);
|
|
962
|
+
await sleep(100);
|
|
963
|
+
await adb.shell('input keyevent KEYCODE_PASTE');
|
|
964
|
+
await sleep(100);
|
|
965
|
+
debugDevice(`Clipboard input completed via content provider: "${text}"`);
|
|
966
|
+
} catch (error1) {
|
|
967
|
+
debugDevice(`Content provider clipboard failed, trying clipper app: ${error1}`);
|
|
968
|
+
try {
|
|
969
|
+
const base64Text = Buffer.from(text, 'utf-8').toString('base64');
|
|
970
|
+
await adb.shell(`am broadcast -a clipper.set -e text "${base64Text}"`);
|
|
971
|
+
await sleep(100);
|
|
972
|
+
await adb.shell('input keyevent KEYCODE_PASTE');
|
|
973
|
+
await sleep(100);
|
|
974
|
+
debugDevice(`Clipboard input completed via clipper: "${text}"`);
|
|
975
|
+
} catch (error2) {
|
|
976
|
+
debugDevice(`All clipboard methods failed: ${error2}`);
|
|
977
|
+
const isPureAscii = /^[\x00-\x7F]*$/.test(text);
|
|
978
|
+
if (isPureAscii) {
|
|
979
|
+
debugDevice(`Using ADB inputText for ASCII text: "${text}"`);
|
|
980
|
+
await adb.inputText(text);
|
|
981
|
+
} else await this.inputCharByChar(text);
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
async inputCharByChar(text) {
|
|
986
|
+
const adb = await this.getAdb();
|
|
987
|
+
debugDevice(`Inputting character by character (slow method): "${text}"`);
|
|
988
|
+
const chars = Array.from(text);
|
|
989
|
+
for (const char of chars){
|
|
990
|
+
if (' ' === char) await adb.shell('input keyevent KEYCODE_SPACE');
|
|
991
|
+
else await adb.shell(`input text "${char}"`);
|
|
992
|
+
await sleep(50);
|
|
993
|
+
}
|
|
994
|
+
debugDevice("Character-by-character input completed");
|
|
936
995
|
}
|
|
937
996
|
async getElementsInfo() {
|
|
938
997
|
return [];
|
|
@@ -1865,7 +1924,7 @@ class AndroidMidsceneTools extends BaseMidsceneTools {
|
|
|
1865
1924
|
const tools = new AndroidMidsceneTools();
|
|
1866
1925
|
runToolsCLI(tools, 'midscene-android', {
|
|
1867
1926
|
stripPrefix: 'android_',
|
|
1868
|
-
version: "1.6.
|
|
1927
|
+
version: "1.6.7"
|
|
1869
1928
|
}).catch((e)=>{
|
|
1870
1929
|
if (!(e instanceof CLIError)) console.error(e);
|
|
1871
1930
|
process.exit(e instanceof CLIError ? e.exitCode : 1);
|
package/dist/es/index.mjs
CHANGED
|
@@ -835,7 +835,66 @@ ${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
|
+
const inputPromise = adb.shell(command);
|
|
842
|
+
const timeoutPromise = new Promise((_, reject)=>setTimeout(()=>reject(new Error('YADB timeout')), 1000));
|
|
843
|
+
await Promise.race([
|
|
844
|
+
inputPromise,
|
|
845
|
+
timeoutPromise
|
|
846
|
+
]);
|
|
847
|
+
debugDevice(`YADB input completed: "${keyboardContent}"`);
|
|
848
|
+
} catch (error) {
|
|
849
|
+
const isAccessibilityConflict = error?.cause?.stderr?.includes('UiAutomationService') || error?.cause?.stderr?.includes('already registered') || error?.message?.includes('UiAutomationService');
|
|
850
|
+
if (isAccessibilityConflict) {
|
|
851
|
+
debugDevice("YADB failed due to AccessibilityService conflict (likely Appium running), falling back to clipboard method");
|
|
852
|
+
await this.inputViaClipboard(keyboardContent);
|
|
853
|
+
} else 'YADB timeout' === error.message ? debugDevice(`YADB timed out after 2s, assuming input succeeded: "${keyboardContent}"`) : debugDevice(`YADB execution may have completed despite error: ${error}`);
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
async inputViaClipboard(text) {
|
|
857
|
+
const adb = await this.getAdb();
|
|
858
|
+
try {
|
|
859
|
+
debugDevice(`Inputting via clipboard: "${text}"`);
|
|
860
|
+
const escapedText = text.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
861
|
+
const setClipboardCmd = `
|
|
862
|
+
content insert --uri content://settings/system --bind name:s:clipboard_text --bind value:s:"${escapedText}"
|
|
863
|
+
`;
|
|
864
|
+
await adb.shell(setClipboardCmd);
|
|
865
|
+
await sleep(100);
|
|
866
|
+
await adb.shell('input keyevent KEYCODE_PASTE');
|
|
867
|
+
await sleep(100);
|
|
868
|
+
debugDevice(`Clipboard input completed via content provider: "${text}"`);
|
|
869
|
+
} catch (error1) {
|
|
870
|
+
debugDevice(`Content provider clipboard failed, trying clipper app: ${error1}`);
|
|
871
|
+
try {
|
|
872
|
+
const base64Text = Buffer.from(text, 'utf-8').toString('base64');
|
|
873
|
+
await adb.shell(`am broadcast -a clipper.set -e text "${base64Text}"`);
|
|
874
|
+
await sleep(100);
|
|
875
|
+
await adb.shell('input keyevent KEYCODE_PASTE');
|
|
876
|
+
await sleep(100);
|
|
877
|
+
debugDevice(`Clipboard input completed via clipper: "${text}"`);
|
|
878
|
+
} catch (error2) {
|
|
879
|
+
debugDevice(`All clipboard methods failed: ${error2}`);
|
|
880
|
+
const isPureAscii = /^[\x00-\x7F]*$/.test(text);
|
|
881
|
+
if (isPureAscii) {
|
|
882
|
+
debugDevice(`Using ADB inputText for ASCII text: "${text}"`);
|
|
883
|
+
await adb.inputText(text);
|
|
884
|
+
} else await this.inputCharByChar(text);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
async inputCharByChar(text) {
|
|
889
|
+
const adb = await this.getAdb();
|
|
890
|
+
debugDevice(`Inputting character by character (slow method): "${text}"`);
|
|
891
|
+
const chars = Array.from(text);
|
|
892
|
+
for (const char of chars){
|
|
893
|
+
if (' ' === char) await adb.shell('input keyevent KEYCODE_SPACE');
|
|
894
|
+
else await adb.shell(`input text "${char}"`);
|
|
895
|
+
await sleep(50);
|
|
896
|
+
}
|
|
897
|
+
debugDevice("Character-by-character input completed");
|
|
839
898
|
}
|
|
840
899
|
async getElementsInfo() {
|
|
841
900
|
return [];
|
package/dist/es/mcp-server.mjs
CHANGED
|
@@ -931,7 +931,66 @@ ${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
|
+
const inputPromise = adb.shell(command);
|
|
938
|
+
const timeoutPromise = new Promise((_, reject)=>setTimeout(()=>reject(new Error('YADB timeout')), 1000));
|
|
939
|
+
await Promise.race([
|
|
940
|
+
inputPromise,
|
|
941
|
+
timeoutPromise
|
|
942
|
+
]);
|
|
943
|
+
debugDevice(`YADB input completed: "${keyboardContent}"`);
|
|
944
|
+
} catch (error) {
|
|
945
|
+
const isAccessibilityConflict = error?.cause?.stderr?.includes('UiAutomationService') || error?.cause?.stderr?.includes('already registered') || error?.message?.includes('UiAutomationService');
|
|
946
|
+
if (isAccessibilityConflict) {
|
|
947
|
+
debugDevice("YADB failed due to AccessibilityService conflict (likely Appium running), falling back to clipboard method");
|
|
948
|
+
await this.inputViaClipboard(keyboardContent);
|
|
949
|
+
} else 'YADB timeout' === error.message ? debugDevice(`YADB timed out after 2s, assuming input succeeded: "${keyboardContent}"`) : debugDevice(`YADB execution may have completed despite error: ${error}`);
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
async inputViaClipboard(text) {
|
|
953
|
+
const adb = await this.getAdb();
|
|
954
|
+
try {
|
|
955
|
+
debugDevice(`Inputting via clipboard: "${text}"`);
|
|
956
|
+
const escapedText = text.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
957
|
+
const setClipboardCmd = `
|
|
958
|
+
content insert --uri content://settings/system --bind name:s:clipboard_text --bind value:s:"${escapedText}"
|
|
959
|
+
`;
|
|
960
|
+
await adb.shell(setClipboardCmd);
|
|
961
|
+
await sleep(100);
|
|
962
|
+
await adb.shell('input keyevent KEYCODE_PASTE');
|
|
963
|
+
await sleep(100);
|
|
964
|
+
debugDevice(`Clipboard input completed via content provider: "${text}"`);
|
|
965
|
+
} catch (error1) {
|
|
966
|
+
debugDevice(`Content provider clipboard failed, trying clipper app: ${error1}`);
|
|
967
|
+
try {
|
|
968
|
+
const base64Text = Buffer.from(text, 'utf-8').toString('base64');
|
|
969
|
+
await adb.shell(`am broadcast -a clipper.set -e text "${base64Text}"`);
|
|
970
|
+
await sleep(100);
|
|
971
|
+
await adb.shell('input keyevent KEYCODE_PASTE');
|
|
972
|
+
await sleep(100);
|
|
973
|
+
debugDevice(`Clipboard input completed via clipper: "${text}"`);
|
|
974
|
+
} catch (error2) {
|
|
975
|
+
debugDevice(`All clipboard methods failed: ${error2}`);
|
|
976
|
+
const isPureAscii = /^[\x00-\x7F]*$/.test(text);
|
|
977
|
+
if (isPureAscii) {
|
|
978
|
+
debugDevice(`Using ADB inputText for ASCII text: "${text}"`);
|
|
979
|
+
await adb.inputText(text);
|
|
980
|
+
} else await this.inputCharByChar(text);
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
async inputCharByChar(text) {
|
|
985
|
+
const adb = await this.getAdb();
|
|
986
|
+
debugDevice(`Inputting character by character (slow method): "${text}"`);
|
|
987
|
+
const chars = Array.from(text);
|
|
988
|
+
for (const char of chars){
|
|
989
|
+
if (' ' === char) await adb.shell('input keyevent KEYCODE_SPACE');
|
|
990
|
+
else await adb.shell(`input text "${char}"`);
|
|
991
|
+
await sleep(50);
|
|
992
|
+
}
|
|
993
|
+
debugDevice("Character-by-character input completed");
|
|
935
994
|
}
|
|
936
995
|
async getElementsInfo() {
|
|
937
996
|
return [];
|
|
@@ -1868,7 +1927,7 @@ class AndroidMCPServer extends BaseMCPServer {
|
|
|
1868
1927
|
constructor(toolsManager){
|
|
1869
1928
|
super({
|
|
1870
1929
|
name: '@midscene/android-mcp',
|
|
1871
|
-
version: "1.6.
|
|
1930
|
+
version: "1.6.7",
|
|
1872
1931
|
description: 'Control the Android device using natural language commands'
|
|
1873
1932
|
}, toolsManager);
|
|
1874
1933
|
}
|
package/dist/lib/cli.js
CHANGED
|
@@ -947,7 +947,66 @@ ${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
|
+
const inputPromise = adb.shell(command);
|
|
954
|
+
const timeoutPromise = new Promise((_, reject)=>setTimeout(()=>reject(new Error('YADB timeout')), 1000));
|
|
955
|
+
await Promise.race([
|
|
956
|
+
inputPromise,
|
|
957
|
+
timeoutPromise
|
|
958
|
+
]);
|
|
959
|
+
debugDevice(`YADB input completed: "${keyboardContent}"`);
|
|
960
|
+
} catch (error) {
|
|
961
|
+
const isAccessibilityConflict = error?.cause?.stderr?.includes('UiAutomationService') || error?.cause?.stderr?.includes('already registered') || error?.message?.includes('UiAutomationService');
|
|
962
|
+
if (isAccessibilityConflict) {
|
|
963
|
+
debugDevice("YADB failed due to AccessibilityService conflict (likely Appium running), falling back to clipboard method");
|
|
964
|
+
await this.inputViaClipboard(keyboardContent);
|
|
965
|
+
} else 'YADB timeout' === error.message ? debugDevice(`YADB timed out after 2s, assuming input succeeded: "${keyboardContent}"`) : debugDevice(`YADB execution may have completed despite error: ${error}`);
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
async inputViaClipboard(text) {
|
|
969
|
+
const adb = await this.getAdb();
|
|
970
|
+
try {
|
|
971
|
+
debugDevice(`Inputting via clipboard: "${text}"`);
|
|
972
|
+
const escapedText = text.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
973
|
+
const setClipboardCmd = `
|
|
974
|
+
content insert --uri content://settings/system --bind name:s:clipboard_text --bind value:s:"${escapedText}"
|
|
975
|
+
`;
|
|
976
|
+
await adb.shell(setClipboardCmd);
|
|
977
|
+
await (0, core_utils_namespaceObject.sleep)(100);
|
|
978
|
+
await adb.shell('input keyevent KEYCODE_PASTE');
|
|
979
|
+
await (0, core_utils_namespaceObject.sleep)(100);
|
|
980
|
+
debugDevice(`Clipboard input completed via content provider: "${text}"`);
|
|
981
|
+
} catch (error1) {
|
|
982
|
+
debugDevice(`Content provider clipboard failed, trying clipper app: ${error1}`);
|
|
983
|
+
try {
|
|
984
|
+
const base64Text = Buffer.from(text, 'utf-8').toString('base64');
|
|
985
|
+
await adb.shell(`am broadcast -a clipper.set -e text "${base64Text}"`);
|
|
986
|
+
await (0, core_utils_namespaceObject.sleep)(100);
|
|
987
|
+
await adb.shell('input keyevent KEYCODE_PASTE');
|
|
988
|
+
await (0, core_utils_namespaceObject.sleep)(100);
|
|
989
|
+
debugDevice(`Clipboard input completed via clipper: "${text}"`);
|
|
990
|
+
} catch (error2) {
|
|
991
|
+
debugDevice(`All clipboard methods failed: ${error2}`);
|
|
992
|
+
const isPureAscii = /^[\x00-\x7F]*$/.test(text);
|
|
993
|
+
if (isPureAscii) {
|
|
994
|
+
debugDevice(`Using ADB inputText for ASCII text: "${text}"`);
|
|
995
|
+
await adb.inputText(text);
|
|
996
|
+
} else await this.inputCharByChar(text);
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
async inputCharByChar(text) {
|
|
1001
|
+
const adb = await this.getAdb();
|
|
1002
|
+
debugDevice(`Inputting character by character (slow method): "${text}"`);
|
|
1003
|
+
const chars = Array.from(text);
|
|
1004
|
+
for (const char of chars){
|
|
1005
|
+
if (' ' === char) await adb.shell('input keyevent KEYCODE_SPACE');
|
|
1006
|
+
else await adb.shell(`input text "${char}"`);
|
|
1007
|
+
await (0, core_utils_namespaceObject.sleep)(50);
|
|
1008
|
+
}
|
|
1009
|
+
debugDevice("Character-by-character input completed");
|
|
951
1010
|
}
|
|
952
1011
|
async getElementsInfo() {
|
|
953
1012
|
return [];
|
|
@@ -1880,7 +1939,7 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
1880
1939
|
const tools = new AndroidMidsceneTools();
|
|
1881
1940
|
(0, cli_namespaceObject.runToolsCLI)(tools, 'midscene-android', {
|
|
1882
1941
|
stripPrefix: 'android_',
|
|
1883
|
-
version: "1.6.
|
|
1942
|
+
version: "1.6.7"
|
|
1884
1943
|
}).catch((e)=>{
|
|
1885
1944
|
if (!(e instanceof cli_namespaceObject.CLIError)) console.error(e);
|
|
1886
1945
|
process.exit(e instanceof cli_namespaceObject.CLIError ? e.exitCode : 1);
|
package/dist/lib/index.js
CHANGED
|
@@ -868,7 +868,66 @@ ${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
|
+
const inputPromise = adb.shell(command);
|
|
875
|
+
const timeoutPromise = new Promise((_, reject)=>setTimeout(()=>reject(new Error('YADB timeout')), 1000));
|
|
876
|
+
await Promise.race([
|
|
877
|
+
inputPromise,
|
|
878
|
+
timeoutPromise
|
|
879
|
+
]);
|
|
880
|
+
debugDevice(`YADB input completed: "${keyboardContent}"`);
|
|
881
|
+
} catch (error) {
|
|
882
|
+
const isAccessibilityConflict = error?.cause?.stderr?.includes('UiAutomationService') || error?.cause?.stderr?.includes('already registered') || error?.message?.includes('UiAutomationService');
|
|
883
|
+
if (isAccessibilityConflict) {
|
|
884
|
+
debugDevice("YADB failed due to AccessibilityService conflict (likely Appium running), falling back to clipboard method");
|
|
885
|
+
await this.inputViaClipboard(keyboardContent);
|
|
886
|
+
} else 'YADB timeout' === error.message ? debugDevice(`YADB timed out after 2s, assuming input succeeded: "${keyboardContent}"`) : debugDevice(`YADB execution may have completed despite error: ${error}`);
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
async inputViaClipboard(text) {
|
|
890
|
+
const adb = await this.getAdb();
|
|
891
|
+
try {
|
|
892
|
+
debugDevice(`Inputting via clipboard: "${text}"`);
|
|
893
|
+
const escapedText = text.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
894
|
+
const setClipboardCmd = `
|
|
895
|
+
content insert --uri content://settings/system --bind name:s:clipboard_text --bind value:s:"${escapedText}"
|
|
896
|
+
`;
|
|
897
|
+
await adb.shell(setClipboardCmd);
|
|
898
|
+
await (0, utils_namespaceObject.sleep)(100);
|
|
899
|
+
await adb.shell('input keyevent KEYCODE_PASTE');
|
|
900
|
+
await (0, utils_namespaceObject.sleep)(100);
|
|
901
|
+
debugDevice(`Clipboard input completed via content provider: "${text}"`);
|
|
902
|
+
} catch (error1) {
|
|
903
|
+
debugDevice(`Content provider clipboard failed, trying clipper app: ${error1}`);
|
|
904
|
+
try {
|
|
905
|
+
const base64Text = Buffer.from(text, 'utf-8').toString('base64');
|
|
906
|
+
await adb.shell(`am broadcast -a clipper.set -e text "${base64Text}"`);
|
|
907
|
+
await (0, utils_namespaceObject.sleep)(100);
|
|
908
|
+
await adb.shell('input keyevent KEYCODE_PASTE');
|
|
909
|
+
await (0, utils_namespaceObject.sleep)(100);
|
|
910
|
+
debugDevice(`Clipboard input completed via clipper: "${text}"`);
|
|
911
|
+
} catch (error2) {
|
|
912
|
+
debugDevice(`All clipboard methods failed: ${error2}`);
|
|
913
|
+
const isPureAscii = /^[\x00-\x7F]*$/.test(text);
|
|
914
|
+
if (isPureAscii) {
|
|
915
|
+
debugDevice(`Using ADB inputText for ASCII text: "${text}"`);
|
|
916
|
+
await adb.inputText(text);
|
|
917
|
+
} else await this.inputCharByChar(text);
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
async inputCharByChar(text) {
|
|
922
|
+
const adb = await this.getAdb();
|
|
923
|
+
debugDevice(`Inputting character by character (slow method): "${text}"`);
|
|
924
|
+
const chars = Array.from(text);
|
|
925
|
+
for (const char of chars){
|
|
926
|
+
if (' ' === char) await adb.shell('input keyevent KEYCODE_SPACE');
|
|
927
|
+
else await adb.shell(`input text "${char}"`);
|
|
928
|
+
await (0, utils_namespaceObject.sleep)(50);
|
|
929
|
+
}
|
|
930
|
+
debugDevice("Character-by-character input completed");
|
|
872
931
|
}
|
|
873
932
|
async getElementsInfo() {
|
|
874
933
|
return [];
|
package/dist/lib/mcp-server.js
CHANGED
|
@@ -962,7 +962,66 @@ ${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
|
+
const inputPromise = adb.shell(command);
|
|
969
|
+
const timeoutPromise = new Promise((_, reject)=>setTimeout(()=>reject(new Error('YADB timeout')), 1000));
|
|
970
|
+
await Promise.race([
|
|
971
|
+
inputPromise,
|
|
972
|
+
timeoutPromise
|
|
973
|
+
]);
|
|
974
|
+
debugDevice(`YADB input completed: "${keyboardContent}"`);
|
|
975
|
+
} catch (error) {
|
|
976
|
+
const isAccessibilityConflict = error?.cause?.stderr?.includes('UiAutomationService') || error?.cause?.stderr?.includes('already registered') || error?.message?.includes('UiAutomationService');
|
|
977
|
+
if (isAccessibilityConflict) {
|
|
978
|
+
debugDevice("YADB failed due to AccessibilityService conflict (likely Appium running), falling back to clipboard method");
|
|
979
|
+
await this.inputViaClipboard(keyboardContent);
|
|
980
|
+
} else 'YADB timeout' === error.message ? debugDevice(`YADB timed out after 2s, assuming input succeeded: "${keyboardContent}"`) : debugDevice(`YADB execution may have completed despite error: ${error}`);
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
async inputViaClipboard(text) {
|
|
984
|
+
const adb = await this.getAdb();
|
|
985
|
+
try {
|
|
986
|
+
debugDevice(`Inputting via clipboard: "${text}"`);
|
|
987
|
+
const escapedText = text.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
988
|
+
const setClipboardCmd = `
|
|
989
|
+
content insert --uri content://settings/system --bind name:s:clipboard_text --bind value:s:"${escapedText}"
|
|
990
|
+
`;
|
|
991
|
+
await adb.shell(setClipboardCmd);
|
|
992
|
+
await (0, core_utils_namespaceObject.sleep)(100);
|
|
993
|
+
await adb.shell('input keyevent KEYCODE_PASTE');
|
|
994
|
+
await (0, core_utils_namespaceObject.sleep)(100);
|
|
995
|
+
debugDevice(`Clipboard input completed via content provider: "${text}"`);
|
|
996
|
+
} catch (error1) {
|
|
997
|
+
debugDevice(`Content provider clipboard failed, trying clipper app: ${error1}`);
|
|
998
|
+
try {
|
|
999
|
+
const base64Text = Buffer.from(text, 'utf-8').toString('base64');
|
|
1000
|
+
await adb.shell(`am broadcast -a clipper.set -e text "${base64Text}"`);
|
|
1001
|
+
await (0, core_utils_namespaceObject.sleep)(100);
|
|
1002
|
+
await adb.shell('input keyevent KEYCODE_PASTE');
|
|
1003
|
+
await (0, core_utils_namespaceObject.sleep)(100);
|
|
1004
|
+
debugDevice(`Clipboard input completed via clipper: "${text}"`);
|
|
1005
|
+
} catch (error2) {
|
|
1006
|
+
debugDevice(`All clipboard methods failed: ${error2}`);
|
|
1007
|
+
const isPureAscii = /^[\x00-\x7F]*$/.test(text);
|
|
1008
|
+
if (isPureAscii) {
|
|
1009
|
+
debugDevice(`Using ADB inputText for ASCII text: "${text}"`);
|
|
1010
|
+
await adb.inputText(text);
|
|
1011
|
+
} else await this.inputCharByChar(text);
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
async inputCharByChar(text) {
|
|
1016
|
+
const adb = await this.getAdb();
|
|
1017
|
+
debugDevice(`Inputting character by character (slow method): "${text}"`);
|
|
1018
|
+
const chars = Array.from(text);
|
|
1019
|
+
for (const char of chars){
|
|
1020
|
+
if (' ' === char) await adb.shell('input keyevent KEYCODE_SPACE');
|
|
1021
|
+
else await adb.shell(`input text "${char}"`);
|
|
1022
|
+
await (0, core_utils_namespaceObject.sleep)(50);
|
|
1023
|
+
}
|
|
1024
|
+
debugDevice("Character-by-character input completed");
|
|
966
1025
|
}
|
|
967
1026
|
async getElementsInfo() {
|
|
968
1027
|
return [];
|
|
@@ -1899,7 +1958,7 @@ ${Object.keys(size).filter((key)=>size[key]).map((key)=>` ${key} size: ${size[k
|
|
|
1899
1958
|
constructor(toolsManager){
|
|
1900
1959
|
super({
|
|
1901
1960
|
name: '@midscene/android-mcp',
|
|
1902
|
-
version: "1.6.
|
|
1961
|
+
version: "1.6.7",
|
|
1903
1962
|
description: 'Control the Android device using natural language commands'
|
|
1904
1963
|
}, toolsManager);
|
|
1905
1964
|
}
|
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
|