@sensaiorg/adapter-android 0.1.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.
Files changed (89) hide show
  1. package/dist/android-adapter.d.ts.map +1 -0
  2. package/dist/android-adapter.js +89 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +12 -0
  5. package/dist/tools/accessibility.d.ts.map +1 -0
  6. package/dist/tools/accessibility.js +85 -0
  7. package/dist/tools/adb.d.ts.map +1 -0
  8. package/dist/tools/adb.js +66 -0
  9. package/dist/tools/app-state.d.ts.map +1 -0
  10. package/dist/tools/app-state.js +173 -0
  11. package/dist/tools/diagnose.d.ts.map +1 -0
  12. package/dist/tools/diagnose.js +128 -0
  13. package/dist/tools/hot-reload.d.ts.map +1 -0
  14. package/dist/tools/hot-reload.js +97 -0
  15. package/dist/tools/index.d.ts.map +1 -0
  16. package/dist/tools/index.js +66 -0
  17. package/dist/tools/interaction.d.ts.map +1 -0
  18. package/dist/tools/interaction.js +395 -0
  19. package/dist/tools/logcat.d.ts.map +1 -0
  20. package/dist/tools/logcat.js +216 -0
  21. package/dist/tools/network.d.ts.map +1 -0
  22. package/dist/tools/network.js +123 -0
  23. package/dist/tools/performance.d.ts.map +1 -0
  24. package/dist/tools/performance.js +143 -0
  25. package/dist/tools/recording.d.ts.map +1 -0
  26. package/dist/tools/recording.js +102 -0
  27. package/dist/tools/rn-tools.d.ts.map +1 -0
  28. package/dist/tools/rn-tools.js +120 -0
  29. package/dist/tools/smart-actions.d.ts.map +1 -0
  30. package/dist/tools/smart-actions.js +506 -0
  31. package/dist/tools/ui-tree.d.ts.map +1 -0
  32. package/dist/tools/ui-tree.js +226 -0
  33. package/dist/transport/adb-client.d.ts.map +1 -0
  34. package/dist/transport/adb-client.js +124 -0
  35. package/dist/transport/adb-client.test.d.ts.map +1 -0
  36. package/dist/transport/adb-client.test.js +153 -0
  37. package/dist/transport/agent-client.d.ts.map +1 -0
  38. package/dist/transport/agent-client.js +157 -0
  39. package/dist/transport/agent-client.test.d.ts.map +1 -0
  40. package/dist/transport/agent-client.test.js +199 -0
  41. package/dist/transport/connection-manager.d.ts.map +1 -0
  42. package/dist/transport/connection-manager.js +119 -0
  43. package/dist/util/logcat-parser.d.ts.map +1 -0
  44. package/dist/util/logcat-parser.js +79 -0
  45. package/dist/util/safety.d.ts.map +1 -0
  46. package/dist/util/safety.js +132 -0
  47. package/dist/util/safety.test.d.ts.map +1 -0
  48. package/dist/util/safety.test.js +205 -0
  49. package/dist/util/text-extractor.d.ts.map +1 -0
  50. package/dist/util/text-extractor.js +71 -0
  51. package/dist/util/ui-tree-cache.d.ts.map +1 -0
  52. package/dist/util/ui-tree-cache.js +46 -0
  53. package/dist/util/ui-tree-cache.test.d.ts.map +1 -0
  54. package/dist/util/ui-tree-cache.test.js +84 -0
  55. package/dist/util/ui-tree-parser.d.ts.map +1 -0
  56. package/dist/util/ui-tree-parser.js +123 -0
  57. package/dist/util/ui-tree-parser.test.d.ts.map +1 -0
  58. package/dist/util/ui-tree-parser.test.js +167 -0
  59. package/package.json +22 -0
  60. package/src/android-adapter.ts +124 -0
  61. package/src/index.ts +8 -0
  62. package/src/tools/accessibility.ts +94 -0
  63. package/src/tools/adb.ts +75 -0
  64. package/src/tools/app-state.ts +193 -0
  65. package/src/tools/diagnose.ts +146 -0
  66. package/src/tools/hot-reload.ts +103 -0
  67. package/src/tools/index.ts +66 -0
  68. package/src/tools/interaction.ts +448 -0
  69. package/src/tools/logcat.ts +252 -0
  70. package/src/tools/network.ts +145 -0
  71. package/src/tools/performance.ts +169 -0
  72. package/src/tools/recording.ts +123 -0
  73. package/src/tools/rn-tools.ts +143 -0
  74. package/src/tools/smart-actions.ts +593 -0
  75. package/src/tools/ui-tree.ts +258 -0
  76. package/src/transport/adb-client.test.ts +228 -0
  77. package/src/transport/adb-client.ts +139 -0
  78. package/src/transport/agent-client.test.ts +267 -0
  79. package/src/transport/agent-client.ts +188 -0
  80. package/src/transport/connection-manager.ts +140 -0
  81. package/src/util/logcat-parser.ts +94 -0
  82. package/src/util/safety.test.ts +251 -0
  83. package/src/util/safety.ts +143 -0
  84. package/src/util/text-extractor.ts +87 -0
  85. package/src/util/ui-tree-cache.test.ts +105 -0
  86. package/src/util/ui-tree-cache.ts +54 -0
  87. package/src/util/ui-tree-parser.test.ts +182 -0
  88. package/src/util/ui-tree-parser.ts +169 -0
  89. package/tsconfig.json +11 -0
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Diagnose Tool - Composite screen diagnosis.
3
+ *
4
+ * Combines multiple data sources into a single comprehensive report:
5
+ * screen name, all visible text, interactive elements, recent errors,
6
+ * and app state summary. Ideal for understanding the current app state
7
+ * in one call.
8
+ *
9
+ * Optimized: runs independent ADB calls in parallel to minimize latency.
10
+ */
11
+
12
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
13
+ import type { ConnectionManager } from "../transport/connection-manager.js";
14
+ import { flattenTree } from "../util/ui-tree-parser.js";
15
+ import { extractTextStrings } from "../util/text-extractor.js";
16
+ import { parseLogcat, filterByLevel } from "../util/logcat-parser.js";
17
+ import { getCachedTree } from "./ui-tree.js";
18
+
19
+ export function registerDiagnoseTools(server: McpServer, cm: ConnectionManager): void {
20
+ server.tool(
21
+ "diagnose_screen",
22
+ "Comprehensive screen diagnosis: returns the current screen/activity name, all visible text, interactive elements (buttons, inputs), recent errors from logcat, and a summary of app state. One-stop tool for understanding what's on screen.",
23
+ {},
24
+ async () => {
25
+ const targetPackage = process.env.TARGET_PACKAGE ?? "com.emudebug.target";
26
+ const diagnosis: Record<string, unknown> = {};
27
+
28
+ // Run independent data fetches in PARALLEL for speed
29
+ const [activityResult, treeResult, logcatResult, processResult] = await Promise.allSettled([
30
+ // 1. Current activity
31
+ cm.adb.shell("dumpsys activity activities | grep mResumedActivity"),
32
+ // 2. UI tree (uses cache if fresh)
33
+ getCachedTree(cm, { visibleOnly: true, includeSystemUI: false }),
34
+ // 3. Recent logcat + PID lookup (chained)
35
+ (async () => {
36
+ const [raw, pidOutput] = await Promise.allSettled([
37
+ cm.adb.shell("logcat -d -v threadtime -t 500", 10_000),
38
+ cm.adb.shell(`pidof ${targetPackage}`),
39
+ ]);
40
+ return {
41
+ raw: raw.status === "fulfilled" ? raw.value : "",
42
+ pid: pidOutput.status === "fulfilled" ? pidOutput.value.trim() : null,
43
+ };
44
+ })(),
45
+ // 4. Process state
46
+ cm.adb.shell(`ps -A | grep ${targetPackage}`),
47
+ ]);
48
+
49
+ // Process activity result
50
+ if (activityResult.status === "fulfilled") {
51
+ const match = activityResult.value.match(/u0\s+(.+?)\s+\w+/);
52
+ diagnosis.currentActivity = match ? match[1].trim() : activityResult.value.trim();
53
+ } else {
54
+ diagnosis.currentActivity = "unknown";
55
+ }
56
+
57
+ // Process UI tree result
58
+ if (treeResult.status === "fulfilled") {
59
+ const tree = treeResult.value;
60
+ const flat = flattenTree(tree);
61
+
62
+ diagnosis.screenText = extractTextStrings(tree);
63
+
64
+ diagnosis.interactiveElements = flat
65
+ .filter((n) => n.clickable || n.focusable || n.scrollable)
66
+ .map((n) => ({
67
+ type: n.className.split(".").pop(),
68
+ text: n.text || n.contentDescription || n.resourceId || "(unnamed)",
69
+ resourceId: n.resourceId,
70
+ clickable: n.clickable,
71
+ enabled: n.enabled,
72
+ bounds: n.bounds,
73
+ center: n.bounds
74
+ ? {
75
+ x: Math.round((n.bounds.left + n.bounds.right) / 2),
76
+ y: Math.round((n.bounds.top + n.bounds.bottom) / 2),
77
+ }
78
+ : null,
79
+ }));
80
+
81
+ diagnosis.uiSummary = {
82
+ totalNodes: flat.length,
83
+ textNodes: flat.filter((n) => n.text).length,
84
+ clickableNodes: flat.filter((n) => n.clickable).length,
85
+ inputFields: flat.filter(
86
+ (n) =>
87
+ n.className.includes("EditText") || n.className.includes("TextInput"),
88
+ ).length,
89
+ scrollableContainers: flat.filter((n) => n.scrollable).length,
90
+ };
91
+ } else {
92
+ diagnosis.uiError = `Failed to get UI tree: ${treeResult.reason}`;
93
+ }
94
+
95
+ // Process logcat result
96
+ if (logcatResult.status === "fulfilled") {
97
+ const { raw, pid } = logcatResult.value;
98
+ if (raw) {
99
+ const entries = parseLogcat(raw);
100
+ const errors = filterByLevel(entries, "E");
101
+
102
+ const appErrors = pid
103
+ ? errors.filter((e) => e.pid === pid)
104
+ : errors.filter(
105
+ (e) =>
106
+ e.tag.includes("ReactNativeJS") ||
107
+ e.tag.includes("ReactNative") ||
108
+ e.tag.includes(targetPackage),
109
+ );
110
+
111
+ diagnosis.recentErrors = appErrors.slice(-10).map((e) => ({
112
+ timestamp: e.timestamp,
113
+ tag: e.tag,
114
+ message: e.message,
115
+ }));
116
+ diagnosis.errorCount = appErrors.length;
117
+ } else {
118
+ diagnosis.recentErrors = [];
119
+ diagnosis.errorCount = 0;
120
+ }
121
+ } else {
122
+ diagnosis.recentErrors = [];
123
+ diagnosis.errorCount = 0;
124
+ }
125
+
126
+ // Process state result
127
+ if (processResult.status === "fulfilled") {
128
+ diagnosis.processInfo = processResult.value.trim() || "App not running";
129
+ } else {
130
+ diagnosis.processInfo = "Unable to determine process state";
131
+ }
132
+
133
+ // Agent status (synchronous)
134
+ diagnosis.agentConnected = cm.agent.isConnected();
135
+ if (!cm.agent.isConnected()) {
136
+ diagnosis.agentNote =
137
+ "On-device agent not connected. Some data (RN components, app state, network) " +
138
+ "is limited to ADB-based inspection.";
139
+ }
140
+
141
+ return {
142
+ content: [{ type: "text" as const, text: JSON.stringify(diagnosis) }],
143
+ };
144
+ },
145
+ );
146
+ }
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Hot Reload Tool - Trigger Metro bundler hot or full reload.
3
+ *
4
+ * Communicates with the Metro dev server to trigger a reload of the
5
+ * JavaScript bundle, either incremental (hot) or full.
6
+ */
7
+
8
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
9
+ import { z } from "zod";
10
+ import type { ConnectionManager } from "../transport/connection-manager.js";
11
+
12
+ /** Default Metro bundler port. */
13
+ const METRO_PORT = 8081;
14
+
15
+ export function registerHotReloadTools(server: McpServer, cm: ConnectionManager): void {
16
+ server.tool(
17
+ "hot_reload",
18
+ "Trigger a React Native reload via the Metro bundler. 'hot' performs a fast refresh (preserves state), 'full' performs a complete bundle reload. Works by sending key events or hitting the Metro dev server endpoint.",
19
+ {
20
+ type: z
21
+ .enum(["hot", "full"])
22
+ .optional()
23
+ .describe("Reload type: 'hot' for fast refresh (default), 'full' for complete reload"),
24
+ },
25
+ async (params) => {
26
+ const reloadType = params.type ?? "hot";
27
+
28
+ try {
29
+ if (reloadType === "full") {
30
+ // Method 1: Send "RR" key sequence (double-tap R in dev menu)
31
+ // This triggers a full reload in React Native dev mode
32
+ try {
33
+ await cm.adb.shell("input keyevent 46 46"); // R R
34
+ } catch {
35
+ // Method 2: Hit Metro /reload endpoint
36
+ try {
37
+ await cm.adb.shell(`curl -s http://localhost:${METRO_PORT}/reload`);
38
+ } catch {
39
+ // Method 3: Send the menu key then look for reload option
40
+ await cm.adb.shell("input keyevent 82"); // MENU key opens RN dev menu
41
+ }
42
+ }
43
+
44
+ return {
45
+ content: [
46
+ {
47
+ type: "text" as const,
48
+ text: JSON.stringify({
49
+ success: true,
50
+ type: "full",
51
+ message: "Full reload triggered. The app will reload the JS bundle from Metro.",
52
+ }),
53
+ },
54
+ ],
55
+ };
56
+ } else {
57
+ // Hot reload: trigger via Metro /message endpoint
58
+ try {
59
+ // Send HMR trigger through ADB reverse + Metro
60
+ await cm.adb.shell(
61
+ `curl -s -X POST http://localhost:${METRO_PORT}/message -H "Content-Type: application/json" -d '{"method":"reload"}'`,
62
+ );
63
+ } catch {
64
+ // Fallback: the shake gesture or menu key
65
+ // On emulators, Ctrl+M or MENU key opens dev menu
66
+ await cm.adb.shell("input keyevent 82");
67
+ }
68
+
69
+ return {
70
+ content: [
71
+ {
72
+ type: "text" as const,
73
+ text: JSON.stringify({
74
+ success: true,
75
+ type: "hot",
76
+ message: "Hot reload triggered. Fast Refresh will apply changes without losing state.",
77
+ }),
78
+ },
79
+ ],
80
+ };
81
+ }
82
+ } catch (err) {
83
+ return {
84
+ content: [
85
+ {
86
+ type: "text" as const,
87
+ text: JSON.stringify({
88
+ success: false,
89
+ error: err instanceof Error ? err.message : String(err),
90
+ hints: [
91
+ "Ensure Metro bundler is running (npx react-native start)",
92
+ "Ensure ADB reverse port forwarding is set up: adb reverse tcp:8081 tcp:8081",
93
+ "Ensure the app is in dev mode (not a release build)",
94
+ ],
95
+ }),
96
+ },
97
+ ],
98
+ isError: true,
99
+ };
100
+ }
101
+ },
102
+ );
103
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Tool Registry - Registers all EmuDebug MCP tools with the server.
3
+ */
4
+
5
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6
+ import type { ConnectionManager } from "../transport/connection-manager.js";
7
+ import { registerUiTreeTools } from "./ui-tree.js";
8
+ import { registerAccessibilityTools } from "./accessibility.js";
9
+ import { registerLogcatTools } from "./logcat.js";
10
+ import { registerRnTools } from "./rn-tools.js";
11
+ import { registerAppStateTools } from "./app-state.js";
12
+ import { registerNetworkTools } from "./network.js";
13
+ import { registerInteractionTools } from "./interaction.js";
14
+ import { registerPerformanceTools } from "./performance.js";
15
+ import { registerAdbTools } from "./adb.js";
16
+ import { registerDiagnoseTools } from "./diagnose.js";
17
+ import { registerHotReloadTools } from "./hot-reload.js";
18
+ import { registerSmartActionTools } from "./smart-actions.js";
19
+ import { registerRecordingTools } from "./recording.js";
20
+
21
+ /**
22
+ * Register all 26 EmuDebug tools with the MCP server.
23
+ *
24
+ * Tools registered:
25
+ * 1. get_ui_tree - UI hierarchy as JSON
26
+ * 2. get_screen_text - Visible text extraction
27
+ * 3. get_element_details - Deep element inspection
28
+ * 4. get_accessibility - Accessibility tree
29
+ * 5. get_logcat - Filtered Android logs
30
+ * 6. get_crash_info - JS exceptions, native crashes, ANRs
31
+ * 7. get_rn_component_tree - React Native component tree (Phase 2)
32
+ * 8. get_rn_bridge - TurboModule / bridge inspection (Phase 2)
33
+ * 9. get_app_state - AsyncStorage, query cache, nav, auth, etc.
34
+ * 10. get_network - HTTP traffic inspection
35
+ * 11. tap - Tap by text/resourceId/coordinates
36
+ * 12. type_text - Type into focused field
37
+ * 13. swipe - Swipe gesture
38
+ * 14. press_key - Android key events
39
+ * 15. long_press - Long press (press and hold)
40
+ * 16. get_performance - FPS, memory, CPU, bridge metrics
41
+ * 17. run_adb - Safe ADB command passthrough
42
+ * 18. diagnose_screen - Composite screen diagnosis (parallelized)
43
+ * 19. hot_reload - Metro bundler hot/full reload
44
+ * 20. wait_for_text - Poll until text appears on screen
45
+ * 21. scroll_to_text - Scroll until element is visible
46
+ * 22. fill_form - Batch fill multiple form fields
47
+ * 23. tap_and_wait - Tap element then wait for text
48
+ * 24. assert_screen - Quick screen assertions
49
+ * 25. open_deep_link - Open app via deep link URL
50
+ * 26. take_screenshot - Capture screen as base64 PNG
51
+ */
52
+ export function registerAllTools(server: McpServer, cm: ConnectionManager): void {
53
+ registerUiTreeTools(server, cm); // 3 tools: get_ui_tree, get_screen_text, get_element_details
54
+ registerAccessibilityTools(server, cm); // 1 tool: get_accessibility
55
+ registerLogcatTools(server, cm); // 2 tools: get_logcat, get_crash_info
56
+ registerRnTools(server, cm); // 2 tools: get_rn_component_tree, get_rn_bridge
57
+ registerAppStateTools(server, cm); // 1 tool: get_app_state
58
+ registerNetworkTools(server, cm); // 1 tool: get_network
59
+ registerInteractionTools(server, cm); // 5 tools: tap, type_text, swipe, press_key, long_press
60
+ registerPerformanceTools(server, cm); // 1 tool: get_performance
61
+ registerAdbTools(server, cm); // 1 tool: run_adb
62
+ registerDiagnoseTools(server, cm); // 1 tool: diagnose_screen
63
+ registerHotReloadTools(server, cm); // 1 tool: hot_reload
64
+ registerSmartActionTools(server, cm); // 7 tools: wait_for_text, scroll_to_text, fill_form, tap_and_wait, assert_screen, open_deep_link, take_screenshot
65
+ registerRecordingTools(server, cm); // 2 tools: start_recording, stop_recording
66
+ }