react-native-ai-debugger 1.6.8 → 1.6.10

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
@@ -71,18 +71,28 @@ function resolveNetworkBuffer(device) {
71
71
  const deviceName = app.deviceInfo.deviceName || app.deviceInfo.title || "unknown";
72
72
  return getNetworkBuffer(deviceName);
73
73
  }
74
- // Merge all network requests into a temporary buffer for read operations
74
+ // Merge all network requests into a temporary buffer for read operations.
75
+ // Key by the request's own id so get(requestId) works — earlier this used
76
+ // Math.random() which made every direct lookup miss while listings still
77
+ // appeared correct (listings render req.requestId from the value).
78
+ // Multiple devices can share a request id; suffix with deviceName for
79
+ // collisions so we don't lose entries.
75
80
  const merged = new NetworkBuffer(5000);
76
- for (const buffer of networkBuffers.values()) {
81
+ const seen = new Set();
82
+ for (const [deviceName, buffer] of networkBuffers.entries()) {
77
83
  for (const req of buffer.getAll({})) {
78
- merged.set(`${Math.random()}`, req);
84
+ let key = req.requestId;
85
+ if (seen.has(key))
86
+ key = `${req.requestId}@${deviceName}`;
87
+ seen.add(key);
88
+ merged.set(key, req);
79
89
  }
80
90
  }
81
91
  return merged;
82
92
  }
83
93
  // Create MCP server
