Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .claude/skills/bdg/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ bdg <url> # Start session (1920x1080, headless if no display)
bdg <url> --headless # Force headless mode
bdg <url> --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
```
Expand All @@ -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):
Expand Down
2 changes: 1 addition & 1 deletion docs/CLI_REFERENCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

63 changes: 4 additions & 59 deletions src/commands/network/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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';

/**
Expand All @@ -42,63 +37,13 @@ export async function fetchFromLiveSession(): Promise<NetworkRequest[]> {
}

/**
* 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<NetworkRequest[]> {
try {
return await fetchFromLiveSession();
} catch (error) {
if (isDaemonUnavailable(error)) {
return fetchFromOfflineSession();
}
throw error;
}
return fetchFromLiveSession();
}

/**
Expand Down
9 changes: 2 additions & 7 deletions src/commands/stop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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)
Expand All @@ -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<StopCommandOptions, StopResult>(
async (opts) => {
Expand Down
33 changes: 1 addition & 32 deletions src/daemon/lifecycle/workerCleanup.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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';
Expand Down Expand Up @@ -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)}`);
Expand Down Expand Up @@ -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)}`);
}
}
}
2 changes: 1 addition & 1 deletion src/ipc/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
27 changes: 0 additions & 27 deletions src/session/output.ts

This file was deleted.

2 changes: 1 addition & 1 deletion src/ui/formatters/sessionFormatters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
]);
}
Expand Down
2 changes: 1 addition & 1 deletion src/ui/formatters/status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,9 @@ export function formatSessionStatus(
fmt
.blank()
.section('Commands:', [
'Stop session: bdg stop',
'Peek data: bdg peek',
'Query browser: bdg query <script>',
'End session: bdg stop',
]);

return fmt.build();
Expand Down
4 changes: 2 additions & 2 deletions src/ui/messages/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const PREVIEW_HEADERS = {
* @returns Single-line tip for basic peek usage
*/
export function compactTipsMessage(): string {
return 'Tip: bdg stop | bdg peek --last 50 | bdg peek --verbose';
return 'Tip: bdg peek --last 50 | bdg peek --verbose';
}

/**
Expand All @@ -32,9 +32,9 @@ export function compactTipsMessage(): string {
export function verboseCommandsMessage(): string {
return [
'Commands:',
' Stop session: bdg stop',
' Full preview: bdg peek --last 50',
' Watch live: bdg tail',
' End session: bdg stop',
].join('\n');
}

Expand Down
5 changes: 2 additions & 3 deletions src/ui/messages/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,10 @@ export function landingPage(options: LandingPageOptions): string {
/**
* Generate "session stopped" success message.
*
* @param outputPath - Path to session output file
* @returns Formatted success message
*/
export function sessionStopped(outputPath: string): string {
return `Session stopped. Output saved to: ${outputPath}`;
export function sessionStopped(): string {
return 'Session stopped';
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/utils/decisionTrees.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ export const DECISION_TREES: Record<string, DecisionTree> = {
noAction: 'next',
},
{
question: 'Ready to end session and save output?',
question: 'Ready to end session?',
yesCommand: 'stop',
noAction: 'next',
},
Expand Down
28 changes: 10 additions & 18 deletions tests/agent-benchmark/scenarios/00-golden-cdp-workflow.sh
Original file line number Diff line number Diff line change
Expand Up @@ -168,32 +168,24 @@ log_success "Extracted $LINK_COUNT links"
record_metric "link_count" "$LINK_COUNT"

# ============================================================================
# SECTION 7: Stop Session and Validate Final Output
# SECTION 7: Validate Telemetry and Stop Session
# ============================================================================
log_step "Stopping session and validating final output"
log_step "Validating telemetry and stopping session"

bdg stop >/dev/null

log_success "Session stopped"

# Wait for session.json to be created and validated
SESSION_JSON_PATH="$HOME/.bdg/session.json"
if ! wait_for_session_json 10; then
die "session.json not created or invalid after bdg stop"
fi

# Extract final metrics
DURATION=$(jq -r '.duration' "$SESSION_JSON_PATH")
NET_REQUESTS=$(jq '.data.network | length' "$SESSION_JSON_PATH")
CONSOLE_MSGS=$(jq '.data.console | length' "$SESSION_JSON_PATH")
# Get telemetry data via peek before stopping
PEEK_DATA=$(bdg peek --json --last 0 2>/dev/null)
NET_REQUESTS=$(echo "$PEEK_DATA" | jq '.data.preview.data.network | length')
CONSOLE_MSGS=$(echo "$PEEK_DATA" | jq '.data.preview.data.console | length')

assert_gte "$NET_REQUESTS" 1 "Should have captured network requests"
log_success "Final session: ${DURATION}ms, $NET_REQUESTS requests, $CONSOLE_MSGS console messages"
log_success "Telemetry: $NET_REQUESTS requests, $CONSOLE_MSGS console messages"

record_metric "session_duration_ms" "$DURATION"
record_metric "network_requests" "$NET_REQUESTS"
record_metric "console_messages" "$CONSOLE_MSGS"

bdg stop >/dev/null
log_success "Session stopped"

# End timing
end_time=$(date +%s)
elapsed=$((end_time - start_time))
Expand Down