react-native-ai-devtools 1.1.8 → 1.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.
package/build/index.js CHANGED
@@ -6,6 +6,7 @@ import { createServer as createHttpServer } from "node:http";
6
6
  import { z } from "zod";
7
7
  import { getGuideOverview, getGuideByTopic, getAvailableTopics } from "./core/guides.js";
8
8
  import { initLicense, getLicenseStatus, getDashboardUrl } from "./core/license.js";
9
+ import { isSDKInstalled, readSDKNetworkRequests, readSDKNetworkRequest, readSDKNetworkStats, clearSDKNetwork } from "./core/sdkBridge.js";
9
10
  import { tap } from "./pro/tap.js";
10
11
  import { logBuffer, networkBuffer, bundleErrorBuffer, connectedApps, getActiveSimulatorUdid, scanMetroPorts, fetchDevices, selectMainDevice, connectToDevice, getConnectedApps, executeInApp, listDebugGlobals, inspectGlobal, reloadApp,
11
12
  // React Component Inspection
@@ -116,7 +117,7 @@ function estimateImageTokens(base64Data) {
116
117
  // Registry for dev meta-tool — stores handlers and configs for dynamic dispatch
117
118
  /* eslint-disable @typescript-eslint/no-explicit-any */
118
119
  const toolRegistry = new Map();
119
- function registerToolWithTelemetry(toolName, config, handler) {
120
+ function registerToolWithTelemetry(toolName, config, handler, emptyResultDetector) {
120
121
  toolRegistry.set(toolName, { config, handler });
121
122
  server.registerTool(toolName, config, async (args) => {
122
123
  const startTime = Date.now();
@@ -125,6 +126,7 @@ function registerToolWithTelemetry(toolName, config, handler) {
125
126
  let errorContext;
126
127
  let inputTokens;
127
128
  let outputTokens;
129
+ let emptyResult;
128
130
  try {
129
131
  inputTokens = Math.ceil(JSON.stringify(args).length / 4);
130
132
  }
@@ -140,6 +142,15 @@ function registerToolWithTelemetry(toolName, config, handler) {
140
142
  // Extract error context if provided (e.g., the expression that caused a syntax error)
141
143
  errorContext = result._errorContext;
142
144
  }
145
+ // Check for empty result (only on success, only if detector provided)
146
+ if (success && emptyResultDetector) {
147
+ try {
148
+ emptyResult = emptyResultDetector(result);
149
+ }
150
+ catch {
151
+ // Detector failure should never affect tool execution
152
+ }
153
+ }
143
154
  if (Array.isArray(result?.content)) {
144
155
  let totalTokens = 0;
145
156
  for (const item of result.content) {
@@ -162,7 +173,7 @@ function registerToolWithTelemetry(toolName, config, handler) {
162
173
  }
163
174
  finally {
164
175
  const duration = Date.now() - startTime;
165
- trackToolInvocation(toolName, success, duration, errorMessage, errorContext, inputTokens, outputTokens, getTargetPlatform());
176
+ trackToolInvocation(toolName, success, duration, errorMessage, errorContext, inputTokens, outputTokens, getTargetPlatform(), emptyResult);
166
177
  }
167
178
  });
168
179
  }
@@ -556,7 +567,9 @@ registerToolWithTelemetry("get_logs", {
556
567
  }
557
568
  ]
558
569
  };
559
- });
570
+ },
571
+ // Empty result detector: buffer has no entries at all
572
+ () => logBuffer.size === 0);
560
573
  // Tool: Search logs
561
574
  registerToolWithTelemetry("search_logs", {
562
575
  description: "Search console logs for text (case-insensitive)",
@@ -801,6 +814,13 @@ registerToolWithTelemetry("execute_in_app", {
801
814
  }
802
815
  ]
803
816
  };
817
+ },
818
+ // Empty result detector: successful execution but no meaningful output
819
+ (result) => {
820
+ if (result?.isError)
821
+ return false;
822
+ const text = result?.content?.[0]?.text;
823
+ return text === undefined || text === "" || text === "undefined" || text === "null";
804
824
  });
805
825
  // Tool: List debug globals available in the app
