@metamask/device-mcp 0.1.0 → 0.2.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 (110) hide show
  1. package/CHANGELOG.md +9 -14
  2. package/LICENSE +6 -0
  3. package/LICENSE.APACHE2 +201 -0
  4. package/LICENSE.MIT +21 -0
  5. package/README.md +61 -33
  6. package/dist/backends/adb-backend.cjs +9 -0
  7. package/dist/backends/adb-backend.cjs.map +1 -1
  8. package/dist/backends/adb-backend.d.cts +1 -0
  9. package/dist/backends/adb-backend.d.cts.map +1 -1
  10. package/dist/backends/adb-backend.d.mts +1 -0
  11. package/dist/backends/adb-backend.d.mts.map +1 -1
  12. package/dist/backends/adb-backend.mjs +9 -0
  13. package/dist/backends/adb-backend.mjs.map +1 -1
  14. package/dist/backends/appium-backend.cjs +9 -0
  15. package/dist/backends/appium-backend.cjs.map +1 -1
  16. package/dist/backends/appium-backend.d.cts +1 -0
  17. package/dist/backends/appium-backend.d.cts.map +1 -1
  18. package/dist/backends/appium-backend.d.mts +1 -0
  19. package/dist/backends/appium-backend.d.mts.map +1 -1
  20. package/dist/backends/appium-backend.mjs +9 -0
  21. package/dist/backends/appium-backend.mjs.map +1 -1
  22. package/dist/backends/idb-backend.cjs +84 -33
  23. package/dist/backends/idb-backend.cjs.map +1 -1
  24. package/dist/backends/idb-backend.d.cts +1 -0
  25. package/dist/backends/idb-backend.d.cts.map +1 -1
  26. package/dist/backends/idb-backend.d.mts +1 -0
  27. package/dist/backends/idb-backend.d.mts.map +1 -1
  28. package/dist/backends/idb-backend.mjs +85 -34
  29. package/dist/backends/idb-backend.mjs.map +1 -1
  30. package/dist/backends/index.cjs +33 -12
  31. package/dist/backends/index.cjs.map +1 -1
  32. package/dist/backends/index.d.cts +8 -11
  33. package/dist/backends/index.d.cts.map +1 -1
  34. package/dist/backends/index.d.mts +8 -11
  35. package/dist/backends/index.d.mts.map +1 -1
  36. package/dist/backends/index.mjs +34 -13
  37. package/dist/backends/index.mjs.map +1 -1
  38. package/dist/backends/types.cjs.map +1 -1
  39. package/dist/backends/types.d.cts +1 -0
  40. package/dist/backends/types.d.cts.map +1 -1
  41. package/dist/backends/types.d.mts +1 -0
  42. package/dist/backends/types.d.mts.map +1 -1
  43. package/dist/backends/types.mjs.map +1 -1
  44. package/dist/cli/device-mcp.cjs +11 -2
  45. package/dist/cli/device-mcp.cjs.map +1 -1
  46. package/dist/cli/device-mcp.mjs +11 -2
  47. package/dist/cli/device-mcp.mjs.map +1 -1
  48. package/dist/index.cjs +4 -1
  49. package/dist/index.cjs.map +1 -1
  50. package/dist/index.d.cts +3 -0
  51. package/dist/index.d.cts.map +1 -1
  52. package/dist/index.d.mts +3 -0
  53. package/dist/index.d.mts.map +1 -1
  54. package/dist/index.mjs +1 -0
  55. package/dist/index.mjs.map +1 -1
  56. package/dist/server.cjs +5 -0
  57. package/dist/server.cjs.map +1 -1
  58. package/dist/server.d.cts +2 -2
  59. package/dist/server.d.cts.map +1 -1
  60. package/dist/server.d.mts +2 -2
  61. package/dist/server.d.mts.map +1 -1
  62. package/dist/server.mjs +6 -1
  63. package/dist/server.mjs.map +1 -1
  64. package/dist/tools/get-element-text.cjs +46 -0
  65. package/dist/tools/get-element-text.cjs.map +1 -0
  66. package/dist/tools/get-element-text.d.cts +4 -0
  67. package/dist/tools/get-element-text.d.cts.map +1 -0
  68. package/dist/tools/get-element-text.d.mts +4 -0
  69. package/dist/tools/get-element-text.d.mts.map +1 -0
  70. package/dist/tools/get-element-text.mjs +43 -0
  71. package/dist/tools/get-element-text.mjs.map +1 -0
  72. package/dist/tools/index.cjs +7 -1
  73. package/dist/tools/index.cjs.map +1 -1
  74. package/dist/tools/index.d.cts +3 -0
  75. package/dist/tools/index.d.cts.map +1 -1
  76. package/dist/tools/index.d.mts +3 -0
  77. package/dist/tools/index.d.mts.map +1 -1
  78. package/dist/tools/index.mjs +3 -0
  79. package/dist/tools/index.mjs.map +1 -1
  80. package/dist/tools/list-devices.cjs +36 -0
  81. package/dist/tools/list-devices.cjs.map +1 -0
  82. package/dist/tools/list-devices.d.cts +4 -0
  83. package/dist/tools/list-devices.d.cts.map +1 -0
  84. package/dist/tools/list-devices.d.mts +4 -0
  85. package/dist/tools/list-devices.d.mts.map +1 -0
  86. package/dist/tools/list-devices.mjs +33 -0
  87. package/dist/tools/list-devices.mjs.map +1 -0
  88. package/dist/tools/select-device.cjs +38 -0
  89. package/dist/tools/select-device.cjs.map +1 -0
  90. package/dist/tools/select-device.d.cts +4 -0
  91. package/dist/tools/select-device.d.cts.map +1 -0
  92. package/dist/tools/select-device.d.mts +4 -0
  93. package/dist/tools/select-device.d.mts.map +1 -0
  94. package/dist/tools/select-device.mjs +35 -0
  95. package/dist/tools/select-device.mjs.map +1 -0
  96. package/dist/utils/exec.cjs +11 -9
  97. package/dist/utils/exec.cjs.map +1 -1
  98. package/dist/utils/exec.d.cts.map +1 -1
  99. package/dist/utils/exec.d.mts.map +1 -1
  100. package/dist/utils/exec.mjs +11 -9
  101. package/dist/utils/exec.mjs.map +1 -1
  102. package/dist/utils/platform.cjs +121 -36
  103. package/dist/utils/platform.cjs.map +1 -1
  104. package/dist/utils/platform.d.cts +14 -3
  105. package/dist/utils/platform.d.cts.map +1 -1
  106. package/dist/utils/platform.d.mts +14 -3
  107. package/dist/utils/platform.d.mts.map +1 -1
  108. package/dist/utils/platform.mjs +117 -36
  109. package/dist/utils/platform.mjs.map +1 -1
  110. package/package.json +2 -2
