execbro 0.0.1-reserve → 1.10.13
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/LICENSE +32 -0
- package/README.md +496 -0
- package/build/__tests__/helpers/fake-cdp-server.d.ts +56 -0
- package/build/__tests__/helpers/fake-cdp-server.d.ts.map +1 -0
- package/build/__tests__/helpers/fake-cdp-server.js +120 -0
- package/build/__tests__/helpers/fake-cdp-server.js.map +1 -0
- package/build/__tests__/helpers/tap-test-helpers.d.ts +36 -0
- package/build/__tests__/helpers/tap-test-helpers.d.ts.map +1 -0
- package/build/__tests__/helpers/tap-test-helpers.js +130 -0
- package/build/__tests__/helpers/tap-test-helpers.js.map +1 -0
- package/build/__tests__/integration/auto-reconnect.test.d.ts +2 -0
- package/build/__tests__/integration/auto-reconnect.test.d.ts.map +1 -0
- package/build/__tests__/integration/auto-reconnect.test.js +118 -0
- package/build/__tests__/integration/auto-reconnect.test.js.map +1 -0
- package/build/__tests__/integration/connection-health.test.d.ts +2 -0
- package/build/__tests__/integration/connection-health.test.d.ts.map +1 -0
- package/build/__tests__/integration/connection-health.test.js +157 -0
- package/build/__tests__/integration/connection-health.test.js.map +1 -0
- package/build/__tests__/integration/execute-in-app.test.d.ts +2 -0
- package/build/__tests__/integration/execute-in-app.test.d.ts.map +1 -0
- package/build/__tests__/integration/execute-in-app.test.js +124 -0
- package/build/__tests__/integration/execute-in-app.test.js.map +1 -0
- package/build/__tests__/integration/per-call-timeout.test.d.ts +2 -0
- package/build/__tests__/integration/per-call-timeout.test.d.ts.map +1 -0
- package/build/__tests__/integration/per-call-timeout.test.js +103 -0
- package/build/__tests__/integration/per-call-timeout.test.js.map +1 -0
- package/build/__tests__/integration/tap-strategies.test.d.ts +2 -0
- package/build/__tests__/integration/tap-strategies.test.d.ts.map +1 -0
- package/build/__tests__/integration/tap-strategies.test.js +330 -0
- package/build/__tests__/integration/tap-strategies.test.js.map +1 -0
- package/build/__tests__/integration/tools.test.d.ts +2 -0
- package/build/__tests__/integration/tools.test.d.ts.map +1 -0
- package/build/__tests__/integration/tools.test.js +227 -0
- package/build/__tests__/integration/tools.test.js.map +1 -0
- package/build/__tests__/setup.d.ts +2 -0
- package/build/__tests__/setup.d.ts.map +1 -0
- package/build/__tests__/setup.js +15 -0
- package/build/__tests__/setup.js.map +1 -0
- package/build/__tests__/unit/androidEmulatorAvds.test.d.ts +2 -0
- package/build/__tests__/unit/androidEmulatorAvds.test.d.ts.map +1 -0
- package/build/__tests__/unit/androidEmulatorAvds.test.js +98 -0
- package/build/__tests__/unit/androidEmulatorAvds.test.js.map +1 -0
- package/build/__tests__/unit/buildToken.test.d.ts +2 -0
- package/build/__tests__/unit/buildToken.test.d.ts.map +1 -0
- package/build/__tests__/unit/buildToken.test.js +22 -0
- package/build/__tests__/unit/buildToken.test.js.map +1 -0
- package/build/__tests__/unit/bundle.test.d.ts +2 -0
- package/build/__tests__/unit/bundle.test.d.ts.map +1 -0
- package/build/__tests__/unit/bundle.test.js +53 -0
- package/build/__tests__/unit/bundle.test.js.map +1 -0
- package/build/__tests__/unit/burst.test.d.ts +2 -0
- package/build/__tests__/unit/burst.test.d.ts.map +1 -0
- package/build/__tests__/unit/burst.test.js +54 -0
- package/build/__tests__/unit/burst.test.js.map +1 -0
- package/build/__tests__/unit/connection-health.test.d.ts +2 -0
- package/build/__tests__/unit/connection-health.test.d.ts.map +1 -0
- package/build/__tests__/unit/connection-health.test.js +38 -0
- package/build/__tests__/unit/connection-health.test.js.map +1 -0
- package/build/__tests__/unit/debugGlobals.test.d.ts +2 -0
- package/build/__tests__/unit/debugGlobals.test.d.ts.map +1 -0
- package/build/__tests__/unit/debugGlobals.test.js +14 -0
- package/build/__tests__/unit/debugGlobals.test.js.map +1 -0
- package/build/__tests__/unit/deviceDiscovery.test.d.ts +2 -0
- package/build/__tests__/unit/deviceDiscovery.test.d.ts.map +1 -0
- package/build/__tests__/unit/deviceDiscovery.test.js +119 -0
- package/build/__tests__/unit/deviceDiscovery.test.js.map +1 -0
- package/build/__tests__/unit/deviceResolver.test.d.ts +2 -0
- package/build/__tests__/unit/deviceResolver.test.d.ts.map +1 -0
- package/build/__tests__/unit/deviceResolver.test.js +261 -0
- package/build/__tests__/unit/deviceResolver.test.js.map +1 -0
- package/build/__tests__/unit/escapeNonAscii.test.d.ts +2 -0
- package/build/__tests__/unit/escapeNonAscii.test.d.ts.map +1 -0
- package/build/__tests__/unit/escapeNonAscii.test.js +76 -0
- package/build/__tests__/unit/escapeNonAscii.test.js.map +1 -0
- package/build/__tests__/unit/exec.test.d.ts +2 -0
- package/build/__tests__/unit/exec.test.d.ts.map +1 -0
- package/build/__tests__/unit/exec.test.js +54 -0
- package/build/__tests__/unit/exec.test.js.map +1 -0
- package/build/__tests__/unit/executor.test.d.ts +2 -0
- package/build/__tests__/unit/executor.test.d.ts.map +1 -0
- package/build/__tests__/unit/executor.test.js +220 -0
- package/build/__tests__/unit/executor.test.js.map +1 -0
- package/build/__tests__/unit/failureArtifact.test.d.ts +2 -0
- package/build/__tests__/unit/failureArtifact.test.d.ts.map +1 -0
- package/build/__tests__/unit/failureArtifact.test.js +207 -0
- package/build/__tests__/unit/failureArtifact.test.js.map +1 -0
- package/build/__tests__/unit/fastRefreshRecorder.test.d.ts +2 -0
- package/build/__tests__/unit/fastRefreshRecorder.test.d.ts.map +1 -0
- package/build/__tests__/unit/fastRefreshRecorder.test.js +68 -0
- package/build/__tests__/unit/fastRefreshRecorder.test.js.map +1 -0
- package/build/__tests__/unit/fastRefreshTools.test.d.ts +2 -0
- package/build/__tests__/unit/fastRefreshTools.test.d.ts.map +1 -0
- package/build/__tests__/unit/fastRefreshTools.test.js +112 -0
- package/build/__tests__/unit/fastRefreshTools.test.js.map +1 -0
- package/build/__tests__/unit/fiberMatch.test.d.ts +2 -0
- package/build/__tests__/unit/fiberMatch.test.d.ts.map +1 -0
- package/build/__tests__/unit/fiberMatch.test.js +114 -0
- package/build/__tests__/unit/fiberMatch.test.js.map +1 -0
- package/build/__tests__/unit/fingerprint.test.d.ts +2 -0
- package/build/__tests__/unit/fingerprint.test.d.ts.map +1 -0
- package/build/__tests__/unit/fingerprint.test.js +99 -0
- package/build/__tests__/unit/fingerprint.test.js.map +1 -0
- package/build/__tests__/unit/focusedInput.test.d.ts +2 -0
- package/build/__tests__/unit/focusedInput.test.d.ts.map +1 -0
- package/build/__tests__/unit/focusedInput.test.js +58 -0
- package/build/__tests__/unit/focusedInput.test.js.map +1 -0
- package/build/__tests__/unit/focusedInputTools.test.d.ts +2 -0
- package/build/__tests__/unit/focusedInputTools.test.d.ts.map +1 -0
- package/build/__tests__/unit/focusedInputTools.test.js +79 -0
- package/build/__tests__/unit/focusedInputTools.test.js.map +1 -0
- package/build/__tests__/unit/iconSemantics.test.d.ts +2 -0
- package/build/__tests__/unit/iconSemantics.test.d.ts.map +1 -0
- package/build/__tests__/unit/iconSemantics.test.js +80 -0
- package/build/__tests__/unit/iconSemantics.test.js.map +1 -0
- package/build/__tests__/unit/imageBuffer.test.d.ts +2 -0
- package/build/__tests__/unit/imageBuffer.test.d.ts.map +1 -0
- package/build/__tests__/unit/imageBuffer.test.js +128 -0
- package/build/__tests__/unit/imageBuffer.test.js.map +1 -0
- package/build/__tests__/unit/logs.test.d.ts +2 -0
- package/build/__tests__/unit/logs.test.d.ts.map +1 -0
- package/build/__tests__/unit/logs.test.js +90 -0
- package/build/__tests__/unit/logs.test.js.map +1 -0
- package/build/__tests__/unit/measureComponent.test.d.ts +2 -0
- package/build/__tests__/unit/measureComponent.test.d.ts.map +1 -0
- package/build/__tests__/unit/measureComponent.test.js +66 -0
- package/build/__tests__/unit/measureComponent.test.js.map +1 -0
- package/build/__tests__/unit/measureComponentTools.test.d.ts +2 -0
- package/build/__tests__/unit/measureComponentTools.test.d.ts.map +1 -0
- package/build/__tests__/unit/measureComponentTools.test.js +94 -0
- package/build/__tests__/unit/measureComponentTools.test.js.map +1 -0
- package/build/__tests__/unit/metro.test.d.ts +2 -0
- package/build/__tests__/unit/metro.test.d.ts.map +1 -0
- package/build/__tests__/unit/metro.test.js +61 -0
- package/build/__tests__/unit/metro.test.js.map +1 -0
- package/build/__tests__/unit/network.test.d.ts +2 -0
- package/build/__tests__/unit/network.test.d.ts.map +1 -0
- package/build/__tests__/unit/network.test.js +102 -0
- package/build/__tests__/unit/network.test.js.map +1 -0
- package/build/__tests__/unit/networkInterceptor.test.d.ts +2 -0
- package/build/__tests__/unit/networkInterceptor.test.d.ts.map +1 -0
- package/build/__tests__/unit/networkInterceptor.test.js +104 -0
- package/build/__tests__/unit/networkInterceptor.test.js.map +1 -0
- package/build/__tests__/unit/probe.test.d.ts +2 -0
- package/build/__tests__/unit/probe.test.d.ts.map +1 -0
- package/build/__tests__/unit/probe.test.js +62 -0
- package/build/__tests__/unit/probe.test.js.map +1 -0
- package/build/__tests__/unit/rnGlobalsBootstrap.test.d.ts +2 -0
- package/build/__tests__/unit/rnGlobalsBootstrap.test.d.ts.map +1 -0
- package/build/__tests__/unit/rnGlobalsBootstrap.test.js +29 -0
- package/build/__tests__/unit/rnGlobalsBootstrap.test.js.map +1 -0
- package/build/__tests__/unit/screenState.test.d.ts +2 -0
- package/build/__tests__/unit/screenState.test.d.ts.map +1 -0
- package/build/__tests__/unit/screenState.test.js +384 -0
- package/build/__tests__/unit/screenState.test.js.map +1 -0
- package/build/__tests__/unit/screenshot-diff.test.d.ts +2 -0
- package/build/__tests__/unit/screenshot-diff.test.d.ts.map +1 -0
- package/build/__tests__/unit/screenshot-diff.test.js +97 -0
- package/build/__tests__/unit/screenshot-diff.test.js.map +1 -0
- package/build/__tests__/unit/tap-evidence.test.d.ts +2 -0
- package/build/__tests__/unit/tap-evidence.test.d.ts.map +1 -0
- package/build/__tests__/unit/tap-evidence.test.js +89 -0
- package/build/__tests__/unit/tap-evidence.test.js.map +1 -0
- package/build/__tests__/unit/tap.test.d.ts +2 -0
- package/build/__tests__/unit/tap.test.d.ts.map +1 -0
- package/build/__tests__/unit/tap.test.js +767 -0
- package/build/__tests__/unit/tap.test.js.map +1 -0
- package/build/__tests__/unit/timeoutClamp.test.d.ts +2 -0
- package/build/__tests__/unit/timeoutClamp.test.d.ts.map +1 -0
- package/build/__tests__/unit/timeoutClamp.test.js +17 -0
- package/build/__tests__/unit/timeoutClamp.test.js.map +1 -0
- package/build/__tests__/unit/toolDescriptions.test.d.ts +2 -0
- package/build/__tests__/unit/toolDescriptions.test.d.ts.map +1 -0
- package/build/__tests__/unit/toolDescriptions.test.js +145 -0
- package/build/__tests__/unit/toolDescriptions.test.js.map +1 -0
- package/build/__tests__/unit/transportClassify.test.d.ts +2 -0
- package/build/__tests__/unit/transportClassify.test.d.ts.map +1 -0
- package/build/__tests__/unit/transportClassify.test.js +60 -0
- package/build/__tests__/unit/transportClassify.test.js.map +1 -0
- package/build/__tests__/unit/verifyAction.test.d.ts +2 -0
- package/build/__tests__/unit/verifyAction.test.d.ts.map +1 -0
- package/build/__tests__/unit/verifyAction.test.js +26 -0
- package/build/__tests__/unit/verifyAction.test.js.map +1 -0
- package/build/core/android.d.ts +299 -0
- package/build/core/android.d.ts.map +1 -0
- package/build/core/android.js +1582 -0
- package/build/core/android.js.map +1 -0
- package/build/core/appDetection.d.ts +20 -0
- package/build/core/appDetection.d.ts.map +1 -0
- package/build/core/appDetection.js +167 -0
- package/build/core/appDetection.js.map +1 -0
- package/build/core/bundle.d.ts +49 -0
- package/build/core/bundle.d.ts.map +1 -0
- package/build/core/bundle.js +356 -0
- package/build/core/bundle.js.map +1 -0
- package/build/core/componentSearch.d.ts +41 -0
- package/build/core/componentSearch.d.ts.map +1 -0
- package/build/core/componentSearch.js +588 -0
- package/build/core/componentSearch.js.map +1 -0
- package/build/core/componentTree.d.ts +13 -0
- package/build/core/componentTree.d.ts.map +1 -0
- package/build/core/componentTree.js +275 -0
- package/build/core/componentTree.js.map +1 -0
- package/build/core/config.d.ts +9 -0
- package/build/core/config.d.ts.map +1 -0
- package/build/core/config.js +28 -0
- package/build/core/config.js.map +1 -0
- package/build/core/connection.d.ts +102 -0
- package/build/core/connection.d.ts.map +1 -0
- package/build/core/connection.js +1710 -0
- package/build/core/connection.js.map +1 -0
- package/build/core/connectionState.d.ts +122 -0
- package/build/core/connectionState.d.ts.map +1 -0
- package/build/core/connectionState.js +313 -0
- package/build/core/connectionState.js.map +1 -0
- package/build/core/debugGlobals.d.ts +6 -0
- package/build/core/debugGlobals.d.ts.map +1 -0
- package/build/core/debugGlobals.js +370 -0
- package/build/core/debugGlobals.js.map +1 -0
- package/build/core/deviceDiscovery.d.ts +52 -0
- package/build/core/deviceDiscovery.d.ts.map +1 -0
- package/build/core/deviceDiscovery.js +74 -0
- package/build/core/deviceDiscovery.js.map +1 -0
- package/build/core/deviceResolver.d.ts +44 -0
- package/build/core/deviceResolver.d.ts.map +1 -0
- package/build/core/deviceResolver.js +225 -0
- package/build/core/deviceResolver.js.map +1 -0
- package/build/core/errorScreenParser.d.ts +30 -0
- package/build/core/errorScreenParser.d.ts.map +1 -0
- package/build/core/errorScreenParser.js +198 -0
- package/build/core/errorScreenParser.js.map +1 -0
- package/build/core/errors.d.ts +14 -0
- package/build/core/errors.d.ts.map +1 -0
- package/build/core/errors.js +17 -0
- package/build/core/errors.js.map +1 -0
- package/build/core/escapeNonAscii.d.ts +18 -0
- package/build/core/escapeNonAscii.d.ts.map +1 -0
- package/build/core/escapeNonAscii.js +352 -0
- package/build/core/escapeNonAscii.js.map +1 -0
- package/build/core/exec.d.ts +22 -0
- package/build/core/exec.d.ts.map +1 -0
- package/build/core/exec.js +54 -0
- package/build/core/exec.js.map +1 -0
- package/build/core/executor.d.ts +9 -0
- package/build/core/executor.d.ts.map +1 -0
- package/build/core/executor.js +12 -0
- package/build/core/executor.js.map +1 -0
- package/build/core/failureArtifact.d.ts +120 -0
- package/build/core/failureArtifact.d.ts.map +1 -0
- package/build/core/failureArtifact.js +113 -0
- package/build/core/failureArtifact.js.map +1 -0
- package/build/core/fastRefreshRecorder.d.ts +23 -0
- package/build/core/fastRefreshRecorder.d.ts.map +1 -0
- package/build/core/fastRefreshRecorder.js +83 -0
- package/build/core/fastRefreshRecorder.js.map +1 -0
- package/build/core/fastRefreshTools.d.ts +19 -0
- package/build/core/fastRefreshTools.d.ts.map +1 -0
- package/build/core/fastRefreshTools.js +71 -0
- package/build/core/fastRefreshTools.js.map +1 -0
- package/build/core/feedback.d.ts +18 -0
- package/build/core/feedback.d.ts.map +1 -0
- package/build/core/feedback.js +73 -0
- package/build/core/feedback.js.map +1 -0
- package/build/core/fiberMatch.d.ts +25 -0
- package/build/core/fiberMatch.d.ts.map +1 -0
- package/build/core/fiberMatch.js +35 -0
- package/build/core/fiberMatch.js.map +1 -0
- package/build/core/fingerprint.d.ts +4 -0
- package/build/core/fingerprint.d.ts.map +1 -0
- package/build/core/fingerprint.js +47 -0
- package/build/core/fingerprint.js.map +1 -0
- package/build/core/focusedInput.d.ts +27 -0
- package/build/core/focusedInput.d.ts.map +1 -0
- package/build/core/focusedInput.js +110 -0
- package/build/core/focusedInput.js.map +1 -0
- package/build/core/focusedInputTools.d.ts +21 -0
- package/build/core/focusedInputTools.d.ts.map +1 -0
- package/build/core/focusedInputTools.js +51 -0
- package/build/core/focusedInputTools.js.map +1 -0
- package/build/core/format.d.ts +8 -0
- package/build/core/format.d.ts.map +1 -0
- package/build/core/format.js +34 -0
- package/build/core/format.js.map +1 -0
- package/build/core/guides.d.ts +21 -0
- package/build/core/guides.d.ts.map +1 -0
- package/build/core/guides.js +397 -0
- package/build/core/guides.js.map +1 -0
- package/build/core/iconSemantics.d.ts +18 -0
- package/build/core/iconSemantics.d.ts.map +1 -0
- package/build/core/iconSemantics.js +114 -0
- package/build/core/iconSemantics.js.map +1 -0
- package/build/core/imageBuffer.d.ts +37 -0
- package/build/core/imageBuffer.d.ts.map +1 -0
- package/build/core/imageBuffer.js +58 -0
- package/build/core/imageBuffer.js.map +1 -0
- package/build/core/index.d.ts +37 -0
- package/build/core/index.d.ts.map +1 -0
- package/build/core/index.js +66 -0
- package/build/core/index.js.map +1 -0
- package/build/core/inspector.d.ts +55 -0
- package/build/core/inspector.d.ts.map +1 -0
- package/build/core/inspector.js +603 -0
- package/build/core/inspector.js.map +1 -0
- package/build/core/ios.d.ts +263 -0
- package/build/core/ios.d.ts.map +1 -0
- package/build/core/ios.js +1705 -0
- package/build/core/ios.js.map +1 -0
- package/build/core/jsExecute.d.ts +62 -0
- package/build/core/jsExecute.d.ts.map +1 -0
- package/build/core/jsExecute.js +696 -0
- package/build/core/jsExecute.js.map +1 -0
- package/build/core/license.d.ts +48 -0
- package/build/core/license.d.ts.map +1 -0
- package/build/core/license.js +267 -0
- package/build/core/license.js.map +1 -0
- package/build/core/logbox.d.ts +58 -0
- package/build/core/logbox.d.ts.map +1 -0
- package/build/core/logbox.js +330 -0
- package/build/core/logbox.js.map +1 -0
- package/build/core/logs.d.ts +44 -0
- package/build/core/logs.d.ts.map +1 -0
- package/build/core/logs.js +149 -0
- package/build/core/logs.js.map +1 -0
- package/build/core/measureComponent.d.ts +19 -0
- package/build/core/measureComponent.d.ts.map +1 -0
- package/build/core/measureComponent.js +94 -0
- package/build/core/measureComponent.js.map +1 -0
- package/build/core/measureComponentTools.d.ts +6 -0
- package/build/core/measureComponentTools.d.ts.map +1 -0
- package/build/core/measureComponentTools.js +43 -0
- package/build/core/measureComponentTools.js.map +1 -0
- package/build/core/metro.d.ts +31 -0
- package/build/core/metro.d.ts.map +1 -0
- package/build/core/metro.js +124 -0
- package/build/core/metro.js.map +1 -0
- package/build/core/nativeOnlyHints.d.ts +14 -0
- package/build/core/nativeOnlyHints.d.ts.map +1 -0
- package/build/core/nativeOnlyHints.js +58 -0
- package/build/core/nativeOnlyHints.js.map +1 -0
- package/build/core/network.d.ts +43 -0
- package/build/core/network.d.ts.map +1 -0
- package/build/core/network.js +217 -0
- package/build/core/network.js.map +1 -0
- package/build/core/networkInterceptor.d.ts +30 -0
- package/build/core/networkInterceptor.d.ts.map +1 -0
- package/build/core/networkInterceptor.js +211 -0
- package/build/core/networkInterceptor.js.map +1 -0
- package/build/core/ocr.d.ts +87 -0
- package/build/core/ocr.d.ts.map +1 -0
- package/build/core/ocr.js +335 -0
- package/build/core/ocr.js.map +1 -0
- package/build/core/paths.d.ts +2 -0
- package/build/core/paths.d.ts.map +1 -0
- package/build/core/paths.js +28 -0
- package/build/core/paths.js.map +1 -0
- package/build/core/posthog.d.ts +5 -0
- package/build/core/posthog.d.ts.map +1 -0
- package/build/core/posthog.js +52 -0
- package/build/core/posthog.js.map +1 -0
- package/build/core/pressables.d.ts +46 -0
- package/build/core/pressables.d.ts.map +1 -0
- package/build/core/pressables.js +1719 -0
- package/build/core/pressables.js.map +1 -0
- package/build/core/probe.d.ts +12 -0
- package/build/core/probe.d.ts.map +1 -0
- package/build/core/probe.js +56 -0
- package/build/core/probe.js.map +1 -0
- package/build/core/redux.d.ts +22 -0
- package/build/core/redux.d.ts.map +1 -0
- package/build/core/redux.js +116 -0
- package/build/core/redux.js.map +1 -0
- package/build/core/register.d.ts +8 -0
- package/build/core/register.d.ts.map +1 -0
- package/build/core/register.js +202 -0
- package/build/core/register.js.map +1 -0
- package/build/core/rnGlobalsBootstrap.d.ts +16 -0
- package/build/core/rnGlobalsBootstrap.d.ts.map +1 -0
- package/build/core/rnGlobalsBootstrap.js +116 -0
- package/build/core/rnGlobalsBootstrap.js.map +1 -0
- package/build/core/screenLayout.d.ts +53 -0
- package/build/core/screenLayout.d.ts.map +1 -0
- package/build/core/screenLayout.js +878 -0
- package/build/core/screenLayout.js.map +1 -0
- package/build/core/screenState.d.ts +166 -0
- package/build/core/screenState.d.ts.map +1 -0
- package/build/core/screenState.js +1352 -0
- package/build/core/screenState.js.map +1 -0
- package/build/core/sdkBridge.d.ts +67 -0
- package/build/core/sdkBridge.d.ts.map +1 -0
- package/build/core/sdkBridge.js +148 -0
- package/build/core/sdkBridge.js.map +1 -0
- package/build/core/state.d.ts +37 -0
- package/build/core/state.d.ts.map +1 -0
- package/build/core/state.js +124 -0
- package/build/core/state.js.map +1 -0
- package/build/core/telemetry.d.ts +50 -0
- package/build/core/telemetry.d.ts.map +1 -0
- package/build/core/telemetry.js +653 -0
- package/build/core/telemetry.js.map +1 -0
- package/build/core/toolHelpers.d.ts +24 -0
- package/build/core/toolHelpers.d.ts.map +1 -0
- package/build/core/toolHelpers.js +118 -0
- package/build/core/toolHelpers.js.map +1 -0
- package/build/core/types.d.ts +162 -0
- package/build/core/types.d.ts.map +1 -0
- package/build/core/types.js +2 -0
- package/build/core/types.js.map +1 -0
- package/build/index.d.ts +4 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +132 -0
- package/build/index.js.map +1 -0
- package/build/pro/screenshot-diff.d.ts +10 -0
- package/build/pro/screenshot-diff.d.ts.map +1 -0
- package/build/pro/screenshot-diff.js +38 -0
- package/build/pro/screenshot-diff.js.map +1 -0
- package/build/pro/tap.d.ts +291 -0
- package/build/pro/tap.d.ts.map +1 -0
- package/build/pro/tap.js +1839 -0
- package/build/pro/tap.js.map +1 -0
- package/build/pro/verifyAction.d.ts +50 -0
- package/build/pro/verifyAction.d.ts.map +1 -0
- package/build/pro/verifyAction.js +285 -0
- package/build/pro/verifyAction.js.map +1 -0
- package/build/tools/_deviceArg.d.ts +45 -0
- package/build/tools/_deviceArg.d.ts.map +1 -0
- package/build/tools/_deviceArg.js +58 -0
- package/build/tools/_deviceArg.js.map +1 -0
- package/build/tools/accountTools.d.ts +46 -0
- package/build/tools/accountTools.d.ts.map +1 -0
- package/build/tools/accountTools.js +223 -0
- package/build/tools/accountTools.js.map +1 -0
- package/build/tools/bundleTools.d.ts +3 -0
- package/build/tools/bundleTools.d.ts.map +1 -0
- package/build/tools/bundleTools.js +375 -0
- package/build/tools/bundleTools.js.map +1 -0
- package/build/tools/componentTools.d.ts +3 -0
- package/build/tools/componentTools.d.ts.map +1 -0
- package/build/tools/componentTools.js +746 -0
- package/build/tools/componentTools.js.map +1 -0
- package/build/tools/connectionTools.d.ts +3 -0
- package/build/tools/connectionTools.d.ts.map +1 -0
- package/build/tools/connectionTools.js +625 -0
- package/build/tools/connectionTools.js.map +1 -0
- package/build/tools/deviceTools.d.ts +3 -0
- package/build/tools/deviceTools.d.ts.map +1 -0
- package/build/tools/deviceTools.js +423 -0
- package/build/tools/deviceTools.js.map +1 -0
- package/build/tools/executionTools.d.ts +3 -0
- package/build/tools/executionTools.d.ts.map +1 -0
- package/build/tools/executionTools.js +243 -0
- package/build/tools/executionTools.js.map +1 -0
- package/build/tools/interactionTools.d.ts +3 -0
- package/build/tools/interactionTools.d.ts.map +1 -0
- package/build/tools/interactionTools.js +539 -0
- package/build/tools/interactionTools.js.map +1 -0
- package/build/tools/logTools.d.ts +3 -0
- package/build/tools/logTools.d.ts.map +1 -0
- package/build/tools/logTools.js +371 -0
- package/build/tools/logTools.js.map +1 -0
- package/build/tools/metaTools.d.ts +7 -0
- package/build/tools/metaTools.d.ts.map +1 -0
- package/build/tools/metaTools.js +186 -0
- package/build/tools/metaTools.js.map +1 -0
- package/build/tools/networkTools.d.ts +3 -0
- package/build/tools/networkTools.d.ts.map +1 -0
- package/build/tools/networkTools.js +478 -0
- package/build/tools/networkTools.js.map +1 -0
- package/build/tools/reduxTools.d.ts +3 -0
- package/build/tools/reduxTools.d.ts.map +1 -0
- package/build/tools/reduxTools.js +83 -0
- package/build/tools/reduxTools.js.map +1 -0
- package/build/tools/screenshotTools.d.ts +3 -0
- package/build/tools/screenshotTools.d.ts.map +1 -0
- package/build/tools/screenshotTools.js +703 -0
- package/build/tools/screenshotTools.js.map +1 -0
- package/package.json +63 -2
|
@@ -0,0 +1,746 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { registerToolWithTelemetry } from "../core/register.js";
|
|
3
|
+
import { iconLabel } from "../core/iconSemantics.js";
|
|
4
|
+
import { getComponentTree, getScreenLayout, getPressableElements, getScreenState, formatScreenStateSummary, inspectComponent, findComponents, toggleElementInspector, getInspectorSelection, getInspectorSelectionAtPoint, inspectAtPoint, measureComponent, iosScreenshot, androidScreenshot, androidGetDensity, getDevicePixelRatio, getIOSSafeAreaTop, inferIOSDevicePixelRatio, metroMissingHintIfAbsent, hasMetro, getConnectedAppByDevice, getFirstConnectedApp, } from "../core/index.js";
|
|
5
|
+
import { primaryInteractionBanner } from "../core/toolHelpers.js";
|
|
6
|
+
function collectMetaNotes(r) {
|
|
7
|
+
const out = [];
|
|
8
|
+
if (r._meta?.reconnected) {
|
|
9
|
+
out.push(`[reconnected: transport error "${r._meta.transportError ?? "unknown"}" was auto-recovered]`);
|
|
10
|
+
}
|
|
11
|
+
if (r._meta?.timeoutClampedFrom !== undefined) {
|
|
12
|
+
out.push(`[warning: timeoutMs ${r._meta.timeoutClampedFrom} clamped to 120000]`);
|
|
13
|
+
}
|
|
14
|
+
return out;
|
|
15
|
+
}
|
|
16
|
+
export function registerComponentTools(server) {
|
|
17
|
+
// Tool: Get full screen layout (all components with layout styles)
|
|
18
|
+
registerToolWithTelemetry(server, "get_screen_layout", {
|
|
19
|
+
description: "Get a screen map showing visible components as an indented tree with actual screen positions. Uses measureInWindow for real coordinates and filters out off-screen components. Returns meaningful component names with text content and frame data (x,y width x height). Coordinates are in **points** (iOS) or **dp** (Android) — NOT screenshot pixels. Use tap(text=...) or tap(testID=...) to interact with discovered components. Use extended=true to include layout styles (padding, margin, flex, backgroundColor, etc.)." +
|
|
20
|
+
primaryInteractionBanner() + "\n" +
|
|
21
|
+
"PURPOSE: Quickest textual map of what is actually on screen right now — component names, positions, and text — so you can plan taps and inspections without guessing.\n" +
|
|
22
|
+
"WHEN TO USE: First step whenever the user asks \"what's on screen\", \"why is X covering Y\", or before tapping a visually ambiguous element.\n" +
|
|
23
|
+
"WORKFLOW: get_screen_layout -> find_components(pattern=\"...\") or inspect_component(componentName=\"...\") -> tap(testID=...) -> get_screen_layout again to confirm.\n" +
|
|
24
|
+
"LIMITATIONS: Coordinates are points/dp, not screenshot pixels — pass them to tap() which handles conversion, do not multiply by devicePixelRatio yourself.\n" +
|
|
25
|
+
"GOOD: get_screen_layout({ extended: true })\n" +
|
|
26
|
+
"BAD: get_screen_layout({ summary: true }) when you actually need to pick a specific element — summary hides the tree.\n" +
|
|
27
|
+
"SEE ALSO: call get_usage_guide(topic=\"layout\") for the full layout-check playbook.",
|
|
28
|
+
inputSchema: {
|
|
29
|
+
extended: z
|
|
30
|
+
.boolean()
|
|
31
|
+
.optional()
|
|
32
|
+
.default(false)
|
|
33
|
+
.describe("Include layout styles (padding, margin, flex, backgroundColor, borderRadius, etc.) for each component. Default: false for compact output."),
|
|
34
|
+
summary: z
|
|
35
|
+
.boolean()
|
|
36
|
+
.optional()
|
|
37
|
+
.default(false)
|
|
38
|
+
.describe("Return only component counts by name instead of full tree (default: false)"),
|
|
39
|
+
device: z.string().optional().describe("Target device name (substring match). Omit for default device. Run get_apps to see connected devices."),
|
|
40
|
+
timeoutMs: z.coerce
|
|
41
|
+
.number()
|
|
42
|
+
.optional()
|
|
43
|
+
.describe("Per-call timeout in milliseconds. Default: 5000; bumped to 15000 when extended=true. Hard cap: 120000.")
|
|
44
|
+
}
|
|
45
|
+
}, async ({ extended, summary, device, timeoutMs }) => {
|
|
46
|
+
if (!hasMetro()) {
|
|
47
|
+
const hint = await metroMissingHintIfAbsent("get_screen_layout");
|
|
48
|
+
return {
|
|
49
|
+
content: [{ type: "text", text: `Screen Layout unavailable.${hint}` }],
|
|
50
|
+
isError: true
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
const effectiveTimeoutMs = timeoutMs ?? (extended ? 15000 : 5000);
|
|
54
|
+
const result = await getScreenLayout({ extended, summary, device, timeoutMs: effectiveTimeoutMs });
|
|
55
|
+
const metaNotes = collectMetaNotes(result);
|
|
56
|
+
if (!result.success) {
|
|
57
|
+
const errText = metaNotes.length > 0 ? `Error: ${result.error}\n\n${metaNotes.join("\n")}` : `Error: ${result.error}`;
|
|
58
|
+
return {
|
|
59
|
+
content: [{ type: "text", text: errText }],
|
|
60
|
+
isError: true
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const body = metaNotes.length > 0
|
|
64
|
+
? `Screen Layout:\n\n${result.result}\n\n${metaNotes.join("\n")}`
|
|
65
|
+
: `Screen Layout:\n\n${result.result}`;
|
|
66
|
+
return { content: [{ type: "text", text: body }] };
|
|
67
|
+
});
|
|
68
|
+
// ============================================================================
|
|
69
|
+
// React Component Inspection Tools
|
|
70
|
+
// ============================================================================
|
|
71
|
+
// Tool: Get the React component tree
|
|
72
|
+
registerToolWithTelemetry(server, "get_component_tree", {
|
|
73
|
+
description: "Get the full React component tree from the running app. Shows the complete fiber hierarchy including providers, navigation wrappers, and internal components. For a screen overview with positions and text, use get_screen_layout instead. Use structureOnly=true for compact names-only output.\n" +
|
|
74
|
+
"PURPOSE: Expose the entire fiber tree — including providers, navigators, and off-screen subtrees — when get_screen_layout's visible-only view isn't enough.\n" +
|
|
75
|
+
"WHEN TO USE: Debugging context propagation, navigation wrappers, hidden modals, or when you need to understand the full React architecture.\n" +
|
|
76
|
+
"WORKFLOW: get_component_tree(structureOnly=true) for overview -> find_components for targeted lookup -> inspect_component for props/state.\n" +
|
|
77
|
+
"LIMITATIONS: Full trees can be large; always start with structureOnly=true. Ignores non-React native views. Minified builds return display names that may be opaque.\n" +
|
|
78
|
+
"GOOD: get_component_tree({ structureOnly: true })\n" +
|
|
79
|
+
"BAD: get_component_tree({ includeProps: true, includeStyles: true }) on a large app — likely hits response-size limits. Prefer inspect_component for specific nodes.\n" +
|
|
80
|
+
"SEE ALSO: call get_usage_guide(topic=\"inspect\") for the full component-inspect playbook.",
|
|
81
|
+
inputSchema: {
|
|
82
|
+
structureOnly: z
|
|
83
|
+
.boolean()
|
|
84
|
+
.optional()
|
|
85
|
+
.default(false)
|
|
86
|
+
.describe("Return ultra-compact structure with just component names (no props, styles, or paths). Use this first for overview, then drill down with inspect_component."),
|
|
87
|
+
maxDepth: z
|
|
88
|
+
.number()
|
|
89
|
+
.optional()
|
|
90
|
+
.describe("Maximum tree depth (default: 5000)"),
|
|
91
|
+
includeProps: z
|
|
92
|
+
.boolean()
|
|
93
|
+
.optional()
|
|
94
|
+
.default(false)
|
|
95
|
+
.describe("Include component props (excluding children and style). Ignored if structureOnly=true."),
|
|
96
|
+
includeStyles: z
|
|
97
|
+
.boolean()
|
|
98
|
+
.optional()
|
|
99
|
+
.default(false)
|
|
100
|
+
.describe("Include layout styles (padding, margin, flex, etc.). Ignored if structureOnly=true."),
|
|
101
|
+
hideInternals: z
|
|
102
|
+
.boolean()
|
|
103
|
+
.optional()
|
|
104
|
+
.default(true)
|
|
105
|
+
.describe("Hide internal RN components (RCTView, RNS*, Animated, etc.) for cleaner output (default: true)"),
|
|
106
|
+
format: z
|
|
107
|
+
.enum(["json", "tonl"])
|
|
108
|
+
.optional()
|
|
109
|
+
.default("tonl")
|
|
110
|
+
.describe("Output format: 'json' or 'tonl' (default, compact indented tree). Ignored if structureOnly=true."),
|
|
111
|
+
device: z.string().optional().describe("Target device name (substring match). Omit for default device. Run get_apps to see connected devices."),
|
|
112
|
+
timeoutMs: z.coerce
|
|
113
|
+
.number()
|
|
114
|
+
.optional()
|
|
115
|
+
.describe("Per-call timeout in milliseconds. Default: 5000. Hard cap: 120000.")
|
|
116
|
+
}
|
|
117
|
+
}, async ({ structureOnly, maxDepth, includeProps, includeStyles, hideInternals, format, device, timeoutMs }) => {
|
|
118
|
+
const effectiveTimeoutMs = timeoutMs ?? 5000;
|
|
119
|
+
const result = await getComponentTree({
|
|
120
|
+
structureOnly,
|
|
121
|
+
maxDepth,
|
|
122
|
+
includeProps,
|
|
123
|
+
includeStyles,
|
|
124
|
+
hideInternals,
|
|
125
|
+
format,
|
|
126
|
+
device,
|
|
127
|
+
timeoutMs: effectiveTimeoutMs
|
|
128
|
+
});
|
|
129
|
+
const metaNotes = collectMetaNotes(result);
|
|
130
|
+
if (!result.success) {
|
|
131
|
+
const errText = metaNotes.length > 0 ? `Error: ${result.error}\n\n${metaNotes.join("\n")}` : `Error: ${result.error}`;
|
|
132
|
+
return {
|
|
133
|
+
content: [{ type: "text", text: errText }],
|
|
134
|
+
isError: true
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
const body = metaNotes.length > 0
|
|
138
|
+
? `React Component Tree:\n\n${result.result}\n\n${metaNotes.join("\n")}`
|
|
139
|
+
: `React Component Tree:\n\n${result.result}`;
|
|
140
|
+
return { content: [{ type: "text", text: body }] };
|
|
141
|
+
});
|
|
142
|
+
// Tool: Get all pressable elements on screen
|
|
143
|
+
registerToolWithTelemetry(server, "get_pressable_elements", {
|
|
144
|
+
description: "Prefer get_screen_state after navigation (route + overlays + pressables in one call).\n\n" +
|
|
145
|
+
"Find all pressable (onPress) and input (TextInput) elements currently visible on screen. Returns component names, ready-to-tap center coordinates in SCREENSHOT PIXELS (same space as ios_screenshot/android_screenshot — pass directly to tap(x, y)), text labels, testID, accessibilityLabel, and a spatial nearbyText hint for icon-only buttons. Each element includes hasLabel (true if it contains text) and isInput (true for TextInput fields).\n" +
|
|
146
|
+
"HELPER — call before `tap` when you need to enumerate candidate elements before committing to a target; not a replacement for tap itself.\n" +
|
|
147
|
+
"PURPOSE: Produce a ready-to-tap inventory of every interactive element on screen with screenshot-pixel coordinates that tap(x, y) accepts directly.\n" +
|
|
148
|
+
"WHEN TO USE: Before tapping icon-only buttons, when text-based tap keeps failing, or to enumerate what the user can actually interact with.\n" +
|
|
149
|
+
"WORKFLOW: ios_screenshot / android_screenshot -> get_pressable_elements -> tap(testID=\"...\") or tap(x, y) using the center coordinates.\n" +
|
|
150
|
+
"LIMITATIONS: Visible-only (off-screen pressables are excluded). Requires a live React connection. Coordinates are in screenshot pixels — the tap tool converts to points internally.\n" +
|
|
151
|
+
"GOOD: get_pressable_elements()\n" +
|
|
152
|
+
"BAD: Calling when tap(testID=\"...\") already works — testID matching is faster and more stable.\n" +
|
|
153
|
+
"SEE ALSO: call get_usage_guide(topic=\"interact\") for the full interaction playbook.",
|
|
154
|
+
inputSchema: {
|
|
155
|
+
device: z.string().optional().describe("Target device name (substring match). Omit for default device. Run get_apps to see connected devices.")
|
|
156
|
+
}
|
|
157
|
+
}, async ({ device }) => {
|
|
158
|
+
const result = await getPressableElements({ device });
|
|
159
|
+
if (!result.success) {
|
|
160
|
+
return {
|
|
161
|
+
content: [
|
|
162
|
+
{
|
|
163
|
+
type: "text",
|
|
164
|
+
text: `Error: ${result.error}`
|
|
165
|
+
}
|
|
166
|
+
],
|
|
167
|
+
isError: true
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
const elements = result.parsedElements;
|
|
171
|
+
if (!elements || elements.length === 0) {
|
|
172
|
+
return {
|
|
173
|
+
content: [{ type: "text", text: result.result || "No pressable elements found." }]
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
// Resolve target app so we can pick the right simulator / android device
|
|
177
|
+
const targetApp = device ? getConnectedAppByDevice(device) : getFirstConnectedApp();
|
|
178
|
+
if (!targetApp) {
|
|
179
|
+
// No connected app — return raw (points) output; better than nothing
|
|
180
|
+
return {
|
|
181
|
+
content: [{ type: "text", text: result.result || "No pressable elements found." }]
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
// Capture a lightweight screenshot to learn scaleFactor (downscale) and dimensions.
|
|
185
|
+
// Conversion: screenshot_px = native_coord * devicePixelRatio / screenshotScale
|
|
186
|
+
let lines = [];
|
|
187
|
+
try {
|
|
188
|
+
if (targetApp.platform === "ios") {
|
|
189
|
+
const udid = targetApp.simulatorUdid;
|
|
190
|
+
const shot = await iosScreenshot(undefined, udid);
|
|
191
|
+
const screenshotScale = shot.scaleFactor || 1;
|
|
192
|
+
const devicePixelRatio = (shot.originalWidth && shot.originalHeight
|
|
193
|
+
? inferIOSDevicePixelRatio(shot.originalWidth, shot.originalHeight)
|
|
194
|
+
: null) ?? (await getDevicePixelRatio(udid)) ?? 3;
|
|
195
|
+
// Fallback to 59pt (iPhone typical) when the UI driver preflight can't
|
|
196
|
+
// resolve the true inset — matches ios_screenshot's default. Without this
|
|
197
|
+
// shift, react-native-screens modal-presented screens report y relative
|
|
198
|
+
// to content origin and taps land in the status bar instead of the button.
|
|
199
|
+
const safeAreaTop = (await getIOSSafeAreaTop(udid).catch(() => 0)) || 59;
|
|
200
|
+
// Keep the app's lastScreenshot metadata in sync so tap(x, y) uses the
|
|
201
|
+
// same scaleFactor when converting our pixel coords back to points.
|
|
202
|
+
if (shot.originalWidth && shot.originalHeight) {
|
|
203
|
+
targetApp.lastScreenshot = {
|
|
204
|
+
originalWidth: shot.originalWidth,
|
|
205
|
+
originalHeight: shot.originalHeight,
|
|
206
|
+
scaleFactor: screenshotScale
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
lines = formatPressablesInPixels(elements, {
|
|
210
|
+
platform: "ios",
|
|
211
|
+
devicePixelRatio,
|
|
212
|
+
screenshotScale,
|
|
213
|
+
safeAreaTop
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
// Metro's `deviceName` is the device model (e.g. "sdk_gphone16k_arm64"),
|
|
218
|
+
// not the adb serial (e.g. "emulator-5554"), so passing it as -s makes
|
|
219
|
+
// adb miss the device and androidScreenshot/androidGetDensity silently
|
|
220
|
+
// return defaults (scale=1, density=160). That leaves coords in raw
|
|
221
|
+
// device pixels — off by ~1.2× from the screenshot/JPEG space the tool
|
|
222
|
+
// description promises. Pass undefined to let adb auto-pick (matches
|
|
223
|
+
// how android_screenshot works when called without deviceId). Multi-
|
|
224
|
+
// Android-device support tracks separately under the multi-device
|
|
225
|
+
// refactor; this path is a single-Android-device fix.
|
|
226
|
+
const shot = await androidScreenshot(undefined, undefined);
|
|
227
|
+
const screenshotScale = shot.scaleFactor || 1;
|
|
228
|
+
const density = await androidGetDensity(undefined).catch(() => ({ density: 160 }));
|
|
229
|
+
const devicePixelRatio = (density.density || 160) / 160;
|
|
230
|
+
if (shot.originalWidth && shot.originalHeight) {
|
|
231
|
+
targetApp.lastScreenshot = {
|
|
232
|
+
originalWidth: shot.originalWidth,
|
|
233
|
+
originalHeight: shot.originalHeight,
|
|
234
|
+
scaleFactor: screenshotScale
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
lines = formatPressablesInPixels(elements, {
|
|
238
|
+
platform: "android",
|
|
239
|
+
devicePixelRatio,
|
|
240
|
+
screenshotScale,
|
|
241
|
+
safeAreaTop: 0
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
// Fallback to points if screenshot/metadata unavailable
|
|
247
|
+
return {
|
|
248
|
+
content: [{ type: "text", text: result.result || "No pressable elements found." }]
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
const iconCount = elements.filter((e) => !e.hasLabel).length;
|
|
252
|
+
const labeledCount = elements.length - iconCount;
|
|
253
|
+
const summary = `Found ${elements.length} pressable elements (${iconCount} icon-only, ${labeledCount} with text labels)`;
|
|
254
|
+
const text = [summary, "", ...lines].join("\n");
|
|
255
|
+
return {
|
|
256
|
+
content: [{ type: "text", text }]
|
|
257
|
+
};
|
|
258
|
+
});
|
|
259
|
+
// Tool: Get current screen state — route, overlays, pressables (post-navigation checkpoint)
|
|
260
|
+
registerToolWithTelemetry(server, "get_screen_state", {
|
|
261
|
+
description: "Screenshot-free snapshot of the current screen: active route + params, blocking overlays (sheets, modals, alerts), and every on-screen element merged top-to-bottom within reachability groups. Call after any tap or navigation to orient before the next action. " +
|
|
262
|
+
"Each line carries an (x, y) center + frame bounds (so anything is a tap(x, y) target), typed by a leading marker: 🔘 pressable (with component JSX tag, label, testID, onPress hint), 📝 text, 🖼 image (with src/alt). " +
|
|
263
|
+
"Elements covered by an open overlay are grouped under 🚫 Blocked — visible for context, but taps will NOT reach them until the overlay closes. Long text truncates to 80 chars (fullText=true for full strings); pressablesOnly=true returns just the lean tappable list.\n\n" +
|
|
264
|
+
"WHEN TO USE: After every tap/swipe that may navigate, and to read screen content (prices, labels, which image loaded) without a screenshot+OCR round-trip.\n" +
|
|
265
|
+
"LIMITATIONS: route is null without React Navigation / Expo Router. Requires a live Metro connection. Coordinates in points (iOS) / dp (Android); text frames are container-level (climb to nearest measurable host).\n" +
|
|
266
|
+
"SEE ALSO: get_screen_layout for the full hierarchical component tree (deep inspection) — this gives a flat, tap-ready content list instead.",
|
|
267
|
+
inputSchema: {
|
|
268
|
+
device: z.string().optional().describe("Target device name (substring match). Omit for default device. Run get_apps to see connected devices."),
|
|
269
|
+
pressablesOnly: z.boolean().optional().describe("Return only route + overlays + pressables (the lean orientation snapshot), omitting on-screen text and images. Default false."),
|
|
270
|
+
fullText: z.boolean().optional().describe("Emit each text node's full string instead of the 80-char truncation. Default false.")
|
|
271
|
+
}
|
|
272
|
+
}, async ({ device, pressablesOnly, fullText }) => {
|
|
273
|
+
if (!hasMetro()) {
|
|
274
|
+
const hint = await metroMissingHintIfAbsent("get_screen_state");
|
|
275
|
+
return {
|
|
276
|
+
content: [{ type: "text", text: `get_screen_state unavailable.${hint}` }],
|
|
277
|
+
isError: true
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
const result = await getScreenState({ device });
|
|
281
|
+
const metaNotes = collectMetaNotes(result);
|
|
282
|
+
if (!result.success) {
|
|
283
|
+
const errText = metaNotes.length > 0
|
|
284
|
+
? `Error: ${result.error}\n\n${metaNotes.join("\n")}`
|
|
285
|
+
: `Error: ${result.error}`;
|
|
286
|
+
return {
|
|
287
|
+
content: [{ type: "text", text: errText }],
|
|
288
|
+
isError: true
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
const ss = result.screenState;
|
|
292
|
+
const summary = ss ? formatScreenStateSummary(ss, undefined, { pressablesOnly, fullText }) : (result.result ?? "{}");
|
|
293
|
+
const body = metaNotes.length > 0
|
|
294
|
+
? `${summary}\n\n${metaNotes.join("\n")}`
|
|
295
|
+
: summary;
|
|
296
|
+
return { content: [{ type: "text", text: body }] };
|
|
297
|
+
});
|
|
298
|
+
function formatPressablesInPixels(elements, opts) {
|
|
299
|
+
const { platform, devicePixelRatio, screenshotScale, safeAreaTop } = opts;
|
|
300
|
+
const out = [];
|
|
301
|
+
for (let i = 0; i < elements.length; i++) {
|
|
302
|
+
const el = elements[i];
|
|
303
|
+
// react-native-screens modal presentations report y relative to content origin;
|
|
304
|
+
// shift into the window frame when the element's center sits inside the safe-area
|
|
305
|
+
// band. The shift MUST be applied to center and frame atomically — independent
|
|
306
|
+
// checks (cy<inset, fy<inset) drift apart on iPad-style insets where a header's
|
|
307
|
+
// center is just below the inset but its frame top is just above, producing
|
|
308
|
+
// center/frame.y values that don't satisfy center = frame.y + frame.h/2.
|
|
309
|
+
let cy = el.center.y;
|
|
310
|
+
let fy = el.frame.y;
|
|
311
|
+
if (platform === "ios" && safeAreaTop > 0 && cy < safeAreaTop) {
|
|
312
|
+
cy += safeAreaTop;
|
|
313
|
+
fy += safeAreaTop;
|
|
314
|
+
}
|
|
315
|
+
// iOS: fiber returns points → convert points × DPR / screenshotScale = JPEG px.
|
|
316
|
+
// Android: getPressableElements reconciles fiber DP against uiautomator device-pixel
|
|
317
|
+
// bounds (executor.ts, 2026-05-17). After reconciliation, coords are already in
|
|
318
|
+
// device pixels — only the JPEG downscale needs to be applied. Multiplying by DPR
|
|
319
|
+
// here would re-inflate them by ~density/160 (~2.6× on a 420dpi device), reproducing
|
|
320
|
+
// the original out-of-bounds bug.
|
|
321
|
+
const toPx = (v) => platform === "android"
|
|
322
|
+
? Math.round(v / screenshotScale)
|
|
323
|
+
: Math.round((v * devicePixelRatio) / screenshotScale);
|
|
324
|
+
const cx = toPx(el.center.x);
|
|
325
|
+
const cyPx = toPx(cy);
|
|
326
|
+
const fx = toPx(el.frame.x);
|
|
327
|
+
const fyPx = toPx(fy);
|
|
328
|
+
const fw = toPx(el.frame.width);
|
|
329
|
+
const fh = toPx(el.frame.height);
|
|
330
|
+
const num = i + 1;
|
|
331
|
+
const iconHint = !el.hasLabel ? iconLabel(el.component, el.icon) : null;
|
|
332
|
+
const label = el.hasLabel
|
|
333
|
+
? `"${el.text}"`
|
|
334
|
+
: iconHint
|
|
335
|
+
? `(${iconHint})`
|
|
336
|
+
: el.intent
|
|
337
|
+
? `(${el.intent} icon)`
|
|
338
|
+
: "(icon/image)";
|
|
339
|
+
const ids = [];
|
|
340
|
+
if (el.testID)
|
|
341
|
+
ids.push(`testID="${el.testID}"`);
|
|
342
|
+
if (el.accessibilityLabel)
|
|
343
|
+
ids.push(`a11y="${el.accessibilityLabel}"`);
|
|
344
|
+
const idStr = ids.length > 0 ? ` [${ids.join(", ")}]` : "";
|
|
345
|
+
const inputStr = el.isInput ? " (input)" : "";
|
|
346
|
+
const wrapperStr = el.isWrapper ? " [wrapper — skip unless dismissing keyboard]" : "";
|
|
347
|
+
const nearPart = el.nearbyText ? ` near "${el.nearbyText}"` : "";
|
|
348
|
+
out.push(`${num}. ${el.component} ${label}${nearPart} — center:(${cx},${cyPx}) frame:(${fx},${fyPx} ${fw}x${fh})${idStr}${inputStr}${wrapperStr}`);
|
|
349
|
+
if (el.path)
|
|
350
|
+
out.push(` path: ${el.path}`);
|
|
351
|
+
}
|
|
352
|
+
return out;
|
|
353
|
+
}
|
|
354
|
+
// Tool: Inspect a specific component by name
|
|
355
|
+
registerToolWithTelemetry(server, "inspect_component", {
|
|
356
|
+
description: "Inspect a specific React component by name. **DRILL-DOWN TOOL**: Use after get_screen_layout or find_components to identify which component to inspect. Returns props, style, state (hooks), and optionally children tree. Use childrenDepth to control how deep nested children go." +
|
|
357
|
+
primaryInteractionBanner() + "\n" +
|
|
358
|
+
"PURPOSE: Reveal a mounted component's live props, hook state, and (optionally) child subtree so you can reason about why it renders the way it does.\n" +
|
|
359
|
+
"WHEN TO USE: User asks \"why is this button disabled\", \"what props does X receive\", or you need to confirm state changed after a tap.\n" +
|
|
360
|
+
"WORKFLOW: get_screen_layout or find_components -> inspect_component(componentName=\"Foo\") -> tap or execute_in_app to change state -> inspect_component again.\n" +
|
|
361
|
+
"LIMITATIONS: Requires the component to be currently mounted in the fiber tree. Name matching is exact; use find_components for fuzzy/regex lookup.\n" +
|
|
362
|
+
"GOOD: inspect_component({ componentName: \"SneakerCard\", index: 0 })\n" +
|
|
363
|
+
"BAD: inspect_component({ componentName: \"Card\" }) when many Card instances exist — pass index or narrow via find_components.\n" +
|
|
364
|
+
"SEE ALSO: call get_usage_guide(topic=\"inspect\") for the full component-inspect playbook.",
|
|
365
|
+
inputSchema: {
|
|
366
|
+
componentName: z
|
|
367
|
+
.string()
|
|
368
|
+
.describe("Name of the component to inspect (e.g., 'Button', 'HomeScreen', 'FlatList')"),
|
|
369
|
+
index: z
|
|
370
|
+
.number()
|
|
371
|
+
.optional()
|
|
372
|
+
.default(0)
|
|
373
|
+
.describe("If multiple instances exist, which one to inspect (0-based index, default: 0)"),
|
|
374
|
+
includeState: z
|
|
375
|
+
.boolean()
|
|
376
|
+
.optional()
|
|
377
|
+
.default(true)
|
|
378
|
+
.describe("Include component state/hooks (default: true)"),
|
|
379
|
+
includeChildren: z.boolean().optional().default(false).describe("Include children component tree"),
|
|
380
|
+
childrenDepth: z
|
|
381
|
+
.number()
|
|
382
|
+
.optional()
|
|
383
|
+
.default(1)
|
|
384
|
+
.describe("How many levels deep to show children (default: 1 = direct children only, 2+ = nested tree)"),
|
|
385
|
+
includeStyle: z
|
|
386
|
+
.boolean()
|
|
387
|
+
.optional()
|
|
388
|
+
.default(false)
|
|
389
|
+
.describe("Include flattened style on each child entry (only with includeChildren). Use when debugging 'why isn't X style applying' or cascade-like inheritance on nested elements (e.g., textAlign on an inner Text, flex on a wrapper View). Resolves StyleSheet IDs and array/conditional styles to a single object. Default: false."),
|
|
390
|
+
shortPath: z.boolean().optional().default(true).describe("Show only last 3 path segments (default: true)"),
|
|
391
|
+
simplifyHooks: z
|
|
392
|
+
.boolean()
|
|
393
|
+
.optional()
|
|
394
|
+
.default(true)
|
|
395
|
+
.describe("Simplify hooks output by hiding effects and reducing depth (default: true)"),
|
|
396
|
+
device: z.string().optional().describe("Target device name (substring match). Omit for default device. Run get_apps to see connected devices."),
|
|
397
|
+
timeoutMs: z.coerce
|
|
398
|
+
.number()
|
|
399
|
+
.optional()
|
|
400
|
+
.describe("Per-call timeout in milliseconds. Default: 5000. Hard cap: 120000.")
|
|
401
|
+
}
|
|
402
|
+
}, async ({ componentName, index, includeState, includeChildren, childrenDepth, includeStyle, shortPath, simplifyHooks, device, timeoutMs }) => {
|
|
403
|
+
const effectiveTimeoutMs = timeoutMs ?? 5000;
|
|
404
|
+
const result = await inspectComponent(componentName, {
|
|
405
|
+
index,
|
|
406
|
+
includeState,
|
|
407
|
+
includeChildren,
|
|
408
|
+
childrenDepth,
|
|
409
|
+
includeStyle,
|
|
410
|
+
shortPath,
|
|
411
|
+
simplifyHooks,
|
|
412
|
+
device,
|
|
413
|
+
timeoutMs: effectiveTimeoutMs
|
|
414
|
+
});
|
|
415
|
+
const metaNotes = collectMetaNotes(result);
|
|
416
|
+
if (!result.success) {
|
|
417
|
+
const errText = metaNotes.length > 0 ? `Error: ${result.error}\n\n${metaNotes.join("\n")}` : `Error: ${result.error}`;
|
|
418
|
+
return {
|
|
419
|
+
content: [{ type: "text", text: errText }],
|
|
420
|
+
isError: true
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
const body = metaNotes.length > 0
|
|
424
|
+
? `Component Inspection: ${componentName}\n\n${result.result}\n\n${metaNotes.join("\n")}`
|
|
425
|
+
: `Component Inspection: ${componentName}\n\n${result.result}`;
|
|
426
|
+
return { content: [{ type: "text", text: body }] };
|
|
427
|
+
});
|
|
428
|
+
// Tool: Find components matching a pattern
|
|
429
|
+
registerToolWithTelemetry(server, "find_components", {
|
|
430
|
+
description: "Find components matching a name pattern. **TARGETED SEARCH**: Use after get_screen_layout or get_component_tree(structureOnly=true) to find specific components by pattern. Use includeLayout=true to get padding/margin/flex styles." +
|
|
431
|
+
primaryInteractionBanner() + "\n" +
|
|
432
|
+
"PURPOSE: Fast regex search over the entire fiber tree — including off-screen and wrapper components — to locate every instance of a component by name.\n" +
|
|
433
|
+
"WHEN TO USE: You know roughly what the component is called (e.g., \"Button\", \"Screen$\") but not where it lives, or you need counts/paths before drilling in with inspect_component.\n" +
|
|
434
|
+
"WORKFLOW: get_screen_layout (orient) -> find_components(pattern=\"...\") -> inspect_component(componentName=\"...\", index=N).\n" +
|
|
435
|
+
"LIMITATIONS: Matches the React display name only; minified builds may return opaque names. Large result sets — use maxResults or a tighter pattern.\n" +
|
|
436
|
+
"GOOD: find_components({ pattern: \"Button\" }); find_components({ pattern: \"Screen$\" })\n" +
|
|
437
|
+
"BAD: find_components({ pattern: \".*\" }) — floods the response; narrow the regex.\n" +
|
|
438
|
+
"SEE ALSO: call get_usage_guide(topic=\"inspect\") for the full component-inspect playbook.",
|
|
439
|
+
inputSchema: {
|
|
440
|
+
pattern: z
|
|
441
|
+
.string()
|
|
442
|
+
.describe("Regex pattern to match component names (case-insensitive). Examples: 'Button', 'Screen$', 'List.*Item'"),
|
|
443
|
+
maxResults: z.number().optional().default(20).describe("Maximum number of results to return (default: 20)"),
|
|
444
|
+
includeLayout: z
|
|
445
|
+
.boolean()
|
|
446
|
+
.optional()
|
|
447
|
+
.default(false)
|
|
448
|
+
.describe("Include layout styles (padding, margin, flex) for each matched component"),
|
|
449
|
+
shortPath: z.boolean().optional().default(true).describe("Show only last 3 path segments (default: true)"),
|
|
450
|
+
summary: z
|
|
451
|
+
.boolean()
|
|
452
|
+
.optional()
|
|
453
|
+
.default(false)
|
|
454
|
+
.describe("Return only component counts by name instead of full list (default: false)"),
|
|
455
|
+
format: z
|
|
456
|
+
.enum(["json", "tonl"])
|
|
457
|
+
.optional()
|
|
458
|
+
.default("tonl")
|
|
459
|
+
.describe("Output format: 'json' or 'tonl' (default, pipe-delimited rows, ~40% smaller)"),
|
|
460
|
+
device: z.string().optional().describe("Target device name (substring match). Omit for default device. Run get_apps to see connected devices."),
|
|
461
|
+
timeoutMs: z.coerce
|
|
462
|
+
.number()
|
|
463
|
+
.optional()
|
|
464
|
+
.describe("Per-call timeout in milliseconds. Default: 5000. Hard cap: 120000.")
|
|
465
|
+
}
|
|
466
|
+
}, async ({ pattern, maxResults, includeLayout, shortPath, summary, format, device, timeoutMs }) => {
|
|
467
|
+
const effectiveTimeoutMs = timeoutMs ?? 5000;
|
|
468
|
+
const result = await findComponents(pattern, {
|
|
469
|
+
maxResults, includeLayout, shortPath, summary, format, device,
|
|
470
|
+
timeoutMs: effectiveTimeoutMs,
|
|
471
|
+
});
|
|
472
|
+
const metaNotes = collectMetaNotes(result);
|
|
473
|
+
if (!result.success) {
|
|
474
|
+
const errText = metaNotes.length > 0 ? `Error: ${result.error}\n\n${metaNotes.join("\n")}` : `Error: ${result.error}`;
|
|
475
|
+
return {
|
|
476
|
+
content: [{ type: "text", text: errText }],
|
|
477
|
+
isError: true
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
const body = metaNotes.length > 0
|
|
481
|
+
? `Find Components (pattern: "${pattern}"):\n\n${result.result}\n\n${metaNotes.join("\n")}`
|
|
482
|
+
: `Find Components (pattern: "${pattern}"):\n\n${result.result}`;
|
|
483
|
+
return { content: [{ type: "text", text: body }] };
|
|
484
|
+
});
|
|
485
|
+
// Tool: Toggle Element Inspector programmatically
|
|
486
|
+
registerToolWithTelemetry(server, "toggle_element_inspector", {
|
|
487
|
+
description: "Toggle React Native's Element Inspector overlay on/off. Rarely needed directly — get_inspector_selection auto-toggles the overlay on for capture and back off afterward. Use only for edge cases (e.g., leaving the overlay visible on screen for a user-facing screenshot).\n" +
|
|
488
|
+
"PURPOSE: Manual control over the on-device inspector overlay.\n" +
|
|
489
|
+
"WHEN TO USE: Only for special cases like capturing a screenshot WITH the inspector visible. Normal inspection workflows should call get_inspector_selection directly.\n" +
|
|
490
|
+
"SEE ALSO: call get_usage_guide(topic=\"inspect\") for the full component-inspect playbook.",
|
|
491
|
+
inputSchema: {
|
|
492
|
+
device: z.string().optional().describe("Target device name (substring match). Omit for default device. Run get_apps to see connected devices.")
|
|
493
|
+
}
|
|
494
|
+
}, async ({ device }) => {
|
|
495
|
+
const result = await toggleElementInspector(device);
|
|
496
|
+
if (!result.success) {
|
|
497
|
+
return {
|
|
498
|
+
content: [
|
|
499
|
+
{
|
|
500
|
+
type: "text",
|
|
501
|
+
text: `Error: ${result.error}`
|
|
502
|
+
}
|
|
503
|
+
],
|
|
504
|
+
isError: true
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
try {
|
|
508
|
+
const parsed = JSON.parse(result.result || "{}");
|
|
509
|
+
if (parsed.error) {
|
|
510
|
+
return {
|
|
511
|
+
content: [
|
|
512
|
+
{
|
|
513
|
+
type: "text",
|
|
514
|
+
text: `Failed to toggle Element Inspector: ${parsed.error}`
|
|
515
|
+
}
|
|
516
|
+
],
|
|
517
|
+
isError: true
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
return {
|
|
521
|
+
content: [
|
|
522
|
+
{
|
|
523
|
+
type: "text",
|
|
524
|
+
text: parsed.message || "Element Inspector toggled successfully"
|
|
525
|
+
}
|
|
526
|
+
]
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
catch {
|
|
530
|
+
return {
|
|
531
|
+
content: [
|
|
532
|
+
{
|
|
533
|
+
type: "text",
|
|
534
|
+
text: result.result || "Element Inspector toggled"
|
|
535
|
+
}
|
|
536
|
+
]
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
// Tool: Get currently selected element from Element Inspector
|
|
541
|
+
registerToolWithTelemetry(server, "get_inspector_selection", {
|
|
542
|
+
description: "Identify the React component at a screen location AND read its full styling. Returns RN's curated owner-tree hierarchy with per-component STYLE (padding, margin, border, layout, colors, fontSize, etc.) — the same data the on-device Element Inspector shows. Works on Bridgeless/new arch by invoking RN's inspector programmatically. With x/y: toggles the overlay on, captures, toggles it off. Without coordinates: reads the current selection from a manually-driven overlay.\n" +
|
|
543
|
+
"PURPOSE: Identity + styling — \"what is this and how is it styled?\" The primary tool for visual/style debugging at a coordinate.\n" +
|
|
544
|
+
"WHEN TO USE: You see a visual issue at a pixel and want the component name AND its style values (e.g. \"why is borderRadius 14 instead of 16?\").\n" +
|
|
545
|
+
"WORKFLOW: screenshot → note suspect pixel → get_inspector_selection(x, y) → edit returned style values.\n" +
|
|
546
|
+
"LIMITATIONS: Requires RN dev mode. Brief overlay flicker (~600ms). Source paths are null on React 19 (where _debugSource was dropped); name + style is always returned.\n" +
|
|
547
|
+
"VS inspect_at_point: this returns RICH STYLE per ancestor but only ONE frame. inspect_at_point returns FRAME PER ANCESTOR + PROPS but no rich style merging.\n" +
|
|
548
|
+
"SEE ALSO: get_usage_guide(topic=\"inspect\") for the full playbook.",
|
|
549
|
+
inputSchema: {
|
|
550
|
+
x: z
|
|
551
|
+
.number()
|
|
552
|
+
.optional()
|
|
553
|
+
.describe("X coordinate (in points). If provided with y, auto-taps at this location."),
|
|
554
|
+
y: z
|
|
555
|
+
.number()
|
|
556
|
+
.optional()
|
|
557
|
+
.describe("Y coordinate (in points). If provided with x, auto-taps at this location."),
|
|
558
|
+
device: z.string().optional().describe("Target device name (substring match). Omit for default device. Run get_apps to see connected devices.")
|
|
559
|
+
}
|
|
560
|
+
}, async ({ x, y, device }) => {
|
|
561
|
+
if (!hasMetro()) {
|
|
562
|
+
const hint = await metroMissingHintIfAbsent("get_inspector_selection");
|
|
563
|
+
return {
|
|
564
|
+
content: [{ type: "text", text: `Inspector selection unavailable.${hint}` }],
|
|
565
|
+
isError: true
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
// Coordinate path: use fiber-based hit testing (works on Bridgeless / new arch
|
|
569
|
+
// where RN's built-in inspector cannot populate hierarchy via UIManager.findSubviewIn).
|
|
570
|
+
// Avoids toggling the on-device overlay so screenshots stay clean.
|
|
571
|
+
const result = x !== undefined && y !== undefined
|
|
572
|
+
? await getInspectorSelectionAtPoint(x, y, device)
|
|
573
|
+
: await getInspectorSelection(device);
|
|
574
|
+
if (!result.success) {
|
|
575
|
+
return {
|
|
576
|
+
content: [{ type: "text", text: `Error: ${result.error}` }],
|
|
577
|
+
isError: true
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
try {
|
|
581
|
+
const parsed = JSON.parse(result.result || "{}");
|
|
582
|
+
if (parsed.error) {
|
|
583
|
+
const hint = parsed.hint ? `\n\n${parsed.hint}` : "";
|
|
584
|
+
return {
|
|
585
|
+
content: [{ type: "text", text: `${parsed.error}${hint}` }],
|
|
586
|
+
isError: true
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
let output = `Element: ${parsed.element}\n`;
|
|
590
|
+
output += `Path: ${parsed.path}\n`;
|
|
591
|
+
if (parsed.frame) {
|
|
592
|
+
const f = parsed.frame;
|
|
593
|
+
output += `Frame: (${f.left?.toFixed(1)}, ${f.top?.toFixed(1)}) ${f.width?.toFixed?.(1) ?? f.width}x${f.height?.toFixed?.(1) ?? f.height}\n`;
|
|
594
|
+
}
|
|
595
|
+
if (parsed.style) {
|
|
596
|
+
output += `Style: ${JSON.stringify(parsed.style, null, 2)}\n`;
|
|
597
|
+
}
|
|
598
|
+
if (Array.isArray(parsed.hierarchy) && parsed.hierarchy.length > 0) {
|
|
599
|
+
output += `\nHierarchy:\n`;
|
|
600
|
+
for (const h of parsed.hierarchy) {
|
|
601
|
+
output += ` - ${h.name}`;
|
|
602
|
+
if (h.source)
|
|
603
|
+
output += ` (${h.source})`;
|
|
604
|
+
output += `\n`;
|
|
605
|
+
if (h.style && Object.keys(h.style).length > 0) {
|
|
606
|
+
const styleStr = JSON.stringify(h.style);
|
|
607
|
+
output += ` style: ${styleStr.length > 300 ? styleStr.slice(0, 300) + "…" : styleStr}\n`;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
return {
|
|
612
|
+
content: [{ type: "text", text: output }]
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
catch {
|
|
616
|
+
return {
|
|
617
|
+
content: [{ type: "text", text: result.result || "No selection data" }]
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
});
|
|
621
|
+
// Tool: Inspect component at coordinates (like Element Inspector)
|
|
622
|
+
registerToolWithTelemetry(server, "inspect_at_point", {
|
|
623
|
+
description: "Inspect layout AND props at (x, y). Returns FRAME PER ANCESTOR (position/size in dp for every ancestor that hit-tested the point) + the innermost component's PROPS (handlers as [Function], refs, custom props like onPress/data/testID). Pure JS hit-test via fiber + measureInWindow — no overlay toggled, zero visual side effect. Works on Paper and Fabric.\n" +
|
|
624
|
+
"PURPOSE: Layout/props diagnosis — \"where is each ancestor positioned, and what props does the touched component expose?\"\n" +
|
|
625
|
+
"WHEN TO USE: A button is clipped, hit area is wrong, animated frame is unexpected — or you need handler/ref/non-style props. Also preferred for tight loops (no overlay flicker).\n" +
|
|
626
|
+
"WORKFLOW: screenshot → suspect pixel → divide by pixel ratio → inspect_at_point(x, y).\n" +
|
|
627
|
+
"LIMITATIONS: Coordinates MUST be in dp, not screenshot pixels — wrong unit = wrong node. Style is shown for reference only (no rich merging); for style debugging use get_inspector_selection.\n" +
|
|
628
|
+
"VS get_inspector_selection: this returns FRAME PER ANCESTOR + PROPS, no flicker. Inspector returns RICH STYLE per ancestor but only one frame and briefly toggles the overlay.\n" +
|
|
629
|
+
"SEE ALSO: get_usage_guide(topic=\"inspect\") for the full playbook.",
|
|
630
|
+
inputSchema: {
|
|
631
|
+
x: z
|
|
632
|
+
.number()
|
|
633
|
+
.describe("X coordinate in dp (logical pixels). Convert from screenshot pixels by dividing by the device pixel ratio."),
|
|
634
|
+
y: z
|
|
635
|
+
.number()
|
|
636
|
+
.describe("Y coordinate in dp (logical pixels). Convert from screenshot pixels by dividing by the device pixel ratio."),
|
|
637
|
+
includeProps: z
|
|
638
|
+
.boolean()
|
|
639
|
+
.optional()
|
|
640
|
+
.default(true)
|
|
641
|
+
.describe("Include component props in the output (default: true)"),
|
|
642
|
+
includeFrame: z
|
|
643
|
+
.boolean()
|
|
644
|
+
.optional()
|
|
645
|
+
.default(true)
|
|
646
|
+
.describe("Include position/dimensions (frame) in the output (default: true)"),
|
|
647
|
+
device: z.string().optional().describe("Target device name (substring match). Omit for default device. Run get_apps to see connected devices.")
|
|
648
|
+
}
|
|
649
|
+
}, async ({ x, y, includeProps, includeFrame, device }) => {
|
|
650
|
+
const result = await inspectAtPoint(x, y, { includeProps, includeFrame, device });
|
|
651
|
+
if (!result.success) {
|
|
652
|
+
return {
|
|
653
|
+
content: [
|
|
654
|
+
{
|
|
655
|
+
type: "text",
|
|
656
|
+
text: `Error: ${result.error}`
|
|
657
|
+
}
|
|
658
|
+
],
|
|
659
|
+
isError: true
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
// Parse the result to check for errors in the response
|
|
663
|
+
try {
|
|
664
|
+
const parsed = JSON.parse(result.result || "{}");
|
|
665
|
+
if (parsed.error) {
|
|
666
|
+
const hint = parsed.hint ? `\n\n${parsed.hint}` : "";
|
|
667
|
+
const alternatives = parsed.alternatives
|
|
668
|
+
? `\n\nAlternatives:\n${parsed.alternatives.map((a) => ` - ${a}`).join("\n")}`
|
|
669
|
+
: "";
|
|
670
|
+
return {
|
|
671
|
+
content: [
|
|
672
|
+
{
|
|
673
|
+
type: "text",
|
|
674
|
+
text: `Inspect at (${x}, ${y}): ${parsed.error}${hint}${alternatives}`
|
|
675
|
+
}
|
|
676
|
+
],
|
|
677
|
+
isError: true
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
catch {
|
|
682
|
+
// If parsing fails, just return the raw result
|
|
683
|
+
}
|
|
684
|
+
return {
|
|
685
|
+
content: [
|
|
686
|
+
{
|
|
687
|
+
type: "text",
|
|
688
|
+
text: `Element at (${x}, ${y}):\n\n${result.result}`
|
|
689
|
+
}
|
|
690
|
+
]
|
|
691
|
+
};
|
|
692
|
+
});
|
|
693
|
+
// Tool: Measure on-screen geometry of a named component
|
|
694
|
+
registerToolWithTelemetry(server, "measure", {
|
|
695
|
+
description: "Get on-screen geometry {x, y, width, height} for a named React component instance. Calls measureInWindow on the matched fiber (or its nearest host descendant for composite components). Coordinates are in points (iOS) / dp (Android), same space as get_screen_layout and inspect_at_point.\n" +
|
|
696
|
+
"PURPOSE: One-shot, name-based component measurement — avoids hand-rolling fiber walks and Promise-wrapping measureInWindow callbacks in execute_in_app.\n" +
|
|
697
|
+
"WHEN TO USE: You already know the component's display name (from get_screen_layout or find_components) and just need its current bounds — e.g. to verify a layout change, compute a tap target, or compare against design specs.\n" +
|
|
698
|
+
"WORKFLOW: find_components(pattern=\"...\") -> measure(componentName=\"...\", index=N) -> tap(x, y) at the center, or inspect_at_point at the center to verify identity.\n" +
|
|
699
|
+
"LIMITATIONS: Returns post-layout on-screen geometry only — for static style use find_components({ includeLayout: true }). For point-based lookup use inspect_at_point. Off-screen fibers may return zeros; that's the truth, not an error. Composites with multiple host descendants return the first host descendant's bounds.\n" +
|
|
700
|
+
"GOOD: measure({ componentName: \"SneakerCard\", index: 0 })\n" +
|
|
701
|
+
"BAD: measure({ componentName: \"View\" }) — too generic; narrow with find_components first.\n" +
|
|
702
|
+
"SEE ALSO: inspect_at_point for point-based variant; find_components({ includeLayout: true }) for static style; get_usage_guide(topic=\"inspect\") for the full playbook.",
|
|
703
|
+
inputSchema: {
|
|
704
|
+
componentName: z
|
|
705
|
+
.string()
|
|
706
|
+
.describe("Exact React display name to match (same matcher as inspect_component)."),
|
|
707
|
+
index: z
|
|
708
|
+
.number()
|
|
709
|
+
.optional()
|
|
710
|
+
.default(0)
|
|
711
|
+
.describe("0-based index when multiple instances match (default: 0)."),
|
|
712
|
+
device: z
|
|
713
|
+
.string()
|
|
714
|
+
.optional()
|
|
715
|
+
.describe("Target device name (substring match). Omit for default device. Run get_apps to see connected devices.")
|
|
716
|
+
}
|
|
717
|
+
}, async ({ componentName, index, device }) => {
|
|
718
|
+
if (!hasMetro()) {
|
|
719
|
+
const hint = await metroMissingHintIfAbsent("measure");
|
|
720
|
+
return {
|
|
721
|
+
content: [{ type: "text", text: `measure unavailable.${hint}` }],
|
|
722
|
+
isError: true
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
const result = await measureComponent(componentName, index ?? 0, device);
|
|
726
|
+
if (!result.success) {
|
|
727
|
+
return {
|
|
728
|
+
content: [{ type: "text", text: `Error: ${result.error}` }],
|
|
729
|
+
isError: true,
|
|
730
|
+
_errorContext: result.outcome
|
|
731
|
+
};
|
|
732
|
+
}
|
|
733
|
+
const lines = [
|
|
734
|
+
`Component: ${result.name}`,
|
|
735
|
+
`Frame: (${result.x.toFixed(1)}, ${result.y.toFixed(1)}) ${result.width.toFixed(1)}x${result.height.toFixed(1)}`,
|
|
736
|
+
`Center: (${(result.x + result.width / 2).toFixed(1)}, ${(result.y + result.height / 2).toFixed(1)})`,
|
|
737
|
+
];
|
|
738
|
+
if (typeof result.nativeTag === "number") {
|
|
739
|
+
lines.push(`nativeTag: ${result.nativeTag}`);
|
|
740
|
+
}
|
|
741
|
+
return {
|
|
742
|
+
content: [{ type: "text", text: lines.join("\n") }]
|
|
743
|
+
};
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
//# sourceMappingURL=componentTools.js.map
|