start.ts•6.18 kB
import { z } from 'zod';
import { Logger } from '../server/logger';
import { MetricsCollector } from '../server/metrics';
import { SessionManager } from '../server/session-manager';
import { createNodeJSAdapter } from '../adapters/nodejs-adapter';
import { DapStartRequest, DapStartResponse } from '../schemas/dap-tools.schemas';
/**
* dap.start tool implementation
*
* Handles both launch and attach configurations for Node.js debugging
*/
export class DapStartTool {
private logger: Logger;
// private config: ConfigManager; // Unused for now
private metrics: MetricsCollector;
private sessionManager: SessionManager;
constructor() {
this.logger = new Logger('dap.start');
// this.config = ConfigManager.getInstance(); // Unused for now
this.metrics = MetricsCollector.getInstance();
this.sessionManager = new SessionManager();
}
/**
* Execute the dap.start tool
*/
async execute(args: any): Promise<DapStartResponse> {
this.metrics.startTimer('dap.start.tool');
this.metrics.increment('dap.start.count');
try {
// Validate input
const validatedArgs = this.validateArgs(args);
this.logger.info('Starting debugging session', {
program: validatedArgs.program,
processId: validatedArgs.processId,
});
// Handle launch configuration (assume launch for now)
return await this.handleLaunch(validatedArgs);
} catch (error) {
this.logger.error('dap.start failed:', error);
this.metrics.increment('dap.start.error.count');
return this.createErrorResponse((error as Error).message);
} finally {
this.metrics.stopTimer('dap.start.tool');
}
}
/**
* Validate input arguments
*/
private validateArgs(args: any): DapStartRequest['arguments'] {
const schema = z.object({
type: z.enum(['launch', 'attach']),
program: z.string().optional(),
processId: z.number().optional(),
cwd: z.string().optional(),
args: z.array(z.string()).optional(),
noDebug: z.boolean().default(false),
sourceMaps: z.boolean().default(true),
skipFiles: z.array(z.string()).optional(),
console: z.enum(['internalConsole', 'integratedTerminal', 'externalTerminal']).optional(),
});
const parsed = schema.parse(args);
return {
...parsed,
__type__: 'LaunchRequestArguments' as const,
};
}
/**
* Handle launch configuration
*/
private async handleLaunch(config: DapStartRequest['arguments']): Promise<DapStartResponse> {
// Validate launch configuration
if (!config.program) {
throw new Error('program is required for launch debugging');
}
this.logger.debug('Starting Node.js launch debugging', { program: config.program });
// Create Node.js adapter
const adapter = createNodeJSAdapter();
await adapter.initialize();
// Create DAP launch request
const launchRequest: DapStartRequest = {
type: 'request',
seq: 1,
command: 'launch',
arguments: {
__type__: 'LaunchRequestArguments',
program: config.program,
cwd: config.cwd || process.cwd(),
args: config.args || [],
noDebug: config.noDebug,
sourceMaps: config.sourceMaps,
skipFiles: config.skipFiles || [],
console: config.console || 'internalConsole',
// Node.js specific
runtimeExecutable: 'node',
runtimeArgs: [],
},
};
// Launch through session manager
const { sessionId, capabilities } =
await this.sessionManager.handleLaunchRequest(launchRequest);
// Start adapter
await adapter.launch(launchRequest);
// Update session with adapter info
await this.sessionManager.activateSession(sessionId, 1);
this.logger.info('Debugging session started', { sessionId, program: config.program });
return this.createSuccessResponse({
sessionId,
capabilities,
});
}
/**
* Handle attach configuration
*/
// private async handleAttach(config: DapStartRequest['arguments']): Promise<DapStartResponse> { // Unused for now
// // Validate attach configuration
// if (!config.processId) {
// throw new Error('processId is required for attach debugging');
// }
//
// this.logger.debug('Starting Node.js attach debugging', { processId: config.processId });
//
// // Create Node.js adapter
// const adapter = createNodeJSAdapter();
// await adapter.initialize();
//
// // Create DAP attach request
// const attachRequest = {
// type: 'request' as const,
// seq: 1,
// command: 'attach',
// arguments: {
// __type__: 'AttachRequestArguments',
// processId: config.processId,
// cwd: config.cwd || process.cwd(),
// sourceMaps: config.sourceMaps,
// skipFiles: config.skipFiles || [],
// },
// };
//
// // Attach through session manager
// const { sessionId, capabilities } = await this.sessionManager.handleAttachRequest(attachRequest);
//
// // Start adapter
// await adapter.attach(attachRequest);
//
// // Update session with adapter info
// await this.sessionManager.activateSession(sessionId, 1);
//
// this.logger.info('Debugging session attached', { sessionId, processId: config.processId });
//
// return this.createSuccessResponse({
// sessionId,
// capabilities,
// });
// }
/**
* Create success response
*/
private createSuccessResponse(body: any): any {
return {
type: 'response' as const,
seq: 1,
command: 'launch',
request_seq: 1,
success: true,
body,
};
}
/**
* Create error response
*/
private createErrorResponse(message: string): any {
return {
type: 'response' as const,
seq: 1,
command: 'launch',
request_seq: 1,
success: false,
message,
body: {},
};
}
}
// Singleton instance
export const dapStartTool = new DapStartTool();
// Tool execution function
export async function executeDapStart(args: any): Promise<DapStartResponse> {
return await dapStartTool.execute(args);
}