@@ -0,0 +1,43 @@
1
+ import { z } from "zod";
2
+ import { errorResult } from "./shared.mjs";
3
+ export function registerGetElementTextTool(server, backend) {
4
+ server.registerTool('device_get_element_text', {
5
+ title: 'Get Element Text',
6
+ description: 'Find an element in the UI hierarchy by query and return its text content (label or value). ' +
7
+ 'Specify at least one of: label, identifier, text, type. ' +
8
+ 'Call device_snapshot first to see available elements.',
9
+ inputSchema: {
10
+ label: z
11
+ .string()
12
+ .optional()
13
+ .describe('Accessibility label (iOS) or content-description (Android)'),
14
+ identifier: z
15
+ .string()
16
+ .optional()
17
+ .describe('Accessibility identifier (iOS) or resource-id (Android)'),
18
+ text: z.string().optional().describe('Visible text content'),
19
+ type: z.string().optional().describe('Element type/class name'),
20
+ },
21
+ }, async ({ label, identifier, text, type }) => {
22
+ try {
23
+ const result = await backend.getElementText({
24
+ label,
25
+ identifier,
26
+ text,
27
+ type,
28
+ });
29
+ return {
30
+ content: [
31
+ {
32
+ type: 'text',
33
+ text: result || '(empty)',
34
+ },
35
+ ],
36
+ };
37
+ }
38
+ catch (error) {
39
+ return errorResult(error);
40
+ }
41
+ });
42
+ }
43
+ //# sourceMappingURL=get-element-text.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-element-text.mjs","sourceRoot":"","sources":["../../src/tools/get-element-text.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,YAAY;AAExB,OAAO,EAAE,WAAW,EAAE,qBAAoB;AAG1C,MAAM,UAAU,0BAA0B,CACxC,MAAiB,EACjB,OAAsB;IAEtB,MAAM,CAAC,YAAY,CACjB,yBAAyB,EACzB;QACE,KAAK,EAAE,kBAAkB;QACzB,WAAW,EACT,6FAA6F;YAC7F,0DAA0D;YAC1D,uDAAuD;QACzD,WAAW,EAAE;YACX,KAAK,EAAE,CAAC;iBACL,MAAM,EAAE;iBACR,QAAQ,EAAE;iBACV,QAAQ,CACP,4DAA4D,CAC7D;YACH,UAAU,EAAE,CAAC;iBACV,MAAM,EAAE;iBACR,QAAQ,EAAE;iBACV,QAAQ,CAAC,yDAAyD,CAAC;YACtE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,sBAAsB,CAAC;YAC5D,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,yBAAyB,CAAC;SAChE;KACF,EACD,KAAK,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE;QAC1C,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,cAAc,CAAC;gBAC1C,KAAK;gBACL,UAAU;gBACV,IAAI;gBACJ,IAAI;aACL,CAAC,CAAC;YACH,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,MAAM,IAAI,SAAS;qBAC1B;iBACF;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC","sourcesContent":["import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\n\nimport { errorResult } from './shared.js';\nimport type { DeviceBackend } from '../backends/types.js';\n\nexport function registerGetElementTextTool(\n server: McpServer,\n backend: DeviceBackend,\n): void {\n server.registerTool(\n 'device_get_element_text',\n {\n title: 'Get Element Text',\n description:\n 'Find an element in the UI hierarchy by query and return its text content (label or value). ' +\n 'Specify at least one of: label, identifier, text, type. ' +\n 'Call device_snapshot first to see available elements.',\n inputSchema: {\n label: z\n .string()\n .optional()\n .describe(\n 'Accessibility label (iOS) or content-description (Android)',\n ),\n identifier: z\n .string()\n .optional()\n .describe('Accessibility identifier (iOS) or resource-id (Android)'),\n text: z.string().optional().describe('Visible text content'),\n type: z.string().optional().describe('Element type/class name'),\n },\n },\n async ({ label, identifier, text, type }) => {\n try {\n const result = await backend.getElementText({\n label,\n identifier,\n text,\n type,\n });\n return {\n content: [\n {\n type: 'text' as const,\n text: result || '(empty)',\n },\n ],\n };\n } catch (error) {\n return errorResult(error);\n }\n },\n );\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.registerGenerateLocatorsTool = exports.registerScreenRecordingTool = exports.registerClipboardTool = exports.registerContextTool = exports.registerWindowSizeTool = exports.registerAlertTextTool = exports.registerScrollToElementTool = exports.registerLogsTool = exports.registerDismissAlertTool = exports.registerDismissKeyboardTool = exports.registerPressButtonTool = exports.registerCloseAppTool = exports.registerOpenAppTool = exports.registerDeviceInfoTool = exports.registerAppStateTool = exports.registerWaitForTool = exports.registerLongPressTool = exports.registerSwipeTool = exports.registerTypeTool = exports.registerTapCoordinatesTool = exports.registerTapElementTool = exports.registerScreenshotTool = exports.registerSnapshotTool = void 0;
3
+ exports.registerSelectDeviceTool = exports.registerListDevicesTool = exports.registerGetElementTextTool = exports.registerGenerateLocatorsTool = exports.registerScreenRecordingTool = exports.registerClipboardTool = exports.registerContextTool = exports.registerWindowSizeTool = exports.registerAlertTextTool = exports.registerScrollToElementTool = exports.registerLogsTool = exports.registerDismissAlertTool = exports.registerDismissKeyboardTool = exports.registerPressButtonTool = exports.registerCloseAppTool = exports.registerOpenAppTool = exports.registerDeviceInfoTool = exports.registerAppStateTool = exports.registerWaitForTool = exports.registerLongPressTool = exports.registerSwipeTool = exports.registerTypeTool = exports.registerTapCoordinatesTool = exports.registerTapElementTool = exports.registerScreenshotTool = exports.registerSnapshotTool = void 0;
4
4
  var snapshot_js_1 = require("./snapshot.cjs");
5
5
  Object.defineProperty(exports, "registerSnapshotTool", { enumerable: true, get: function () { return snapshot_js_1.registerSnapshotTool; } });
6
6
  var screenshot_js_1 = require("./screenshot.cjs");
@@ -47,4 +47,10 @@ var screen_recording_js_1 = require("./screen-recording.cjs");
47
47
  Object.defineProperty(exports, "registerScreenRecordingTool", { enumerable: true, get: function () { return screen_recording_js_1.registerScreenRecordingTool; } });
48
48
  var generate_locators_js_1 = require("./generate-locators.cjs");
49
49
  Object.defineProperty(exports, "registerGenerateLocatorsTool", { enumerable: true, get: function () { return generate_locators_js_1.registerGenerateLocatorsTool; } });
