mobile-debug-mcp 0.30.2 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -5
- package/dist/manage/ios.js +1 -1
- package/dist/server-core.js +1 -1
- package/dist/system/index.js +485 -48
- package/dist/system/ios.js +15 -7
- package/dist/utils/android/utils.js +4 -4
- package/dist/utils/cli/idb/check-idb.js +10 -3
- package/dist/utils/cli/idb/idb-helper.js +47 -13
- package/dist/utils/ios/utils.js +5 -9
- package/docs/CHANGELOG.md +3 -0
- package/docs/ROADMAP.md +4 -4
- package/docs/specs/baseline-spec-v0.md +1 -1
- package/docs/specs-deltas/016-environment-tools.md +533 -0
- package/docs/tools/TOOLS.md +2 -0
- package/docs/tools/manage.md +4 -2
- package/docs/tools/system.md +9 -2
- package/package.json +1 -1
- package/src/manage/ios.ts +1 -1
- package/src/server-core.ts +1 -1
- package/src/system/index.ts +572 -50
- package/src/system/ios.ts +14 -7
- package/src/utils/android/utils.ts +3 -3
- package/src/utils/cli/idb/check-idb.ts +11 -2
- package/src/utils/cli/idb/idb-helper.ts +49 -12
- package/src/utils/ios/utils.ts +4 -8
- package/test/unit/interact/tap_element.test.ts +1 -1
- package/test/unit/system/get_system_status.test.ts +3 -0
- package/test/unit/system/system_status.test.ts +40 -0
- package/test/unit/utils/idb_health.test.ts +74 -0
package/dist/system/ios.js
CHANGED
|
@@ -11,14 +11,22 @@ export async function checkIOS() {
|
|
|
11
11
|
iosAvailable = true;
|
|
12
12
|
try {
|
|
13
13
|
const simOut = execSync(`${xcrun} simctl list devices booted --json`, { encoding: 'utf8', timeout: 1500, stdio: ['ignore', 'pipe', 'ignore'] });
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
try {
|
|
15
|
+
const data = JSON.parse(simOut);
|
|
16
|
+
let count = 0;
|
|
17
|
+
for (const k in data.devices) {
|
|
18
|
+
const arr = data.devices[k];
|
|
19
|
+
if (Array.isArray(arr))
|
|
20
|
+
count += arr.filter((d) => (d.state || '').toLowerCase() === 'booted').length;
|
|
21
|
+
}
|
|
22
|
+
iosDevices = count;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// Some xcrun builds include extra text around the JSON response.
|
|
26
|
+
// Fall back to counting explicit booted state markers so readiness remains deterministic.
|
|
27
|
+
const bootedMatches = (simOut.match(/"state"\s*:\s*"Booted"/gi) || []).length;
|
|
28
|
+
iosDevices = bootedMatches;
|
|
20
29
|
}
|
|
21
|
-
iosDevices = count;
|
|
22
30
|
if (iosDevices === 0)
|
|
23
31
|
issues.push('No iOS simulators/devices booted');
|
|
24
32
|
}
|
|
@@ -25,9 +25,12 @@ function findInPath(cmd) {
|
|
|
25
25
|
return null;
|
|
26
26
|
}
|
|
27
27
|
export function resolveAdbCmd() {
|
|
28
|
-
// Priority: explicit env ADB_PATH ->
|
|
28
|
+
// Priority: explicit env ADB_PATH -> PATH -> known SDK locations -> fallback to 'adb'
|
|
29
29
|
if (process.env.ADB_PATH && process.env.ADB_PATH.trim())
|
|
30
30
|
return process.env.ADB_PATH;
|
|
31
|
+
const found = findInPath('adb');
|
|
32
|
+
if (found)
|
|
33
|
+
return found;
|
|
31
34
|
const sdkRoot = process.env.ANDROID_SDK_ROOT || process.env.ANDROID_HOME;
|
|
32
35
|
if (sdkRoot) {
|
|
33
36
|
const candidate = path.join(sdkRoot, 'platform-tools', process.platform === 'win32' ? 'adb.exe' : 'adb');
|
|
@@ -38,9 +41,6 @@ export function resolveAdbCmd() {
|
|
|
38
41
|
const homeSdk = path.join(process.env.HOME || '', 'Library', 'Android', 'sdk', 'platform-tools', process.platform === 'win32' ? 'adb.exe' : 'adb');
|
|
39
42
|
if (existsSync(homeSdk))
|
|
40
43
|
return homeSdk;
|
|
41
|
-
const found = findInPath('adb');
|
|
42
|
-
if (found)
|
|
43
|
-
return found;
|
|
44
44
|
return 'adb';
|
|
45
45
|
}
|
|
46
46
|
export function getAdbCmd() { return resolveAdbCmd(); }
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { execSync, spawnSync } from 'child_process';
|
|
3
3
|
import { main as installMain } from './install-idb.js';
|
|
4
|
-
import { getIdbCmd, isIDBInstalled } from './idb-helper.js';
|
|
4
|
+
import { getIdbCmd, getIdbPackageVersion, isIDBInstalled } from './idb-helper.js';
|
|
5
5
|
function which(cmd) {
|
|
6
6
|
try {
|
|
7
7
|
const r = spawnSync('command', ['-v', cmd], { stdio: ['ignore', 'pipe', 'ignore'] });
|
|
@@ -48,11 +48,18 @@ try {
|
|
|
48
48
|
const idb = process.env.IDB_PATH || getIdbCmd();
|
|
49
49
|
print('idb:', idb);
|
|
50
50
|
if (idb && isIDBInstalled()) {
|
|
51
|
+
const version = getIdbPackageVersion();
|
|
52
|
+
if (version)
|
|
53
|
+
print('fb-idb version:', version);
|
|
51
54
|
try {
|
|
52
|
-
|
|
55
|
+
const output = execSync(`${idb} list-targets --json`, { stdio: ['ignore', 'pipe', 'pipe'], timeout: 5000 }).toString().trim();
|
|
56
|
+
print('idb list-targets --json: ok');
|
|
57
|
+
if (output)
|
|
58
|
+
print(output);
|
|
53
59
|
}
|
|
54
60
|
catch (e) {
|
|
55
|
-
print('idb --
|
|
61
|
+
print('idb list-targets --json: (failed)', e instanceof Error ? e.message : String(e));
|
|
62
|
+
process.exit(2);
|
|
56
63
|
}
|
|
57
64
|
const companion = which('idb_companion');
|
|
58
65
|
print('which idb_companion:', companion);
|
|
@@ -71,21 +71,55 @@ export function getIdbCmd() {
|
|
|
71
71
|
}
|
|
72
72
|
return null;
|
|
73
73
|
}
|
|
74
|
-
export function
|
|
75
|
-
const
|
|
74
|
+
export function getIdbPackageVersion() {
|
|
75
|
+
const runners = ['python3', 'python'];
|
|
76
|
+
for (const runner of runners) {
|
|
77
|
+
try {
|
|
78
|
+
const result = spawnSync(runner, ['-m', 'pip', 'show', 'fb-idb'], {
|
|
79
|
+
encoding: 'utf8',
|
|
80
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
81
|
+
});
|
|
82
|
+
if (result.status !== 0)
|
|
83
|
+
continue;
|
|
84
|
+
const output = `${result.stdout || ''}\n${result.stderr || ''}`;
|
|
85
|
+
const match = output.match(/^Version:\s*(.+)$/mi);
|
|
86
|
+
if (match?.[1])
|
|
87
|
+
return match[1].trim();
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
export function probeIdb(cmd = getIdbCmd()) {
|
|
76
96
|
if (!cmd)
|
|
77
|
-
return false;
|
|
97
|
+
return { ok: false, raw: null, parsedVersion: null };
|
|
78
98
|
try {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
99
|
+
const result = spawnSync(cmd, ['list-targets', '--json'], {
|
|
100
|
+
encoding: 'utf8',
|
|
101
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
102
|
+
timeout: 5000
|
|
103
|
+
});
|
|
104
|
+
if (result.status !== 0) {
|
|
105
|
+
return {
|
|
106
|
+
ok: false,
|
|
107
|
+
raw: `${result.stdout || ''}${result.stderr || ''}`.trim() || null,
|
|
108
|
+
parsedVersion: null
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
const version = getIdbPackageVersion();
|
|
112
|
+
const raw = version ? `fb-idb ${version}` : `${result.stdout || ''}${result.stderr || ''}`.trim() || null;
|
|
113
|
+
return {
|
|
114
|
+
ok: true,
|
|
115
|
+
raw,
|
|
116
|
+
parsedVersion: version
|
|
117
|
+
};
|
|
83
118
|
}
|
|
84
|
-
catch {
|
|
85
|
-
|
|
86
|
-
execSync(`${cmd} list-targets --json`, { stdio: ['ignore', 'pipe', 'ignore'], timeout: 2000 });
|
|
87
|
-
return true;
|
|
119
|
+
catch {
|
|
120
|
+
return { ok: false, raw: null, parsedVersion: null };
|
|
88
121
|
}
|
|
89
|
-
|
|
90
|
-
|
|
122
|
+
}
|
|
123
|
+
export function isIDBInstalled() {
|
|
124
|
+
return probeIdb().ok;
|
|
91
125
|
}
|
package/dist/utils/ios/utils.js
CHANGED
|
@@ -71,19 +71,15 @@ export function getIdbCmd() {
|
|
|
71
71
|
}
|
|
72
72
|
export async function isIDBInstalled() {
|
|
73
73
|
const cmd = getIdbCmd();
|
|
74
|
+
if (!cmd)
|
|
75
|
+
return false;
|
|
74
76
|
try {
|
|
75
|
-
execSync(
|
|
77
|
+
execSync(`${cmd} list-targets --json`, { stdio: ['ignore', 'pipe', 'pipe'], timeout: 5000 });
|
|
76
78
|
return true;
|
|
77
79
|
}
|
|
78
80
|
catch (e) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
return true;
|
|
82
|
-
}
|
|
83
|
-
catch (e2) {
|
|
84
|
-
console.debug(`[isIDBInstalled] idb presence check failed for '${cmd}': ${e instanceof Error ? e.message : String(e2)}`);
|
|
85
|
-
return false;
|
|
86
|
-
}
|
|
81
|
+
console.debug(`[isIDBInstalled] idb probe failed for '${cmd}': ${e instanceof Error ? e.message : String(e)}`);
|
|
82
|
+
return false;
|
|
87
83
|
}
|
|
88
84
|
}
|
|
89
85
|
// Validate bundle ID to prevent any potential injection or invalid characters
|
package/docs/CHANGELOG.md
CHANGED
package/docs/ROADMAP.md
CHANGED
|
@@ -9,12 +9,12 @@
|
|
|
9
9
|
- Action Trace and Execution Observability
|
|
10
10
|
- Wait and Synchronization Reliability
|
|
11
11
|
- Actionability Resolution
|
|
12
|
+
- Environment Auto-Configuration and Toolchain Discovery
|
|
13
|
+
- Adjustable Control Support
|
|
12
14
|
|
|
13
15
|
## Todo
|
|
14
16
|
|
|
15
|
-
- Adjustable Control Support
|
|
16
17
|
- Adjustable Control Precision Hardening
|
|
17
|
-
- Environment Auto-Configuration and Toolchain Discovery
|
|
18
18
|
- Signal-Oriented Diagnostic Filtering
|
|
19
19
|
- Long Press Gesture
|
|
20
20
|
- Runtime Debugger Introspection
|
|
@@ -205,7 +205,7 @@ Strengthens:
|
|
|
205
205
|
## Rationale
|
|
206
206
|
Reduces failures caused by interacting with discoverable but non-actionable UI nodes.
|
|
207
207
|
|
|
208
|
-
**Status:**
|
|
208
|
+
**Status:** Completed
|
|
209
209
|
|
|
210
210
|
Addresses cases where:
|
|
211
211
|
- visible text is not the true click target
|
|
@@ -251,7 +251,7 @@ Blocks or strengthens:
|
|
|
251
251
|
## Rationale
|
|
252
252
|
High leverage improvement for sliders and parameterized controls.
|
|
253
253
|
|
|
254
|
-
**Status:**
|
|
254
|
+
**Status:** Completed
|
|
255
255
|
|
|
256
256
|
Addresses friction around:
|
|
257
257
|
- coordinate-calibrated slider interaction
|
|
@@ -81,7 +81,7 @@ That flow is applied to `start_app`, `restart_app`, `tap`, `swipe`, `scroll_to_e
|
|
|
81
81
|
| --- | --- | --- | --- | --- |
|
|
82
82
|
| `classify_action_outcome` | Deterministic rule-based classifier over supplied signals. | `{ uiChanged: boolean, expectedElementVisible?: boolean, networkRequests?: { url?: string, status: 'success'\|'failure'\|'retryable' }[], hasLogErrors?: boolean }` | `{ outcome: 'success'\|'no_op'\|'backend_failure'\|'ui_failure'\|'unknown', reasoning: string, nextAction?: 'call_get_network_activity' }` | Pure computation. |
|
|
83
83
|
| `get_network_activity` | Return normalized request events since last action window. | `{}` | `{ requests: NetworkRequestSummary[], count: number }` | Reads logs, advances internal `lastConsumedTimestamp`. |
|
|
84
|
-
| `get_system_status` | Aggregate Android/iOS/Gradle readiness. | `{}` |
|
|
84
|
+
| `get_system_status` | Aggregate Android/iOS/Gradle readiness. | `{}` | Additive response: legacy flat fields remain available, with nested `host`, `android`, and `ios` objects added alongside them. | Reads toolchain/device state. |
|
|
85
85
|
|
|
86
86
|
## 3. Action Tools (Mutation Tools)
|
|
87
87
|
|