@metamask/device-mcp 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.
- package/CHANGELOG.md +34 -0
- package/README.md +266 -0
- package/dist/backends/adb-backend.cjs +442 -0
- package/dist/backends/adb-backend.cjs.map +1 -0
- package/dist/backends/adb-backend.d.cts +35 -0
- package/dist/backends/adb-backend.d.cts.map +1 -0
- package/dist/backends/adb-backend.d.mts +35 -0
- package/dist/backends/adb-backend.d.mts.map +1 -0
- package/dist/backends/adb-backend.mjs +436 -0
- package/dist/backends/adb-backend.mjs.map +1 -0
- package/dist/backends/appium-backend.cjs +518 -0
- package/dist/backends/appium-backend.cjs.map +1 -0
- package/dist/backends/appium-backend.d.cts +36 -0
- package/dist/backends/appium-backend.d.cts.map +1 -0
- package/dist/backends/appium-backend.d.mts +36 -0
- package/dist/backends/appium-backend.d.mts.map +1 -0
- package/dist/backends/appium-backend.mjs +512 -0
- package/dist/backends/appium-backend.mjs.map +1 -0
- package/dist/backends/idb-backend.cjs +442 -0
- package/dist/backends/idb-backend.cjs.map +1 -0
- package/dist/backends/idb-backend.d.cts +35 -0
- package/dist/backends/idb-backend.d.cts.map +1 -0
- package/dist/backends/idb-backend.d.mts +35 -0
- package/dist/backends/idb-backend.d.mts.map +1 -0
- package/dist/backends/idb-backend.mjs +436 -0
- package/dist/backends/idb-backend.mjs.map +1 -0
- package/dist/backends/index.cjs +134 -0
- package/dist/backends/index.cjs.map +1 -0
- package/dist/backends/index.d.cts +16 -0
- package/dist/backends/index.d.cts.map +1 -0
- package/dist/backends/index.d.mts +16 -0
- package/dist/backends/index.d.mts.map +1 -0
- package/dist/backends/index.mjs +126 -0
- package/dist/backends/index.mjs.map +1 -0
- package/dist/backends/session-file.cjs +62 -0
- package/dist/backends/session-file.cjs.map +1 -0
- package/dist/backends/session-file.d.cts +24 -0
- package/dist/backends/session-file.d.cts.map +1 -0
- package/dist/backends/session-file.d.mts +24 -0
- package/dist/backends/session-file.d.mts.map +1 -0
- package/dist/backends/session-file.mjs +59 -0
- package/dist/backends/session-file.mjs.map +1 -0
- package/dist/backends/types.cjs +3 -0
- package/dist/backends/types.cjs.map +1 -0
- package/dist/backends/types.d.cts +93 -0
- package/dist/backends/types.d.cts.map +1 -0
- package/dist/backends/types.d.mts +93 -0
- package/dist/backends/types.d.mts.map +1 -0
- package/dist/backends/types.mjs +2 -0
- package/dist/backends/types.mjs.map +1 -0
- package/dist/backends/webdriver-client.cjs +167 -0
- package/dist/backends/webdriver-client.cjs.map +1 -0
- package/dist/backends/webdriver-client.d.cts +55 -0
- package/dist/backends/webdriver-client.d.cts.map +1 -0
- package/dist/backends/webdriver-client.d.mts +55 -0
- package/dist/backends/webdriver-client.d.mts.map +1 -0
- package/dist/backends/webdriver-client.mjs +162 -0
- package/dist/backends/webdriver-client.mjs.map +1 -0
- package/dist/cli/device-mcp.cjs +20 -0
- package/dist/cli/device-mcp.cjs.map +1 -0
- package/dist/cli/device-mcp.d.cts +3 -0
- package/dist/cli/device-mcp.d.cts.map +1 -0
- package/dist/cli/device-mcp.d.mts +3 -0
- package/dist/cli/device-mcp.d.mts.map +1 -0
- package/dist/cli/device-mcp.mjs +18 -0
- package/dist/cli/device-mcp.mjs.map +1 -0
- package/dist/index.cjs +9 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +5 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +5 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +3 -0
- package/dist/index.mjs.map +1 -0
- package/dist/server.cjs +43 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +4 -0
- package/dist/server.d.cts.map +1 -0
- package/dist/server.d.mts +4 -0
- package/dist/server.d.mts.map +1 -0
- package/dist/server.mjs +40 -0
- package/dist/server.mjs.map +1 -0
- package/dist/tools/alert-text.cjs +25 -0
- package/dist/tools/alert-text.cjs.map +1 -0
- package/dist/tools/alert-text.d.cts +4 -0
- package/dist/tools/alert-text.d.cts.map +1 -0
- package/dist/tools/alert-text.d.mts +4 -0
- package/dist/tools/alert-text.d.mts.map +1 -0
- package/dist/tools/alert-text.mjs +22 -0
- package/dist/tools/alert-text.mjs.map +1 -0
- package/dist/tools/app-state.cjs +40 -0
- package/dist/tools/app-state.cjs.map +1 -0
- package/dist/tools/app-state.d.cts +4 -0
- package/dist/tools/app-state.d.cts.map +1 -0
- package/dist/tools/app-state.d.mts +4 -0
- package/dist/tools/app-state.d.mts.map +1 -0
- package/dist/tools/app-state.mjs +37 -0
- package/dist/tools/app-state.mjs.map +1 -0
- package/dist/tools/clipboard.cjs +42 -0
- package/dist/tools/clipboard.cjs.map +1 -0
- package/dist/tools/clipboard.d.cts +4 -0
- package/dist/tools/clipboard.d.cts.map +1 -0
- package/dist/tools/clipboard.d.mts +4 -0
- package/dist/tools/clipboard.d.mts.map +1 -0
- package/dist/tools/clipboard.mjs +39 -0
- package/dist/tools/clipboard.mjs.map +1 -0
- package/dist/tools/close-app.cjs +25 -0
- package/dist/tools/close-app.cjs.map +1 -0
- package/dist/tools/close-app.d.cts +4 -0
- package/dist/tools/close-app.d.cts.map +1 -0
- package/dist/tools/close-app.d.mts +4 -0
- package/dist/tools/close-app.d.mts.map +1 -0
- package/dist/tools/close-app.mjs +22 -0
- package/dist/tools/close-app.mjs.map +1 -0
- package/dist/tools/context.cjs +52 -0
- package/dist/tools/context.cjs.map +1 -0
- package/dist/tools/context.d.cts +4 -0
- package/dist/tools/context.d.cts.map +1 -0
- package/dist/tools/context.d.mts +4 -0
- package/dist/tools/context.d.mts.map +1 -0
- package/dist/tools/context.mjs +49 -0
- package/dist/tools/context.mjs.map +1 -0
- package/dist/tools/device-info.cjs +34 -0
- package/dist/tools/device-info.cjs.map +1 -0
- package/dist/tools/device-info.d.cts +4 -0
- package/dist/tools/device-info.d.cts.map +1 -0
- package/dist/tools/device-info.d.mts +4 -0
- package/dist/tools/device-info.d.mts.map +1 -0
- package/dist/tools/device-info.mjs +31 -0
- package/dist/tools/device-info.mjs.map +1 -0
- package/dist/tools/dismiss-alert.cjs +34 -0
- package/dist/tools/dismiss-alert.cjs.map +1 -0
- package/dist/tools/dismiss-alert.d.cts +4 -0
- package/dist/tools/dismiss-alert.d.cts.map +1 -0
- package/dist/tools/dismiss-alert.d.mts +4 -0
- package/dist/tools/dismiss-alert.d.mts.map +1 -0
- package/dist/tools/dismiss-alert.mjs +31 -0
- package/dist/tools/dismiss-alert.mjs.map +1 -0
- package/dist/tools/dismiss-keyboard.cjs +23 -0
- package/dist/tools/dismiss-keyboard.cjs.map +1 -0
- package/dist/tools/dismiss-keyboard.d.cts +4 -0
- package/dist/tools/dismiss-keyboard.d.cts.map +1 -0
- package/dist/tools/dismiss-keyboard.d.mts +4 -0
- package/dist/tools/dismiss-keyboard.d.mts.map +1 -0
- package/dist/tools/dismiss-keyboard.mjs +20 -0
- package/dist/tools/dismiss-keyboard.mjs.map +1 -0
- package/dist/tools/generate-locators.cjs +33 -0
- package/dist/tools/generate-locators.cjs.map +1 -0
- package/dist/tools/generate-locators.d.cts +4 -0
- package/dist/tools/generate-locators.d.cts.map +1 -0
- package/dist/tools/generate-locators.d.mts +4 -0
- package/dist/tools/generate-locators.d.mts.map +1 -0
- package/dist/tools/generate-locators.mjs +30 -0
- package/dist/tools/generate-locators.mjs.map +1 -0
- package/dist/tools/index.cjs +50 -0
- package/dist/tools/index.cjs.map +1 -0
- package/dist/tools/index.d.cts +24 -0
- package/dist/tools/index.d.cts.map +1 -0
- package/dist/tools/index.d.mts +24 -0
- package/dist/tools/index.d.mts.map +1 -0
- package/dist/tools/index.mjs +24 -0
- package/dist/tools/index.mjs.map +1 -0
- package/dist/tools/logs.cjs +48 -0
- package/dist/tools/logs.cjs.map +1 -0
- package/dist/tools/logs.d.cts +4 -0
- package/dist/tools/logs.d.cts.map +1 -0
- package/dist/tools/logs.d.mts +4 -0
- package/dist/tools/logs.d.mts.map +1 -0
- package/dist/tools/logs.mjs +45 -0
- package/dist/tools/logs.mjs.map +1 -0
- package/dist/tools/long-press.cjs +38 -0
- package/dist/tools/long-press.cjs.map +1 -0
- package/dist/tools/long-press.d.cts +4 -0
- package/dist/tools/long-press.d.cts.map +1 -0
- package/dist/tools/long-press.d.mts +4 -0
- package/dist/tools/long-press.d.mts.map +1 -0
- package/dist/tools/long-press.mjs +35 -0
- package/dist/tools/long-press.mjs.map +1 -0
- package/dist/tools/open-app.cjs +26 -0
- package/dist/tools/open-app.cjs.map +1 -0
- package/dist/tools/open-app.d.cts +4 -0
- package/dist/tools/open-app.d.cts.map +1 -0
- package/dist/tools/open-app.d.mts +4 -0
- package/dist/tools/open-app.d.mts.map +1 -0
- package/dist/tools/open-app.mjs +23 -0
- package/dist/tools/open-app.mjs.map +1 -0
- package/dist/tools/press-button.cjs +30 -0
- package/dist/tools/press-button.cjs.map +1 -0
- package/dist/tools/press-button.d.cts +4 -0
- package/dist/tools/press-button.d.cts.map +1 -0
- package/dist/tools/press-button.d.mts +4 -0
- package/dist/tools/press-button.d.mts.map +1 -0
- package/dist/tools/press-button.mjs +27 -0
- package/dist/tools/press-button.mjs.map +1 -0
- package/dist/tools/screen-recording.cjs +46 -0
- package/dist/tools/screen-recording.cjs.map +1 -0
- package/dist/tools/screen-recording.d.cts +4 -0
- package/dist/tools/screen-recording.d.cts.map +1 -0
- package/dist/tools/screen-recording.d.mts +4 -0
- package/dist/tools/screen-recording.d.mts.map +1 -0
- package/dist/tools/screen-recording.mjs +43 -0
- package/dist/tools/screen-recording.mjs.map +1 -0
- package/dist/tools/screenshot.cjs +42 -0
- package/dist/tools/screenshot.cjs.map +1 -0
- package/dist/tools/screenshot.d.cts +4 -0
- package/dist/tools/screenshot.d.cts.map +1 -0
- package/dist/tools/screenshot.d.mts +4 -0
- package/dist/tools/screenshot.d.mts.map +1 -0
- package/dist/tools/screenshot.mjs +39 -0
- package/dist/tools/screenshot.mjs.map +1 -0
- package/dist/tools/scroll-to-element.cjs +56 -0
- package/dist/tools/scroll-to-element.cjs.map +1 -0
- package/dist/tools/scroll-to-element.d.cts +4 -0
- package/dist/tools/scroll-to-element.d.cts.map +1 -0
- package/dist/tools/scroll-to-element.d.mts +4 -0
- package/dist/tools/scroll-to-element.d.mts.map +1 -0
- package/dist/tools/scroll-to-element.mjs +53 -0
- package/dist/tools/scroll-to-element.mjs.map +1 -0
- package/dist/tools/shared.cjs +11 -0
- package/dist/tools/shared.cjs.map +1 -0
- package/dist/tools/shared.d.cts +8 -0
- package/dist/tools/shared.d.cts.map +1 -0
- package/dist/tools/shared.d.mts +8 -0
- package/dist/tools/shared.d.mts.map +1 -0
- package/dist/tools/shared.mjs +8 -0
- package/dist/tools/shared.mjs.map +1 -0
- package/dist/tools/snapshot.cjs +37 -0
- package/dist/tools/snapshot.cjs.map +1 -0
- package/dist/tools/snapshot.d.cts +4 -0
- package/dist/tools/snapshot.d.cts.map +1 -0
- package/dist/tools/snapshot.d.mts +4 -0
- package/dist/tools/snapshot.d.mts.map +1 -0
- package/dist/tools/snapshot.mjs +34 -0
- package/dist/tools/snapshot.mjs.map +1 -0
- package/dist/tools/swipe.cjs +39 -0
- package/dist/tools/swipe.cjs.map +1 -0
- package/dist/tools/swipe.d.cts +4 -0
- package/dist/tools/swipe.d.cts.map +1 -0
- package/dist/tools/swipe.d.mts +4 -0
- package/dist/tools/swipe.d.mts.map +1 -0
- package/dist/tools/swipe.mjs +36 -0
- package/dist/tools/swipe.mjs.map +1 -0
- package/dist/tools/tap-coordinates.cjs +27 -0
- package/dist/tools/tap-coordinates.cjs.map +1 -0
- package/dist/tools/tap-coordinates.d.cts +4 -0
- package/dist/tools/tap-coordinates.d.cts.map +1 -0
- package/dist/tools/tap-coordinates.d.mts +4 -0
- package/dist/tools/tap-coordinates.d.mts.map +1 -0
- package/dist/tools/tap-coordinates.mjs +24 -0
- package/dist/tools/tap-coordinates.mjs.map +1 -0
- package/dist/tools/tap-element.cjs +46 -0
- package/dist/tools/tap-element.cjs.map +1 -0
- package/dist/tools/tap-element.d.cts +4 -0
- package/dist/tools/tap-element.d.cts.map +1 -0
- package/dist/tools/tap-element.d.mts +4 -0
- package/dist/tools/tap-element.d.mts.map +1 -0
- package/dist/tools/tap-element.mjs +43 -0
- package/dist/tools/tap-element.mjs.map +1 -0
- package/dist/tools/type-text.cjs +32 -0
- package/dist/tools/type-text.cjs.map +1 -0
- package/dist/tools/type-text.d.cts +4 -0
- package/dist/tools/type-text.d.cts.map +1 -0
- package/dist/tools/type-text.d.mts +4 -0
- package/dist/tools/type-text.d.mts.map +1 -0
- package/dist/tools/type-text.mjs +29 -0
- package/dist/tools/type-text.mjs.map +1 -0
- package/dist/tools/wait-for.cjs +51 -0
- package/dist/tools/wait-for.cjs.map +1 -0
- package/dist/tools/wait-for.d.cts +4 -0
- package/dist/tools/wait-for.d.cts.map +1 -0
- package/dist/tools/wait-for.d.mts +4 -0
- package/dist/tools/wait-for.d.mts.map +1 -0
- package/dist/tools/wait-for.mjs +48 -0
- package/dist/tools/wait-for.mjs.map +1 -0
- package/dist/tools/window-size.cjs +29 -0
- package/dist/tools/window-size.cjs.map +1 -0
- package/dist/tools/window-size.d.cts +4 -0
- package/dist/tools/window-size.d.cts.map +1 -0
- package/dist/tools/window-size.d.mts +4 -0
- package/dist/tools/window-size.d.mts.map +1 -0
- package/dist/tools/window-size.mjs +26 -0
- package/dist/tools/window-size.mjs.map +1 -0
- package/dist/utils/alert-labels.cjs +26 -0
- package/dist/utils/alert-labels.cjs.map +1 -0
- package/dist/utils/alert-labels.d.cts +3 -0
- package/dist/utils/alert-labels.d.cts.map +1 -0
- package/dist/utils/alert-labels.d.mts +3 -0
- package/dist/utils/alert-labels.d.mts.map +1 -0
- package/dist/utils/alert-labels.mjs +23 -0
- package/dist/utils/alert-labels.mjs.map +1 -0
- package/dist/utils/element.cjs +212 -0
- package/dist/utils/element.cjs.map +1 -0
- package/dist/utils/element.d.cts +24 -0
- package/dist/utils/element.d.cts.map +1 -0
- package/dist/utils/element.d.mts +24 -0
- package/dist/utils/element.d.mts.map +1 -0
- package/dist/utils/element.mjs +203 -0
- package/dist/utils/element.mjs.map +1 -0
- package/dist/utils/exec.cjs +57 -0
- package/dist/utils/exec.cjs.map +1 -0
- package/dist/utils/exec.d.cts +23 -0
- package/dist/utils/exec.d.cts.map +1 -0
- package/dist/utils/exec.d.mts +23 -0
- package/dist/utils/exec.d.mts.map +1 -0
- package/dist/utils/exec.mjs +52 -0
- package/dist/utils/exec.mjs.map +1 -0
- package/dist/utils/platform.cjs +93 -0
- package/dist/utils/platform.cjs.map +1 -0
- package/dist/utils/platform.d.cts +8 -0
- package/dist/utils/platform.d.cts.map +1 -0
- package/dist/utils/platform.d.mts +8 -0
- package/dist/utils/platform.d.mts.map +1 -0
- package/dist/utils/platform.mjs +90 -0
- package/dist/utils/platform.mjs.map +1 -0
- package/package.json +106 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
/**
|
|
3
|
+
* Execute a command and return stdout/stderr/exitCode.
|
|
4
|
+
* Rejects only on spawn failure — non-zero exit codes are returned, not thrown.
|
|
5
|
+
*
|
|
6
|
+
* @param command - The command to execute.
|
|
7
|
+
* @param args - Arguments to pass to the command.
|
|
8
|
+
* @param options - Execution options including timeoutMs.
|
|
9
|
+
* @returns The stdout, stderr, and exit code.
|
|
10
|
+
*/
|
|
11
|
+
export async function exec(command, args, options) {
|
|
12
|
+
const { timeoutMs = 30000, ...execOptions } = options ?? {};
|
|
13
|
+
return new Promise((resolve, reject) => {
|
|
14
|
+
const child = execFile(command, args, {
|
|
15
|
+
...execOptions,
|
|
16
|
+
timeout: timeoutMs,
|
|
17
|
+
maxBuffer: 10 * 1024 * 1024, // 10MB — UI hierarchies can be large
|
|
18
|
+
}, (error, stdout, stderr) => {
|
|
19
|
+
if (error && !('code' in error)) {
|
|
20
|
+
// Spawn failure (command not found, ENOENT, etc.)
|
|
21
|
+
reject(new Error(error.message));
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
resolve({
|
|
25
|
+
stdout: stdout?.toString() ?? '',
|
|
26
|
+
stderr: stderr?.toString() ?? '',
|
|
27
|
+
exitCode: error?.code
|
|
28
|
+
? 1
|
|
29
|
+
: (child.exitCode ?? 0),
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
export async function execStrict(command, args, options) {
|
|
35
|
+
const result = await exec(command, args, options);
|
|
36
|
+
if (result.exitCode !== 0) {
|
|
37
|
+
throw new Error(`Command failed: ${command} ${args.join(' ')}\n` +
|
|
38
|
+
`Exit code: ${result.exitCode}\n` +
|
|
39
|
+
`stderr: ${result.stderr}`);
|
|
40
|
+
}
|
|
41
|
+
return result.stdout;
|
|
42
|
+
}
|
|
43
|
+
export async function isCommandAvailable(command) {
|
|
44
|
+
try {
|
|
45
|
+
await exec('which', [command], { timeoutMs: 5000 });
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=exec.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec.mjs","sourceRoot":"","sources":["../../src/utils/exec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAmB,2BAA2B;AAQ/D;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,IAAI,CACxB,OAAe,EACf,IAAc,EACd,OAAkD;IAElD,MAAM,EAAE,SAAS,GAAG,KAAM,EAAE,GAAG,WAAW,EAAE,GAAG,OAAO,IAAI,EAAE,CAAC;IAE7D,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,KAAK,GAAG,QAAQ,CACpB,OAAO,EACP,IAAI,EACJ;YACE,GAAG,WAAW;YACd,OAAO,EAAE,SAAS;YAClB,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,qCAAqC;SACnE,EACD,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YACxB,IAAI,KAAK,IAAI,CAAC,CAAC,MAAM,IAAI,KAAK,CAAC,EAAE,CAAC;gBAChC,kDAAkD;gBAClD,MAAM,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;gBACjC,OAAO;YACT,CAAC;YACD,OAAO,CAAC;gBACN,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAChC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAChC,QAAQ,EAAG,KAA+B,EAAE,IAAI;oBAC9C,CAAC,CAAC,CAAC;oBACH,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,IAAI,CAAC,CAAC;aAC1B,CAAC,CAAC;QACL,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,OAAe,EACf,IAAc,EACd,OAAkD;IAElD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IAClD,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CACb,mBAAmB,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI;YAC9C,cAAc,MAAM,CAAC,QAAQ,IAAI;YACjC,WAAW,MAAM,CAAC,MAAM,EAAE,CAC7B,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC;AACvB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,OAAe;IACtD,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAK,EAAE,CAAC,CAAC;QACrD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC","sourcesContent":["import { execFile, ExecFileOptions } from 'node:child_process';\n\nexport type ExecResult = {\n stdout: string;\n stderr: string;\n exitCode: number;\n};\n\n/**\n * Execute a command and return stdout/stderr/exitCode.\n * Rejects only on spawn failure — non-zero exit codes are returned, not thrown.\n *\n * @param command - The command to execute.\n * @param args - Arguments to pass to the command.\n * @param options - Execution options including timeoutMs.\n * @returns The stdout, stderr, and exit code.\n */\nexport async function exec(\n command: string,\n args: string[],\n options?: ExecFileOptions & { timeoutMs?: number },\n): Promise<ExecResult> {\n const { timeoutMs = 30_000, ...execOptions } = options ?? {};\n\n return new Promise((resolve, reject) => {\n const child = execFile(\n command,\n args,\n {\n ...execOptions,\n timeout: timeoutMs,\n maxBuffer: 10 * 1024 * 1024, // 10MB — UI hierarchies can be large\n },\n (error, stdout, stderr) => {\n if (error && !('code' in error)) {\n // Spawn failure (command not found, ENOENT, etc.)\n reject(new Error(error.message));\n return;\n }\n resolve({\n stdout: stdout?.toString() ?? '',\n stderr: stderr?.toString() ?? '',\n exitCode: (error as NodeJS.ErrnoException)?.code\n ? 1\n : (child.exitCode ?? 0),\n });\n },\n );\n });\n}\n\nexport async function execStrict(\n command: string,\n args: string[],\n options?: ExecFileOptions & { timeoutMs?: number },\n): Promise<string> {\n const result = await exec(command, args, options);\n if (result.exitCode !== 0) {\n throw new Error(\n `Command failed: ${command} ${args.join(' ')}\\n` +\n `Exit code: ${result.exitCode}\\n` +\n `stderr: ${result.stderr}`,\n );\n }\n return result.stdout;\n}\n\nexport async function isCommandAvailable(command: string): Promise<boolean> {\n try {\n await exec('which', [command], { timeoutMs: 5_000 });\n return true;\n } catch {\n return false;\n }\n}\n"]}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.detectPlatform = detectPlatform;
|
|
4
|
+
const exec_js_1 = require("./exec.cjs");
|
|
5
|
+
async function detectPlatform(explicitDeviceId) {
|
|
6
|
+
if (explicitDeviceId) {
|
|
7
|
+
return detectFromExplicitId(explicitDeviceId);
|
|
8
|
+
}
|
|
9
|
+
const [hasIdb, hasAdb] = await Promise.all([
|
|
10
|
+
(0, exec_js_1.isCommandAvailable)('idb'),
|
|
11
|
+
(0, exec_js_1.isCommandAvailable)('adb'),
|
|
12
|
+
]);
|
|
13
|
+
if (hasIdb) {
|
|
14
|
+
const iosDevice = await findBootedIOSDevice();
|
|
15
|
+
if (iosDevice) {
|
|
16
|
+
return iosDevice;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
if (hasAdb) {
|
|
20
|
+
const androidDevice = await findConnectedAndroidDevice();
|
|
21
|
+
if (androidDevice) {
|
|
22
|
+
return androidDevice;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
throw new Error('No connected device found.\n' +
|
|
26
|
+
` iOS (idb): ${hasIdb ? 'available, no booted simulator' : 'not installed'}\n` +
|
|
27
|
+
` Android (adb): ${hasAdb ? 'available, no connected device' : 'not installed'}\n` +
|
|
28
|
+
'Boot a simulator/emulator or set DEVICE_ID explicitly.');
|
|
29
|
+
}
|
|
30
|
+
async function detectFromExplicitId(deviceId) {
|
|
31
|
+
// UUIDs with dashes are typically iOS simulator UDIDs
|
|
32
|
+
if (/^[0-9A-F]{8}-([0-9A-F]{4}-){3}[0-9A-F]{12}$/iu.test(deviceId)) {
|
|
33
|
+
return { platform: 'ios', deviceId };
|
|
34
|
+
}
|
|
35
|
+
// Android emulator or device serial patterns
|
|
36
|
+
if (deviceId.startsWith('emulator-') || /^[A-Za-z0-9]+$/u.test(deviceId)) {
|
|
37
|
+
return { platform: 'android', deviceId };
|
|
38
|
+
}
|
|
39
|
+
const [hasIdb, hasAdb] = await Promise.all([
|
|
40
|
+
(0, exec_js_1.isCommandAvailable)('idb'),
|
|
41
|
+
(0, exec_js_1.isCommandAvailable)('adb'),
|
|
42
|
+
]);
|
|
43
|
+
if (hasIdb) {
|
|
44
|
+
const result = await (0, exec_js_1.exec)('idb', ['describe', '--udid', deviceId]);
|
|
45
|
+
if (result.exitCode === 0) {
|
|
46
|
+
return { platform: 'ios', deviceId };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (hasAdb) {
|
|
50
|
+
const result = await (0, exec_js_1.exec)('adb', ['-s', deviceId, 'get-state']);
|
|
51
|
+
if (result.exitCode === 0) {
|
|
52
|
+
return { platform: 'android', deviceId };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
throw new Error(`Cannot determine platform for device ID: ${deviceId}. ` +
|
|
56
|
+
'Ensure the device is connected and idb/adb is available.');
|
|
57
|
+
}
|
|
58
|
+
async function findBootedIOSDevice() {
|
|
59
|
+
const result = await (0, exec_js_1.exec)('idb', ['list-targets', '--json']);
|
|
60
|
+
if (result.exitCode !== 0) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
for (const line of result.stdout.trim().split('\n')) {
|
|
64
|
+
if (!line.trim()) {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
const target = JSON.parse(line);
|
|
69
|
+
if (target.state === 'Booted' && target.type === 'simulator') {
|
|
70
|
+
return { platform: 'ios', deviceId: target.udid };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
async function findConnectedAndroidDevice() {
|
|
80
|
+
const result = await (0, exec_js_1.exec)('adb', ['devices']);
|
|
81
|
+
if (result.exitCode !== 0) {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
const lines = result.stdout.trim().split('\n').slice(1);
|
|
85
|
+
for (const line of lines) {
|
|
86
|
+
const [serial, state] = line.trim().split(/\s+/u);
|
|
87
|
+
if (serial && state === 'device') {
|
|
88
|
+
return { platform: 'android', deviceId: serial };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=platform.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform.cjs","sourceRoot":"","sources":["../../src/utils/platform.ts"],"names":[],"mappings":"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","sourcesContent":["import { exec, isCommandAvailable } from './exec.js';\nimport type { Platform } from '../backends/types.js';\n\ntype DetectedDevice = {\n platform: Platform;\n deviceId: string;\n};\n\nexport async function detectPlatform(\n explicitDeviceId?: string,\n): Promise<DetectedDevice> {\n if (explicitDeviceId) {\n return detectFromExplicitId(explicitDeviceId);\n }\n\n const [hasIdb, hasAdb] = await Promise.all([\n isCommandAvailable('idb'),\n isCommandAvailable('adb'),\n ]);\n\n if (hasIdb) {\n const iosDevice = await findBootedIOSDevice();\n if (iosDevice) {\n return iosDevice;\n }\n }\n\n if (hasAdb) {\n const androidDevice = await findConnectedAndroidDevice();\n if (androidDevice) {\n return androidDevice;\n }\n }\n\n throw new Error(\n 'No connected device found.\\n' +\n ` iOS (idb): ${hasIdb ? 'available, no booted simulator' : 'not installed'}\\n` +\n ` Android (adb): ${hasAdb ? 'available, no connected device' : 'not installed'}\\n` +\n 'Boot a simulator/emulator or set DEVICE_ID explicitly.',\n );\n}\n\nasync function detectFromExplicitId(deviceId: string): Promise<DetectedDevice> {\n // UUIDs with dashes are typically iOS simulator UDIDs\n if (/^[0-9A-F]{8}-([0-9A-F]{4}-){3}[0-9A-F]{12}$/iu.test(deviceId)) {\n return { platform: 'ios', deviceId };\n }\n\n // Android emulator or device serial patterns\n if (deviceId.startsWith('emulator-') || /^[A-Za-z0-9]+$/u.test(deviceId)) {\n return { platform: 'android', deviceId };\n }\n\n const [hasIdb, hasAdb] = await Promise.all([\n isCommandAvailable('idb'),\n isCommandAvailable('adb'),\n ]);\n\n if (hasIdb) {\n const result = await exec('idb', ['describe', '--udid', deviceId]);\n if (result.exitCode === 0) {\n return { platform: 'ios', deviceId };\n }\n }\n\n if (hasAdb) {\n const result = await exec('adb', ['-s', deviceId, 'get-state']);\n if (result.exitCode === 0) {\n return { platform: 'android', deviceId };\n }\n }\n\n throw new Error(\n `Cannot determine platform for device ID: ${deviceId}. ` +\n 'Ensure the device is connected and idb/adb is available.',\n );\n}\n\nasync function findBootedIOSDevice(): Promise<DetectedDevice | null> {\n const result = await exec('idb', ['list-targets', '--json']);\n if (result.exitCode !== 0) {\n return null;\n }\n\n for (const line of result.stdout.trim().split('\\n')) {\n if (!line.trim()) {\n continue;\n }\n try {\n const target = JSON.parse(line);\n if (target.state === 'Booted' && target.type === 'simulator') {\n return { platform: 'ios', deviceId: target.udid };\n }\n } catch {\n continue;\n }\n }\n return null;\n}\n\nasync function findConnectedAndroidDevice(): Promise<DetectedDevice | null> {\n const result = await exec('adb', ['devices']);\n if (result.exitCode !== 0) {\n return null;\n }\n\n const lines = result.stdout.trim().split('\\n').slice(1);\n for (const line of lines) {\n const [serial, state] = line.trim().split(/\\s+/u);\n if (serial && state === 'device') {\n return { platform: 'android', deviceId: serial };\n }\n }\n return null;\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Platform } from "../backends/types.cjs";
|
|
2
|
+
type DetectedDevice = {
|
|
3
|
+
platform: Platform;
|
|
4
|
+
deviceId: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function detectPlatform(explicitDeviceId?: string): Promise<DetectedDevice>;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=platform.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform.d.cts","sourceRoot":"","sources":["../../src/utils/platform.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,8BAA6B;AAErD,KAAK,cAAc,GAAG;IACpB,QAAQ,EAAE,QAAQ,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,wBAAsB,cAAc,CAClC,gBAAgB,CAAC,EAAE,MAAM,GACxB,OAAO,CAAC,cAAc,CAAC,CA8BzB"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Platform } from "../backends/types.mjs";
|
|
2
|
+
type DetectedDevice = {
|
|
3
|
+
platform: Platform;
|
|
4
|
+
deviceId: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function detectPlatform(explicitDeviceId?: string): Promise<DetectedDevice>;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=platform.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform.d.mts","sourceRoot":"","sources":["../../src/utils/platform.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,8BAA6B;AAErD,KAAK,cAAc,GAAG;IACpB,QAAQ,EAAE,QAAQ,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,wBAAsB,cAAc,CAClC,gBAAgB,CAAC,EAAE,MAAM,GACxB,OAAO,CAAC,cAAc,CAAC,CA8BzB"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { exec, isCommandAvailable } from "./exec.mjs";
|
|
2
|
+
export async function detectPlatform(explicitDeviceId) {
|
|
3
|
+
if (explicitDeviceId) {
|
|
4
|
+
return detectFromExplicitId(explicitDeviceId);
|
|
5
|
+
}
|
|
6
|
+
const [hasIdb, hasAdb] = await Promise.all([
|
|
7
|
+
isCommandAvailable('idb'),
|
|
8
|
+
isCommandAvailable('adb'),
|
|
9
|
+
]);
|
|
10
|
+
if (hasIdb) {
|
|
11
|
+
const iosDevice = await findBootedIOSDevice();
|
|
12
|
+
if (iosDevice) {
|
|
13
|
+
return iosDevice;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
if (hasAdb) {
|
|
17
|
+
const androidDevice = await findConnectedAndroidDevice();
|
|
18
|
+
if (androidDevice) {
|
|
19
|
+
return androidDevice;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
throw new Error('No connected device found.\n' +
|
|
23
|
+
` iOS (idb): ${hasIdb ? 'available, no booted simulator' : 'not installed'}\n` +
|
|
24
|
+
` Android (adb): ${hasAdb ? 'available, no connected device' : 'not installed'}\n` +
|
|
25
|
+
'Boot a simulator/emulator or set DEVICE_ID explicitly.');
|
|
26
|
+
}
|
|
27
|
+
async function detectFromExplicitId(deviceId) {
|
|
28
|
+
// UUIDs with dashes are typically iOS simulator UDIDs
|
|
29
|
+
if (/^[0-9A-F]{8}-([0-9A-F]{4}-){3}[0-9A-F]{12}$/iu.test(deviceId)) {
|
|
30
|
+
return { platform: 'ios', deviceId };
|
|
31
|
+
}
|
|
32
|
+
// Android emulator or device serial patterns
|
|
33
|
+
if (deviceId.startsWith('emulator-') || /^[A-Za-z0-9]+$/u.test(deviceId)) {
|
|
34
|
+
return { platform: 'android', deviceId };
|
|
35
|
+
}
|
|
36
|
+
const [hasIdb, hasAdb] = await Promise.all([
|
|
37
|
+
isCommandAvailable('idb'),
|
|
38
|
+
isCommandAvailable('adb'),
|
|
39
|
+
]);
|
|
40
|
+
if (hasIdb) {
|
|
41
|
+
const result = await exec('idb', ['describe', '--udid', deviceId]);
|
|
42
|
+
if (result.exitCode === 0) {
|
|
43
|
+
return { platform: 'ios', deviceId };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (hasAdb) {
|
|
47
|
+
const result = await exec('adb', ['-s', deviceId, 'get-state']);
|
|
48
|
+
if (result.exitCode === 0) {
|
|
49
|
+
return { platform: 'android', deviceId };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
throw new Error(`Cannot determine platform for device ID: ${deviceId}. ` +
|
|
53
|
+
'Ensure the device is connected and idb/adb is available.');
|
|
54
|
+
}
|
|
55
|
+
async function findBootedIOSDevice() {
|
|
56
|
+
const result = await exec('idb', ['list-targets', '--json']);
|
|
57
|
+
if (result.exitCode !== 0) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
for (const line of result.stdout.trim().split('\n')) {
|
|
61
|
+
if (!line.trim()) {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
const target = JSON.parse(line);
|
|
66
|
+
if (target.state === 'Booted' && target.type === 'simulator') {
|
|
67
|
+
return { platform: 'ios', deviceId: target.udid };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
async function findConnectedAndroidDevice() {
|
|
77
|
+
const result = await exec('adb', ['devices']);
|
|
78
|
+
if (result.exitCode !== 0) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
const lines = result.stdout.trim().split('\n').slice(1);
|
|
82
|
+
for (const line of lines) {
|
|
83
|
+
const [serial, state] = line.trim().split(/\s+/u);
|
|
84
|
+
if (serial && state === 'device') {
|
|
85
|
+
return { platform: 'android', deviceId: serial };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=platform.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"platform.mjs","sourceRoot":"","sources":["../../src/utils/platform.ts"],"names":[],"mappings":"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","sourcesContent":["import { exec, isCommandAvailable } from './exec.js';\nimport type { Platform } from '../backends/types.js';\n\ntype DetectedDevice = {\n platform: Platform;\n deviceId: string;\n};\n\nexport async function detectPlatform(\n explicitDeviceId?: string,\n): Promise<DetectedDevice> {\n if (explicitDeviceId) {\n return detectFromExplicitId(explicitDeviceId);\n }\n\n const [hasIdb, hasAdb] = await Promise.all([\n isCommandAvailable('idb'),\n isCommandAvailable('adb'),\n ]);\n\n if (hasIdb) {\n const iosDevice = await findBootedIOSDevice();\n if (iosDevice) {\n return iosDevice;\n }\n }\n\n if (hasAdb) {\n const androidDevice = await findConnectedAndroidDevice();\n if (androidDevice) {\n return androidDevice;\n }\n }\n\n throw new Error(\n 'No connected device found.\\n' +\n ` iOS (idb): ${hasIdb ? 'available, no booted simulator' : 'not installed'}\\n` +\n ` Android (adb): ${hasAdb ? 'available, no connected device' : 'not installed'}\\n` +\n 'Boot a simulator/emulator or set DEVICE_ID explicitly.',\n );\n}\n\nasync function detectFromExplicitId(deviceId: string): Promise<DetectedDevice> {\n // UUIDs with dashes are typically iOS simulator UDIDs\n if (/^[0-9A-F]{8}-([0-9A-F]{4}-){3}[0-9A-F]{12}$/iu.test(deviceId)) {\n return { platform: 'ios', deviceId };\n }\n\n // Android emulator or device serial patterns\n if (deviceId.startsWith('emulator-') || /^[A-Za-z0-9]+$/u.test(deviceId)) {\n return { platform: 'android', deviceId };\n }\n\n const [hasIdb, hasAdb] = await Promise.all([\n isCommandAvailable('idb'),\n isCommandAvailable('adb'),\n ]);\n\n if (hasIdb) {\n const result = await exec('idb', ['describe', '--udid', deviceId]);\n if (result.exitCode === 0) {\n return { platform: 'ios', deviceId };\n }\n }\n\n if (hasAdb) {\n const result = await exec('adb', ['-s', deviceId, 'get-state']);\n if (result.exitCode === 0) {\n return { platform: 'android', deviceId };\n }\n }\n\n throw new Error(\n `Cannot determine platform for device ID: ${deviceId}. ` +\n 'Ensure the device is connected and idb/adb is available.',\n );\n}\n\nasync function findBootedIOSDevice(): Promise<DetectedDevice | null> {\n const result = await exec('idb', ['list-targets', '--json']);\n if (result.exitCode !== 0) {\n return null;\n }\n\n for (const line of result.stdout.trim().split('\\n')) {\n if (!line.trim()) {\n continue;\n }\n try {\n const target = JSON.parse(line);\n if (target.state === 'Booted' && target.type === 'simulator') {\n return { platform: 'ios', deviceId: target.udid };\n }\n } catch {\n continue;\n }\n }\n return null;\n}\n\nasync function findConnectedAndroidDevice(): Promise<DetectedDevice | null> {\n const result = await exec('adb', ['devices']);\n if (result.exitCode !== 0) {\n return null;\n }\n\n const lines = result.stdout.trim().split('\\n').slice(1);\n for (const line of lines) {\n const [serial, state] = line.trim().split(/\\s+/u);\n if (serial && state === 'device') {\n return { platform: 'android', deviceId: serial };\n }\n }\n return null;\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@metamask/device-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for mobile device interaction — iOS (IDB), Android (ADB), and Appium/BrowserStack",
|
|
5
|
+
"homepage": "https://github.com/MetaMask/device-mcp#readme",
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://github.com/MetaMask/device-mcp/issues"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/MetaMask/device-mcp.git"
|
|
12
|
+
},
|
|
13
|
+
"license": "(MIT OR Apache-2.0)",
|
|
14
|
+
"sideEffects": false,
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"import": {
|
|
18
|
+
"types": "./dist/index.d.mts",
|
|
19
|
+
"default": "./dist/index.mjs"
|
|
20
|
+
},
|
|
21
|
+
"require": {
|
|
22
|
+
"types": "./dist/index.d.cts",
|
|
23
|
+
"default": "./dist/index.cjs"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"./package.json": "./package.json"
|
|
27
|
+
},
|
|
28
|
+
"main": "./dist/index.cjs",
|
|
29
|
+
"module": "./dist/index.mjs",
|
|
30
|
+
"types": "./dist/index.d.cts",
|
|
31
|
+
"bin": "./dist/cli/device-mcp.mjs",
|
|
32
|
+
"files": [
|
|
33
|
+
"dist"
|
|
34
|
+
],
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "ts-bridge --project tsconfig.build.json --clean",
|
|
37
|
+
"build:docs": "typedoc",
|
|
38
|
+
"clean": "rm -rf dist",
|
|
39
|
+
"dev": "tsc --watch",
|
|
40
|
+
"lint": "yarn lint:eslint && yarn lint:constraints && yarn lint:misc --check && yarn lint:dependencies --check && yarn lint:changelog",
|
|
41
|
+
"lint:changelog": "auto-changelog validate --prettier",
|
|
42
|
+
"lint:constraints": "yarn constraints",
|
|
43
|
+
"lint:dependencies": "depcheck && yarn dedupe --check",
|
|
44
|
+
"lint:dependencies:fix": "depcheck && yarn dedupe",
|
|
45
|
+
"lint:eslint": "eslint .",
|
|
46
|
+
"lint:fix": "yarn lint:eslint --fix && yarn lint:constraints --fix && yarn lint:misc --write && yarn lint:dependencies:fix && yarn lint:changelog",
|
|
47
|
+
"lint:misc": "prettier '**/*.json' '**/*.md' '**/*.yml' '!.yarnrc.yml' --ignore-path .gitignore --no-error-on-unmatched-pattern",
|
|
48
|
+
"prepack": "./scripts/prepack.sh",
|
|
49
|
+
"start": "node dist/index.mjs",
|
|
50
|
+
"test": "vitest",
|
|
51
|
+
"test:watch": "vitest --watch"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
55
|
+
"zod": "^4.4.3"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@lavamoat/allow-scripts": "^3.0.4",
|
|
59
|
+
"@lavamoat/preinstall-always-fail": "^2.0.0",
|
|
60
|
+
"@metamask/auto-changelog": "^5.3.0",
|
|
61
|
+
"@metamask/eslint-config": "^15.0.0",
|
|
62
|
+
"@metamask/eslint-config-nodejs": "^15.0.0",
|
|
63
|
+
"@metamask/eslint-config-typescript": "^15.0.0",
|
|
64
|
+
"@metamask/eslint-config-vitest": "^15.0.0",
|
|
65
|
+
"@ts-bridge/cli": "^0.6.3",
|
|
66
|
+
"@types/node": "^20.0.0",
|
|
67
|
+
"@typescript-eslint/utils": "^8.6.0",
|
|
68
|
+
"@vitest/coverage-istanbul": "^3.0.7",
|
|
69
|
+
"@vitest/eslint-plugin": "^1.1.4",
|
|
70
|
+
"@yarnpkg/types": "^4.0.0-rc.52",
|
|
71
|
+
"depcheck": "^1.4.3",
|
|
72
|
+
"eslint": "^9.11.0",
|
|
73
|
+
"eslint-config-prettier": "^9.1.0",
|
|
74
|
+
"eslint-import-resolver-typescript": "^3.6.3",
|
|
75
|
+
"eslint-plugin-import-x": "^4.3.0",
|
|
76
|
+
"eslint-plugin-jsdoc": "^50.2.4",
|
|
77
|
+
"eslint-plugin-n": "^17.10.3",
|
|
78
|
+
"eslint-plugin-prettier": "^5.2.1",
|
|
79
|
+
"eslint-plugin-promise": "^7.1.0",
|
|
80
|
+
"prettier": "^3.3.3",
|
|
81
|
+
"prettier-plugin-packagejson": "^2.5.8",
|
|
82
|
+
"ts-node": "^10.7.0",
|
|
83
|
+
"typedoc": "^0.26.11",
|
|
84
|
+
"typescript": "~5.7.3",
|
|
85
|
+
"typescript-eslint": "^8.48.1",
|
|
86
|
+
"vite": "^6.4.1",
|
|
87
|
+
"vitest": "^3.2.1"
|
|
88
|
+
},
|
|
89
|
+
"packageManager": "yarn@4.11.0",
|
|
90
|
+
"engines": {
|
|
91
|
+
"node": "^20 || ^22 || >=24"
|
|
92
|
+
},
|
|
93
|
+
"publishConfig": {
|
|
94
|
+
"access": "public",
|
|
95
|
+
"registry": "https://registry.npmjs.org/"
|
|
96
|
+
},
|
|
97
|
+
"lavamoat": {
|
|
98
|
+
"allowScripts": {
|
|
99
|
+
"@lavamoat/preinstall-always-fail": false,
|
|
100
|
+
"eslint-plugin-import-x>unrs-resolver": false,
|
|
101
|
+
"vite>esbuild": false,
|
|
102
|
+
"vitest>vite-node>vite>esbuild": false,
|
|
103
|
+
"vitest>vite>esbuild": false
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|