50
+ var get_element_text_js_1 = require("./get-element-text.cjs");
51
+ Object.defineProperty(exports, "registerGetElementTextTool", { enumerable: true, get: function () { return get_element_text_js_1.registerGetElementTextTool; } });
52
+ var list_devices_js_1 = require("./list-devices.cjs");
53
+ Object.defineProperty(exports, "registerListDevicesTool", { enumerable: true, get: function () { return list_devices_js_1.registerListDevicesTool; } });
54
+ var select_device_js_1 = require("./select-device.cjs");
55
+ Object.defineProperty(exports, "registerSelectDeviceTool", { enumerable: true, get: function () { return select_device_js_1.registerSelectDeviceTool; } });
50
56
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":";;;AAAA,8CAAqD;AAA5C,mHAAA,oBAAoB,OAAA;AAC7B,kDAAyD;AAAhD,uHAAA,sBAAsB,OAAA;AAC/B,oDAA0D;AAAjD,wHAAA,sBAAsB,OAAA;AAC/B,4DAAkE;AAAzD,gIAAA,0BAA0B,OAAA;AACnC,gDAAkD;AAAzC,gHAAA,gBAAgB,OAAA;AACzB,wCAA+C;AAAtC,6GAAA,iBAAiB,OAAA;AAC1B,kDAAwD;AAA/C,sHAAA,qBAAqB,OAAA;AAC9B,8CAAoD;AAA3C,kHAAA,mBAAmB,OAAA;AAC5B,gDAAsD;AAA7C,oHAAA,oBAAoB,OAAA;AAC7B,oDAA0D;AAAjD,wHAAA,sBAAsB,OAAA;AAC/B,8CAAoD;AAA3C,kHAAA,mBAAmB,OAAA;AAC5B,gDAAsD;AAA7C,oHAAA,oBAAoB,OAAA;AAC7B,sDAA4D;AAAnD,0HAAA,uBAAuB,OAAA;AAChC,8DAAoE;AAA3D,kIAAA,2BAA2B,OAAA;AACpC,wDAA8D;AAArD,4HAAA,wBAAwB,OAAA;AACjC,sCAA6C;AAApC,2GAAA,gBAAgB,OAAA;AACzB,gEAAqE;AAA5D,mIAAA,2BAA2B,OAAA;AACpC,kDAAwD;AAA/C,sHAAA,qBAAqB,OAAA;AAC9B,oDAA0D;AAAjD,wHAAA,sBAAsB,OAAA;AAC/B,4CAAmD;AAA1C,iHAAA,mBAAmB,OAAA;AAC5B,gDAAuD;AAA9C,qHAAA,qBAAqB,OAAA;AAC9B,8DAAoE;AAA3D,kIAAA,2BAA2B,OAAA;AACpC,gEAAsE;AAA7D,oIAAA,4BAA4B,OAAA","sourcesContent":["export { registerSnapshotTool } from './snapshot.js';\nexport { registerScreenshotTool } from './screenshot.js';\nexport { registerTapElementTool } from './tap-element.js';\nexport { registerTapCoordinatesTool } from './tap-coordinates.js';\nexport { registerTypeTool } from './type-text.js';\nexport { registerSwipeTool } from './swipe.js';\nexport { registerLongPressTool } from './long-press.js';\nexport { registerWaitForTool } from './wait-for.js';\nexport { registerAppStateTool } from './app-state.js';\nexport { registerDeviceInfoTool } from './device-info.js';\nexport { registerOpenAppTool } from './open-app.js';\nexport { registerCloseAppTool } from './close-app.js';\nexport { registerPressButtonTool } from './press-button.js';\nexport { registerDismissKeyboardTool } from './dismiss-keyboard.js';\nexport { registerDismissAlertTool } from './dismiss-alert.js';\nexport { registerLogsTool } from './logs.js';\nexport { registerScrollToElementTool } from './scroll-to-element.js';\nexport { registerAlertTextTool } from './alert-text.js';\nexport { registerWindowSizeTool } from './window-size.js';\nexport { registerContextTool } from './context.js';\nexport { registerClipboardTool } from './clipboard.js';\nexport { registerScreenRecordingTool } from './screen-recording.js';\nexport { registerGenerateLocatorsTool } from './generate-locators.js';\n"]}
1
+ {"version":3,"file":"index.cjs","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":";;;AAAA,8CAAqD;AAA5C,mHAAA,oBAAoB,OAAA;AAC7B,kDAAyD;AAAhD,uHAAA,sBAAsB,OAAA;AAC/B,oDAA0D;AAAjD,wHAAA,sBAAsB,OAAA;AAC/B,4DAAkE;AAAzD,gIAAA,0BAA0B,OAAA;AACnC,gDAAkD;AAAzC,gHAAA,gBAAgB,OAAA;AACzB,wCAA+C;AAAtC,6GAAA,iBAAiB,OAAA;AAC1B,kDAAwD;AAA/C,sHAAA,qBAAqB,OAAA;AAC9B,8CAAoD;AAA3C,kHAAA,mBAAmB,OAAA;AAC5B,gDAAsD;AAA7C,oHAAA,oBAAoB,OAAA;AAC7B,oDAA0D;AAAjD,wHAAA,sBAAsB,OAAA;AAC/B,8CAAoD;AAA3C,kHAAA,mBAAmB,OAAA;AAC5B,gDAAsD;AAA7C,oHAAA,oBAAoB,OAAA;AAC7B,sDAA4D;AAAnD,0HAAA,uBAAuB,OAAA;AAChC,8DAAoE;AAA3D,kIAAA,2BAA2B,OAAA;AACpC,wDAA8D;AAArD,4HAAA,wBAAwB,OAAA;AACjC,sCAA6C;AAApC,2GAAA,gBAAgB,OAAA;AACzB,gEAAqE;AAA5D,mIAAA,2BAA2B,OAAA;AACpC,kDAAwD;AAA/C,sHAAA,qBAAqB,OAAA;AAC9B,oDAA0D;AAAjD,wHAAA,sBAAsB,OAAA;AAC/B,4CAAmD;AAA1C,iHAAA,mBAAmB,OAAA;AAC5B,gDAAuD;AAA9C,qHAAA,qBAAqB,OAAA;AAC9B,8DAAoE;AAA3D,kIAAA,2BAA2B,OAAA;AACpC,gEAAsE;AAA7D,oIAAA,4BAA4B,OAAA;AACrC,8DAAmE;AAA1D,iIAAA,0BAA0B,OAAA;AACnC,sDAA4D;AAAnD,0HAAA,uBAAuB,OAAA;AAChC,wDAA8D;AAArD,4HAAA,wBAAwB,OAAA","sourcesContent":["export { registerSnapshotTool } from './snapshot.js';\nexport { registerScreenshotTool } from './screenshot.js';\nexport { registerTapElementTool } from './tap-element.js';\nexport { registerTapCoordinatesTool } from './tap-coordinates.js';\nexport { registerTypeTool } from './type-text.js';\nexport { registerSwipeTool } from './swipe.js';\nexport { registerLongPressTool } from './long-press.js';\nexport { registerWaitForTool } from './wait-for.js';\nexport { registerAppStateTool } from './app-state.js';\nexport { registerDeviceInfoTool } from './device-info.js';\nexport { registerOpenAppTool } from './open-app.js';\nexport { registerCloseAppTool } from './close-app.js';\nexport { registerPressButtonTool } from './press-button.js';\nexport { registerDismissKeyboardTool } from './dismiss-keyboard.js';\nexport { registerDismissAlertTool } from './dismiss-alert.js';\nexport { registerLogsTool } from './logs.js';\nexport { registerScrollToElementTool } from './scroll-to-element.js';\nexport { registerAlertTextTool } from './alert-text.js';\nexport { registerWindowSizeTool } from './window-size.js';\nexport { registerContextTool } from './context.js';\nexport { registerClipboardTool } from './clipboard.js';\nexport { registerScreenRecordingTool } from './screen-recording.js';\nexport { registerGenerateLocatorsTool } from './generate-locators.js';\nexport { registerGetElementTextTool } from './get-element-text.js';\nexport { registerListDevicesTool } from './list-devices.js';\nexport { registerSelectDeviceTool } from './select-device.js';\n"]}
@@ -21,4 +21,7 @@ export { registerContextTool } from "./context.cjs";
21
21
  export { registerClipboardTool } from "./clipboard.cjs";
22
22
  export { registerScreenRecordingTool } from "./screen-recording.cjs";
23
23
  export { registerGenerateLocatorsTool } from "./generate-locators.cjs";