84
94
  const server = new McpServer({
85
- name: "react-native-ai-devtools",
95
+ name: "ExecBro (Mobile DevTools)",
86
96
  version: "1.0.0"
87
97
  }, {
88
98
  instructions: [
@@ -311,7 +321,7 @@ function registerToolWithTelemetry(toolName, config, handler, emptyResultDetecto
311
321
  if (!NON_METRO_TOOLS.has(toolName) && shouldShowFeedbackHint()) {
312
322
  markFeedbackHintShown();
313
323
  // Fire-and-forget — don't block the tool response
314
- pushLogBox("Congratulations on your first tool call! If you encounter any issues or have ideas for improvement, ask your AI assistant to call send_feedback. Your feedback helps me make this product better for everyone. Best regards, rn-ai-devtools developer.", "warning", true, "logbox").catch(() => {
324
+ pushLogBox("Congratulations on your first tool call! If you encounter any issues or have ideas for improvement, ask your AI assistant to call send_feedback. Your feedback helps me make this product better for everyone. Best regards, ExecBro developer.", "warning", true, "logbox").catch(() => {
315
325
  // Non-fatal — hint delivery failure should not affect tool execution
316
326
  });
317
327
  }
@@ -1698,7 +1708,7 @@ registerToolWithTelemetry("get_license_status", {
1698
1708
  });
1699
1709
  // Tool: Send feedback / bug report / feature request
1700
1710
  registerToolWithTelemetry("send_feedback", {
1701
- description: "Create a feedback report (bug, feature request, or general feedback) for the react-native-ai-devtools team. " +
1711
+ description: "Create a feedback report (bug, feature request, or general feedback) for the ExecBro team. " +
1702
1712
  "Auto-collects environment info. Returns a pre-filled GitHub issue URL and formatted issue body. " +
1703
1713
  "Ask the user to open the URL and paste the body to submit.",
1704
1714
  inputSchema: {
@@ -2010,7 +2020,9 @@ registerToolWithTelemetry("list_debug_globals", {
2010
2020
  "PURPOSE: Enumerate the app's `globalThis.*` surface so you know which stores, clients, and debug hooks you can drill into.\n" +
2011
2021
  "WHEN TO USE: Start of a state-debugging session, or when you don't know whether the app exposes a Redux/Apollo/Zustand handle.\n" +
2012
2022
  "WORKFLOW: list_debug_globals -> inspect_global(objectName=\"...\") -> execute_in_app for reads/mutations.\n" +
2013
- "LIMITATIONS: Only sees variables explicitly assigned to a global (e.g., `globalThis.store = store`). Module-scoped state is invisible — expose it first.\n" +
2023
+ "SDK INTEGRATION: If the app uses `react-native-ai-devtools-sdk` and called `init({ stores, navigation, custom })`, the response includes an `sdk.paths` array of ready-to-use dotted paths (e.g. `__RN_AI_DEVTOOLS__.stores.redux`, `__RN_AI_DEVTOOLS__.custom.mmkv`). Pass these straight to inspect_global or execute_in_app.\n" +
2024
+ "OUTPUT SHAPE: { sdk: { version, capabilities, paths, hint } | null, categories: { 'Apollo Client': [...], 'Redux': [...], 'Other Debug': [...], ... } }\n" +
2025
+ "LIMITATIONS: Only sees variables explicitly assigned to a global (e.g., `globalThis.store = store`). Module-scoped state is invisible — expose it first or use the SDK's init().\n" +
2014
2026
  "GOOD: list_debug_globals()\n" +
2015
2027
  "BAD: Calling before scan_metro — needs a live connection.\n" +
2016
2028
  "SEE ALSO: call get_usage_guide(topic=\"state\") for the full app-state playbook.",
@@ -2041,28 +2053,37 @@ registerToolWithTelemetry("list_debug_globals", {
2041
2053
  });
2042
2054
  // Tool: Inspect a global object to see its properties and types
2043
2055
  registerToolWithTelemetry("inspect_global", {
2044
- description: "Inspect a global object to see its properties, types, and whether they are callable functions. Use this BEFORE calling methods on unfamiliar objects to avoid errors.\n" +
2045
- "PURPOSE: Surface the shape of a global (Apollo client, Redux store, Expo Router, etc.) — keys, types, and which members are callable — without executing arbitrary code.\n" +
2056
+ description: "Inspect a global object (or a dotted path into one) to see its properties, types, and whether they are callable functions. Use this BEFORE calling methods on unfamiliar objects to avoid errors.\n" +
2057
+ "PURPOSE: Surface the shape of a global (Apollo client, Redux store, Expo Router, SDK-registered store, etc.) — keys, types, and which members are callable — without executing arbitrary code.\n" +
2046
2058
  "WHEN TO USE: After list_debug_globals identifies a promising global and before you try execute_in_app on it.\n" +
2047
2059
  "WORKFLOW: list_debug_globals -> inspect_global(objectName=\"__APOLLO_CLIENT__\") -> execute_in_app(\"__APOLLO_CLIENT__.cache.extract()\").\n" +
2048
- "LIMITATIONS: Only reads one level deep; nested objects show as `[object Object]` — re-inspect the child path with execute_in_app. Throws ReferenceError for globals that don't exist.\n" +
2049
- "GOOD: inspect_global({ objectName: \"__APOLLO_CLIENT__\" })\n" +
2050
- "BAD: inspect_global({ objectName: \"store.state\" }) — path expressions aren't supported; use execute_in_app for property chains.\n" +
2060
+ "DOTTED PATHS: Pass dotted paths to drill into the SDK surface, e.g. inspect_global({ objectName: \"__RN_AI_DEVTOOLS__.stores.redux\" }) or \"__RN_AI_DEVTOOLS__.custom.mmkv\". Only identifier paths are accepted — for arbitrary expressions, use execute_in_app.\n" +
2061
+ "LIMITATIONS: Only reads one level deep; nested objects show as a 100-char JSON preview — re-inspect the child path. Returns an error object (not a throw) when the path doesn't resolve.\n" +
2062
+ "GOOD: inspect_global({ objectName: \"__APOLLO_CLIENT__\" }) | inspect_global({ objectName: \"__RN_AI_DEVTOOLS__.stores.redux\" })\n" +
2063
+ "BAD: inspect_global({ objectName: \"store.getState()\" }) — call expressions aren't supported; use execute_in_app.\n" +
2051
2064
  "SEE ALSO: call get_usage_guide(topic=\"state\") for the full app-state playbook.",
2052
2065
  inputSchema: {
2053
2066
  objectName: z
2054
2067
  .string()
2055
- .describe("Name of the global object to inspect (e.g., '__EXPO_ROUTER__', '__APOLLO_CLIENT__')"),
2068
+ .describe("Identifier or dotted path of the global to inspect (e.g., '__APOLLO_CLIENT__', '__RN_AI_DEVTOOLS__.stores.redux', '__RN_AI_DEVTOOLS__.custom.mmkv')"),
2056
2069
  device: z.string().optional().describe("Target device name (substring match). Omit for default device. Run get_apps to see connected devices.")
2057
2070
  }
2058
2071
  }, async ({ objectName, device }) => {
2059
2072
  const result = await inspectGlobal(objectName, device);
2060
2073
  if (!result.success) {
2061
2074
  let errorText = `Error: ${result.error}`;
2062
- // If the error is a ReferenceError (accessing a global that doesn't exist),
2063
- // guide the agent to expose the variable as a global first
2064
- if (result.error?.includes("ReferenceError")) {
2065
- errorText += `\n\nNOTE: '${objectName}' is not exposed as a global variable. To inspect it, first assign it to a global in your app code (e.g., \`globalThis.${objectName} = ${objectName.replace(/^__/, "").replace(/__$/, "")};\`), then call inspect_global again. Use list_debug_globals to see what globals ARE currently available.`;
2075
+ // If the global (or path) doesn't resolve, guide the agent toward
2076
+ // either exposing it manually or registering it via the SDK.
2077
+ const looksMissing = result.error?.includes("ReferenceError") || result.error?.includes("NotFound") || result.error?.includes("not found");
2078
+ if (looksMissing) {
2079
+ const isPath = objectName.includes(".");
2080
+ if (isPath) {
2081
+ errorText += `\n\nNOTE: '${objectName}' did not resolve. Call list_debug_globals to confirm the path. If you expected the SDK to expose it, verify init({ stores, navigation, custom }) was called and check the sdk.paths array.`;
2082
+ }
2083
+ else {
2084
+ const suggested = objectName.replace(/^__/, "").replace(/__$/, "");
2085
+ errorText += `\n\nNOTE: '${objectName}' is not exposed as a global variable. Either (a) assign it in app code (\`globalThis.${objectName} = ${suggested};\`), or (b) register it via react-native-ai-devtools-sdk's init({ custom: { ${suggested}: ${suggested} } }) and access it as __RN_AI_DEVTOOLS__.custom.${suggested}. Then call list_debug_globals to confirm.`;
2086
+ }
2066
2087
  }
2067
2088
  return {
2068
2089
  content: [
@@ -2909,7 +2930,10 @@ registerToolWithTelemetry("get_request_details", {
2909
2930
  device: z.string().optional().describe("Target device name (substring match). Omit for all devices. Run get_apps to see connected devices.")
2910
2931
  }
2911
2932
  }, async ({ requestId, maxBodyLength, verbose, device }) => {
2912
- // Check SDK first — it has full headers and body
2933
+ // Resolve once per call so a mid-call SDK socket flap can't flip
2934
+ // routing partway through. Try SDK first (richer data), but ALWAYS
2935
+ // fall back to the CDP buffer on a miss — CDP records carry numeric
2936
+ // ids the SDK store doesn't know about, and vice versa.
2913
2937
  const sdkAvailable = await isSDKInstalled();
2914
2938
  if (sdkAvailable) {
2915
2939
  const sdkResult = await getSDKNetworkEntry(requestId);
@@ -4185,20 +4209,28 @@ async function main() {
4185
4209
  if (useHttp) {
4186
4210
  // Register dev meta-tool — proxies calls to any tool using the latest handlers
4187
4211
  server.registerTool("dev", {
4188
- description: 'Development meta-tool for hot-reload testing. Use action="list" to get all available tools with descriptions. ' +
4212
+ description: 'Development meta-tool for hot-reload testing. Use action="list" for a compact tool listing (name + first description line); pass filter to narrow by substring or verbose=true for full descriptions. ' +
4189
4213
  'Use action="call" with tool and args to invoke any tool using the latest code after hot-reload. ' +
4190
4214
  "This tool always reflects the latest server code without needing a session restart.",
4191
4215
  inputSchema: {
4192
4216
  action: z.enum(["list", "call"]).describe('"list" to see all tools, "call" to invoke a tool'),
4193
4217
  tool: z.string().optional().describe("Tool name to call (required when action is call)"),
4194
4218
  args: z.record(z.any()).optional().describe("Arguments to pass to the tool (optional, default {})"),
4219
+ filter: z.string().optional().describe("list only: case-insensitive substring filter on tool name"),
4220
+ verbose: z.boolean().optional().describe("list only: include full multi-line descriptions (default: first line only)"),
4195
4221
  },
4196
- }, async ({ action, tool, args }) => {
4222
+ }, async ({ action, tool, args, filter, verbose }) => {
4197
4223
  if (action === "list") {
4198
- const tools = Array.from(toolRegistry.entries()).map(([name, { config }]) => ({
4199
- name,
4200
- description: config.description || "",
4201
- }));
4224
+ const needle = filter?.toLowerCase();
4225
+ const tools = Array.from(toolRegistry.entries())
4226
+ .filter(([name]) => !needle || name.toLowerCase().includes(needle))
4227
+ .map(([name, { config }]) => {
4228
+ const full = config.description || "";
4229
+ return {
4230
+ name,
4231
+ description: verbose ? full : full.split("\n")[0],
4232
+ };
4233
+ });
4202
4234
  return {
4203
4235
  content: [{ type: "text", text: JSON.stringify(tools, null, 2) }],
4204
4236
  };
@@ -4240,14 +4272,14 @@ async function main() {
4240
4272
  res.end("Not found");
4241
4273
  });
4242
4274
  httpServer.listen(httpPort, () => {
4243
- console.error(`[rn-ai-debugger] MCP HTTP server listening on http://localhost:${httpPort}/mcp`);
4275
+ console.error(`[execbro] MCP HTTP server listening on http://localhost:${httpPort}/mcp`);
4244
4276
  });
4245
4277
  }
4246
4278
  else {
4247
4279
  // Stdio transport mode — default for production
4248
4280
  const transport = new StdioServerTransport();
4249
4281
  await server.connect(transport);
4250
- console.error("[rn-ai-debugger] Server started on stdio");
4282
+ console.error("[execbro] Server started on stdio");
4251
4283
  }
4252
4284
  }
4253
4285
  // Skip boot when loaded by unit tests — tests import this module purely to
@@ -4255,7 +4287,7 @@ async function main() {
4255
4287
  // is a manual escape hatch. Production + dev:mcp leave both unset and boot.
4256
4288
  if (!process.env.RN_AI_DEVTOOLS_TEST_MODE && !process.env.JEST_WORKER_ID) {
4257
4289
  main().catch((error) => {
4258
- console.error("[rn-ai-debugger] Fatal error:", error);
4290
+ console.error("[execbro] Fatal error:", error);
4259
4291
  process.exit(1);
4260
4292
  });
4261
4293
  }