react-native-ai-devtools 1.4.4 → 1.4.6

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/build/index.js CHANGED
@@ -12,7 +12,7 @@ import { getInstallationId, getServerVersion } from "./core/telemetry.js";
12
12
  import { isSDKInstalled, querySDKNetwork, getSDKNetworkEntry, getSDKNetworkStats, clearSDKNetwork, querySDKConsole, getSDKConsoleStats, clearSDKConsole } from "./core/sdkBridge.js";
13
13
  import { tap } from "./pro/tap.js";
14
14
  import { getActivateLicenseConfig, handleActivateLicense, getDeleteAccountConfig, handleDeleteAccount, } from "./tools/accountTools.js";
15
- import { logBuffers, networkBuffers, getLogBuffer, getNetworkBuffer, getTotalLogCount, getConnectedAppByDevice, LogBuffer, NetworkBuffer, bundleErrorBuffer, connectedApps, scanMetroPorts, fetchDevices, selectMainDevice, filterBridgelessDevices, connectToDevice, getConnectedApps, executeInApp, listDebugGlobals, inspectGlobal, reloadApp,
15
+ import { logBuffers, networkBuffers, getLogBuffer, getNetworkBuffer, getTotalLogCount, getConnectedAppByDevice, LogBuffer, NetworkBuffer, bundleErrorBuffer, imageBuffer, connectedApps, scanMetroPorts, fetchDevices, selectMainDevice, filterBridgelessDevices, connectToDevice, getConnectedApps, executeInApp, listDebugGlobals, inspectGlobal, reloadApp,
16
16
  // React Component Inspection
17
17
  getComponentTree, getScreenLayout, inspectComponent, findComponents, inspectAtPoint, toggleElementInspector, isInspectorActive, getInspectorSelection, getFirstConnectedApp, getLogs, searchLogs, getLogSummary, getNetworkRequests, searchNetworkRequests, getNetworkStats, formatRequestDetails,
18
18
  // Connection state
@@ -1464,6 +1464,15 @@ registerToolWithTelemetry("tap", {
1464
1464
  .optional()
1465
1465
  .describe("Run before/after screenshot diff to detect if the tap had a meaningful visual effect. " +
1466
1466
  "Default: true for coordinate/accessibility/ocr strategies, false for fiber. Requires screenshot=true."),
1467
+ burst: z
1468
+ .boolean()
1469
+ .optional()
1470
+ .default(false)
1471
+ .describe("Enable burst screenshot capture for enhanced verification. " +
1472
+ "Captures 4 rapid screenshots (~150ms intervals) after the tap to detect transient visual feedback " +
1473
+ "(press animations, highlights, ripples) that may settle before a standard after-screenshot. " +
1474
+ "Results are stored in the image buffer (use get_images to inspect individual frames). " +
1475
+ "Default: false."),
1467
1476
  },
1468
1477
  }, async (args) => {
1469
1478
  const result = await tap({
@@ -1479,6 +1488,7 @@ registerToolWithTelemetry("tap", {
1479
1488
  platform: args.platform,
1480
1489
  screenshot: args.screenshot,
1481
1490
  verify: args.verify,
1491
+ burst: args.burst,
1482
1492
  });
1483
1493
  const { screenshot: screenshotData, ...resultWithoutScreenshot } = result;
1484
1494
  const text = JSON.stringify(resultWithoutScreenshot, null, 2);
@@ -2132,6 +2142,68 @@ registerToolWithTelemetry("clear_network", {
2132
2142
  ]
2133
2143
  };
2134
2144
  });
2145
+ // Tool: Get images from shared image buffer
2146
+ registerToolWithTelemetry("get_images", {
2147
+ description: "Access the shared image buffer containing screenshots from all tools (ios_screenshot, android_screenshot, ocr_screenshot, tap verification). Returns metadata only by default — use id or groupId+frameIndex to retrieve actual image data. Tap burst verification stores frame groups here when burst=true is used.",
2148
+ inputSchema: {
2149
+ list: z.boolean().optional().describe("List all entries and groups (metadata only, no image data)"),
2150
+ id: z.string().optional().describe("Retrieve a specific image by ID (returns image data)"),
2151
+ groupId: z.string().optional().describe("List frames in a group (metadata only), or combine with frameIndex to retrieve a specific frame"),
2152
+ frameIndex: z.coerce.number().optional().describe("Retrieve a specific frame from a group (requires groupId)"),
2153
+ last: z.coerce.number().optional().describe("Return the N most recent entries (metadata only)"),
2154
+ source: z.string().optional().describe("Filter entries by source"),
2155
+ clear: z.boolean().optional().describe("Clear the buffer")
2156
+ }
2157
+ }, async ({ id, groupId, frameIndex, last, source, clear }) => {
2158
+ if (clear) {
2159
+ const count = imageBuffer.clear();
2160
+ return {
2161
+ content: [{ type: "text", text: `Cleared ${count} images from buffer.` }]
2162
+ };
2163
+ }
2164
+ if (id) {
2165
+ const entry = imageBuffer.getById(id);
2166
+ if (!entry) {
2167
+ return {
2168
+ content: [{ type: "text", text: `No image found with id "${id}".` }],
2169
+ isError: true
2170
+ };
2171
+ }
2172
+ const { image, ...meta } = entry;
2173
+ return {
2174
+ content: [
2175
+ { type: "text", text: JSON.stringify(meta, null, 2) },
2176
+ { type: "image", data: image.toString("base64"), mimeType: "image/png" }
2177
+ ]
2178
+ };
2179
+ }
2180
+ if (groupId !== undefined && frameIndex !== undefined) {
2181
+ const entry = imageBuffer.getByGroupFrame(groupId, frameIndex);
2182
+ if (!entry) {
2183
+ return {
2184
+ content: [{ type: "text", text: `No frame ${frameIndex} found in group "${groupId}".` }],
2185
+ isError: true
2186
+ };
2187
+ }
2188
+ const { image, ...meta } = entry;
2189
+ return {
2190
+ content: [
2191
+ { type: "text", text: JSON.stringify(meta, null, 2) },
2192
+ { type: "image", data: image.toString("base64"), mimeType: "image/png" }
2193
+ ]
2194
+ };
2195
+ }
2196
+ const entries = imageBuffer.listEntries({ source, groupId, last });
2197
+ const groups = imageBuffer.listGroups();
2198
+ return {
2199
+ content: [
2200
+ {
2201
+ type: "text",
2202
+ text: JSON.stringify({ entries, groups, total: imageBuffer.size }, null, 2)
2203
+ }
2204
+ ]
2205
+ };
2206
+ });
2135
2207
  // Tool: Reload the app
2136
2208
  registerToolWithTelemetry("reload_app", {
2137
2209
  description: "Reload the React Native app (triggers JavaScript bundle reload like pressing 'r' in Metro). Will auto-connect to Metro if no connection exists. Note: After reload, the app may take a few seconds to fully restart and become responsive — wait before running other tools. IMPORTANT: React Native has Fast Refresh enabled by default - code changes are automatically applied without needing reload. Only use when: (1) logs/behavior don't reflect code changes after a few seconds, (2) app is in broken/error state, or (3) need to reset app state completely (navigation stack, context, etc.).",
@@ -2417,6 +2489,18 @@ registerToolWithTelemetry("android_screenshot", {
2417
2489
  infoText += `\n • tap(x=<px>, y=<px>) — tap at coordinates from this screenshot`;
2418
2490
  infoText += `\n • android_describe_all — get full UI tree with exact tap coordinates`;
2419
2491
  infoText += `\n • android_find_element(text="...") — find element coordinates without tapping`;
2492
+ imageBuffer.add({
2493
+ id: `android-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
2494
+ image: result.data,
2495
+ timestamp: Date.now(),
2496
+ source: "android_screenshot",
2497
+ metadata: {
2498
+ width: result.originalWidth || 0,
2499
+ height: result.originalHeight || 0,
2500
+ scaleFactor: result.scaleFactor || 1,
2501
+ platform: "android",
2502
+ },
2503
+ });
2420
2504
  return {
2421
2505
  content: [
2422
2506
  {
@@ -2947,6 +3031,18 @@ registerToolWithTelemetry("ios_screenshot", {
2947
3031
  infoText += `\n • tap(x=<px>, y=<px>) — tap at coordinates from this screenshot`;
2948
3032
  infoText += `\n • ios_describe_all — get full UI tree with exact tap coordinates`;
2949
3033
  infoText += `\n • ios_find_element(label="...") — find element coordinates without tapping`;
3034
+ imageBuffer.add({
3035
+ id: `ios-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
3036
+ image: result.data,
3037
+ timestamp: Date.now(),
3038
+ source: "ios_screenshot",
3039
+ metadata: {
3040
+ width: result.originalWidth || 0,
3041
+ height: result.originalHeight || 0,
3042
+ scaleFactor: result.scaleFactor || 1,
3043
+ platform: "ios",
3044
+ },
3045
+ });
2950
3046
  return {
2951
3047
  content: [
2952
3048
  {
@@ -3033,6 +3129,29 @@ registerToolWithTelemetry("ocr_screenshot", {
3033
3129
  elements,
3034
3130
  note: "tapX/tapY are ready to use with tap commands (already converted for platform)"
3035
3131
  };
3132
+ // Capture screenshot and store in image buffer
3133
+ try {
3134
+ const screenshotResult = platform === "android"
3135
+ ? await androidScreenshot(undefined, deviceId)
3136
+ : await iosScreenshot(undefined, deviceId);
3137
+ if (screenshotResult.success && screenshotResult.data) {
3138
+ imageBuffer.add({
3139
+ id: `ocr-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
3140
+ image: screenshotResult.data,
3141
+ timestamp: Date.now(),
3142
+ source: "ocr_screenshot",
3143
+ metadata: {
3144
+ width: screenshotResult.originalWidth || 0,
3145
+ height: screenshotResult.originalHeight || 0,
3146
+ scaleFactor: screenshotResult.scaleFactor || 1,
3147
+ platform,
3148
+ },
3149
+ });
3150
+ }
3151
+ }
3152
+ catch {
3153
+ // Non-fatal: image buffer write failure should not break OCR response
3154
+ }
3036
3155
  return {
3037
3156
  content: [
3038
3157
  {