@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.
Files changed (41) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +558 -0
  3. package/package.json +53 -0
  4. package/src/agents.llm/browser-monitor-section.md +18 -0
  5. package/src/cli.mjs +202 -0
  6. package/src/http-server.mjs +536 -0
  7. package/src/init.mjs +162 -0
  8. package/src/intro.mjs +36 -0
  9. package/src/logging/LogBuffer.mjs +178 -0
  10. package/src/logging/constants.mjs +19 -0
  11. package/src/logging/dump.mjs +207 -0
  12. package/src/logging/index.mjs +13 -0
  13. package/src/logging/timestamps.mjs +13 -0
  14. package/src/monitor/README.md +10 -0
  15. package/src/monitor/index.mjs +18 -0
  16. package/src/monitor/interactive-mode.mjs +275 -0
  17. package/src/monitor/join-mode.mjs +654 -0
  18. package/src/monitor/open-mode.mjs +889 -0
  19. package/src/monitor/page-monitoring.mjs +199 -0
  20. package/src/monitor/tab-selection.mjs +53 -0
  21. package/src/monitor.mjs +39 -0
  22. package/src/os/README.md +4 -0
  23. package/src/os/wsl/chrome.mjs +503 -0
  24. package/src/os/wsl/detect.mjs +68 -0
  25. package/src/os/wsl/diagnostics.mjs +729 -0
  26. package/src/os/wsl/index.mjs +45 -0
  27. package/src/os/wsl/port-proxy.mjs +190 -0
  28. package/src/settings.mjs +101 -0
  29. package/src/templates/api-help.mjs +212 -0
  30. package/src/templates/cli-commands.mjs +51 -0
  31. package/src/templates/interactive-keys.mjs +33 -0
  32. package/src/templates/ready-help.mjs +33 -0
  33. package/src/templates/section-heading.mjs +141 -0
  34. package/src/templates/table-helper.mjs +73 -0
  35. package/src/templates/wait-for-chrome.mjs +19 -0
  36. package/src/utils/ask.mjs +49 -0
  37. package/src/utils/chrome-profile-path.mjs +37 -0
  38. package/src/utils/colors.mjs +49 -0
  39. package/src/utils/env.mjs +30 -0
  40. package/src/utils/profile-id.mjs +23 -0
  41. 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
+ })();