mirror of
https://github.com/jlengrand/Maestro.git
synced 2026-03-10 15:51:18 +00:00
MAESTRO: Implement /api/session/:id/interrupt POST endpoint for session interruption
Add REST API endpoint to send SIGINT/Ctrl+C signal to sessions via the web interface. This allows mobile and desktop web clients to gracefully interrupt running AI agents or terminal processes. - Add InterruptSessionCallback type for session interrupt operations - Add setInterruptSessionCallback method to WebServer class - Create /api/session/:id/interrupt POST endpoint with authentication and rate limiting - Wire up callback in main process to use ProcessManager.interrupt()
This commit is contained in:
@@ -339,6 +339,12 @@ app.whenReady().then(() => {
|
||||
return processManager.write(sessionId, data);
|
||||
});
|
||||
|
||||
// Set up callback for web server to interrupt sessions
|
||||
webServer.setInterruptSessionCallback((sessionId: string) => {
|
||||
if (!processManager) return false;
|
||||
return processManager.interrupt(sessionId);
|
||||
});
|
||||
|
||||
// Initialize session web server manager with callbacks
|
||||
sessionWebServerManager = new SessionWebServerManager(
|
||||
// getSessionData callback - fetch session from store
|
||||
|
||||
@@ -98,6 +98,10 @@ export type GetSessionDetailCallback = (sessionId: string) => SessionDetail | nu
|
||||
// Returns true if successful, false if session not found or write failed
|
||||
export type WriteToSessionCallback = (sessionId: string, data: string) => boolean;
|
||||
|
||||
// Callback type for interrupting a session (sending SIGINT/Ctrl+C)
|
||||
// Returns true if successful, false if session not found or interrupt failed
|
||||
export type InterruptSessionCallback = (sessionId: string) => boolean;
|
||||
|
||||
// Theme type for web clients (matches renderer/types/index.ts)
|
||||
export interface WebTheme {
|
||||
id: string;
|
||||
@@ -142,6 +146,7 @@ export class WebServer {
|
||||
private getSessionDetailCallback: GetSessionDetailCallback | null = null;
|
||||
private getThemeCallback: GetThemeCallback | null = null;
|
||||
private writeToSessionCallback: WriteToSessionCallback | null = null;
|
||||
private interruptSessionCallback: InterruptSessionCallback | null = null;
|
||||
|
||||
constructor(port: number = 8000) {
|
||||
this.port = port;
|
||||
@@ -187,6 +192,14 @@ export class WebServer {
|
||||
this.writeToSessionCallback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the callback function for interrupting a session
|
||||
* This is called by the /api/session/:id/interrupt endpoint
|
||||
*/
|
||||
setInterruptSessionCallback(callback: InterruptSessionCallback) {
|
||||
this.interruptSessionCallback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the authentication configuration
|
||||
*/
|
||||
@@ -477,6 +490,62 @@ export class WebServer {
|
||||
};
|
||||
});
|
||||
|
||||
// Interrupt session endpoint - sends SIGINT/Ctrl+C to a specific session
|
||||
// Rate limited using POST rate limit config (more restrictive)
|
||||
this.server.post('/api/session/:id/interrupt', {
|
||||
preHandler: this.authenticateRequest.bind(this),
|
||||
config: {
|
||||
rateLimit: {
|
||||
max: this.rateLimitConfig.maxPost,
|
||||
timeWindow: this.rateLimitConfig.timeWindow,
|
||||
},
|
||||
},
|
||||
}, async (request, reply) => {
|
||||
const { id } = request.params as { id: string };
|
||||
|
||||
// Check if interrupt callback is configured
|
||||
if (!this.interruptSessionCallback) {
|
||||
reply.code(503).send({
|
||||
error: 'Service Unavailable',
|
||||
message: 'Session interrupt service not configured',
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if session exists first
|
||||
if (this.getSessionDetailCallback) {
|
||||
const session = this.getSessionDetailCallback(id);
|
||||
if (!session) {
|
||||
reply.code(404).send({
|
||||
error: 'Not Found',
|
||||
message: `Session with id '${id}' not found`,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Send interrupt signal to the session
|
||||
const success = this.interruptSessionCallback(id);
|
||||
|
||||
if (!success) {
|
||||
reply.code(500).send({
|
||||
error: 'Internal Server Error',
|
||||
message: 'Failed to interrupt session',
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
message: 'Interrupt signal sent successfully',
|
||||
sessionId: id,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
});
|
||||
|
||||
// Setup web interface routes under /web/* namespace
|
||||
this.setupWebInterfaceRoutes();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user