24
+ export { registerGetElementTextTool } from "./get-element-text.cjs";
25
+ export { registerListDevicesTool } from "./list-devices.cjs";
26
+ export { registerSelectDeviceTool } from "./select-device.cjs";
24
27
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,uBAAsB;AACrD,OAAO,EAAE,sBAAsB,EAAE,yBAAwB;AACzD,OAAO,EAAE,sBAAsB,EAAE,0BAAyB;AAC1D,OAAO,EAAE,0BAA0B,EAAE,8BAA6B;AAClE,OAAO,EAAE,gBAAgB,EAAE,wBAAuB;AAClD,OAAO,EAAE,iBAAiB,EAAE,oBAAmB;AAC/C,OAAO,EAAE,qBAAqB,EAAE,yBAAwB;AACxD,OAAO,EAAE,mBAAmB,EAAE,uBAAsB;AACpD,OAAO,EAAE,oBAAoB,EAAE,wBAAuB;AACtD,OAAO,EAAE,sBAAsB,EAAE,0BAAyB;AAC1D,OAAO,EAAE,mBAAmB,EAAE,uBAAsB;AACpD,OAAO,EAAE,oBAAoB,EAAE,wBAAuB;AACtD,OAAO,EAAE,uBAAuB,EAAE,2BAA0B;AAC5D,OAAO,EAAE,2BAA2B,EAAE,+BAA8B;AACpE,OAAO,EAAE,wBAAwB,EAAE,4BAA2B;AAC9D,OAAO,EAAE,gBAAgB,EAAE,mBAAkB;AAC7C,OAAO,EAAE,2BAA2B,EAAE,gCAA+B;AACrE,OAAO,EAAE,qBAAqB,EAAE,yBAAwB;AACxD,OAAO,EAAE,sBAAsB,EAAE,0BAAyB;AAC1D,OAAO,EAAE,mBAAmB,EAAE,sBAAqB;AACnD,OAAO,EAAE,qBAAqB,EAAE,wBAAuB;AACvD,OAAO,EAAE,2BAA2B,EAAE,+BAA8B;AACpE,OAAO,EAAE,4BAA4B,EAAE,gCAA+B"}
1
+ {"version":3,"file":"index.d.cts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,uBAAsB;AACrD,OAAO,EAAE,sBAAsB,EAAE,yBAAwB;AACzD,OAAO,EAAE,sBAAsB,EAAE,0BAAyB;AAC1D,OAAO,EAAE,0BAA0B,EAAE,8BAA6B;AAClE,OAAO,EAAE,gBAAgB,EAAE,wBAAuB;AAClD,OAAO,EAAE,iBAAiB,EAAE,oBAAmB;AAC/C,OAAO,EAAE,qBAAqB,EAAE,yBAAwB;AACxD,OAAO,EAAE,mBAAmB,EAAE,uBAAsB;AACpD,OAAO,EAAE,oBAAoB,EAAE,wBAAuB;AACtD,OAAO,EAAE,sBAAsB,EAAE,0BAAyB;AAC1D,OAAO,EAAE,mBAAmB,EAAE,uBAAsB;AACpD,OAAO,EAAE,oBAAoB,EAAE,wBAAuB;AACtD,OAAO,EAAE,uBAAuB,EAAE,2BAA0B;AAC5D,OAAO,EAAE,2BAA2B,EAAE,+BAA8B;AACpE,OAAO,EAAE,wBAAwB,EAAE,4BAA2B;AAC9D,OAAO,EAAE,gBAAgB,EAAE,mBAAkB;AAC7C,OAAO,EAAE,2BAA2B,EAAE,gCAA+B;AACrE,OAAO,EAAE,qBAAqB,EAAE,yBAAwB;AACxD,OAAO,EAAE,sBAAsB,EAAE,0BAAyB;AAC1D,OAAO,EAAE,mBAAmB,EAAE,sBAAqB;AACnD,OAAO,EAAE,qBAAqB,EAAE,wBAAuB;AACvD,OAAO,EAAE,2BAA2B,EAAE,+BAA8B;AACpE,OAAO,EAAE,4BAA4B,EAAE,gCAA+B;AACtE,OAAO,EAAE,0BAA0B,EAAE,+BAA8B;AACnE,OAAO,EAAE,uBAAuB,EAAE,2BAA0B;AAC5D,OAAO,EAAE,wBAAwB,EAAE,4BAA2B"}
@@ -21,4 +21,7 @@ export { registerContextTool } from "./context.mjs";
21
21
  export { registerClipboardTool } from "./clipboard.mjs";
22
22
  export { registerScreenRecordingTool } from "./screen-recording.mjs";
23
23
  export { registerGenerateLocatorsTool } from "./generate-locators.mjs";
