@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 @@
1
+ {"version":3,"file":"android-adapter.d.ts","sourceRoot":"","sources":["../src/android-adapter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EACV,gBAAgB,EAChB,WAAW,EACX,YAAY,EACZ,oBAAoB,EACpB,gBAAgB,EAChB,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,EACjB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAGtE,MAAM,WAAW,oBAAoB;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,qBAAa,cAAe,YAAW,gBAAgB;IACrD,QAAQ,CAAC,QAAQ,EAAG,SAAS,CAAU;IACvC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,oBAAoB,CAUzC;IAEF,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAoB;IACtD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiC;IAGxD,QAAQ,CAAC,EAAE,EAAE,WAAW,GAAG,IAAI,CAAQ;IACvC,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAAQ;IAC1C,QAAQ,CAAC,WAAW,EAAE,oBAAoB,GAAG,IAAI,CAAQ;IACzD,QAAQ,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAQ;IACjD,QAAQ,CAAC,WAAW,EAAE,oBAAoB,GAAG,IAAI,CAAQ;IACzD,QAAQ,CAAC,UAAU,EAAE,mBAAmB,GAAG,IAAI,CAAQ;IACvD,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAAQ;gBAEvC,MAAM,GAAE,oBAAyB;IAiBvC,UAAU,IAAI,OAAO,CAAC,gBAAgB,CAAC;IAsB7C,QAAQ,IAAI,IAAI;IAIhB,WAAW,IAAI,OAAO;IAItB;;;OAGG;IACH,oBAAoB,IAAI,iBAAiB;IAIzC;;;OAGG;IACH,qBAAqB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI;CAM/C"}
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ /**
3
+ * AndroidAdapter — implements IPlatformAdapter for Android devices/emulators.
4
+ *
5
+ * Phase 1: All tools work via ADB commands (uiautomator, logcat, input, dumpsys)
6
+ * Phase 2: On-device agent provides faster UI tree, React Native inspection, network capture
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.AndroidAdapter = void 0;
10
+ const connection_manager_js_1 = require("./transport/connection-manager.js");
11
+ const index_js_1 = require("./tools/index.js");
12
+ class AndroidAdapter {
13
+ platform = "android";
14
+ displayName;
15
+ capabilities = {
16
+ uiTree: true,
17
+ logs: true,
18
+ interaction: true,
19
+ network: false, // Phase 2 (agent needed)
20
+ performance: true,
21
+ screenshot: true,
22
+ appState: false, // Phase 2 (agent needed)
23
+ hotReload: true,
24
+ evalCode: false,
25
+ };
26
+ connectionManager;
27
+ config;
28
+ // Providers — implemented inline for now, will be extracted to separate classes
29
+ ui = null; // TODO: Extract from tools/ui-tree.ts
30
+ logs = null; // TODO: Extract from tools/logcat.ts
31
+ interaction = null; // TODO: Extract from tools/interaction.ts
32
+ network = null;
33
+ performance = null;
34
+ screenshot = null;
35
+ appState = null;
36
+ constructor(config = {}) {
37
+ this.config = {
38
+ adbDevice: config.adbDevice ?? process.env.ADB_DEVICE ?? "emulator-5554",
39
+ adbPath: config.adbPath ?? process.env.ADB_PATH ?? "adb",
40
+ agentPort: config.agentPort ?? parseInt(process.env.DEBUG_AGENT_PORT ?? "8463", 10),
41
+ targetPackage: config.targetPackage ?? process.env.TARGET_PACKAGE ?? "com.chronocrew",
42
+ };
43
+ this.displayName = `Android (${this.config.adbDevice})`;
44
+ this.connectionManager = new connection_manager_js_1.ConnectionManager(this.config.adbPath, this.config.adbDevice, this.config.agentPort);
45
+ }
46
+ async initialize() {
47
+ const status = await this.connectionManager.initialize();
48
+ // Update capabilities if agent is available
49
+ if (status.agent) {
50
+ this.capabilities.network = true;
51
+ this.capabilities.appState = true;
52
+ }
53
+ return {
54
+ platform: "android",
55
+ device: status.device,
56
+ connected: status.adb,
57
+ capabilities: this.capabilities,
58
+ details: {
59
+ adb: status.adb,
60
+ agent: status.agent,
61
+ targetPackage: this.config.targetPackage,
62
+ },
63
+ };
64
+ }
65
+ shutdown() {
66
+ this.connectionManager.shutdown();
67
+ }
68
+ isConnected() {
69
+ return this.connectionManager.getStatus().adb;
70
+ }
71
+ /**
72
+ * Get the ConnectionManager for direct tool access.
73
+ * Used during the migration period before full provider extraction.
74
+ */
75
+ getConnectionManager() {
76
+ return this.connectionManager;
77
+ }
78
+ /**
79
+ * Register Android-only MCP tools.
80
+ * These have no cross-platform equivalent.
81
+ */
82
+ registerPlatformTools(server) {
83
+ const cm = this.connectionManager;
84
+ // Register all 22 Android debugging tools
85
+ (0, index_js_1.registerAllTools)(server, cm);
86
+ }
87
+ }
88
+ exports.AndroidAdapter = AndroidAdapter;
89
+ //# sourceMappingURL=android-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ /**
3
+ * @sensai/adapter-android — Android platform adapter for SensAI.
4
+ *
5
+ * Provides 22 debugging tools via ADB (Phase 1) and on-device agent (Phase 2).
6
+ * This adapter wraps the original EmuDebug MCP server tools.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.AndroidAdapter = void 0;
10
+ var android_adapter_js_1 = require("./android-adapter.js");
11
+ Object.defineProperty(exports, "AndroidAdapter", { enumerable: true, get: function () { return android_adapter_js_1.AndroidAdapter; } });
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accessibility.d.ts","sourceRoot":"","sources":["../../src/tools/accessibility.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AA8B5E,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,EAAE,iBAAiB,GAAG,IAAI,CAuDzF"}
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ /**
3
+ * Accessibility Tool - Retrieve the Android accessibility tree.
4
+ *
5
+ * Uses `adb shell dumpsys accessibility` to get the accessibility service's
6
+ * view of the screen, which is richer than uiautomator for a11y auditing.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.registerAccessibilityTools = registerAccessibilityTools;
10
+ // A11yNode type reserved for Phase 2 structured accessibility tree parsing.
11
+ /**
12
+ * Parse the raw accessibility dump into a structured tree.
13
+ * The dump format is indentation-based with key: value pairs.
14
+ */
15
+ function parseAccessibilityDump(raw) {
16
+ const lines = raw.split("\n");
17
+ const services = [];
18
+ let nodeCount = 0;
19
+ for (const line of lines) {
20
+ if (line.includes("Service[")) {
21
+ const match = line.match(/Service\[(.+?)]/);
22
+ if (match)
23
+ services.push(match[1]);
24
+ }
25
+ if (line.includes("className:") || line.includes("class:")) {
26
+ nodeCount++;
27
+ }
28
+ }
29
+ return {
30
+ services,
31
+ windows: raw,
32
+ nodeCount,
33
+ };
34
+ }
35
+ function registerAccessibilityTools(server, cm) {
36
+ server.tool("get_accessibility", "Get the Android accessibility tree for the current screen. Shows which elements are accessible, their roles, states, and actions. Useful for auditing screen reader compatibility.", {}, async () => {
37
+ try {
38
+ // Try the agent first for a richer accessibility tree
39
+ if (cm.agent.isConnected()) {
40
+ try {
41
+ const result = await cm.agent.call("getAccessibilityTree");
42
+ return {
43
+ content: [{ type: "text", text: JSON.stringify(result) }],
44
+ };
45
+ }
46
+ catch {
47
+ // Fall through to ADB
48
+ }
49
+ }
50
+ // ADB fallback: use accessibility dumpsys
51
+ const dump = await cm.adb.shell("dumpsys accessibility");
52
+ const parsed = parseAccessibilityDump(dump);
53
+ // Also get window info for additional context
54
+ let windowInfo = "";
55
+ try {
56
+ windowInfo = await cm.adb.shell("dumpsys window windows | head -50");
57
+ }
58
+ catch {
59
+ // Non-critical
60
+ }
61
+ const result = {
62
+ accessibilityServices: parsed.services,
63
+ estimatedNodeCount: parsed.nodeCount,
64
+ currentWindow: windowInfo.trim(),
65
+ rawDump: parsed.windows,
66
+ note: "For a structured accessibility tree, install the EmuDebug on-device agent (Phase 2).",
67
+ };
68
+ return {
69
+ content: [{ type: "text", text: JSON.stringify(result) }],
70
+ };
71
+ }
72
+ catch (err) {
73
+ return {
74
+ content: [
75
+ {
76
+ type: "text",
77
+ text: `Error getting accessibility tree: ${err instanceof Error ? err.message : String(err)}`,
78
+ },
79
+ ],
80
+ isError: true,
81
+ };
82
+ }
83
+ });
84
+ }
85
+ //# sourceMappingURL=accessibility.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adb.d.ts","sourceRoot":"","sources":["../../src/tools/adb.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAG5E,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,EAAE,iBAAiB,GAAG,IAAI,CA8D/E"}
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ /**
3
+ * ADB Passthrough Tool - Execute safe ADB commands on the connected device.
4
+ *
5
+ * Commands are validated against an allowlist before execution to prevent
6
+ * destructive operations. See util/safety.ts for the allowlist.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.registerAdbTools = registerAdbTools;
10
+ const zod_1 = require("zod");
11
+ const safety_js_1 = require("../util/safety.js");
12
+ function registerAdbTools(server, cm) {
13
+ server.tool("run_adb", "Execute an ADB command on the connected device. Commands are validated against a safety allowlist. Allowed: shell, pull, push, install, uninstall, logcat, bugreport, devices, forward, reverse. Blocked: root, remount, reboot-bootloader, sideload, and destructive shell commands.", {
14
+ command: zod_1.z
15
+ .string()
16
+ .describe("ADB command to execute (without the leading 'adb -s <device>' prefix). " +
17
+ "Example: 'shell ls /sdcard' or 'logcat -d -t 50'"),
18
+ timeoutMs: zod_1.z.number().optional().describe("Timeout in milliseconds (default: 30000, max: 60000)"),
19
+ }, async (params) => {
20
+ try {
21
+ const args = (0, safety_js_1.parseCommandString)(params.command);
22
+ // Validate against safety allowlist
23
+ const validation = (0, safety_js_1.validateAdbCommand)(args);
24
+ if (!validation.allowed) {
25
+ return {
26
+ content: [
27
+ {
28
+ type: "text",
29
+ text: JSON.stringify({
30
+ success: false,
31
+ error: "Command blocked by safety check",
32
+ reason: validation.reason,
33
+ }),
34
+ },
35
+ ],
36
+ };
37
+ }
38
+ const timeoutMs = Math.min(params.timeoutMs ?? 30_000, 60_000);
39
+ const output = await cm.adb.exec(args, timeoutMs);
40
+ return {
41
+ content: [
42
+ {
43
+ type: "text",
44
+ text: JSON.stringify({
45
+ success: true,
46
+ command: `adb ${args.join(" ")}`,
47
+ output: output.trim(),
48
+ }),
49
+ },
50
+ ],
51
+ };
52
+ }
53
+ catch (err) {
54
+ return {
55
+ content: [
56
+ {
57
+ type: "text",
58
+ text: `ADB command error: ${err instanceof Error ? err.message : String(err)}`,
59
+ },
60
+ ],
61
+ isError: true,
62
+ };
63
+ }
64
+ });
65
+ }
66
+ //# sourceMappingURL=adb.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-state.d.ts","sourceRoot":"","sources":["../../src/tools/app-state.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAe5E,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,EAAE,iBAAiB,GAAG,IAAI,CAqKpF"}
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+ /**
3
+ * App State Tool - Inspect the target application's internal state.
4
+ *
5
+ * Retrieves AsyncStorage, React Query cache, navigation state, auth tokens,
6
+ * theme, feature flags, and offline queue data from the running app.
7
+ *
8
+ * Requires the on-device agent for full data. In Phase 1, reads what is
9
+ * accessible via ADB (SharedPreferences, app data files).
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.registerAppStateTools = registerAppStateTools;
13
+ const zod_1 = require("zod");
14
+ /** State categories that can be requested. */
15
+ const STATE_CATEGORIES = [
16
+ "asyncStorage",
17
+ "queryCache",
18
+ "navigation",
19
+ "auth",
20
+ "theme",
21
+ "featureFlags",
22
+ "offlineQueue",
23
+ ];
24
+ function registerAppStateTools(server, cm) {
25
+ server.tool("get_app_state", "Inspect the target app's internal state including AsyncStorage, React Query cache, navigation state, auth info, theme, feature flags, and offline queue. Select specific categories or get all.", {
26
+ categories: zod_1.z
27
+ .array(zod_1.z.enum(STATE_CATEGORIES))
28
+ .optional()
29
+ .describe("Which state categories to retrieve (default: all)"),
30
+ asyncStorageKeys: zod_1.z
31
+ .array(zod_1.z.string())
32
+ .optional()
33
+ .describe("Specific AsyncStorage keys to read (default: all)"),
34
+ queryCacheFilter: zod_1.z.string().optional().describe("Filter query cache by key prefix"),
35
+ }, async (params) => {
36
+ const categories = (params.categories ?? [...STATE_CATEGORIES]);
37
+ const targetPackage = process.env.TARGET_PACKAGE ?? "com.emudebug.target";
38
+ // Phase 2: agent provides full app state
39
+ if (cm.agent.isConnected()) {
40
+ try {
41
+ const result = await cm.agent.call("getAppState", {
42
+ categories,
43
+ asyncStorageKeys: params.asyncStorageKeys,
44
+ queryCacheFilter: params.queryCacheFilter,
45
+ });
46
+ return {
47
+ content: [{ type: "text", text: JSON.stringify(result) }],
48
+ };
49
+ }
50
+ catch (err) {
51
+ return {
52
+ content: [
53
+ {
54
+ type: "text",
55
+ text: `Agent error getting app state: ${err instanceof Error ? err.message : String(err)}. Falling back to ADB.`,
56
+ },
57
+ ],
58
+ isError: true,
59
+ };
60
+ }
61
+ }
62
+ // Phase 1: limited state via ADB
63
+ try {
64
+ const state = {
65
+ _phase1Note: "Limited app state available via ADB. Install the on-device agent for full " +
66
+ "AsyncStorage, React Query cache, navigation, and auth state inspection.",
67
+ };
68
+ // AsyncStorage: RN stores in SQLite database (accessible if debuggable or rooted)
69
+ if (categories.includes("asyncStorage")) {
70
+ try {
71
+ const dbPath = `/data/data/${targetPackage}/databases/RKStorage`;
72
+ const output = await cm.adb.shell(`run-as ${targetPackage} sqlite3 ${dbPath} "SELECT key, value FROM catalystLocalStorage LIMIT 50;"`);
73
+ const rows = {};
74
+ for (const line of output.trim().split("\n")) {
75
+ const sep = line.indexOf("|");
76
+ if (sep > 0) {
77
+ rows[line.slice(0, sep)] = line.slice(sep + 1);
78
+ }
79
+ }
80
+ state.asyncStorage = {
81
+ entries: rows,
82
+ count: Object.keys(rows).length,
83
+ note: "Retrieved via run-as + sqlite3 (requires debuggable app).",
84
+ };
85
+ }
86
+ catch {
87
+ state.asyncStorage = {
88
+ error: "Cannot read AsyncStorage via ADB. App may not be debuggable.",
89
+ hint: "Use the on-device agent or ensure the app has android:debuggable=true.",
90
+ };
91
+ }
92
+ }
93
+ // SharedPreferences: accessible via run-as
94
+ if (categories.includes("featureFlags") || categories.includes("theme")) {
95
+ try {
96
+ const prefsDir = `/data/data/${targetPackage}/shared_prefs/`;
97
+ const files = await cm.adb.shell(`run-as ${targetPackage} ls ${prefsDir}`);
98
+ const prefsFiles = files.trim().split("\n").filter(Boolean);
99
+ const prefs = {};
100
+ for (const file of prefsFiles.slice(0, 5)) {
101
+ try {
102
+ const content = await cm.adb.shell(`run-as ${targetPackage} cat ${prefsDir}${file}`);
103
+ prefs[file] = content.trim();
104
+ }
105
+ catch {
106
+ prefs[file] = "(unreadable)";
107
+ }
108
+ }
109
+ if (categories.includes("featureFlags")) {
110
+ state.featureFlags = { sharedPreferences: prefs };
111
+ }
112
+ if (categories.includes("theme")) {
113
+ state.theme = { sharedPreferences: prefs };
114
+ }
115
+ }
116
+ catch {
117
+ if (categories.includes("featureFlags")) {
118
+ state.featureFlags = { error: "Cannot read SharedPreferences via ADB." };
119
+ }
120
+ if (categories.includes("theme")) {
121
+ state.theme = { error: "Cannot read SharedPreferences via ADB." };
122
+ }
123
+ }
124
+ }
125
+ // Navigation: infer from activity stack
126
+ if (categories.includes("navigation")) {
127
+ try {
128
+ const activityDump = await cm.adb.shell(`dumpsys activity activities | grep -A5 "mResumedActivity\\|topResumedActivity"`);
129
+ state.navigation = {
130
+ currentActivity: activityDump.trim(),
131
+ note: "Full React Navigation state requires the on-device agent.",
132
+ };
133
+ }
134
+ catch {
135
+ state.navigation = { error: "Cannot determine current activity." };
136
+ }
137
+ }
138
+ // Auth: check for token files (surface-level only)
139
+ if (categories.includes("auth")) {
140
+ state.auth = {
141
+ note: "Auth token inspection requires the on-device agent. " +
142
+ "Check AsyncStorage for token keys or use get_logcat with auth-related tags.",
143
+ };
144
+ }
145
+ // Query cache and offline queue require the agent
146
+ if (categories.includes("queryCache")) {
147
+ state.queryCache = {
148
+ note: "React Query cache inspection requires the on-device agent.",
149
+ };
150
+ }
151
+ if (categories.includes("offlineQueue")) {
152
+ state.offlineQueue = {
153
+ note: "Offline queue inspection requires the on-device agent.",
154
+ };
155
+ }
156
+ return {
157
+ content: [{ type: "text", text: JSON.stringify(state) }],
158
+ };
159
+ }
160
+ catch (err) {
161
+ return {
162
+ content: [
163
+ {
164
+ type: "text",
165
+ text: `Error getting app state: ${err instanceof Error ? err.message : String(err)}`,
166
+ },
167
+ ],
168
+ isError: true,
169
+ };
170
+ }
171
+ });
172
+ }
173
+ //# sourceMappingURL=app-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnose.d.ts","sourceRoot":"","sources":["../../src/tools/diagnose.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAM5E,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,EAAE,iBAAiB,GAAG,IAAI,CA+HpF"}
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+ /**
3
+ * Diagnose Tool - Composite screen diagnosis.
4
+ *
5
+ * Combines multiple data sources into a single comprehensive report:
6
+ * screen name, all visible text, interactive elements, recent errors,
7
+ * and app state summary. Ideal for understanding the current app state
8
+ * in one call.
9
+ *
10
+ * Optimized: runs independent ADB calls in parallel to minimize latency.
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.registerDiagnoseTools = registerDiagnoseTools;
14
+ const ui_tree_parser_js_1 = require("../util/ui-tree-parser.js");
15
+ const text_extractor_js_1 = require("../util/text-extractor.js");
16
+ const logcat_parser_js_1 = require("../util/logcat-parser.js");
17
+ const ui_tree_js_1 = require("./ui-tree.js");
18
+ function registerDiagnoseTools(server, cm) {
19
+ server.tool("diagnose_screen", "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.", {}, async () => {
20
+ const targetPackage = process.env.TARGET_PACKAGE ?? "com.emudebug.target";
21
+ const diagnosis = {};
22
+ // Run independent data fetches in PARALLEL for speed
23
+ const [activityResult, treeResult, logcatResult, processResult] = await Promise.allSettled([
24
+ // 1. Current activity
25
+ cm.adb.shell("dumpsys activity activities | grep mResumedActivity"),
26
+ // 2. UI tree (uses cache if fresh)
27
+ (0, ui_tree_js_1.getCachedTree)(cm, { visibleOnly: true, includeSystemUI: false }),
28
+ // 3. Recent logcat + PID lookup (chained)
29
+ (async () => {
30
+ const [raw, pidOutput] = await Promise.allSettled([
31
+ cm.adb.shell("logcat -d -v threadtime -t 500", 10_000),
32
+ cm.adb.shell(`pidof ${targetPackage}`),
33
+ ]);
34
+ return {
35
+ raw: raw.status === "fulfilled" ? raw.value : "",
36
+ pid: pidOutput.status === "fulfilled" ? pidOutput.value.trim() : null,
37
+ };
38
+ })(),
39
+ // 4. Process state
40
+ cm.adb.shell(`ps -A | grep ${targetPackage}`),
41
+ ]);
42
+ // Process activity result
43
+ if (activityResult.status === "fulfilled") {
44
+ const match = activityResult.value.match(/u0\s+(.+?)\s+\w+/);
45
+ diagnosis.currentActivity = match ? match[1].trim() : activityResult.value.trim();
46
+ }
47
+ else {
48
+ diagnosis.currentActivity = "unknown";
49
+ }
50
+ // Process UI tree result
51
+ if (treeResult.status === "fulfilled") {
52
+ const tree = treeResult.value;
53
+ const flat = (0, ui_tree_parser_js_1.flattenTree)(tree);
54
+ diagnosis.screenText = (0, text_extractor_js_1.extractTextStrings)(tree);
55
+ diagnosis.interactiveElements = flat
56
+ .filter((n) => n.clickable || n.focusable || n.scrollable)
57
+ .map((n) => ({
58
+ type: n.className.split(".").pop(),
59
+ text: n.text || n.contentDescription || n.resourceId || "(unnamed)",
60
+ resourceId: n.resourceId,
61
+ clickable: n.clickable,
62
+ enabled: n.enabled,
63
+ bounds: n.bounds,
64
+ center: n.bounds
65
+ ? {
66
+ x: Math.round((n.bounds.left + n.bounds.right) / 2),
67
+ y: Math.round((n.bounds.top + n.bounds.bottom) / 2),
68
+ }
69
+ : null,
70
+ }));
71
+ diagnosis.uiSummary = {
72
+ totalNodes: flat.length,
73
+ textNodes: flat.filter((n) => n.text).length,
74
+ clickableNodes: flat.filter((n) => n.clickable).length,
75
+ inputFields: flat.filter((n) => n.className.includes("EditText") || n.className.includes("TextInput")).length,
76
+ scrollableContainers: flat.filter((n) => n.scrollable).length,
77
+ };
78
+ }
79
+ else {
80
+ diagnosis.uiError = `Failed to get UI tree: ${treeResult.reason}`;
81
+ }
82
+ // Process logcat result
83
+ if (logcatResult.status === "fulfilled") {
84
+ const { raw, pid } = logcatResult.value;
85
+ if (raw) {
86
+ const entries = (0, logcat_parser_js_1.parseLogcat)(raw);
87
+ const errors = (0, logcat_parser_js_1.filterByLevel)(entries, "E");
88
+ const appErrors = pid
89
+ ? errors.filter((e) => e.pid === pid)
90
+ : errors.filter((e) => e.tag.includes("ReactNativeJS") ||
91
+ e.tag.includes("ReactNative") ||
92
+ e.tag.includes(targetPackage));
93
+ diagnosis.recentErrors = appErrors.slice(-10).map((e) => ({
94
+ timestamp: e.timestamp,
95
+ tag: e.tag,
96
+ message: e.message,
97
+ }));
98
+ diagnosis.errorCount = appErrors.length;
99
+ }
100
+ else {
101
+ diagnosis.recentErrors = [];
102
+ diagnosis.errorCount = 0;
103
+ }
104
+ }
105
+ else {
106
+ diagnosis.recentErrors = [];
107
+ diagnosis.errorCount = 0;
108
+ }
109
+ // Process state result
110
+ if (processResult.status === "fulfilled") {
111
+ diagnosis.processInfo = processResult.value.trim() || "App not running";
112
+ }
113
+ else {
114
+ diagnosis.processInfo = "Unable to determine process state";
115
+ }
116
+ // Agent status (synchronous)
117
+ diagnosis.agentConnected = cm.agent.isConnected();
118
+ if (!cm.agent.isConnected()) {
119
+ diagnosis.agentNote =
120
+ "On-device agent not connected. Some data (RN components, app state, network) " +
121
+ "is limited to ADB-based inspection.";
122
+ }
123
+ return {
124
+ content: [{ type: "text", text: JSON.stringify(diagnosis) }],
125
+ };
126
+ });
127
+ }
128
+ //# sourceMappingURL=diagnose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hot-reload.d.ts","sourceRoot":"","sources":["../../src/tools/hot-reload.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAK5E,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,EAAE,iBAAiB,GAAG,IAAI,CAwFrF"}