wiretap-mcp 2.0.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/LICENSE +21 -0
- package/README.md +174 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +68 -0
- package/dist/index.js.map +1 -0
- package/dist/monitors/crash-android.d.ts +20 -0
- package/dist/monitors/crash-android.d.ts.map +1 -0
- package/dist/monitors/crash-android.js +134 -0
- package/dist/monitors/crash-android.js.map +1 -0
- package/dist/monitors/crash-ios.d.ts +17 -0
- package/dist/monitors/crash-ios.d.ts.map +1 -0
- package/dist/monitors/crash-ios.js +116 -0
- package/dist/monitors/crash-ios.js.map +1 -0
- package/dist/monitors/hermes.d.ts +26 -0
- package/dist/monitors/hermes.d.ts.map +1 -0
- package/dist/monitors/hermes.js +175 -0
- package/dist/monitors/hermes.js.map +1 -0
- package/dist/monitors/reactotron.d.ts +18 -0
- package/dist/monitors/reactotron.d.ts.map +1 -0
- package/dist/monitors/reactotron.js +106 -0
- package/dist/monitors/reactotron.js.map +1 -0
- package/dist/monitors/terminal.d.ts +25 -0
- package/dist/monitors/terminal.d.ts.map +1 -0
- package/dist/monitors/terminal.js +154 -0
- package/dist/monitors/terminal.js.map +1 -0
- package/dist/parsers/cdp-parser.d.ts +64 -0
- package/dist/parsers/cdp-parser.d.ts.map +1 -0
- package/dist/parsers/cdp-parser.js +115 -0
- package/dist/parsers/cdp-parser.js.map +1 -0
- package/dist/parsers/crash-parser.d.ts +5 -0
- package/dist/parsers/crash-parser.d.ts.map +1 -0
- package/dist/parsers/crash-parser.js +112 -0
- package/dist/parsers/crash-parser.js.map +1 -0
- package/dist/parsers/log-parser.d.ts +5 -0
- package/dist/parsers/log-parser.d.ts.map +1 -0
- package/dist/parsers/log-parser.js +141 -0
- package/dist/parsers/log-parser.js.map +1 -0
- package/dist/parsers/reactotron-parser.d.ts +17 -0
- package/dist/parsers/reactotron-parser.d.ts.map +1 -0
- package/dist/parsers/reactotron-parser.js +83 -0
- package/dist/parsers/reactotron-parser.js.map +1 -0
- package/dist/server.d.ts +19 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +224 -0
- package/dist/server.js.map +1 -0
- package/dist/storage/log-store.d.ts +41 -0
- package/dist/storage/log-store.d.ts.map +1 -0
- package/dist/storage/log-store.js +118 -0
- package/dist/storage/log-store.js.map +1 -0
- package/dist/storage/persistence.d.ts +26 -0
- package/dist/storage/persistence.d.ts.map +1 -0
- package/dist/storage/persistence.js +123 -0
- package/dist/storage/persistence.js.map +1 -0
- package/dist/tools/clear-logs.d.ts +8 -0
- package/dist/tools/clear-logs.d.ts.map +1 -0
- package/dist/tools/clear-logs.js +15 -0
- package/dist/tools/clear-logs.js.map +1 -0
- package/dist/tools/get-api-calls.d.ts +11 -0
- package/dist/tools/get-api-calls.d.ts.map +1 -0
- package/dist/tools/get-api-calls.js +26 -0
- package/dist/tools/get-api-calls.js.map +1 -0
- package/dist/tools/get-crashes.d.ts +11 -0
- package/dist/tools/get-crashes.d.ts.map +1 -0
- package/dist/tools/get-crashes.js +42 -0
- package/dist/tools/get-crashes.js.map +1 -0
- package/dist/tools/get-errors.d.ts +12 -0
- package/dist/tools/get-errors.d.ts.map +1 -0
- package/dist/tools/get-errors.js +52 -0
- package/dist/tools/get-errors.js.map +1 -0
- package/dist/tools/get-hermes-logs.d.ts +12 -0
- package/dist/tools/get-hermes-logs.d.ts.map +1 -0
- package/dist/tools/get-hermes-logs.js +33 -0
- package/dist/tools/get-hermes-logs.js.map +1 -0
- package/dist/tools/get-logs.d.ts +21 -0
- package/dist/tools/get-logs.d.ts.map +1 -0
- package/dist/tools/get-logs.js +50 -0
- package/dist/tools/get-logs.js.map +1 -0
- package/dist/tools/get-network.d.ts +12 -0
- package/dist/tools/get-network.d.ts.map +1 -0
- package/dist/tools/get-network.js +35 -0
- package/dist/tools/get-network.js.map +1 -0
- package/dist/tools/get-performance.d.ts +10 -0
- package/dist/tools/get-performance.d.ts.map +1 -0
- package/dist/tools/get-performance.js +32 -0
- package/dist/tools/get-performance.js.map +1 -0
- package/dist/tools/get-state.d.ts +11 -0
- package/dist/tools/get-state.d.ts.map +1 -0
- package/dist/tools/get-state.js +26 -0
- package/dist/tools/get-state.js.map +1 -0
- package/dist/tools/get-status.d.ts +20 -0
- package/dist/tools/get-status.d.ts.map +1 -0
- package/dist/tools/get-status.js +52 -0
- package/dist/tools/get-status.js.map +1 -0
- package/dist/tools/search-logs.d.ts +11 -0
- package/dist/tools/search-logs.d.ts.map +1 -0
- package/dist/tools/search-logs.js +68 -0
- package/dist/tools/search-logs.js.map +1 -0
- package/dist/types.d.ts +123 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +65 -0
- package/scripts/install.sh +33 -0
- package/wiretap.config.json +8 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jaqueline Gonzaga
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="assets/wiretap-logo.webp" alt="Wiretap" width="280" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">Wiretap</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<strong>Real-time visibility into your React Native app for Claude Code.</strong>
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="https://www.npmjs.com/package/wiretap-mcp"><img src="https://img.shields.io/npm/v/wiretap-mcp?style=flat-square&color=cb3837" alt="npm version" /></a>
|
|
13
|
+
<a href="https://www.npmjs.com/package/wiretap-mcp"><img src="https://img.shields.io/npm/dm/wiretap-mcp?style=flat-square&color=blue" alt="npm downloads" /></a>
|
|
14
|
+
<a href="https://github.com/gonzagajaque/wiretap-mcp/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green?style=flat-square" alt="license" /></a>
|
|
15
|
+
<a href="https://nodejs.org/"><img src="https://img.shields.io/badge/node-%3E%3D18-brightgreen?style=flat-square" alt="node version" /></a>
|
|
16
|
+
<a href="https://modelcontextprotocol.io/"><img src="https://img.shields.io/badge/MCP-compatible-8A2BE2?style=flat-square" alt="MCP compatible" /></a>
|
|
17
|
+
</p>
|
|
18
|
+
|
|
19
|
+
<p align="center">
|
|
20
|
+
<a href="#installation">Installation</a> •
|
|
21
|
+
<a href="#how-it-works">How It Works</a> •
|
|
22
|
+
<a href="#tools">Tools</a> •
|
|
23
|
+
<a href="#usage">Usage</a> •
|
|
24
|
+
<a href="#configuration">Configuration</a>
|
|
25
|
+
</p>
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## The Problem
|
|
30
|
+
|
|
31
|
+
When debugging React Native with Claude Code, you constantly copy-paste logs, describe errors manually, and lose context switching between terminal, Reactotron, and your editor. Claude is powerful, but blind to what your app is actually doing.
|
|
32
|
+
|
|
33
|
+
## The Solution
|
|
34
|
+
|
|
35
|
+
Wiretap is an MCP server that taps directly into every signal your app emits. Terminal logs, JS runtime output, network traffic, state changes, native crashes. Claude Code sees it all in real time, and you interact with it through natural language.
|
|
36
|
+
|
|
37
|
+
No copy-paste. No context switching. Just ask.
|
|
38
|
+
|
|
39
|
+
## Features
|
|
40
|
+
|
|
41
|
+
**Zero copy-paste.** Claude reads your Metro and Expo logs directly.
|
|
42
|
+
|
|
43
|
+
**JS runtime logs.** `console.log`, `console.warn`, and `console.error` captured from Hermes via Chrome DevTools Protocol.
|
|
44
|
+
|
|
45
|
+
**Network inspector.** HTTP requests captured via CDP with no proxy or in-app setup needed.
|
|
46
|
+
|
|
47
|
+
**Reactotron integration.** API calls, Redux and Zustand state, and benchmarks flow into Claude's context automatically.
|
|
48
|
+
|
|
49
|
+
**Native crash reports.** iOS crash reports and Android crashes captured with full stack traces.
|
|
50
|
+
|
|
51
|
+
**Persistent logs.** Optionally persist logs to disk so they survive server restarts.
|
|
52
|
+
|
|
53
|
+
**Always watching.** Auto-detects Metro and Expo processes, reconnects to Reactotron and Hermes on its own.
|
|
54
|
+
|
|
55
|
+
## Installation
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
claude mcp add wiretap -- npx wiretap-mcp
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
After restarting Claude Code, ask:
|
|
62
|
+
|
|
63
|
+
> "What's the app status?"
|
|
64
|
+
|
|
65
|
+
You should see connection info for all active monitors.
|
|
66
|
+
|
|
67
|
+
## How It Works
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
┌─────────────────────────────────────────────────────────┐
|
|
71
|
+
│ Your RN App │
|
|
72
|
+
│ │
|
|
73
|
+
│ Metro/Expo Hermes Runtime Reactotron Native │
|
|
74
|
+
│ │ │ │ │ │
|
|
75
|
+
└───────┼──────────────┼───────────────┼────────────┼─────┘
|
|
76
|
+
│ │ │ │
|
|
77
|
+
▼ ▼ ▼ ▼
|
|
78
|
+
┌─────────────────────────────────────────────────────────┐
|
|
79
|
+
│ │
|
|
80
|
+
│ ⚡ Wiretap MCP │
|
|
81
|
+
│ │
|
|
82
|
+
│ Terminal Hermes/CDP Reactotron Crash │
|
|
83
|
+
│ Monitor Monitor Monitor Monitor │
|
|
84
|
+
│ │
|
|
85
|
+
└────────────────────────┬────────────────────────────────┘
|
|
86
|
+
│
|
|
87
|
+
▼
|
|
88
|
+
┌──────────────┐
|
|
89
|
+
│ Claude Code │
|
|
90
|
+
│ │
|
|
91
|
+
│ "What went │
|
|
92
|
+
│ wrong?" │
|
|
93
|
+
└──────────────┘
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Tools
|
|
97
|
+
|
|
98
|
+
| Tool | Description |
|
|
99
|
+
|------|-------------|
|
|
100
|
+
| `get_terminal_logs` | Metro/Expo terminal output with severity filtering |
|
|
101
|
+
| `get_hermes_logs` | JS runtime logs (`console.log/warn/error`) from Hermes via CDP |
|
|
102
|
+
| `get_network_calls` | HTTP network calls via CDP, filterable by URL and status code |
|
|
103
|
+
| `get_reactotron_logs` | Reactotron logs filtered by type (api, state, log, benchmark) |
|
|
104
|
+
| `get_errors` | Consolidated errors from terminal, Reactotron, and Hermes |
|
|
105
|
+
| `get_api_calls` | HTTP requests from Reactotron, filterable by endpoint |
|
|
106
|
+
| `get_state_changes` | Redux/MobX/Zustand dispatches with before/after diffs |
|
|
107
|
+
| `get_native_crashes` | Native crash reports from iOS and Android |
|
|
108
|
+
| `get_app_status` | Dashboard with all monitor statuses, error counts, and uptime |
|
|
109
|
+
| `get_performance` | Benchmark and render time metrics from Reactotron |
|
|
110
|
+
| `search_logs` | Regex search across all log sources |
|
|
111
|
+
| `clear_logs` | Reset all buffers |
|
|
112
|
+
|
|
113
|
+
## Usage
|
|
114
|
+
|
|
115
|
+
Once installed, just talk to Claude naturally:
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
"What errors happened in the last few minutes?"
|
|
119
|
+
"Show me the failing API calls"
|
|
120
|
+
"What's the JS console output?"
|
|
121
|
+
"Show me the network requests to /api/auth"
|
|
122
|
+
"Did the app crash natively?"
|
|
123
|
+
"What state changes happened after the login action?"
|
|
124
|
+
"Search the logs for 'timeout'"
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Configuration
|
|
128
|
+
|
|
129
|
+
Create a `wiretap.config.json` in your project root. All fields are optional. Defaults work out of the box.
|
|
130
|
+
|
|
131
|
+
```json
|
|
132
|
+
{
|
|
133
|
+
"reactotronPort": 9090,
|
|
134
|
+
"metroPort": 8081,
|
|
135
|
+
"autoDetectTerminal": true
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### All Options
|
|
140
|
+
|
|
141
|
+
| Option | Default | Description |
|
|
142
|
+
|--------|---------|-------------|
|
|
143
|
+
| `reactotronPort` | `9090` | WebSocket port for Reactotron |
|
|
144
|
+
| `terminalLogBufferSize` | `500` | Max terminal log entries in memory |
|
|
145
|
+
| `reactotronLogBufferSize` | `500` | Max Reactotron entries in memory |
|
|
146
|
+
| `autoDetectTerminal` | `true` | Auto-detect Metro/Expo processes |
|
|
147
|
+
| `filterPatterns` | `[]` | Regex patterns to exclude from logs |
|
|
148
|
+
| `hermesEnabled` | `true` | Capture JS runtime logs via Hermes/CDP |
|
|
149
|
+
| `metroPort` | `8081` | Metro bundler port for CDP discovery |
|
|
150
|
+
| `cdpCaptureNetwork` | `true` | Capture HTTP network calls via CDP |
|
|
151
|
+
| `persistLogs` | `false` | Persist logs to disk (JSONL) |
|
|
152
|
+
| `persistPath` | `~/.wiretap` | Directory for persisted logs |
|
|
153
|
+
| `persistFlushIntervalMs` | `5000` | Flush interval in ms |
|
|
154
|
+
| `persistMaxEntriesOnLoad` | `200` | Max entries to reload on startup |
|
|
155
|
+
| `iosCrashMonitorEnabled` | `true` | Watch iOS crash reports (macOS only) |
|
|
156
|
+
| `iosCrashReportPath` | `~/Library/Logs/DiagnosticReports` | iOS crash report directory |
|
|
157
|
+
| `androidCrashMonitorEnabled` | `true` | Monitor Android crashes via adb |
|
|
158
|
+
| `adbPath` | `adb` | Path to adb binary |
|
|
159
|
+
|
|
160
|
+
## Requirements
|
|
161
|
+
|
|
162
|
+
- Node.js >= 18
|
|
163
|
+
- Claude Code
|
|
164
|
+
- React Native project with Metro or Expo
|
|
165
|
+
- Reactotron (optional)
|
|
166
|
+
- adb (optional, for Android crash monitoring)
|
|
167
|
+
|
|
168
|
+
## Contributing
|
|
169
|
+
|
|
170
|
+
Contributions are welcome. Please open an issue first to discuss what you would like to change.
|
|
171
|
+
|
|
172
|
+
## License
|
|
173
|
+
|
|
174
|
+
[MIT](LICENSE)
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
5
|
+
const server_js_1 = require("./server.js");
|
|
6
|
+
const fs_1 = require("fs");
|
|
7
|
+
const path_1 = require("path");
|
|
8
|
+
function loadConfig() {
|
|
9
|
+
const defaults = {
|
|
10
|
+
reactotronPort: 9090,
|
|
11
|
+
terminalLogBufferSize: 500,
|
|
12
|
+
reactotronLogBufferSize: 500,
|
|
13
|
+
autoDetectTerminal: true,
|
|
14
|
+
watchPaths: [],
|
|
15
|
+
filterPatterns: [],
|
|
16
|
+
// Persistence
|
|
17
|
+
persistLogs: false,
|
|
18
|
+
persistPath: "~/.wiretap",
|
|
19
|
+
persistFlushIntervalMs: 5000,
|
|
20
|
+
persistMaxEntriesOnLoad: 200,
|
|
21
|
+
// Hermes/CDP
|
|
22
|
+
hermesEnabled: true,
|
|
23
|
+
metroPort: 8081,
|
|
24
|
+
cdpCaptureNetwork: true,
|
|
25
|
+
// iOS crash monitor
|
|
26
|
+
iosCrashMonitorEnabled: true,
|
|
27
|
+
iosCrashReportPath: "~/Library/Logs/DiagnosticReports",
|
|
28
|
+
// Android crash monitor
|
|
29
|
+
androidCrashMonitorEnabled: true,
|
|
30
|
+
adbPath: "adb",
|
|
31
|
+
};
|
|
32
|
+
// Try wiretap.config.json first, fallback to legacy rn-companion.config.json
|
|
33
|
+
for (const filename of ["wiretap.config.json", "rn-companion.config.json"]) {
|
|
34
|
+
try {
|
|
35
|
+
const configPath = (0, path_1.resolve)(process.cwd(), filename);
|
|
36
|
+
const raw = (0, fs_1.readFileSync)(configPath, "utf-8");
|
|
37
|
+
const userConfig = JSON.parse(raw);
|
|
38
|
+
return { ...defaults, ...userConfig };
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// Config file not found, try next
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return defaults;
|
|
45
|
+
}
|
|
46
|
+
async function main() {
|
|
47
|
+
const config = loadConfig();
|
|
48
|
+
const { server, shutdown } = (0, server_js_1.createCompanionServer)(config);
|
|
49
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
50
|
+
await server.connect(transport);
|
|
51
|
+
const handleShutdown = () => {
|
|
52
|
+
process.stderr.write("[wiretap] Shutting down...\n");
|
|
53
|
+
shutdown();
|
|
54
|
+
process.exit(0);
|
|
55
|
+
};
|
|
56
|
+
process.on("SIGINT", handleShutdown);
|
|
57
|
+
process.on("SIGTERM", handleShutdown);
|
|
58
|
+
process.stderr.write("[wiretap] MCP server started\n");
|
|
59
|
+
process.stderr.write(`[wiretap] Reactotron port: ${config.reactotronPort}\n`);
|
|
60
|
+
process.stderr.write(`[wiretap] Auto-detect terminal: ${config.autoDetectTerminal}\n`);
|
|
61
|
+
process.stderr.write(`[wiretap] Hermes/CDP: ${config.hermesEnabled ? "enabled" : "disabled"}\n`);
|
|
62
|
+
process.stderr.write(`[wiretap] Persistence: ${config.persistLogs ? "enabled" : "disabled"}\n`);
|
|
63
|
+
}
|
|
64
|
+
main().catch((err) => {
|
|
65
|
+
process.stderr.write(`[wiretap] Fatal error: ${err}\n`);
|
|
66
|
+
process.exit(1);
|
|
67
|
+
});
|
|
68
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { EventEmitter } from "events";
|
|
2
|
+
import { LogStore } from "../storage/log-store.js";
|
|
3
|
+
export declare class AndroidCrashMonitor extends EventEmitter {
|
|
4
|
+
private store;
|
|
5
|
+
private adbPath;
|
|
6
|
+
private logcatProcess;
|
|
7
|
+
private _active;
|
|
8
|
+
private fatalBuffer;
|
|
9
|
+
private collectingFatal;
|
|
10
|
+
constructor(store: LogStore, adbPath?: string);
|
|
11
|
+
get isActive(): boolean;
|
|
12
|
+
start(): void;
|
|
13
|
+
stop(): void;
|
|
14
|
+
private isAdbAvailable;
|
|
15
|
+
private processLine;
|
|
16
|
+
private flushFatalBuffer;
|
|
17
|
+
/** Extract the message portion from a logcat line, stripping timestamp/pid/tag prefix */
|
|
18
|
+
private extractMessage;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=crash-android.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crash-android.d.ts","sourceRoot":"","sources":["../../src/monitors/crash-android.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAMnD,qBAAa,mBAAoB,SAAQ,YAAY;IACnD,OAAO,CAAC,KAAK,CAAW;IACxB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,WAAW,CAAgB;IACnC,OAAO,CAAC,eAAe,CAAS;gBAEpB,KAAK,EAAE,QAAQ,EAAE,OAAO,SAAQ;IAM5C,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,KAAK,IAAI,IAAI;IAyCb,IAAI,IAAI,IAAI;IASZ,OAAO,CAAC,cAAc;IAatB,OAAO,CAAC,WAAW;IAsCnB,OAAO,CAAC,gBAAgB;IAiBxB,yFAAyF;IACzF,OAAO,CAAC,cAAc;CAOvB"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AndroidCrashMonitor = void 0;
|
|
4
|
+
const events_1 = require("events");
|
|
5
|
+
const child_process_1 = require("child_process");
|
|
6
|
+
const crash_parser_js_1 = require("../parsers/crash-parser.js");
|
|
7
|
+
class AndroidCrashMonitor extends events_1.EventEmitter {
|
|
8
|
+
store;
|
|
9
|
+
adbPath;
|
|
10
|
+
logcatProcess = null;
|
|
11
|
+
_active = false;
|
|
12
|
+
fatalBuffer = [];
|
|
13
|
+
collectingFatal = false;
|
|
14
|
+
constructor(store, adbPath = "adb") {
|
|
15
|
+
super();
|
|
16
|
+
this.store = store;
|
|
17
|
+
this.adbPath = adbPath;
|
|
18
|
+
}
|
|
19
|
+
get isActive() {
|
|
20
|
+
return this._active;
|
|
21
|
+
}
|
|
22
|
+
start() {
|
|
23
|
+
if (!this.isAdbAvailable()) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
// Use logcat with error-level filter
|
|
28
|
+
this.logcatProcess = (0, child_process_1.spawn)(this.adbPath, [
|
|
29
|
+
"logcat",
|
|
30
|
+
"*:E",
|
|
31
|
+
"-v",
|
|
32
|
+
"threadtime",
|
|
33
|
+
]);
|
|
34
|
+
this.logcatProcess.stdout?.on("data", (data) => {
|
|
35
|
+
const lines = data.toString().split("\n").filter(Boolean);
|
|
36
|
+
for (const line of lines) {
|
|
37
|
+
this.processLine(line);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
this.logcatProcess.stderr?.on("data", () => {
|
|
41
|
+
// adb errors — ignore
|
|
42
|
+
});
|
|
43
|
+
this.logcatProcess.on("error", () => {
|
|
44
|
+
this._active = false;
|
|
45
|
+
});
|
|
46
|
+
this.logcatProcess.on("exit", () => {
|
|
47
|
+
this._active = false;
|
|
48
|
+
this.logcatProcess = null;
|
|
49
|
+
});
|
|
50
|
+
this._active = true;
|
|
51
|
+
this.emit("started");
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
this._active = false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
stop() {
|
|
58
|
+
if (this.logcatProcess) {
|
|
59
|
+
this.logcatProcess.kill();
|
|
60
|
+
this.logcatProcess = null;
|
|
61
|
+
}
|
|
62
|
+
this._active = false;
|
|
63
|
+
this.flushFatalBuffer();
|
|
64
|
+
}
|
|
65
|
+
isAdbAvailable() {
|
|
66
|
+
try {
|
|
67
|
+
(0, child_process_1.execSync)(`${this.adbPath} devices`, {
|
|
68
|
+
encoding: "utf-8",
|
|
69
|
+
timeout: 5000,
|
|
70
|
+
stdio: "pipe",
|
|
71
|
+
});
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
processLine(line) {
|
|
79
|
+
// Detect FATAL EXCEPTION from AndroidRuntime
|
|
80
|
+
if (/AndroidRuntime.*FATAL EXCEPTION/.test(line) || /FATAL EXCEPTION:/.test(line)) {
|
|
81
|
+
// Flush any previous fatal buffer
|
|
82
|
+
this.flushFatalBuffer();
|
|
83
|
+
this.collectingFatal = true;
|
|
84
|
+
this.fatalBuffer = [this.extractMessage(line)];
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
// Continue collecting fatal exception lines
|
|
88
|
+
if (this.collectingFatal) {
|
|
89
|
+
const msg = this.extractMessage(line);
|
|
90
|
+
// Fatal exception block ends when we see a non-continuation line
|
|
91
|
+
if (msg.startsWith("\t") ||
|
|
92
|
+
msg.startsWith(" ") ||
|
|
93
|
+
/^\s*(at |Caused by|\.\.\.)\s/.test(msg) ||
|
|
94
|
+
/^\s*[\w.]+(?:Exception|Error)/.test(msg)) {
|
|
95
|
+
this.fatalBuffer.push(msg);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
this.flushFatalBuffer();
|
|
100
|
+
// Continue processing this line below
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Detect native signals (SIGSEGV, SIGABRT, etc.)
|
|
104
|
+
if (/Fatal signal|libc\s*:\s*Fatal signal/.test(line)) {
|
|
105
|
+
const parsed = (0, crash_parser_js_1.parseAndroidNativeSignal)(this.extractMessage(line));
|
|
106
|
+
if (parsed) {
|
|
107
|
+
const entry = this.store.crashLogs.add(parsed);
|
|
108
|
+
this.emit("crash", entry);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
flushFatalBuffer() {
|
|
113
|
+
if (!this.collectingFatal || this.fatalBuffer.length === 0) {
|
|
114
|
+
this.collectingFatal = false;
|
|
115
|
+
this.fatalBuffer = [];
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const parsed = (0, crash_parser_js_1.parseAndroidFatalException)(this.fatalBuffer);
|
|
119
|
+
if (parsed) {
|
|
120
|
+
const entry = this.store.crashLogs.add(parsed);
|
|
121
|
+
this.emit("crash", entry);
|
|
122
|
+
}
|
|
123
|
+
this.collectingFatal = false;
|
|
124
|
+
this.fatalBuffer = [];
|
|
125
|
+
}
|
|
126
|
+
/** Extract the message portion from a logcat line, stripping timestamp/pid/tag prefix */
|
|
127
|
+
extractMessage(line) {
|
|
128
|
+
// Logcat threadtime format: MM-DD HH:MM:SS.mmm PID TID level tag: message
|
|
129
|
+
const match = line.match(/^\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}\.\d{3}\s+\d+\s+\d+\s+\w\s+[\w./-]+\s*:\s*(.*)/);
|
|
130
|
+
return match?.[1] ?? line;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
exports.AndroidCrashMonitor = AndroidCrashMonitor;
|
|
134
|
+
//# sourceMappingURL=crash-android.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crash-android.js","sourceRoot":"","sources":["../../src/monitors/crash-android.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { EventEmitter } from "events";
|
|
2
|
+
import { LogStore } from "../storage/log-store.js";
|
|
3
|
+
export declare class IOSCrashMonitor extends EventEmitter {
|
|
4
|
+
private store;
|
|
5
|
+
private watchPath;
|
|
6
|
+
private watcher;
|
|
7
|
+
private _active;
|
|
8
|
+
private processedFiles;
|
|
9
|
+
constructor(store: LogStore, crashReportPath: string);
|
|
10
|
+
get isActive(): boolean;
|
|
11
|
+
start(): void;
|
|
12
|
+
stop(): void;
|
|
13
|
+
private isCrashFile;
|
|
14
|
+
private scanExisting;
|
|
15
|
+
private processCrashFile;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=crash-ios.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crash-ios.d.ts","sourceRoot":"","sources":["../../src/monitors/crash-ios.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAGtC,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAGnD,qBAAa,eAAgB,SAAQ,YAAY;IAC/C,OAAO,CAAC,KAAK,CAAW;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,cAAc,CAAqB;gBAE/B,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM;IAcpD,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,KAAK,IAAI,IAAI;IAkCb,IAAI,IAAI,IAAI;IAQZ,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,YAAY;IA8BpB,OAAO,CAAC,gBAAgB;CAgBzB"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.IOSCrashMonitor = void 0;
|
|
4
|
+
const events_1 = require("events");
|
|
5
|
+
const fs_1 = require("fs");
|
|
6
|
+
const path_1 = require("path");
|
|
7
|
+
const crash_parser_js_1 = require("../parsers/crash-parser.js");
|
|
8
|
+
class IOSCrashMonitor extends events_1.EventEmitter {
|
|
9
|
+
store;
|
|
10
|
+
watchPath;
|
|
11
|
+
watcher = null;
|
|
12
|
+
_active = false;
|
|
13
|
+
processedFiles = new Set();
|
|
14
|
+
constructor(store, crashReportPath) {
|
|
15
|
+
super();
|
|
16
|
+
this.store = store;
|
|
17
|
+
// Resolve ~ to home dir
|
|
18
|
+
if (crashReportPath.startsWith("~")) {
|
|
19
|
+
this.watchPath = (0, path_1.join)(process.env.HOME || "/tmp", crashReportPath.slice(1));
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
this.watchPath = crashReportPath;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
get isActive() {
|
|
26
|
+
return this._active;
|
|
27
|
+
}
|
|
28
|
+
start() {
|
|
29
|
+
// Only run on macOS
|
|
30
|
+
if (process.platform !== "darwin") {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
// Scan recent crash files on startup
|
|
35
|
+
this.scanExisting();
|
|
36
|
+
// Watch for new crash files
|
|
37
|
+
this.watcher = (0, fs_1.watch)(this.watchPath, (eventType, filename) => {
|
|
38
|
+
if (!filename)
|
|
39
|
+
return;
|
|
40
|
+
if (eventType === "rename" && this.isCrashFile(filename)) {
|
|
41
|
+
// Small delay to let the OS finish writing
|
|
42
|
+
setTimeout(() => {
|
|
43
|
+
this.processCrashFile((0, path_1.join)(this.watchPath, filename));
|
|
44
|
+
}, 500);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
this.watcher.on("error", () => {
|
|
48
|
+
// Directory might not exist or permission denied
|
|
49
|
+
this._active = false;
|
|
50
|
+
});
|
|
51
|
+
this._active = true;
|
|
52
|
+
this.emit("started");
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// Watch failed — directory might not exist
|
|
56
|
+
this._active = false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
stop() {
|
|
60
|
+
if (this.watcher) {
|
|
61
|
+
this.watcher.close();
|
|
62
|
+
this.watcher = null;
|
|
63
|
+
}
|
|
64
|
+
this._active = false;
|
|
65
|
+
}
|
|
66
|
+
isCrashFile(filename) {
|
|
67
|
+
return filename.endsWith(".crash") || filename.endsWith(".ips");
|
|
68
|
+
}
|
|
69
|
+
scanExisting() {
|
|
70
|
+
try {
|
|
71
|
+
const files = (0, fs_1.readdirSync)(this.watchPath)
|
|
72
|
+
.filter((f) => this.isCrashFile(f))
|
|
73
|
+
.map((f) => {
|
|
74
|
+
const fullPath = (0, path_1.join)(this.watchPath, f);
|
|
75
|
+
try {
|
|
76
|
+
const stat = (0, fs_1.statSync)(fullPath);
|
|
77
|
+
return { path: fullPath, mtime: stat.mtimeMs };
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
})
|
|
83
|
+
.filter((f) => f !== null)
|
|
84
|
+
.sort((a, b) => b.mtime - a.mtime);
|
|
85
|
+
// Only process crashes from last 24 hours, max 10
|
|
86
|
+
const cutoff = Date.now() - 24 * 60 * 60 * 1000;
|
|
87
|
+
const recent = files
|
|
88
|
+
.filter((f) => f.mtime > cutoff)
|
|
89
|
+
.slice(0, 10);
|
|
90
|
+
for (const file of recent) {
|
|
91
|
+
this.processCrashFile(file.path);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
// Can't read directory
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
processCrashFile(filePath) {
|
|
99
|
+
if (this.processedFiles.has(filePath))
|
|
100
|
+
return;
|
|
101
|
+
this.processedFiles.add(filePath);
|
|
102
|
+
try {
|
|
103
|
+
const content = (0, fs_1.readFileSync)(filePath, "utf-8");
|
|
104
|
+
const parsed = (0, crash_parser_js_1.parseIOSCrashReport)(content, filePath);
|
|
105
|
+
if (parsed) {
|
|
106
|
+
const entry = this.store.crashLogs.add(parsed);
|
|
107
|
+
this.emit("crash", entry);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
// Can't read file
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
exports.IOSCrashMonitor = IOSCrashMonitor;
|
|
116
|
+
//# sourceMappingURL=crash-ios.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crash-ios.js","sourceRoot":"","sources":["../../src/monitors/crash-ios.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { EventEmitter } from "events";
|
|
2
|
+
import { LogStore } from "../storage/log-store.js";
|
|
3
|
+
export declare class HermesMonitor extends EventEmitter {
|
|
4
|
+
private store;
|
|
5
|
+
private metroPort;
|
|
6
|
+
private captureNetwork;
|
|
7
|
+
private ws;
|
|
8
|
+
private reconnectTimer;
|
|
9
|
+
private discoveryTimer;
|
|
10
|
+
private _connected;
|
|
11
|
+
private shouldReconnect;
|
|
12
|
+
private pendingRequests;
|
|
13
|
+
private cdpMessageId;
|
|
14
|
+
constructor(store: LogStore, metroPort?: number, captureNetwork?: boolean);
|
|
15
|
+
get isConnected(): boolean;
|
|
16
|
+
start(): void;
|
|
17
|
+
stop(): void;
|
|
18
|
+
private startDiscovery;
|
|
19
|
+
private discover;
|
|
20
|
+
private connectToTarget;
|
|
21
|
+
private enableDomains;
|
|
22
|
+
private sendCDP;
|
|
23
|
+
private scheduleReconnect;
|
|
24
|
+
private handleMessage;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=hermes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hermes.d.ts","sourceRoot":"","sources":["../../src/monitors/hermes.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAiBnD,qBAAa,aAAc,SAAQ,YAAY;IAC7C,OAAO,CAAC,KAAK,CAAW;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,cAAc,CAAU;IAChC,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,eAAe,CAAQ;IAC/B,OAAO,CAAC,eAAe,CAAqC;IAC5D,OAAO,CAAC,YAAY,CAAK;gBAEb,KAAK,EAAE,QAAQ,EAAE,SAAS,SAAO,EAAE,cAAc,UAAO;IAOpE,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED,KAAK,IAAI,IAAI;IAKb,IAAI,IAAI,IAAI;IAiBZ,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,QAAQ;IAmChB,OAAO,CAAC,eAAe;IA0CvB,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,OAAO;IAMf,OAAO,CAAC,iBAAiB;IAYzB,OAAO,CAAC,aAAa;CAwBtB"}
|