24
+ export { registerGetElementTextTool } from "./get-element-text.mjs";
25
+ export { registerListDevicesTool } from "./list-devices.mjs";
26
+ export { registerSelectDeviceTool } from "./select-device.mjs";
24
27
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,uBAAsB;AACrD,OAAO,EAAE,sBAAsB,EAAE,yBAAwB;AACzD,OAAO,EAAE,sBAAsB,EAAE,0BAAyB;AAC1D,OAAO,EAAE,0BAA0B,EAAE,8BAA6B;AAClE,OAAO,EAAE,gBAAgB,EAAE,wBAAuB;AAClD,OAAO,EAAE,iBAAiB,EAAE,oBAAmB;AAC/C,OAAO,EAAE,qBAAqB,EAAE,yBAAwB;AACxD,OAAO,EAAE,mBAAmB,EAAE,uBAAsB;AACpD,OAAO,EAAE,oBAAoB,EAAE,wBAAuB;AACtD,OAAO,EAAE,sBAAsB,EAAE,0BAAyB;AAC1D,OAAO,EAAE,mBAAmB,EAAE,uBAAsB;AACpD,OAAO,EAAE,oBAAoB,EAAE,wBAAuB;AACtD,OAAO,EAAE,uBAAuB,EAAE,2BAA0B;AAC5D,OAAO,EAAE,2BAA2B,EAAE,+BAA8B;AACpE,OAAO,EAAE,wBAAwB,EAAE,4BAA2B;AAC9D,OAAO,EAAE,gBAAgB,EAAE,mBAAkB;AAC7C,OAAO,EAAE,2BAA2B,EAAE,gCAA+B;AACrE,OAAO,EAAE,qBAAqB,EAAE,yBAAwB;AACxD,OAAO,EAAE,sBAAsB,EAAE,0BAAyB;AAC1D,OAAO,EAAE,mBAAmB,EAAE,sBAAqB;AACnD,OAAO,EAAE,qBAAqB,EAAE,wBAAuB;AACvD,OAAO,EAAE,2BAA2B,EAAE,+BAA8B;AACpE,OAAO,EAAE,4BAA4B,EAAE,gCAA+B"}
1
+ {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,uBAAsB;AACrD,OAAO,EAAE,sBAAsB,EAAE,yBAAwB;AACzD,OAAO,EAAE,sBAAsB,EAAE,0BAAyB;AAC1D,OAAO,EAAE,0BAA0B,EAAE,8BAA6B;AAClE,OAAO,EAAE,gBAAgB,EAAE,wBAAuB;AAClD,OAAO,EAAE,iBAAiB,EAAE,oBAAmB;AAC/C,OAAO,EAAE,qBAAqB,EAAE,yBAAwB;AACxD,OAAO,EAAE,mBAAmB,EAAE,uBAAsB;AACpD,OAAO,EAAE,oBAAoB,EAAE,wBAAuB;AACtD,OAAO,EAAE,sBAAsB,EAAE,0BAAyB;AAC1D,OAAO,EAAE,mBAAmB,EAAE,uBAAsB;AACpD,OAAO,EAAE,oBAAoB,EAAE,wBAAuB;AACtD,OAAO,EAAE,uBAAuB,EAAE,2BAA0B;AAC5D,OAAO,EAAE,2BAA2B,EAAE,+BAA8B;AACpE,OAAO,EAAE,wBAAwB,EAAE,4BAA2B;AAC9D,OAAO,EAAE,gBAAgB,EAAE,mBAAkB;AAC7C,OAAO,EAAE,2BAA2B,EAAE,gCAA+B;AACrE,OAAO,EAAE,qBAAqB,EAAE,yBAAwB;AACxD,OAAO,EAAE,sBAAsB,EAAE,0BAAyB;AAC1D,OAAO,EAAE,mBAAmB,EAAE,sBAAqB;AACnD,OAAO,EAAE,qBAAqB,EAAE,wBAAuB;AACvD,OAAO,EAAE,2BAA2B,EAAE,+BAA8B;AACpE,OAAO,EAAE,4BAA4B,EAAE,gCAA+B;AACtE,OAAO,EAAE,0BAA0B,EAAE,+BAA8B;AACnE,OAAO,EAAE,uBAAuB,EAAE,2BAA0B;AAC5D,OAAO,EAAE,wBAAwB,EAAE,4BAA2B"}
@@ -21,4 +21,7 @@ export { registerContextTool } from "./context.mjs";
21
21
  export { registerClipboardTool } from "./clipboard.mjs";
22
22
  export { registerScreenRecordingTool } from "./screen-recording.mjs";
23
23
  export { registerGenerateLocatorsTool } from "./generate-locators.mjs";
24
+ export { registerGetElementTextTool } from "./get-element-text.mjs";
25
+ export { registerListDevicesTool } from "./list-devices.mjs";
26
+ export { registerSelectDeviceTool } from "./select-device.mjs";
24
27
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,uBAAsB;AACrD,OAAO,EAAE,sBAAsB,EAAE,yBAAwB;AACzD,OAAO,EAAE,sBAAsB,EAAE,0BAAyB;AAC1D,OAAO,EAAE,0BAA0B,EAAE,8BAA6B;AAClE,OAAO,EAAE,gBAAgB,EAAE,wBAAuB;AAClD,OAAO,EAAE,iBAAiB,EAAE,oBAAmB;AAC/C,OAAO,EAAE,qBAAqB,EAAE,yBAAwB;AACxD,OAAO,EAAE,mBAAmB,EAAE,uBAAsB;AACpD,OAAO,EAAE,oBAAoB,EAAE,wBAAuB;AACtD,OAAO,EAAE,sBAAsB,EAAE,0BAAyB;AAC1D,OAAO,EAAE,mBAAmB,EAAE,uBAAsB;AACpD,OAAO,EAAE,oBAAoB,EAAE,wBAAuB;AACtD,OAAO,EAAE,uBAAuB,EAAE,2BAA0B;AAC5D,OAAO,EAAE,2BAA2B,EAAE,+BAA8B;AACpE,OAAO,EAAE,wBAAwB,EAAE,4BAA2B;AAC9D,OAAO,EAAE,gBAAgB,EAAE,mBAAkB;AAC7C,OAAO,EAAE,2BAA2B,EAAE,gCAA+B;AACrE,OAAO,EAAE,qBAAqB,EAAE,yBAAwB;AACxD,OAAO,EAAE,sBAAsB,EAAE,0BAAyB;AAC1D,OAAO,EAAE,mBAAmB,EAAE,sBAAqB;AACnD,OAAO,EAAE,qBAAqB,EAAE,wBAAuB;AACvD,OAAO,EAAE,2BAA2B,EAAE,+BAA8B;AACpE,OAAO,EAAE,4BAA4B,EAAE,gCAA+B","sourcesContent":["export { registerSnapshotTool } from './snapshot.js';\nexport { registerScreenshotTool } from './screenshot.js';\nexport { registerTapElementTool } from './tap-element.js';\nexport { registerTapCoordinatesTool } from './tap-coordinates.js';\nexport { registerTypeTool } from './type-text.js';\nexport { registerSwipeTool } from './swipe.js';\nexport { registerLongPressTool } from './long-press.js';\nexport { registerWaitForTool } from './wait-for.js';\nexport { registerAppStateTool } from './app-state.js';\nexport { registerDeviceInfoTool } from './device-info.js';\nexport { registerOpenAppTool } from './open-app.js';\nexport { registerCloseAppTool } from './close-app.js';\nexport { registerPressButtonTool } from './press-button.js';\nexport { registerDismissKeyboardTool } from './dismiss-keyboard.js';\nexport { registerDismissAlertTool } from './dismiss-alert.js';\nexport { registerLogsTool } from './logs.js';\nexport { registerScrollToElementTool } from './scroll-to-element.js';\nexport { registerAlertTextTool } from './alert-text.js';\nexport { registerWindowSizeTool } from './window-size.js';\nexport { registerContextTool } from './context.js';\nexport { registerClipboardTool } from './clipboard.js';\nexport { registerScreenRecordingTool } from './screen-recording.js';\nexport { registerGenerateLocatorsTool } from './generate-locators.js';\n"]}
1
+ {"version":3,"file":"index.mjs","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,uBAAsB;AACrD,OAAO,EAAE,sBAAsB,EAAE,yBAAwB;AACzD,OAAO,EAAE,sBAAsB,EAAE,0BAAyB;AAC1D,OAAO,EAAE,0BAA0B,EAAE,8BAA6B;AAClE,OAAO,EAAE,gBAAgB,EAAE,wBAAuB;AAClD,OAAO,EAAE,iBAAiB,EAAE,oBAAmB;AAC/C,OAAO,EAAE,qBAAqB,EAAE,yBAAwB;AACxD,OAAO,EAAE,mBAAmB,EAAE,uBAAsB;AACpD,OAAO,EAAE,oBAAoB,EAAE,wBAAuB;AACtD,OAAO,EAAE,sBAAsB,EAAE,0BAAyB;AAC1D,OAAO,EAAE,mBAAmB,EAAE,uBAAsB;AACpD,OAAO,EAAE,oBAAoB,EAAE,wBAAuB;AACtD,OAAO,EAAE,uBAAuB,EAAE,2BAA0B;AAC5D,OAAO,EAAE,2BAA2B,EAAE,+BAA8B;AACpE,OAAO,EAAE,wBAAwB,EAAE,4BAA2B;AAC9D,OAAO,EAAE,gBAAgB,EAAE,mBAAkB;AAC7C,OAAO,EAAE,2BAA2B,EAAE,gCAA+B;AACrE,OAAO,EAAE,qBAAqB,EAAE,yBAAwB;AACxD,OAAO,EAAE,sBAAsB,EAAE,0BAAyB;AAC1D,OAAO,EAAE,mBAAmB,EAAE,sBAAqB;AACnD,OAAO,EAAE,qBAAqB,EAAE,wBAAuB;AACvD,OAAO,EAAE,2BAA2B,EAAE,+BAA8B;AACpE,OAAO,EAAE,4BAA4B,EAAE,gCAA+B;AACtE,OAAO,EAAE,0BAA0B,EAAE,+BAA8B;AACnE,OAAO,EAAE,uBAAuB,EAAE,2BAA0B;AAC5D,OAAO,EAAE,wBAAwB,EAAE,4BAA2B","sourcesContent":["export { registerSnapshotTool } from './snapshot.js';\nexport { registerScreenshotTool } from './screenshot.js';\nexport { registerTapElementTool } from './tap-element.js';\nexport { registerTapCoordinatesTool } from './tap-coordinates.js';\nexport { registerTypeTool } from './type-text.js';\nexport { registerSwipeTool } from './swipe.js';\nexport { registerLongPressTool } from './long-press.js';\nexport { registerWaitForTool } from './wait-for.js';\nexport { registerAppStateTool } from './app-state.js';\nexport { registerDeviceInfoTool } from './device-info.js';\nexport { registerOpenAppTool } from './open-app.js';\nexport { registerCloseAppTool } from './close-app.js';\nexport { registerPressButtonTool } from './press-button.js';\nexport { registerDismissKeyboardTool } from './dismiss-keyboard.js';\nexport { registerDismissAlertTool } from './dismiss-alert.js';\nexport { registerLogsTool } from './logs.js';\nexport { registerScrollToElementTool } from './scroll-to-element.js';\nexport { registerAlertTextTool } from './alert-text.js';\nexport { registerWindowSizeTool } from './window-size.js';\nexport { registerContextTool } from './context.js';\nexport { registerClipboardTool } from './clipboard.js';\nexport { registerScreenRecordingTool } from './screen-recording.js';\nexport { registerGenerateLocatorsTool } from './generate-locators.js';\nexport { registerGetElementTextTool } from './get-element-text.js';\nexport { registerListDevicesTool } from './list-devices.js';\nexport { registerSelectDeviceTool } from './select-device.js';\n"]}
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerListDevicesTool = registerListDevicesTool;
4
+ const shared_js_1 = require("./shared.cjs");
5
+ function registerListDevicesTool(server, backend) {
6
+ server.registerTool('device_list_devices', {
7
+ title: 'List Devices',
8
+ description: 'List all connected devices and simulators/emulators.',
9
+ inputSchema: {},
10
+ annotations: { readOnlyHint: true },
11
+ }, async () => {
12
+ try {
13
+ const devices = await backend.listDevices();
14
+ if (devices.length === 0) {
15
+ return {
16
+ content: [
17
+ {
18
+ type: 'text',
19
+ text: 'No connected devices found. Boot a simulator/emulator or connect a device.',
20
+ },
21
+ ],
22
+ };
23
+ }
24
+ const header = 'Platform\tDevice ID';
25
+ const rows = devices.map((d) => `${d.platform}\t${d.deviceId}`);
26
+ const text = [header, ...rows].join('\n');
27
+ return {
28
+ content: [{ type: 'text', text }],
29
+ };
30
+ }
31
+ catch (error) {
32
+ return (0, shared_js_1.errorResult)(error);
33
+ }
34
+ });
35
+ }
36
+ //# sourceMappingURL=list-devices.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-devices.cjs","sourceRoot":"","sources":["../../src/tools/list-devices.ts"],"names":[],"mappings":";;AAKA,0DAuCC;AA1CD,4CAA0C;AAG1C,SAAgB,uBAAuB,CACrC,MAAiB,EACjB,OAA0B;IAE1B,MAAM,CAAC,YAAY,CACjB,qBAAqB,EACrB;QACE,KAAK,EAAE,cAAc;QACrB,WAAW,EAAE,sDAAsD;QACnE,WAAW,EAAE,EAAE;QACf,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE;KACpC,EACD,KAAK,IAAI,EAAE;QACT,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,WAAW,EAAE,CAAC;YAE5C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACzB,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAe;4BACrB,IAAI,EAAE,4EAA4E;yBACnF;qBACF;iBACF,CAAC;YACJ,CAAC;YAED,MAAM,MAAM,GAAG,qBAAqB,CAAC;YACrC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;YAChE,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAE1C,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,CAAC;aAC3C,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,IAAA,uBAAW,EAAC,KAAK,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC","sourcesContent":["import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\n\nimport { errorResult } from './shared.js';\nimport type { LazyDeviceBackend } from '../backends/index.js';\n\nexport function registerListDevicesTool(\n server: McpServer,\n backend: LazyDeviceBackend,\n): void {\n server.registerTool(\n 'device_list_devices',\n {\n title: 'List Devices',\n description: 'List all connected devices and simulators/emulators.',\n inputSchema: {},\n annotations: { readOnlyHint: true },\n },\n async () => {\n try {\n const devices = await backend.listDevices();\n\n if (devices.length === 0) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'No connected devices found. Boot a simulator/emulator or connect a device.',\n },\n ],\n };\n }\n\n const header = 'Platform\\tDevice ID';\n const rows = devices.map((d) => `${d.platform}\\t${d.deviceId}`);\n const text = [header, ...rows].join('\\n');\n\n return {\n content: [{ type: 'text' as const, text }],\n };\n } catch (error) {\n return errorResult(error);\n }\n },\n );\n}\n"]}
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { LazyDeviceBackend } from "../backends/index.cjs";
3
+ export declare function registerListDevicesTool(server: McpServer, backend: LazyDeviceBackend): void;
4
+ //# sourceMappingURL=list-devices.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-devices.d.cts","sourceRoot":"","sources":["../../src/tools/list-devices.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,gDAAgD;AAGzE,OAAO,KAAK,EAAE,iBAAiB,EAAE,8BAA6B;AAE9D,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,iBAAiB,GACzB,IAAI,CAoCN"}
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { LazyDeviceBackend } from "../backends/index.mjs";
3
+ export declare function registerListDevicesTool(server: McpServer, backend: LazyDeviceBackend): void;
4
+ //# sourceMappingURL=list-devices.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-devices.d.mts","sourceRoot":"","sources":["../../src/tools/list-devices.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,gDAAgD;AAGzE,OAAO,KAAK,EAAE,iBAAiB,EAAE,8BAA6B;AAE9D,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,iBAAiB,GACzB,IAAI,CAoCN"}
@@ -0,0 +1,33 @@
1
+ import { errorResult } from "./shared.mjs";
2
+ export function registerListDevicesTool(server, backend) {
3
+ server.registerTool('device_list_devices', {
4
+ title: 'List Devices',
5
+ description: 'List all connected devices and simulators/emulators.',
6
+ inputSchema: {},
7
+ annotations: { readOnlyHint: true },
8
+ }, async () => {
9
+ try {
10
+ const devices = await backend.listDevices();
11
+ if (devices.length === 0) {
12
+ return {
13
+ content: [
14
+ {
15
+ type: 'text',
16
+ text: 'No connected devices found. Boot a simulator/emulator or connect a device.',
17
+ },
18
+ ],
19
+ };
20
+ }
21
+ const header = 'Platform\tDevice ID';
22
+ const rows = devices.map((d) => `${d.platform}\t${d.deviceId}`);
23
+ const text = [header, ...rows].join('\n');
24
+ return {
25
+ content: [{ type: 'text', text }],
26
+ };
27
+ }
28
+ catch (error) {
29
+ return errorResult(error);
30
+ }
31
+ });
32
+ }
33
+ //# sourceMappingURL=list-devices.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-devices.mjs","sourceRoot":"","sources":["../../src/tools/list-devices.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,qBAAoB;AAG1C,MAAM,UAAU,uBAAuB,CACrC,MAAiB,EACjB,OAA0B;IAE1B,MAAM,CAAC,YAAY,CACjB,qBAAqB,EACrB;QACE,KAAK,EAAE,cAAc;QACrB,WAAW,EAAE,sDAAsD;QACnE,WAAW,EAAE,EAAE;QACf,WAAW,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE;KACpC,EACD,KAAK,IAAI,EAAE;QACT,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,WAAW,EAAE,CAAC;YAE5C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACzB,OAAO;oBACL,OAAO,EAAE;wBACP;4BACE,IAAI,EAAE,MAAe;4BACrB,IAAI,EAAE,4EAA4E;yBACnF;qBACF;iBACF,CAAC;YACJ,CAAC;YAED,MAAM,MAAM,GAAG,qBAAqB,CAAC;YACrC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;YAChE,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAE1C,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,CAAC;aAC3C,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC","sourcesContent":["import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\n\nimport { errorResult } from './shared.js';\nimport type { LazyDeviceBackend } from '../backends/index.js';\n\nexport function registerListDevicesTool(\n server: McpServer,\n backend: LazyDeviceBackend,\n): void {\n server.registerTool(\n 'device_list_devices',\n {\n title: 'List Devices',\n description: 'List all connected devices and simulators/emulators.',\n inputSchema: {},\n annotations: { readOnlyHint: true },\n },\n async () => {\n try {\n const devices = await backend.listDevices();\n\n if (devices.length === 0) {\n return {\n content: [\n {\n type: 'text' as const,\n text: 'No connected devices found. Boot a simulator/emulator or connect a device.',\n },\n ],\n };\n }\n\n const header = 'Platform\\tDevice ID';\n const rows = devices.map((d) => `${d.platform}\\t${d.deviceId}`);\n const text = [header, ...rows].join('\\n');\n\n return {\n content: [{ type: 'text' as const, text }],\n };\n } catch (error) {\n return errorResult(error);\n }\n },\n );\n}\n"]}
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerSelectDeviceTool = registerSelectDeviceTool;
4
+ const zod_1 = require("zod");
5
+ const shared_js_1 = require("./shared.cjs");
6
+ function registerSelectDeviceTool(server, backend) {
7
+ server.registerTool('device_select_device', {
8
+ title: 'Select Device',
9
+ description: 'Select a device to use for this session. Call device_list_devices first to see available devices.',
10
+ inputSchema: {
11
+ deviceId: zod_1.z
12
+ .string()
13
+ .describe('Device ID to select (UDID for iOS, serial for Android)'),
14
+ },
15
+ }, async ({ deviceId }) => {
16
+ try {
17
+ backend.selectDevice(deviceId);
18
+ const info = await backend.getDeviceInfo();
19
+ return {
20
+ content: [
21
+ {
22
+ type: 'text',
23
+ text: [
24
+ `Selected device: ${info.name}`,
25
+ `Platform: ${info.platform}`,
26
+ `OS: ${info.osVersion}`,
27
+ `ID: ${info.deviceId}`,
28
+ ].join('\n'),
29
+ },
30
+ ],
31
+ };
32
+ }
33
+ catch (error) {
34
+ return (0, shared_js_1.errorResult)(error);
35
+ }
36
+ });
37
+ }
38
+ //# sourceMappingURL=select-device.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select-device.cjs","sourceRoot":"","sources":["../../src/tools/select-device.ts"],"names":[],"mappings":";;AAMA,4DAuCC;AA5CD,6BAAwB;AAExB,4CAA0C;AAG1C,SAAgB,wBAAwB,CACtC,MAAiB,EACjB,OAA0B;IAE1B,MAAM,CAAC,YAAY,CACjB,sBAAsB,EACtB;QACE,KAAK,EAAE,eAAe;QACtB,WAAW,EACT,mGAAmG;QACrG,WAAW,EAAE;YACX,QAAQ,EAAE,OAAC;iBACR,MAAM,EAAE;iBACR,QAAQ,CAAC,wDAAwD,CAAC;SACtE;KACF,EACD,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE;QACrB,IAAI,CAAC;YACH,OAAO,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;YAC/B,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,aAAa,EAAE,CAAC;YAE3C,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE;4BACJ,oBAAoB,IAAI,CAAC,IAAI,EAAE;4BAC/B,aAAa,IAAI,CAAC,QAAQ,EAAE;4BAC5B,OAAO,IAAI,CAAC,SAAS,EAAE;4BACvB,OAAO,IAAI,CAAC,QAAQ,EAAE;yBACvB,CAAC,IAAI,CAAC,IAAI,CAAC;qBACb;iBACF;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,IAAA,uBAAW,EAAC,KAAK,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC","sourcesContent":["import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\n\nimport { errorResult } from './shared.js';\nimport type { LazyDeviceBackend } from '../backends/index.js';\n\nexport function registerSelectDeviceTool(\n server: McpServer,\n backend: LazyDeviceBackend,\n): void {\n server.registerTool(\n 'device_select_device',\n {\n title: 'Select Device',\n description:\n 'Select a device to use for this session. Call device_list_devices first to see available devices.',\n inputSchema: {\n deviceId: z\n .string()\n .describe('Device ID to select (UDID for iOS, serial for Android)'),\n },\n },\n async ({ deviceId }) => {\n try {\n backend.selectDevice(deviceId);\n const info = await backend.getDeviceInfo();\n\n return {\n content: [\n {\n type: 'text' as const,\n text: [\n `Selected device: ${info.name}`,\n `Platform: ${info.platform}`,\n `OS: ${info.osVersion}`,\n `ID: ${info.deviceId}`,\n ].join('\\n'),\n },\n ],\n };\n } catch (error) {\n return errorResult(error);\n }\n },\n );\n}\n"]}
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { LazyDeviceBackend } from "../backends/index.cjs";
3
+ export declare function registerSelectDeviceTool(server: McpServer, backend: LazyDeviceBackend): void;
4
+ //# sourceMappingURL=select-device.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select-device.d.cts","sourceRoot":"","sources":["../../src/tools/select-device.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,gDAAgD;AAIzE,OAAO,KAAK,EAAE,iBAAiB,EAAE,8BAA6B;AAE9D,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,iBAAiB,GACzB,IAAI,CAoCN"}
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { LazyDeviceBackend } from "../backends/index.mjs";
3
+ export declare function registerSelectDeviceTool(server: McpServer, backend: LazyDeviceBackend): void;
4
+ //# sourceMappingURL=select-device.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select-device.d.mts","sourceRoot":"","sources":["../../src/tools/select-device.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,gDAAgD;AAIzE,OAAO,KAAK,EAAE,iBAAiB,EAAE,8BAA6B;AAE9D,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,iBAAiB,GACzB,IAAI,CAoCN"}
@@ -0,0 +1,35 @@
1
+ import { z } from "zod";
2
+ import { errorResult } from "./shared.mjs";
3
+ export function registerSelectDeviceTool(server, backend) {
4
+ server.registerTool('device_select_device', {
5
+ title: 'Select Device',
6
+ description: 'Select a device to use for this session. Call device_list_devices first to see available devices.',
7
+ inputSchema: {
8
+ deviceId: z
9
+ .string()
10
+ .describe('Device ID to select (UDID for iOS, serial for Android)'),
11
+ },
12
+ }, async ({ deviceId }) => {
13
+ try {
14
+ backend.selectDevice(deviceId);
15
+ const info = await backend.getDeviceInfo();
16
+ return {
17
+ content: [
18
+ {
19
+ type: 'text',
20
+ text: [
21
+ `Selected device: ${info.name}`,
22
+ `Platform: ${info.platform}`,
23
+ `OS: ${info.osVersion}`,
24
+ `ID: ${info.deviceId}`,
25
+ ].join('\n'),
26
+ },
27
+ ],
28
+ };
29
+ }
30
+ catch (error) {
31
+ return errorResult(error);
32
+ }
33
+ });
34
+ }
35
+ //# sourceMappingURL=select-device.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select-device.mjs","sourceRoot":"","sources":["../../src/tools/select-device.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,YAAY;AAExB,OAAO,EAAE,WAAW,EAAE,qBAAoB;AAG1C,MAAM,UAAU,wBAAwB,CACtC,MAAiB,EACjB,OAA0B;IAE1B,MAAM,CAAC,YAAY,CACjB,sBAAsB,EACtB;QACE,KAAK,EAAE,eAAe;QACtB,WAAW,EACT,mGAAmG;QACrG,WAAW,EAAE;YACX,QAAQ,EAAE,CAAC;iBACR,MAAM,EAAE;iBACR,QAAQ,CAAC,wDAAwD,CAAC;SACtE;KACF,EACD,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE;QACrB,IAAI,CAAC;YACH,OAAO,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;YAC/B,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,aAAa,EAAE,CAAC;YAE3C,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE;4BACJ,oBAAoB,IAAI,CAAC,IAAI,EAAE;4BAC/B,aAAa,IAAI,CAAC,QAAQ,EAAE;4BAC5B,OAAO,IAAI,CAAC,SAAS,EAAE;4BACvB,OAAO,IAAI,CAAC,QAAQ,EAAE;yBACvB,CAAC,IAAI,CAAC,IAAI,CAAC;qBACb;iBACF;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC","sourcesContent":["import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { z } from 'zod';\n\nimport { errorResult } from './shared.js';\nimport type { LazyDeviceBackend } from '../backends/index.js';\n\nexport function registerSelectDeviceTool(\n server: McpServer,\n backend: LazyDeviceBackend,\n): void {\n server.registerTool(\n 'device_select_device',\n {\n title: 'Select Device',\n description:\n 'Select a device to use for this session. Call device_list_devices first to see available devices.',\n inputSchema: {\n deviceId: z\n .string()\n .describe('Device ID to select (UDID for iOS, serial for Android)'),\n },\n },\n async ({ deviceId }) => {\n try {\n backend.selectDevice(deviceId);\n const info = await backend.getDeviceInfo();\n\n return {\n content: [\n {\n type: 'text' as const,\n text: [\n `Selected device: ${info.name}`,\n `Platform: ${info.platform}`,\n `OS: ${info.osVersion}`,\n `ID: ${info.deviceId}`,\n ].join('\\n'),\n },\n ],\n };\n } catch (error) {\n return errorResult(error);\n }\n },\n );\n}\n"]}
@@ -21,17 +21,19 @@ async function exec(command, args, options) {
21
21
  timeout: timeoutMs,
22
22
  maxBuffer: 10 * 1024 * 1024, // 10MB — UI hierarchies can be large
23
23
  }, (error, stdout, stderr) => {
24
- if (error && !('code' in error)) {
25
- // Spawn failure (command not found, ENOENT, etc.)
26
- reject(new Error(error.message));
27
- return;
24
+ if (error) {
25
+ const errno = error;
26
+ if (errno.code === 'ENOENT' ||
27
+ errno.code === 'EACCES' ||
28
+ errno.code === 'EPERM') {
29
+ reject(new Error(error.message));
30
+ return;
31
+ }
28
32
  }
29
33
  resolve({
30
34
  stdout: stdout?.toString() ?? '',
31
35
  stderr: stderr?.toString() ?? '',
32
- exitCode: error?.code
33
- ? 1
34
- : (child.exitCode ?? 0),
36
+ exitCode: child.exitCode ?? (error ? 1 : 0),
35
37
  });
36
38
  });
37
39
  });
