diff --git a/.claude/skills/bdg/SKILL.md b/.claude/skills/bdg/SKILL.md index 0870c5df..4f5af85c 100644 --- a/.claude/skills/bdg/SKILL.md +++ b/.claude/skills/bdg/SKILL.md @@ -20,8 +20,8 @@ bdg # Start session (1920x1080, headless if no display) bdg --headless # Force headless mode bdg --no-headless # Force visible browser window bdg status # Check session status -bdg peek # Preview data without stopping -bdg stop # Stop and save output +bdg peek # Preview collected telemetry +bdg stop # End session (use sparingly) bdg cleanup --force # Kill stale session bdg cleanup --aggressive # Kill all Chrome processes ``` @@ -36,6 +36,8 @@ bdg peek # Preview collected data # No need to stop/restart - Chrome stays on the page ``` +**Don't stop sessions prematurely** - use `bdg peek` to inspect data. Only call `bdg stop` when completely done with browser automation. + ## Screenshots Always use `bdg dom screenshot` (raw CDP is blocked): diff --git a/docs/CLI_REFERENCE.md b/docs/CLI_REFERENCE.md index 42aef5dd..b5df9280 100644 --- a/docs/CLI_REFERENCE.md +++ b/docs/CLI_REFERENCE.md @@ -34,7 +34,7 @@ bdg peek # Last 10 items (compact format) bdg peek --last 50 # Show last 50 items bdg peek --network # Show only network requests bdg peek --console # Show only console messages -bdg peek --dom # Show DOM/A11y tree (available after stop) +bdg peek --dom # Show DOM/A11y tree bdg peek --type Document # Filter by resource type (Document requests only) bdg peek --type XHR,Fetch # Multiple types (XHR or Fetch requests) bdg peek --json # JSON output diff --git a/package-lock.json b/package-lock.json index 3e5b0c15..053813ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "browser-debugger-cli", - "version": "0.7.0", + "version": "0.7.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "browser-debugger-cli", - "version": "0.7.0", + "version": "0.7.1", "license": "MIT", "dependencies": { "chrome-launcher": "^1.2.1", diff --git a/src/commands/network/shared.ts b/src/commands/network/shared.ts index c4b422c9..11f6616c 100644 --- a/src/commands/network/shared.ts +++ b/src/commands/network/shared.ts @@ -4,8 +4,6 @@ * Contains getCookies and headers commands, plus shared data fetching utilities. */ -import * as fs from 'fs'; - import type { Command } from 'commander'; import { runCommand } from '@/commands/shared/CommandRunner.js'; @@ -16,12 +14,9 @@ import type { } from '@/commands/shared/optionTypes.js'; import { getHARData, callCDP, getNetworkHeaders } from '@/ipc/client.js'; import { validateIPCResponse } from '@/ipc/index.js'; -import { getSessionFilePath } from '@/session/paths.js'; -import type { BdgOutput, NetworkRequest } from '@/types.js'; -import { isDaemonConnectionError } from '@/ui/errors/utils.js'; +import type { NetworkRequest } from '@/types.js'; import type { Cookie } from '@/ui/formatters/index.js'; import { formatCookies, formatNetworkHeaders } from '@/ui/formatters/index.js'; -import { sessionNotActiveError } from '@/ui/messages/errors.js'; import { EXIT_CODES } from '@/utils/exitCodes.js'; /** @@ -42,63 +37,13 @@ export async function fetchFromLiveSession(): Promise { } /** - * Fetch network requests from offline session.json file. + * Get network requests from live daemon session. * * @returns Network requests array - * @throws Error if session file not found or no network data available - */ -export function fetchFromOfflineSession(): NetworkRequest[] { - const sessionPath = getSessionFilePath('OUTPUT'); - - if (!fs.existsSync(sessionPath)) { - throw new Error(sessionNotActiveError('export network data'), { - cause: { code: EXIT_CODES.RESOURCE_NOT_FOUND }, - }); - } - - const sessionData = JSON.parse(fs.readFileSync(sessionPath, 'utf-8')) as BdgOutput; - - if (!sessionData.data?.network) { - throw new Error('No network data in session file', { - cause: { code: EXIT_CODES.RESOURCE_NOT_FOUND }, - }); - } - - return sessionData.data.network; -} - -/** - * Check if error indicates daemon is unavailable. - * - * @param error - Error to check - * @returns True if error indicates no active session or daemon connection failure - */ -export function isDaemonUnavailable(error: unknown): boolean { - if (isDaemonConnectionError(error)) { - return true; - } - - const errorMessage = error instanceof Error ? error.message : String(error); - return errorMessage.includes('No active session'); -} - -/** - * Get network requests from live session or session.json. - * - * Tries live daemon first, falls back to offline session file. - * - * @returns Network requests array - * @throws Error if no session available (live or offline) + * @throws Error if daemon connection fails or no network data available */ export async function getNetworkRequests(): Promise { - try { - return await fetchFromLiveSession(); - } catch (error) { - if (isDaemonUnavailable(error)) { - return fetchFromOfflineSession(); - } - throw error; - } + return fetchFromLiveSession(); } /** diff --git a/src/commands/stop.ts b/src/commands/stop.ts index 8df74b76..855f7102 100644 --- a/src/commands/stop.ts +++ b/src/commands/stop.ts @@ -7,7 +7,6 @@ import type { StopResult } from '@/commands/types.js'; import { stopSession } from '@/ipc/client.js'; import { IPCErrorCode } from '@/ipc/index.js'; import { performSessionCleanup } from '@/session/cleanup.js'; -import { getSessionFilePath } from '@/session/paths.js'; import { joinLines } from '@/ui/formatting.js'; import { createLogger } from '@/ui/logging/index.js'; import { @@ -28,7 +27,7 @@ const log = createLogger('cleanup'); * @param data - Stop result data */ function formatStop(data: StopResult): string { - const outputLine = data.stopped.bdg ? sessionStopped(getSessionFilePath('OUTPUT')) : undefined; + const outputLine = data.stopped.bdg ? sessionStopped() : undefined; const daemonsLine = data.stopped.daemons && data.orphanedDaemonsCount ? orphanedDaemonsCleanedMessage(data.orphanedDaemonsCount) @@ -50,13 +49,9 @@ function formatStop(data: StopResult): string { export function registerStopCommand(program: Command): void { program .command('stop') - .description('Stop daemon and write collected telemetry to ~/.bdg/session.json') + .description('Stop daemon and close browser session') .option('--kill-chrome', 'Also kill Chrome browser process', false) .addOption(jsonOption()) - .addHelpText( - 'after', - '\nOutput Location:\n Default: ~/.bdg/session.json\n Tip: Copy to custom location with: cp ~/.bdg/session.json /path/to/output.json' - ) .action(async (options: StopCommandOptions) => { await runCommand( async (opts) => { diff --git a/src/daemon/lifecycle/workerCleanup.ts b/src/daemon/lifecycle/workerCleanup.ts index 320da9b5..18533a24 100644 --- a/src/daemon/lifecycle/workerCleanup.ts +++ b/src/daemon/lifecycle/workerCleanup.ts @@ -1,13 +1,12 @@ /** * Worker Cleanup * - * Handles worker cleanup: DOM collection, CDP closure, Chrome termination, and output writing. + * Handles worker cleanup: DOM collection, CDP closure, and Chrome termination. */ import type { CDPConnection } from '@/connection/cdp.js'; import type { TelemetryStore } from '@/daemon/worker/TelemetryStore.js'; import { writeChromePid } from '@/session/chrome.js'; -import { writeSessionOutput } from '@/session/output.js'; import { collectDOM } from '@/telemetry/dom.js'; import type { CleanupFunction, LaunchedChrome } from '@/types'; import type { Logger } from '@/ui/logging/index.js'; @@ -19,7 +18,6 @@ import { workerRunningCleanup, workerClosingCDP, workerShutdownComplete, - workerWritingOutput, } from '@/ui/messages/debug.js'; import { delay } from '@/utils/async.js'; import { getErrorMessage } from '@/utils/errors.js'; @@ -100,8 +98,6 @@ export async function cleanupWorker( } // If both chrome and cdp are null, Chrome launch failed - nothing to terminate - writeOutput(reason, telemetryStore, log); - log.debug(workerShutdownComplete()); } catch (error) { console.error(`[worker] Error during cleanup: ${getErrorMessage(error)}`); @@ -150,30 +146,3 @@ async function terminateChrome( console.error(`[worker] Error killing Chrome: ${getErrorMessage(error)}`); } } - -/** - * Write session output. - */ -function writeOutput( - reason: 'normal' | 'crash' | 'timeout', - telemetryStore: TelemetryStore, - log: Logger -): void { - if (reason === 'normal') { - try { - log.debug(workerWritingOutput()); - const finalOutput = telemetryStore.buildOutput(false); - writeSessionOutput(finalOutput); - } catch (error) { - console.error(`[worker] Error writing final output: ${getErrorMessage(error)}`); - } - } else { - try { - log.debug(`[worker] Writing partial output (reason: ${reason})`); - const partialOutput = telemetryStore.buildOutput(true); - writeSessionOutput(partialOutput); - } catch (error) { - console.error(`[worker] Error writing partial output: ${getErrorMessage(error)}`); - } - } -} diff --git a/src/ipc/client.ts b/src/ipc/client.ts index 00aca578..f98f5db5 100644 --- a/src/ipc/client.ts +++ b/src/ipc/client.ts @@ -173,7 +173,7 @@ export async function startSession( /** * Request session stop from the daemon. - * Stops telemetry collection, closes Chrome, and writes output file. + * Stops telemetry collection and closes Chrome. * * @returns Stop session response with termination status * @throws Error if connection fails, times out, or no active session diff --git a/src/session/output.ts b/src/session/output.ts deleted file mode 100644 index 53efb1ca..00000000 --- a/src/session/output.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Session output file management. - * - * Handles writing final session output to disk. - * Note: Live preview/details now use IPC streaming instead of file writes. - */ - -import type { BdgOutput } from '@/types'; -import { AtomicFileWriter } from '@/utils/atomicFile.js'; - -import { getSessionFilePath, ensureSessionDir } from './paths.js'; - -/** - * Write session output to the final JSON file. - * - * This is written once at the end of a session. - * Note: Preview/details data is now accessed via IPC streaming during collection. - * - * @param output - The BdgOutput data to write - * @param compact - If true, use compact JSON format (no indentation) - */ -export function writeSessionOutput(output: BdgOutput, compact: boolean = false): void { - ensureSessionDir(); - const outputPath = getSessionFilePath('OUTPUT'); - const jsonString = compact ? JSON.stringify(output) : JSON.stringify(output, null, 2); - AtomicFileWriter.writeSync(outputPath, jsonString); -} diff --git a/src/ui/formatters/sessionFormatters.ts b/src/ui/formatters/sessionFormatters.ts index 29319a42..71c2dcc1 100644 --- a/src/ui/formatters/sessionFormatters.ts +++ b/src/ui/formatters/sessionFormatters.ts @@ -84,7 +84,7 @@ export function buildSessionManagementSection(): string { return section('Session Management:', [ 'bdg status Check session state', 'bdg status --verbose Include Chrome diagnostics', - 'bdg stop End session & save output', + 'bdg stop End session', 'bdg cleanup Clean stale sessions', ]); } diff --git a/src/ui/formatters/status.ts b/src/ui/formatters/status.ts index cce215b5..2140ea32 100644 --- a/src/ui/formatters/status.ts +++ b/src/ui/formatters/status.ts @@ -114,9 +114,9 @@ export function formatSessionStatus( fmt .blank() .section('Commands:', [ - 'Stop session: bdg stop', 'Peek data: bdg peek', 'Query browser: bdg query