react-native-ai-devtools 1.4.12 → 1.4.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/build/__tests__/integration/connection-health.test.js +12 -6
- package/build/__tests__/integration/connection-health.test.js.map +1 -1
- package/build/__tests__/unit/connection-health.test.js +26 -16
- package/build/__tests__/unit/connection-health.test.js.map +1 -1
- package/build/__tests__/unit/logs.test.js +9 -0
- package/build/__tests__/unit/logs.test.js.map +1 -1
- package/build/core/connection.d.ts +23 -2
- package/build/core/connection.d.ts.map +1 -1
- package/build/core/connection.js +177 -24
- package/build/core/connection.js.map +1 -1
- package/build/core/index.d.ts +3 -3
- package/build/core/index.d.ts.map +1 -1
- package/build/core/index.js +2 -2
- package/build/core/index.js.map +1 -1
- package/build/core/logs.d.ts +1 -0
- package/build/core/logs.d.ts.map +1 -1
- package/build/core/logs.js +5 -0
- package/build/core/logs.js.map +1 -1
- package/build/core/state.d.ts +16 -2
- package/build/core/state.d.ts.map +1 -1
- package/build/core/state.js +34 -6
- package/build/core/state.js.map +1 -1
- package/build/core/telemetry.d.ts +4 -1
- package/build/core/telemetry.d.ts.map +1 -1
- package/build/core/telemetry.js +51 -2
- package/build/core/telemetry.js.map +1 -1
- package/build/index.js +106 -12
- package/build/index.js.map +1 -1
- package/package.json +1 -1
package/build/index.js
CHANGED
|
@@ -3,12 +3,13 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
5
5
|
import { createServer as createHttpServer } from "node:http";
|
|
6
|
+
import { existsSync, unlinkSync } from "fs";
|
|
6
7
|
import { z } from "zod";
|
|
7
8
|
import { getGuideOverview, getGuideByTopic, getAvailableTopics } from "./core/guides.js";
|
|
8
9
|
import { getLicenseStatus, getDashboardUrl, getUsageInfo } from "./core/license.js";
|
|
9
10
|
import { API_BASE_URL } from "./core/config.js";
|
|
10
11
|
import { getPostHogClient, identifyIfDevMode, shutdownPostHog } from "./core/posthog.js";
|
|
11
|
-
import { getInstallationId, getServerVersion } from "./core/telemetry.js";
|
|
12
|
+
import { getInstallationId, getServerVersion, isDevMode, TELEMETRY_JSONL_PATH } from "./core/telemetry.js";
|
|
12
13
|
import { isSDKInstalled, querySDKNetwork, getSDKNetworkEntry, getSDKNetworkStats, clearSDKNetwork, querySDKConsole, getSDKConsoleStats, clearSDKConsole } from "./core/sdkBridge.js";
|
|
13
14
|
import { tap } from "./pro/tap.js";
|
|
14
15
|
import { getActivateLicenseConfig, handleActivateLicense, getDeleteAccountConfig, handleDeleteAccount, } from "./tools/accountTools.js";
|
|
@@ -16,7 +17,7 @@ import { logBuffers, networkBuffers, getLogBuffer, getNetworkBuffer, getTotalLog
|
|
|
16
17
|
// React Component Inspection
|
|
17
18
|
getComponentTree, getScreenLayout, inspectComponent, findComponents, inspectAtPoint, toggleElementInspector, isInspectorActive, getInspectorSelection, getFirstConnectedApp, getLogs, searchLogs, getLogSummary, getNetworkRequests, searchNetworkRequests, getNetworkStats, formatRequestDetails,
|
|
18
19
|
// Connection state
|
|
19
|
-
getAllConnectionStates, getAllConnectionMetadata, getRecentGaps, formatDuration, cancelAllReconnectionTimers, cancelReconnectionTimer, clearAllConnectionState, suppressReconnection, suppressReconnectionForKey, clearReconnectionSuppression,
|
|
20
|
+
getAllConnectionStates, getAllConnectionMetadata, getRecentGaps, formatDuration, cancelAllReconnectionTimers, cancelReconnectionTimer, clearAllConnectionState, clearAllCDPMessageTimes, suppressReconnection, suppressReconnectionForKey, clearReconnectionSuppression,
|
|
20
21
|
// Context health tracking
|
|
21
22
|
getContextHealth,
|
|
22
23
|
// Connection resilience
|
|
@@ -48,7 +49,7 @@ initTelemetry, trackToolInvocation, getTargetPlatform,
|
|
|
48
49
|
// Format utilities (TONL)
|
|
49
50
|
formatLogsAsTonl, formatNetworkAsTonl,
|
|
50
51
|
// LogBox detection & control
|
|
51
|
-
detectLogBox, formatLogBoxWarning, dismissLogBox, formatDismissedEntries, pushLogBox, addLogBoxIgnorePatterns } from "./core/index.js";
|
|
52
|
+
detectLogBox, formatLogBoxWarning, dismissLogBox, formatDismissedEntries, pushLogBox, addLogBoxIgnorePatterns, verifyLogPipeline } from "./core/index.js";
|
|
52
53
|
// Helper: resolve log buffer for a device (or create a merged buffer from all devices)
|
|
53
54
|
function resolveLogBuffer(device) {
|
|
54
55
|
if (device) {
|
|
@@ -182,6 +183,8 @@ function registerToolWithTelemetry(toolName, config, handler, emptyResultDetecto
|
|
|
182
183
|
let meaningful;
|
|
183
184
|
let changeRate;
|
|
184
185
|
let tapStrategy;
|
|
186
|
+
let responsePreview;
|
|
187
|
+
let emptyReason;
|
|
185
188
|
try {
|
|
186
189
|
inputTokens = Math.ceil(JSON.stringify(args).length / 4);
|
|
187
190
|
}
|
|
@@ -214,6 +217,8 @@ function registerToolWithTelemetry(toolName, config, handler, emptyResultDetecto
|
|
|
214
217
|
changeRate = result._changeRate;
|
|
215
218
|
if (result?._tapStrategy)
|
|
216
219
|
tapStrategy = result._tapStrategy;
|
|
220
|
+
if (result?._emptyReason)
|
|
221
|
+
emptyReason = result._emptyReason;
|
|
217
222
|
if (Array.isArray(result?.content)) {
|
|
218
223
|
let totalTokens = 0;
|
|
219
224
|
for (const item of result.content) {
|
|
@@ -227,6 +232,15 @@ function registerToolWithTelemetry(toolName, config, handler, emptyResultDetecto
|
|
|
227
232
|
if (totalTokens > 0)
|
|
228
233
|
outputTokens = totalTokens;
|
|
229
234
|
}
|
|
235
|
+
// Capture response text preview for local dev dashboard
|
|
236
|
+
if (Array.isArray(result?.content)) {
|
|
237
|
+
const textParts = result.content
|
|
238
|
+
.filter((item) => item.type === "text")
|
|
239
|
+
.map((item) => item.text);
|
|
240
|
+
if (textParts.length > 0) {
|
|
241
|
+
responsePreview = textParts.join("\n").substring(0, 2000);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
230
244
|
return result;
|
|
231
245
|
}
|
|
232
246
|
catch (error) {
|
|
@@ -237,7 +251,7 @@ function registerToolWithTelemetry(toolName, config, handler, emptyResultDetecto
|
|
|
237
251
|
}
|
|
238
252
|
finally {
|
|
239
253
|
const duration = Date.now() - startTime;
|
|
240
|
-
trackToolInvocation(toolName, success, duration, errorMessage, errorContext, inputTokens, outputTokens, getTargetPlatform(), emptyResult, meaningful, changeRate, tapStrategy);
|
|
254
|
+
trackToolInvocation(toolName, success, duration, errorMessage, errorContext, inputTokens, outputTokens, getTargetPlatform(), emptyResult, meaningful, changeRate, tapStrategy, responsePreview, emptyReason);
|
|
241
255
|
getPostHogClient()?.capture({
|
|
242
256
|
distinctId: getInstallationId(),
|
|
243
257
|
event: toolName,
|
|
@@ -694,8 +708,17 @@ registerToolWithTelemetry("get_logs", {
|
|
|
694
708
|
const summaryText = getLogSummary(resolveLogBuffer(device), { lastN: 5, maxMessageLength: 100 });
|
|
695
709
|
let connectionWarning = "";
|
|
696
710
|
if (getTotalLogCount() === 0) {
|
|
697
|
-
const status = await checkAndEnsureConnection();
|
|
711
|
+
const status = await checkAndEnsureConnection(device);
|
|
698
712
|
connectionWarning = status.message ? `\n\n${status.message}` : "";
|
|
713
|
+
if (status.connected) {
|
|
714
|
+
const targetApp = device ? getConnectedAppByDevice(device) : getFirstConnectedApp();
|
|
715
|
+
if (targetApp) {
|
|
716
|
+
const pipeline = await verifyLogPipeline(targetApp);
|
|
717
|
+
if (pipeline.message) {
|
|
718
|
+
connectionWarning += `\n\n${pipeline.message}`;
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
}
|
|
699
722
|
}
|
|
700
723
|
return {
|
|
701
724
|
content: [
|
|
@@ -715,9 +738,60 @@ registerToolWithTelemetry("get_logs", {
|
|
|
715
738
|
});
|
|
716
739
|
// Check connection health
|
|
717
740
|
let connectionWarning = "";
|
|
741
|
+
let emptyReason;
|
|
718
742
|
if (count === 0) {
|
|
719
|
-
const status = await checkAndEnsureConnection();
|
|
743
|
+
const status = await checkAndEnsureConnection(device);
|
|
720
744
|
connectionWarning = status.message ? `\n\n${status.message}` : "";
|
|
745
|
+
// Track empty reason for telemetry
|
|
746
|
+
emptyReason = "no_logs";
|
|
747
|
+
if (!status.connected) {
|
|
748
|
+
emptyReason = "disconnected";
|
|
749
|
+
}
|
|
750
|
+
else if (status.wasReconnected) {
|
|
751
|
+
emptyReason = "post_reconnect";
|
|
752
|
+
}
|
|
753
|
+
// End-to-end log pipeline verification (with automatic recovery)
|
|
754
|
+
if (status.connected) {
|
|
755
|
+
const targetApp = device ? getConnectedAppByDevice(device) : getFirstConnectedApp();
|
|
756
|
+
if (targetApp) {
|
|
757
|
+
const pipeline = await verifyLogPipeline(targetApp);
|
|
758
|
+
if (pipeline.message) {
|
|
759
|
+
connectionWarning += `\n\n${pipeline.message}`;
|
|
760
|
+
}
|
|
761
|
+
if (!pipeline.ok) {
|
|
762
|
+
emptyReason = "pipeline_failed";
|
|
763
|
+
}
|
|
764
|
+
else if (pipeline.recovered) {
|
|
765
|
+
emptyReason = "pipeline_recovered";
|
|
766
|
+
}
|
|
767
|
+
// If pipeline recovered, re-read the buffer — new logs may have arrived
|
|
768
|
+
if (pipeline.recovered && pipeline.ok) {
|
|
769
|
+
const retryResult = getLogs(resolveLogBuffer(device), {
|
|
770
|
+
maxLogs, level, startFromText, maxMessageLength, verbose
|
|
771
|
+
});
|
|
772
|
+
if (retryResult.count > 0) {
|
|
773
|
+
// Return the recovered logs instead of empty
|
|
774
|
+
return {
|
|
775
|
+
_emptyReason: "pipeline_recovered",
|
|
776
|
+
content: [{
|
|
777
|
+
type: "text",
|
|
778
|
+
text: `React Native Console Logs (${retryResult.count} entries):\n\n${retryResult.formatted}${connectionWarning}`
|
|
779
|
+
}]
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
// Add diagnostic metadata for empty results (captured by telemetry via responsePreview)
|
|
786
|
+
if (count === 0) {
|
|
787
|
+
const diagParts = [
|
|
788
|
+
`empty_reason=${status?.wasReconnected ? "post_reconnect" : "no_logs"}`,
|
|
789
|
+
`connection=${getPassiveConnectionStatus().reason}`,
|
|
790
|
+
`device_count=${connectedApps.size}`,
|
|
791
|
+
`buffer_sizes=${JSON.stringify(Object.fromEntries([...logBuffers.entries()].map(([k, v]) => [k, v.size])))}`,
|
|
792
|
+
];
|
|
793
|
+
connectionWarning += `\n\n[DIAG] ${diagParts.join(", ")}`;
|
|
794
|
+
}
|
|
721
795
|
}
|
|
722
796
|
else {
|
|
723
797
|
const passive = getPassiveConnectionStatus();
|
|
@@ -745,6 +819,7 @@ registerToolWithTelemetry("get_logs", {
|
|
|
745
819
|
if (format === "tonl") {
|
|
746
820
|
const tonlOutput = formatLogsAsTonl(logs, { maxMessageLength: verbose ? 0 : maxMessageLength });
|
|
747
821
|
return {
|
|
822
|
+
...(emptyReason && { _emptyReason: emptyReason }),
|
|
748
823
|
content: [
|
|
749
824
|
{
|
|
750
825
|
type: "text",
|
|
@@ -754,6 +829,7 @@ registerToolWithTelemetry("get_logs", {
|
|
|
754
829
|
};
|
|
755
830
|
}
|
|
756
831
|
return {
|
|
832
|
+
...(emptyReason && { _emptyReason: emptyReason }),
|
|
757
833
|
content: [
|
|
758
834
|
{
|
|
759
835
|
type: "text",
|
|
@@ -823,7 +899,7 @@ registerToolWithTelemetry("search_logs", {
|
|
|
823
899
|
// Check connection health
|
|
824
900
|
let connectionWarning = "";
|
|
825
901
|
if (count === 0) {
|
|
826
|
-
const status = await checkAndEnsureConnection();
|
|
902
|
+
const status = await checkAndEnsureConnection(device);
|
|
827
903
|
connectionWarning = status.message ? `\n\n${status.message}` : "";
|
|
828
904
|
}
|
|
829
905
|
else {
|
|
@@ -1008,6 +1084,7 @@ registerToolWithTelemetry("disconnect_metro", {
|
|
|
1008
1084
|
disconnectMetroBuildEvents();
|
|
1009
1085
|
// Clear connection state (but NOT log/network buffers)
|
|
1010
1086
|
clearAllConnectionState();
|
|
1087
|
+
clearAllCDPMessageTimes();
|
|
1011
1088
|
const lines = [
|
|
1012
1089
|
`Disconnected from ${disconnected.length} app(s):`,
|
|
1013
1090
|
...disconnected.map((d) => ` - ${d}`),
|
|
@@ -1813,7 +1890,7 @@ registerToolWithTelemetry("get_network_requests", {
|
|
|
1813
1890
|
const stats = getNetworkStats(resolveNetworkBuffer(device));
|
|
1814
1891
|
let connectionWarning = "";
|
|
1815
1892
|
if (resolveNetworkBuffer(device).size === 0) {
|
|
1816
|
-
const connStatus = await checkAndEnsureConnection();
|
|
1893
|
+
const connStatus = await checkAndEnsureConnection(device);
|
|
1817
1894
|
connectionWarning = connStatus.message ? `\n\n${connStatus.message}` : "";
|
|
1818
1895
|
if (!sdkAvailable) {
|
|
1819
1896
|
connectionWarning += "\n\n[TIP] For full network capture including startup requests and response bodies, install the SDK: npm install react-native-ai-devtools-sdk";
|
|
@@ -1837,7 +1914,7 @@ registerToolWithTelemetry("get_network_requests", {
|
|
|
1837
1914
|
// Check connection health
|
|
1838
1915
|
let connectionWarning = "";
|
|
1839
1916
|
if (count === 0) {
|
|
1840
|
-
const connStatus = await checkAndEnsureConnection();
|
|
1917
|
+
const connStatus = await checkAndEnsureConnection(device);
|
|
1841
1918
|
connectionWarning = connStatus.message ? `\n\n${connStatus.message}` : "";
|
|
1842
1919
|
if (!sdkAvailable) {
|
|
1843
1920
|
connectionWarning += "\n\n[TIP] For full network capture including startup requests and response bodies, install the SDK: npm install react-native-ai-devtools-sdk";
|
|
@@ -1927,7 +2004,7 @@ registerToolWithTelemetry("search_network", {
|
|
|
1927
2004
|
// Check connection health
|
|
1928
2005
|
let connectionWarning = "";
|
|
1929
2006
|
if (count === 0) {
|
|
1930
|
-
const status = await checkAndEnsureConnection();
|
|
2007
|
+
const status = await checkAndEnsureConnection(device);
|
|
1931
2008
|
connectionWarning = status.message ? `\n\n${status.message}` : "";
|
|
1932
2009
|
}
|
|
1933
2010
|
else {
|
|
@@ -2024,7 +2101,7 @@ registerToolWithTelemetry("get_request_details", {
|
|
|
2024
2101
|
// Fallback: read from in-process buffer
|
|
2025
2102
|
const request = resolveNetworkBuffer(device).get(requestId);
|
|
2026
2103
|
if (!request) {
|
|
2027
|
-
const status = await checkAndEnsureConnection();
|
|
2104
|
+
const status = await checkAndEnsureConnection(device);
|
|
2028
2105
|
const connectionNote = status.message ? `\n\n${status.message}` : "";
|
|
2029
2106
|
return {
|
|
2030
2107
|
content: [
|
|
@@ -2086,7 +2163,7 @@ registerToolWithTelemetry("get_network_stats", {
|
|
|
2086
2163
|
// Check connection health
|
|
2087
2164
|
let connectionWarning = "";
|
|
2088
2165
|
if (resolveNetworkBuffer(device).size === 0) {
|
|
2089
|
-
const status = await checkAndEnsureConnection();
|
|
2166
|
+
const status = await checkAndEnsureConnection(device);
|
|
2090
2167
|
connectionWarning = status.message ? `\n\n${status.message}` : "";
|
|
2091
2168
|
}
|
|
2092
2169
|
else {
|
|
@@ -3804,6 +3881,23 @@ registerToolWithTelemetry("restart_http_server", {
|
|
|
3804
3881
|
registerToolWithTelemetry("activate_license", getActivateLicenseConfig(), handleActivateLicense);
|
|
3805
3882
|
// Tool: Delete account
|
|
3806
3883
|
registerToolWithTelemetry("delete_account", getDeleteAccountConfig(), handleDeleteAccount);
|
|
3884
|
+
// Dev-only tool: reset local telemetry data
|
|
3885
|
+
if (isDevMode()) {
|
|
3886
|
+
registerToolWithTelemetry("reset_telemetry", {
|
|
3887
|
+
description: "Clear local telemetry data file (/tmp/rn-devtools-telemetry.jsonl). Only available in development mode.",
|
|
3888
|
+
inputSchema: {},
|
|
3889
|
+
}, async () => {
|
|
3890
|
+
if (existsSync(TELEMETRY_JSONL_PATH)) {
|
|
3891
|
+
unlinkSync(TELEMETRY_JSONL_PATH);
|
|
3892
|
+
return {
|
|
3893
|
+
content: [{ type: "text", text: "Local telemetry data cleared." }],
|
|
3894
|
+
};
|
|
3895
|
+
}
|
|
3896
|
+
return {
|
|
3897
|
+
content: [{ type: "text", text: "No local telemetry data file found." }],
|
|
3898
|
+
};
|
|
3899
|
+
});
|
|
3900
|
+
}
|
|
3807
3901
|
/**
|
|
3808
3902
|
* Auto-connect to Metro bundler on startup
|
|
3809
3903
|
* Scans common ports and connects to any running Metro servers
|