@@ -47,8 +49,8 @@ async function execStrict(command, args, options) {
47
49
  }
48
50
  async function isCommandAvailable(command) {
49
51
  try {
50
- await exec('which', [command], { timeoutMs: 5000 });
51
- return true;
52
+ const result = await exec('which', [command], { timeoutMs: 5000 });
53
+ return result.exitCode === 0;
52
54
  }
53
55
  catch {
54
56
  return false;
@@ -1 +1 @@
1
- {"version":3,"file":"exec.cjs","sourceRoot":"","sources":["../../src/utils/exec.ts"],"names":[],"mappings":";;AAiBA,oBAgCC;AAED,gCAcC;AAED,gDAOC;AA1ED,2DAA+D;AAQ/D;;;;;;;;GAQG;AACI,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,IAAA,6BAAQ,EACpB,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;AAEM,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;AAEM,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"]}
1
+ {"version":3,"file":"exec.cjs","sourceRoot":"","sources":["../../src/utils/exec.ts"],"names":[],"mappings":";;AAiBA,oBAoCC;AAED,gCAcC;AAED,gDAOC;AA9ED,2DAA+D;AAQ/D;;;;;;;;GAQG;AACI,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,IAAA,6BAAQ,EACpB,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,EAAE,CAAC;gBACV,MAAM,KAAK,GAAG,KAA8B,CAAC;gBAC7C,IACE,KAAK,CAAC,IAAI,KAAK,QAAQ;oBACvB,KAAK,CAAC,IAAI,KAAK,QAAQ;oBACvB,KAAK,CAAC,IAAI,KAAK,OAAO,EACtB,CAAC;oBACD,MAAM,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;oBACjC,OAAO;gBACT,CAAC;YACH,CAAC;YACD,OAAO,CAAC;gBACN,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAChC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAChC,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;aAC5C,CAAC,CAAC;QACL,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAEM,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;AAEM,KAAK,UAAU,kBAAkB,CAAC,OAAe;IACtD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAK,EAAE,CAAC,CAAC;QACpE,OAAO,MAAM,CAAC,QAAQ,KAAK,CAAC,CAAC;IAC/B,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) {\n const errno = error as NodeJS.ErrnoException;\n if (\n errno.code === 'ENOENT' ||\n errno.code === 'EACCES' ||\n errno.code === 'EPERM'\n ) {\n reject(new Error(error.message));\n return;\n }\n }\n resolve({\n stdout: stdout?.toString() ?? '',\n stderr: stderr?.toString() ?? '',\n exitCode: child.exitCode ?? (error ? 1 : 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 const result = await exec('which', [command], { timeoutMs: 5_000 });\n return result.exitCode === 0;\n } catch {\n return false;\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"exec.d.cts","sourceRoot":"","sources":["../../src/utils/exec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,eAAe,EAAE,2BAA2B;AAE/D,MAAM,MAAM,UAAU,GAAG;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAsB,IAAI,CACxB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE,eAAe,GAAG;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACjD,OAAO,CAAC,UAAU,CAAC,CA4BrB;AAED,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE,eAAe,GAAG;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACjD,OAAO,CAAC,MAAM,CAAC,CAUjB;AAED,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO1E"}
1
+ {"version":3,"file":"exec.d.cts","sourceRoot":"","sources":["../../src/utils/exec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,eAAe,EAAE,2BAA2B;AAE/D,MAAM,MAAM,UAAU,GAAG;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAsB,IAAI,CACxB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE,eAAe,GAAG;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACjD,OAAO,CAAC,UAAU,CAAC,CAgCrB;AAED,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE,eAAe,GAAG;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACjD,OAAO,CAAC,MAAM,CAAC,CAUjB;AAED,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO1E"}
@@ -1 +1 @@
1
- {"version":3,"file":"exec.d.mts","sourceRoot":"","sources":["../../src/utils/exec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,eAAe,EAAE,2BAA2B;AAE/D,MAAM,MAAM,UAAU,GAAG;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAsB,IAAI,CACxB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE,eAAe,GAAG;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACjD,OAAO,CAAC,UAAU,CAAC,CA4BrB;AAED,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE,eAAe,GAAG;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACjD,OAAO,CAAC,MAAM,CAAC,CAUjB;AAED,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO1E"}
1
+ {"version":3,"file":"exec.d.mts","sourceRoot":"","sources":["../../src/utils/exec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,eAAe,EAAE,2BAA2B;AAE/D,MAAM,MAAM,UAAU,GAAG;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAsB,IAAI,CACxB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE,eAAe,GAAG;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACjD,OAAO,CAAC,UAAU,CAAC,CAgCrB;AAED,wBAAsB,UAAU,CAC9B,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,CAAC,EAAE,eAAe,GAAG;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GACjD,OAAO,CAAC,MAAM,CAAC,CAUjB;AAED,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO1E"}
@@ -16,17 +16,19 @@ export async function exec(command, args, options) {
16
16
  timeout: timeoutMs,
17
17
  maxBuffer: 10 * 1024 * 1024, // 10MB — UI hierarchies can be large
18
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;
19
+ if (error) {
20
+ const errno = error;
21
+ if (errno.code === 'ENOENT' ||
22
+ errno.code === 'EACCES' ||
23
+ errno.code === 'EPERM') {
24
+ reject(new Error(error.message));
25
+ return;
26
+ }
23
27
  }
24
28
  resolve({
25
29
  stdout: stdout?.toString() ?? '',
26
30
  stderr: stderr?.toString() ?? '',
27
- exitCode: error?.code
28
- ? 1
29
- : (child.exitCode ?? 0),
31
+ exitCode: child.exitCode ?? (error ? 1 : 0),
30
32
  });
31
33
  });
32
34
  });
@@ -42,8 +44,8 @@ export async function execStrict(command, args, options) {
42
44
  }
43
45
  export async function isCommandAvailable(command) {
44
46
  try {
45
- await exec('which', [command], { timeoutMs: 5000 });
46
- return true;
47
+ const result = await exec('which', [command], { timeoutMs: 5000 });
48
+ return result.exitCode === 0;
47
49
  }
48
50
  catch {
49
51
  return false;
@@ -1 +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"]}
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,EAAE,CAAC;gBACV,MAAM,KAAK,GAAG,KAA8B,CAAC;gBAC7C,IACE,KAAK,CAAC,IAAI,KAAK,QAAQ;oBACvB,KAAK,CAAC,IAAI,KAAK,QAAQ;oBACvB,KAAK,CAAC,IAAI,KAAK,OAAO,EACtB,CAAC;oBACD,MAAM,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;oBACjC,OAAO;gBACT,CAAC;YACH,CAAC;YACD,OAAO,CAAC;gBACN,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAChC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAChC,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;aAC5C,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,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAK,EAAE,CAAC,CAAC;QACpE,OAAO,MAAM,CAAC,QAAQ,KAAK,CAAC,CAAC;IAC/B,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) {\n const errno = error as NodeJS.ErrnoException;\n if (\n errno.code === 'ENOENT' ||\n errno.code === 'EACCES' ||\n errno.code === 'EPERM'\n ) {\n reject(new Error(error.message));\n return;\n }\n }\n resolve({\n stdout: stdout?.toString() ?? '',\n stderr: stderr?.toString() ?? '',\n exitCode: child.exitCode ?? (error ? 1 : 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 const result = await exec('which', [command], { timeoutMs: 5_000 });\n return result.exitCode === 0;\n } catch {\n return false;\n }\n}\n"]}