breakpoints.ts•6.01 kB
import { z } from 'zod';
import { Logger } from '../server/logger';
import { MetricsCollector } from '../server/metrics';
import { DapSetBreakpointsRequest, DapSetBreakpointsResponse } from '../schemas/dap-tools.schemas';
/**
* dap.setBreakpoints tool implementation
*
* Manages breakpoints across debugging sessions
*/
export class DapSetBreakpointsTool {
private logger: Logger;
private metrics: MetricsCollector;
private sessionBreakpoints: Map<string, Map<string, number[]>> = new Map();
constructor() {
this.logger = new Logger('dap.setBreakpoints');
this.metrics = MetricsCollector.getInstance();
}
/**
* Execute the dap.setBreakpoints tool
*/
async execute(args: any): Promise<DapSetBreakpointsResponse> {
this.metrics.startTimer('dap.setBreakpoints.tool');
this.metrics.increment('dap.setBreakpoints.count');
try {
// Validate input
const validatedArgs = this.validateArgs(args);
this.logger.debug('Setting breakpoints', {
source: validatedArgs.source,
breakpoints: validatedArgs.breakpoints,
});
// Store breakpoints for session
await this.storeBreakpoints(validatedArgs);
// Create response with verification status
const verifiedBreakpoints = this.createVerifiedBreakpoints(validatedArgs.breakpoints);
this.logger.info('Breakpoints set successfully', {
source: validatedArgs.source?.path || 'unknown',
count: verifiedBreakpoints.length,
});
return this.createSuccessResponse({
breakpoints: verifiedBreakpoints,
verified: true,
});
} catch (error) {
this.logger.error('dap.setBreakpoints failed:', error);
this.metrics.increment('dap.setBreakpoints.error.count');
return this.createErrorResponse((error as Error).message);
} finally {
this.metrics.stopTimer('dap.setBreakpoints.tool');
}
}
/**
* Validate input arguments
*/
private validateArgs(args: any): DapSetBreakpointsRequest['arguments'] {
const schema = z.object({
source: z
.object({
path: z.string().optional(),
name: z.string().optional(),
sourceReference: z.number().optional(),
adapterData: z.any().optional(),
})
.optional(),
breakpoints: z
.array(
z.object({
id: z.number().optional(),
line: z.number(),
column: z.number().default(0),
endLine: z.number().optional(),
endColumn: z.number().optional(),
condition: z.string().optional(),
hitCondition: z.string().optional(),
logMessage: z.string().optional(),
verified: z.boolean().optional(),
message: z.string().optional(),
src: z.string().optional(),
encodedMd5: z.string().optional(),
adapterData: z.any().optional(),
})
)
.optional(),
lines: z.array(z.number()).optional(),
sourceModified: z.boolean().optional(),
// Deprecated fields for backward compatibility
breakpoint: z
.object({
id: z.number().optional(),
line: z.number(),
column: z.number().default(0),
condition: z.string().optional(),
hitCondition: z.string().optional(),
logMessage: z.string().optional(),
verified: z.boolean().optional(),
message: z.string().optional(),
})
.optional(),
});
const parsed = schema.parse(args);
return {
...parsed,
__type__: 'SetBreakpointsArguments' as const,
};
}
/**
* Store breakpoints for session
*/
private async storeBreakpoints(args: DapSetBreakpointsRequest['arguments']): Promise<void> {
// Extract session ID from source path or use default
const sessionId = args.source?.path || 'default-session';
if (!this.sessionBreakpoints.has(sessionId)) {
this.sessionBreakpoints.set(sessionId, new Map());
}
const sessionBreakpointMap = this.sessionBreakpoints.get(sessionId)!;
// Store breakpoints by source path
const sourcePath = args.source?.path || 'unknown';
const lines = args.breakpoints?.map(bp => bp.line) || [];
sessionBreakpointMap.set(sourcePath, lines);
this.logger.debug('Breakpoints stored', {
sessionId,
sourcePath,
lines,
});
}
/**
* Create verified breakpoints response
*/
private createVerifiedBreakpoints(inputBreakpoints: any[] = []) {
return inputBreakpoints.map((breakpoint, index) => ({
...breakpoint,
id: breakpoint.id || index + 1,
verified: true,
message: `Breakpoint set at line ${breakpoint.line}`,
src: breakpoint.src || `file://${breakpoint.line}`,
encodedMd5: '', // Placeholder for MD5
}));
}
/**
* Get breakpoints for a session
*/
getSessionBreakpoints(sessionId: string): Map<string, number[]> {
return this.sessionBreakpoints.get(sessionId) || new Map();
}
/**
* Clear breakpoints for a session
*/
clearSessionBreakpoints(sessionId: string): void {
this.sessionBreakpoints.delete(sessionId);
}
/**
* Create success response
*/
private createSuccessResponse(body: any): DapSetBreakpointsResponse {
return {
type: 'response',
seq: 1,
command: 'setBreakpoints',
request_seq: 1,
success: true,
body,
};
}
/**
* Create error response
*/
private createErrorResponse(message: string): DapSetBreakpointsResponse {
return {
type: 'response',
seq: 1,
command: 'setBreakpoints',
request_seq: 1,
success: false,
message,
body: {},
};
}
}
// Singleton instance
export const dapSetBreakpointsTool = new DapSetBreakpointsTool();
// Tool execution function
export async function executeDapSetBreakpoints(args: any): Promise<DapSetBreakpointsResponse> {
return await dapSetBreakpointsTool.execute(args);
}