feat: OpenCode-style session management implementation

This commit is contained in:
uroma
2026-01-20 16:26:03 +00:00
Unverified
parent 5638f7ca23
commit 94f1725675
5 changed files with 892 additions and 133 deletions

107
server.js
View File

@@ -500,17 +500,38 @@ app.get('/claude/api/recent', requireAuth, (req, res) => {
// ============================================
// Session Management
// GET /claude/api/claude/sessions?project=/encoded/path
app.get('/claude/api/claude/sessions', requireAuth, (req, res) => {
try {
const activeSessions = claudeService.listSessions();
const historicalSessions = claudeService.loadHistoricalSessions();
const { project } = req.query;
let activeSessions = claudeService.listSessions();
let historicalSessions = claudeService.loadHistoricalSessions();
// PROJECT FILTERING
if (project) {
const projectPath = decodeURIComponent(project);
console.log('[SESSIONS] Filtering by project path:', projectPath);
activeSessions = activeSessions.filter(s => {
const sessionPath = s.workingDir || '';
return sessionPath.startsWith(projectPath) || sessionPath === projectPath;
});
historicalSessions = historicalSessions.filter(s => {
const sessionPath = s.workingDir || '';
return sessionPath.startsWith(projectPath) || sessionPath === projectPath;
});
console.log('[SESSIONS] Filtered to', activeSessions.length, 'active,', historicalSessions.length, 'historical');
}
res.json({
active: activeSessions,
historical: historicalSessions
});
} catch (error) {
console.error('Error listing sessions:', error);
console.error('[SESSIONS] Error:', error);
res.status(500).json({ error: 'Failed to list sessions' });
}
});
@@ -1608,46 +1629,46 @@ app.post('/claude/api/terminals/:id/input', requireAuth, (req, res) => {
res.status(500).json({ error: 'Failed to send input' });
}
});
// Get terminal output via HTTP polling (bypasses WebSocket issue)
app.get('/claude/api/terminals/:id/output', requireAuth, (req, res) => {
try {
const sinceIndex = parseInt(req.query.since) || 0;
const result = terminalService.getTerminalOutput(req.params.id, sinceIndex);
if (result.success) {
res.json(result);
} else {
res.status(404).json({ error: result.error });
}
} catch (error) {
console.error('Error getting terminal output:', error);
res.status(500).json({ error: 'Failed to get output' });
}
});
// Resize terminal via HTTP
app.post('/claude/api/terminals/:id/resize', requireAuth, (req, res) => {
try {
const { cols, rows } = req.body;
if (!cols || !rows) {
res.status(400).json({ error: 'Missing cols or rows parameter' });
return;
}
const result = terminalService.resizeTerminal(req.params.id, cols, rows);
if (result.success) {
res.json({ success: true });
} else {
res.status(404).json({ error: result.error });
}
} catch (error) {
console.error('Error resizing terminal:', error);
res.status(500).json({ error: 'Failed to resize terminal' });
}
});
// Get terminal output via HTTP polling (bypasses WebSocket issue)
app.get('/claude/api/terminals/:id/output', requireAuth, (req, res) => {
try {
const sinceIndex = parseInt(req.query.since) || 0;
const result = terminalService.getTerminalOutput(req.params.id, sinceIndex);
if (result.success) {
res.json(result);
} else {
res.status(404).json({ error: result.error });
}
} catch (error) {
console.error('Error getting terminal output:', error);
res.status(500).json({ error: 'Failed to get output' });
}
});
// Resize terminal via HTTP
app.post('/claude/api/terminals/:id/resize', requireAuth, (req, res) => {
try {
const { cols, rows } = req.body;
if (!cols || !rows) {
res.status(400).json({ error: 'Missing cols or rows parameter' });
return;
}
const result = terminalService.resizeTerminal(req.params.id, cols, rows);
if (result.success) {
res.json({ success: true });
} else {
res.status(404).json({ error: result.error });
}
} catch (error) {
console.error('Error resizing terminal:', error);
res.status(500).json({ error: 'Failed to resize terminal' });
}
});
// Get recent directories for terminal picker
app.get('/claude/api/files/recent-dirs', requireAuth, (req, res) => {