806
826
  registerToolWithTelemetry("list_debug_globals", {
@@ -1369,7 +1389,7 @@ registerToolWithTelemetry("inspect_at_point", {
1369
1389
  });
1370
1390
  // Tool: Get network requests
1371
1391
  registerToolWithTelemetry("get_network_requests", {
1372
- description: "Retrieve captured network requests from connected React Native app. Shows URL, method, status, and timing. Tip: Use summary=true first for stats overview (counts by method, status, domain), then fetch specific requests as needed.",
1392
+ description: "Retrieve captured network requests from connected React Native app. Shows URL, method, status, and timing. Note: On Bridgeless targets (Expo SDK 52+) without the SDK, capture may miss early startup requests. Install react-native-ai-devtools-sdk for full capture with headers and response bodies. Tip: Use summary=true first for stats overview.",
1373
1393
  inputSchema: {
1374
1394
  maxRequests: z
1375
1395
  .number()
@@ -1391,6 +1411,53 @@ registerToolWithTelemetry("get_network_requests", {
1391
1411
  .describe("Return statistics only (count, methods, domains, status codes). Use for quick overview.")
1392
1412
  }
1393
1413
  }, async ({ maxRequests, method, urlPattern, status, format, summary }) => {
1414
+ // Check if SDK is installed — prefer SDK data over CDP/interceptor buffer
1415
+ const sdkAvailable = await isSDKInstalled();
1416
+ if (sdkAvailable) {
1417
+ if (summary) {
1418
+ const sdkStats = await readSDKNetworkStats();
1419
+ if (sdkStats.success) {
1420
+ const s = sdkStats.data;
1421
+ const lines = [];
1422
+ lines.push(`Total requests: ${s.total}`);
1423
+ lines.push(`Completed: ${s.completed}`);
1424
+ lines.push(`Errors: ${s.errors}`);
1425
+ if (s.avgDuration != null)
1426
+ lines.push(`Avg duration: ${s.avgDuration}ms`);
1427
+ if (s.byMethod && Object.keys(s.byMethod).length > 0) {
1428
+ lines.push("\nBy Method:");
1429
+ for (const [m, c] of Object.entries(s.byMethod))
1430
+ lines.push(` ${m}: ${c}`);
1431
+ }
1432
+ if (s.byStatus && Object.keys(s.byStatus).length > 0) {
1433
+ lines.push("\nBy Status:");
1434
+ for (const [st, c] of Object.entries(s.byStatus))
1435
+ lines.push(` ${st}: ${c}`);
1436
+ }
1437
+ if (s.byDomain && Object.keys(s.byDomain).length > 0) {
1438
+ lines.push("\nBy Domain:");
1439
+ for (const [d, c] of Object.entries(s.byDomain).sort((a, b) => b[1] - a[1]).slice(0, 10))
1440
+ lines.push(` ${d}: ${c}`);
1441
+ }
1442
+ return { content: [{ type: "text", text: `Network Summary (SDK):\n\n${lines.join("\n")}` }] };
1443
+ }
1444
+ }
1445
+ const sdkResult = await readSDKNetworkRequests({ count: maxRequests, method, urlPattern, status });
1446
+ if (sdkResult.success && sdkResult.data) {
1447
+ const entries = sdkResult.data;
1448
+ if (entries.length === 0) {
1449
+ return { content: [{ type: "text", text: "No network requests captured yet." }] };
1450
+ }
1451
+ const lines = entries.map((r) => {
1452
+ const time = new Date(r.timestamp).toLocaleTimeString();
1453
+ const st = r.status ?? "pending";
1454
+ const dur = r.duration != null ? `${r.duration}ms` : "-";
1455
+ return `[${r.id}] ${time} ${r.method} ${st} ${dur} ${r.url}`;
1456
+ });
1457
+ return { content: [{ type: "text", text: `Network Requests (${entries.length} entries, SDK):\n\n${lines.join("\n")}` }] };
1458
+ }
1459
+ }
1460
+ // Fallback: read from in-process buffer (CDP/interceptor)
1394
1461
  // Return summary if requested
1395
1462
  if (summary) {
1396
1463
  const stats = getNetworkStats(networkBuffer);
@@ -1398,6 +1465,9 @@ registerToolWithTelemetry("get_network_requests", {
1398
1465
  if (networkBuffer.size === 0) {
1399
1466
  const connStatus = await checkAndEnsureConnection();
1400
1467
  connectionWarning = connStatus.message ? `\n\n${connStatus.message}` : "";
1468
+ if (!sdkAvailable) {
1469
+ connectionWarning += "\n\n[TIP] For full network capture including startup requests and response bodies, install the SDK: npm install react-native-ai-devtools-sdk";
1470
+ }
1401
1471
  }
1402
1472
  return {
1403
1473
  content: [
@@ -1419,6 +1489,9 @@ registerToolWithTelemetry("get_network_requests", {
1419
1489
  if (count === 0) {
1420
1490
  const connStatus = await checkAndEnsureConnection();
1421
1491
  connectionWarning = connStatus.message ? `\n\n${connStatus.message}` : "";
1492
+ if (!sdkAvailable) {
1493
+ connectionWarning += "\n\n[TIP] For full network capture including startup requests and response bodies, install the SDK: npm install react-native-ai-devtools-sdk";
1494
+ }
1422
1495
  }
1423
1496
  else {
1424
1497
  const passive = getPassiveConnectionStatus();
@@ -1461,7 +1534,9 @@ registerToolWithTelemetry("get_network_requests", {
1461
1534
  }
1462
1535
  ]
1463
1536
  };
1464
- });
1537
+ },
1538
+ // Empty result detector: buffer has no entries at all
1539
+ () => networkBuffer.size === 0);
1465
1540
  // Tool: Search network requests
1466
1541
  registerToolWithTelemetry("search_network", {
1467
1542
  description: "Search network requests by URL pattern (case-insensitive)",
@@ -1511,7 +1586,7 @@ registerToolWithTelemetry("search_network", {
1511
1586
  });
1512
1587
  // Tool: Get request details
1513
1588
  registerToolWithTelemetry("get_request_details", {
1514
- description: "Get full details of a specific network request including headers, body, and timing. Use get_network_requests first to find the request ID.",
1589
+ description: "Get full details of a specific network request including headers, body, and timing. With the SDK installed, includes full request/response bodies. Without SDK, bodies are not available on most targets. Use get_network_requests first to find the request ID.",
1515
1590
  inputSchema: {
1516
1591
  requestId: z.string().describe("The request ID to get details for"),
1517
1592
  maxBodyLength: z.coerce
@@ -1526,6 +1601,53 @@ registerToolWithTelemetry("get_request_details", {
1526
1601
  .describe("Disable body truncation. Tip: Use when you need to inspect full JSON payloads.")
1527
1602
  }
1528
1603
  }, async ({ requestId, maxBodyLength, verbose }) => {
1604
+ // Check SDK first — it has full headers and body
1605
+ const sdkAvailable = await isSDKInstalled();
1606
+ if (sdkAvailable) {
1607
+ const sdkResult = await readSDKNetworkRequest(requestId);
1608
+ if (sdkResult.success && sdkResult.data) {
1609
+ const r = sdkResult.data;
1610
+ const lines = [];
1611
+ lines.push(`=== ${r.method} ${r.url} ===`);
1612
+ lines.push(`Request ID: ${r.id}`);
1613
+ lines.push(`Time: ${new Date(r.timestamp).toISOString()}`);
1614
+ lines.push(`Status: ${r.status ?? "pending"} ${r.statusText ?? ""}`);
1615
+ if (r.duration != null)
1616
+ lines.push(`Duration: ${r.duration}ms`);
1617
+ if (r.mimeType)
1618
+ lines.push(`Content-Type: ${r.mimeType}`);
1619
+ if (r.error)
1620
+ lines.push(`Error: ${r.error}`);
1621
+ if (r.requestHeaders && Object.keys(r.requestHeaders).length > 0) {
1622
+ lines.push("\n--- Request Headers ---");
1623
+ for (const [k, v] of Object.entries(r.requestHeaders))
1624
+ lines.push(`${k}: ${v}`);
1625
+ }
1626
+ if (r.requestBody) {
1627
+ lines.push("\n--- Request Body ---");
1628
+ let body = r.requestBody;
1629
+ if (!verbose && maxBodyLength > 0 && body.length > maxBodyLength) {
1630
+ body = body.slice(0, maxBodyLength) + `... [truncated: ${r.requestBody.length} chars]`;
1631
+ }
1632
+ lines.push(body);
1633
+ }
1634
+ if (r.responseHeaders && Object.keys(r.responseHeaders).length > 0) {
1635
+ lines.push("\n--- Response Headers ---");
1636
+ for (const [k, v] of Object.entries(r.responseHeaders))
1637
+ lines.push(`${k}: ${v}`);
1638
+ }
1639
+ if (r.responseBody) {
1640
+ lines.push("\n--- Response Body ---");
1641
+ let body = r.responseBody;
1642
+ if (!verbose && maxBodyLength > 0 && body.length > maxBodyLength) {
1643
+ body = body.slice(0, maxBodyLength) + `... [truncated: ${r.responseBody.length} chars]`;
1644
+ }
1645
+ lines.push(body);
1646
+ }
1647
+ return { content: [{ type: "text", text: lines.join("\n") }] };
1648
+ }
1649
+ }
1650
+ // Fallback: read from in-process buffer
1529
1651
  const request = networkBuffer.get(requestId);
1530
1652
  if (!request) {
1531
1653
  const status = await checkAndEnsureConnection();
@@ -1575,18 +1697,28 @@ registerToolWithTelemetry("get_network_stats", {
1575
1697
  }
1576
1698
  ]
1577
1699
  };
1578
- });
1700
+ },
1701
+ // Empty result detector: buffer has no entries at all
1702
+ () => networkBuffer.size === 0);
1579
1703
  // Tool: Clear network requests
1580
1704
  registerToolWithTelemetry("clear_network", {
1581
1705
  description: "Clear the network request buffer",
1582
1706
  inputSchema: {}
1583
1707
  }, async () => {
1584
- const count = networkBuffer.clear();
1708
+ let totalCleared = networkBuffer.clear();
1709
+ // Also clear SDK buffer if available
1710
+ const sdkAvailable = await isSDKInstalled();
1711
+ if (sdkAvailable) {
1712
+ const sdkResult = await clearSDKNetwork();
1713
+ if (sdkResult.success && sdkResult.count) {
1714
+ totalCleared += sdkResult.count;
1715
+ }
1716
+ }
1585
1717
  return {
1586
1718
  content: [
1587
1719
  {
1588
1720
  type: "text",
1589
- text: `Cleared ${count} network requests from buffer.`
1721
+ text: `Cleared ${totalCleared} network requests from buffer.`
1590
1722
  }
1591
1723
  ]
1592
1724
  };