@romanmatena/browsermonitor 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 +558 -0
- package/package.json +53 -0
- package/src/agents.llm/browser-monitor-section.md +18 -0
- package/src/cli.mjs +202 -0
- package/src/http-server.mjs +536 -0
- package/src/init.mjs +162 -0
- package/src/intro.mjs +36 -0
- package/src/logging/LogBuffer.mjs +178 -0
- package/src/logging/constants.mjs +19 -0
- package/src/logging/dump.mjs +207 -0
- package/src/logging/index.mjs +13 -0
- package/src/logging/timestamps.mjs +13 -0
- package/src/monitor/README.md +10 -0
- package/src/monitor/index.mjs +18 -0
- package/src/monitor/interactive-mode.mjs +275 -0
- package/src/monitor/join-mode.mjs +654 -0
- package/src/monitor/open-mode.mjs +889 -0
- package/src/monitor/page-monitoring.mjs +199 -0
- package/src/monitor/tab-selection.mjs +53 -0
- package/src/monitor.mjs +39 -0
- package/src/os/README.md +4 -0
- package/src/os/wsl/chrome.mjs +503 -0
- package/src/os/wsl/detect.mjs +68 -0
- package/src/os/wsl/diagnostics.mjs +729 -0
- package/src/os/wsl/index.mjs +45 -0
- package/src/os/wsl/port-proxy.mjs +190 -0
- package/src/settings.mjs +101 -0
- package/src/templates/api-help.mjs +212 -0
- package/src/templates/cli-commands.mjs +51 -0
- package/src/templates/interactive-keys.mjs +33 -0
- package/src/templates/ready-help.mjs +33 -0
- package/src/templates/section-heading.mjs +141 -0
- package/src/templates/table-helper.mjs +73 -0
- package/src/templates/wait-for-chrome.mjs +19 -0
- package/src/utils/ask.mjs +49 -0
- package/src/utils/chrome-profile-path.mjs +37 -0
- package/src/utils/colors.mjs +49 -0
- package/src/utils/env.mjs +30 -0
- package/src/utils/profile-id.mjs +23 -0
- package/src/utils/status-line.mjs +47 -0
package/src/cli.mjs
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Browser Monitor CLI
|
|
4
|
+
*
|
|
5
|
+
* Global CLI tool – run `browsermonitor` in any project directory.
|
|
6
|
+
* Configuration in .browsermonitor/settings.json (created on first run).
|
|
7
|
+
*
|
|
8
|
+
* Subcommands:
|
|
9
|
+
* init → Run setup (create .browsermonitor/, update agent files)
|
|
10
|
+
*
|
|
11
|
+
* Mode is chosen by arguments:
|
|
12
|
+
* (none) → Interactive: menu (o = open, j = join, q = quit)
|
|
13
|
+
* --open → Open mode: launch new Chrome and monitor
|
|
14
|
+
* --join=PORT → Join mode: attach to existing Chrome at localhost:PORT
|
|
15
|
+
*
|
|
16
|
+
* Options:
|
|
17
|
+
* --realtime Write logs immediately (default: lazy)
|
|
18
|
+
* --headless Run in headless mode (default: GUI)
|
|
19
|
+
* --timeout=MS Hard timeout in ms (default: disabled)
|
|
20
|
+
* --nav-timeout=MS Navigation timeout in ms (default: from settings)
|
|
21
|
+
* --help Show help
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { runInteractiveMode, runJoinMode, runOpenMode } from './monitor.mjs';
|
|
25
|
+
import { printAppIntro } from './intro.mjs';
|
|
26
|
+
import { createHttpServer } from './http-server.mjs';
|
|
27
|
+
import { printApiHelpTable } from './templates/api-help.mjs';
|
|
28
|
+
import { printCliCommandsTable } from './templates/cli-commands.mjs';
|
|
29
|
+
import { askHttpPort } from './utils/ask.mjs';
|
|
30
|
+
import { loadSettings, isInitialized, getPaths, ensureDirectories } from './settings.mjs';
|
|
31
|
+
import { runInit } from './init.mjs';
|
|
32
|
+
|
|
33
|
+
// Project root = current working directory
|
|
34
|
+
const projectRoot = process.cwd();
|
|
35
|
+
|
|
36
|
+
// Parse arguments
|
|
37
|
+
const args = process.argv.slice(2);
|
|
38
|
+
|
|
39
|
+
// Handle `browsermonitor init` subcommand
|
|
40
|
+
if (args[0] === 'init') {
|
|
41
|
+
await runInit(projectRoot, { askForUrl: true, updateAgentFiles: true });
|
|
42
|
+
process.exit(0);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Show help
|
|
46
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
47
|
+
console.log(`
|
|
48
|
+
Browser Monitor – capture browser console, network, and DOM for debugging and LLM workflows.
|
|
49
|
+
|
|
50
|
+
What it does:
|
|
51
|
+
Connects to Chrome (via Puppeteer) and records console output, network requests, cookies,
|
|
52
|
+
and the current page HTML. Logs can be written to files on demand (lazy) or in real time.
|
|
53
|
+
Useful for debugging frontend apps, E2E flows, and feeding context to AI assistants
|
|
54
|
+
(e.g. read .browsermonitor/.puppeteer/dom.html for the live DOM).
|
|
55
|
+
|
|
56
|
+
`);
|
|
57
|
+
printCliCommandsTable({ showEntry: true, showUsage: true });
|
|
58
|
+
console.log(`
|
|
59
|
+
Subcommands:
|
|
60
|
+
init Run setup: create .browsermonitor/, settings.json, update agent files
|
|
61
|
+
|
|
62
|
+
Modes (chosen by flags; only one applies):
|
|
63
|
+
INTERACTIVE (default) No flag. Asks for project root, then menu:
|
|
64
|
+
o = open Chrome (launch and monitor)
|
|
65
|
+
j = join running Chrome (pick instance/tab)
|
|
66
|
+
q = quit
|
|
67
|
+
|
|
68
|
+
OPEN (--open) Launch a new Chrome and monitor it. URL = first positional or config.
|
|
69
|
+
Uses current directory for logs. Good for local dev with a fresh profile.
|
|
70
|
+
|
|
71
|
+
JOIN (--join=PORT) Attach to an existing Chrome with remote debugging on PORT.
|
|
72
|
+
Port is required (e.g. --join=9222). Use when Chrome is already
|
|
73
|
+
running (e.g. started by a script or on another machine via tunnel).
|
|
74
|
+
|
|
75
|
+
Options:
|
|
76
|
+
--port=PORT HTTP API port (default: from settings or 60001)
|
|
77
|
+
--realtime Write each event to files immediately (default: lazy, buffer in memory)
|
|
78
|
+
--headless Run Chrome without GUI
|
|
79
|
+
--open Go directly to open mode
|
|
80
|
+
--join=PORT Go directly to join mode (PORT required)
|
|
81
|
+
--timeout=MS Hard timeout in ms; process exits after (0 = disabled)
|
|
82
|
+
--nav-timeout=MS Navigation timeout in ms (default: from settings, 0 = no limit)
|
|
83
|
+
--help, -h Show this help
|
|
84
|
+
|
|
85
|
+
Config (.browsermonitor/settings.json):
|
|
86
|
+
defaultUrl, headless, navigationTimeout, ignorePatterns, httpPort, realtime
|
|
87
|
+
|
|
88
|
+
`);
|
|
89
|
+
printApiHelpTable({ port: 60001, showApi: true, showInteractive: false, showOutputFiles: true });
|
|
90
|
+
process.exit(0);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Auto-init on first run
|
|
94
|
+
if (!isInitialized(projectRoot)) {
|
|
95
|
+
console.log('[browsermonitor] First run detected. Setting up .browsermonitor/ ...');
|
|
96
|
+
await runInit(projectRoot, { askForUrl: process.stdin.isTTY, updateAgentFiles: true });
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Ensure directories exist (in case user deleted .puppeteer/ subdir)
|
|
100
|
+
ensureDirectories(projectRoot);
|
|
101
|
+
|
|
102
|
+
// Load settings from .browsermonitor/settings.json
|
|
103
|
+
const config = loadSettings(projectRoot);
|
|
104
|
+
const paths = getPaths(projectRoot);
|
|
105
|
+
|
|
106
|
+
// ---- Mode dispatch: --open | --join=PORT | interactive ----
|
|
107
|
+
const openMode = args.some((a) => a === '--open' || a.startsWith('--open='));
|
|
108
|
+
const joinArg = args.find((a) => a.startsWith('--join'));
|
|
109
|
+
let joinPort = null;
|
|
110
|
+
if (joinArg) {
|
|
111
|
+
if (joinArg === '--join' || !joinArg.includes('=')) {
|
|
112
|
+
console.error('Error: --join requires a port (e.g. --join=9222)');
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
const portStr = joinArg.split('=')[1];
|
|
116
|
+
joinPort = parseInt(portStr, 10);
|
|
117
|
+
if (Number.isNaN(joinPort) || joinPort < 1 || joinPort > 65535) {
|
|
118
|
+
console.error(`Error: invalid port for --join: ${portStr}`);
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Shared options (CLI args override settings.json)
|
|
124
|
+
const realtimeMode = args.includes('--realtime') || config.realtime;
|
|
125
|
+
const headlessCli = args.includes('--headless');
|
|
126
|
+
const timeoutArg = args.find((a) => a.startsWith('--timeout='));
|
|
127
|
+
const hardTimeout = timeoutArg ? parseInt(timeoutArg.split('=')[1], 10) : 0;
|
|
128
|
+
const navTimeoutArg = args.find((a) => a.startsWith('--nav-timeout='));
|
|
129
|
+
const navigationTimeout = navTimeoutArg
|
|
130
|
+
? parseInt(navTimeoutArg.split('=')[1], 10)
|
|
131
|
+
: (config.navigationTimeout !== undefined ? config.navigationTimeout : 60_000);
|
|
132
|
+
const urlFromArgs = args.find((a) => !a.startsWith('--'));
|
|
133
|
+
const url = urlFromArgs || config.defaultUrl || 'https://localhost:4000/';
|
|
134
|
+
const headless = headlessCli || config.headless || false;
|
|
135
|
+
const ignorePatterns = config.ignorePatterns || [];
|
|
136
|
+
const outputDir = projectRoot;
|
|
137
|
+
|
|
138
|
+
const DEFAULT_HTTP_PORT = config.httpPort || 60001;
|
|
139
|
+
const portArg = args.find((a) => a === '--port' || a.startsWith('--port='));
|
|
140
|
+
let httpPortFromArgs = null;
|
|
141
|
+
if (portArg) {
|
|
142
|
+
const val = portArg.includes('=') ? portArg.split('=')[1] : '';
|
|
143
|
+
const num = parseInt(val, 10);
|
|
144
|
+
if (!Number.isNaN(num) && num >= 1 && num <= 65535) httpPortFromArgs = num;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
(async () => {
|
|
148
|
+
printAppIntro();
|
|
149
|
+
|
|
150
|
+
printApiHelpTable({ port: DEFAULT_HTTP_PORT, showApi: true, showInteractive: false, showOutputFiles: true, noLeadingNewline: true });
|
|
151
|
+
|
|
152
|
+
const httpPort =
|
|
153
|
+
httpPortFromArgs ?? (process.stdin.isTTY ? await askHttpPort(DEFAULT_HTTP_PORT) : DEFAULT_HTTP_PORT);
|
|
154
|
+
|
|
155
|
+
const sharedHttpState = {
|
|
156
|
+
mode: 'interactive',
|
|
157
|
+
logBuffer: null,
|
|
158
|
+
getPages: () => [],
|
|
159
|
+
getCollectingPaused: () => false,
|
|
160
|
+
setCollectingPaused: () => {},
|
|
161
|
+
switchToTab: async () => ({ success: false, error: 'No browser connected' }),
|
|
162
|
+
getAllTabs: async () => [],
|
|
163
|
+
};
|
|
164
|
+
const sharedHttpServer = createHttpServer({
|
|
165
|
+
port: httpPort,
|
|
166
|
+
defaultPort: DEFAULT_HTTP_PORT,
|
|
167
|
+
getState: () => sharedHttpState,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const commonOptions = {
|
|
171
|
+
realtime: realtimeMode,
|
|
172
|
+
outputDir,
|
|
173
|
+
paths,
|
|
174
|
+
ignorePatterns,
|
|
175
|
+
hardTimeout,
|
|
176
|
+
httpPort,
|
|
177
|
+
sharedHttpState,
|
|
178
|
+
sharedHttpServer,
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
if (openMode) {
|
|
182
|
+
console.log(` [CLI] Open mode → ${url}`);
|
|
183
|
+
await runOpenMode(url, {
|
|
184
|
+
...commonOptions,
|
|
185
|
+
headless,
|
|
186
|
+
navigationTimeout,
|
|
187
|
+
});
|
|
188
|
+
} else if (joinPort !== null) {
|
|
189
|
+
console.log(` [CLI] Join mode → localhost:${joinPort}`);
|
|
190
|
+
await runJoinMode(joinPort, {
|
|
191
|
+
...commonOptions,
|
|
192
|
+
defaultUrl: url,
|
|
193
|
+
});
|
|
194
|
+
} else {
|
|
195
|
+
await runInteractiveMode({
|
|
196
|
+
...commonOptions,
|
|
197
|
+
defaultUrl: url,
|
|
198
|
+
headless,
|
|
199
|
+
navigationTimeout,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
})();
|