import { readFile, writeFile, access } from 'fs/promises';
import { join } from 'path';
import { v4 as uuidv4 } from 'uuid';
import { logger } from './logger.js';
const SESSION_FILE = '.claude_session_id';
/**
* Manages the .claude_session_id file in the project root
*/
export class SessionIdManager {
private projectRoot: string;
constructor(projectRoot: string) {
this.projectRoot = projectRoot;
}
private get sessionFilePath(): string {
return join(this.projectRoot, SESSION_FILE);
}
/**
* Checks if the session file exists
*/
async exists(): Promise<boolean> {
try {
await access(this.sessionFilePath);
return true;
} catch {
return false;
}
}
/**
* Reads the session ID from file, or creates a new one if it doesn't exist
*/
async getOrCreate(): Promise<string> {
try {
if (await this.exists()) {
const content = await readFile(this.sessionFilePath, 'utf-8');
const sessionId = content.trim();
// Validate UUID format
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
if (uuidRegex.test(sessionId)) {
logger.info('Session ID loaded from file', { sessionId });
return sessionId;
} else {
logger.warn('Invalid session ID format in file, creating new one');
}
}
// Create new session ID
const newSessionId = uuidv4();
await writeFile(this.sessionFilePath, newSessionId, 'utf-8');
logger.info('New session ID created', { sessionId: newSessionId });
return newSessionId;
} catch (error) {
logger.error('Failed to manage session ID file', { error });
throw new Error(`Session ID management failed: ${error}`);
}
}
/**
* Updates the session ID (useful for rollback scenarios)
*/
async update(sessionId: string): Promise<void> {
try {
await writeFile(this.sessionFilePath, sessionId, 'utf-8');
logger.info('Session ID updated', { sessionId });
} catch (error) {
logger.error('Failed to update session ID', { error });
throw new Error(`Failed to update session ID: ${error}`);
}
}
}