Skip to content

Commit d478a7b

Browse files
committed
refactor(agent-tars): refine interface of server, more clean
1 parent d58a40f commit d478a7b

File tree

3 files changed

+206
-86
lines changed

3 files changed

+206
-86
lines changed

packages/multimodal/agent-tars-cli/src/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*/
66

77
import cac from 'cac';
8-
import { startServer } from './server-ui';
8+
import { startInteractiveWebUI } from './interactive-ui';
99
import { startInteractiveCLI } from './interactive-cli';
1010

1111
const cli = cac('tars');
@@ -20,7 +20,7 @@ cli
2020
.action(async (options) => {
2121
const { port } = options;
2222
try {
23-
await startServer({ port: Number(port), uiMode: 'none' });
23+
await startInteractiveWebUI({ port: Number(port), uiMode: 'none' });
2424
} catch (err) {
2525
console.error('Failed to start server:', err);
2626
process.exit(1);
@@ -43,7 +43,7 @@ cli
4343
}
4444

4545
try {
46-
await startServer({ port: Number(port), uiMode });
46+
await startInteractiveWebUI({ port: Number(port), uiMode });
4747
} catch (err) {
4848
console.error('Failed to start server:', err);
4949
process.exit(1);

packages/multimodal/agent-tars-cli/src/server-ui.ts renamed to packages/multimodal/agent-tars-cli/src/interactive-ui.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import path from 'path';
77
import fs from 'fs';
88
import express from 'express';
99
import http from 'http';
10-
import { startServer as startTarsServer, ServerOptions } from '@agent-tars/server';
10+
import { AgentTARSServer, ServerOptions } from '@agent-tars/server';
1111

1212
interface UIServerOptions extends ServerOptions {
1313
uiMode: 'none' | 'plain' | 'interactive';
@@ -16,23 +16,24 @@ interface UIServerOptions extends ServerOptions {
1616
/**
1717
* Start the Agent TARS server with UI capabilities
1818
*/
19-
export async function startServer(options: UIServerOptions): Promise<http.Server> {
19+
export async function startInteractiveWebUI(options: UIServerOptions): Promise<http.Server> {
2020
const { port, uiMode } = options;
2121

22-
// Start base server first
23-
const server = await startTarsServer({ port });
24-
22+
// Create and start the server
23+
const tarsServer = new AgentTARSServer({ port });
24+
const server = await tarsServer.start();
25+
2526
// If UI mode is none, return the base server
2627
if (uiMode === 'none') {
2728
return server;
2829
}
29-
30-
// Get the Express app instance from the server
31-
const app = server.listeners('request')[0] as unknown as express.Application;
32-
30+
31+
// Get the Express app instance directly from the server
32+
const app = tarsServer.getApp();
33+
3334
// Set up UI based on mode
3435
setupUI(app, uiMode);
35-
36+
3637
return server;
3738
}
3839

@@ -56,7 +57,7 @@ function setupUI(app: express.Application, uiMode: 'plain' | 'interactive'): voi
5657
}
5758

5859
console.log(`Serving ${uiMode} UI from: ${staticPath}`);
59-
60+
6061
// Serve static files
6162
app.use(express.static(staticPath));
6263

packages/multimodal/agent-tars-server/src/server.ts

Lines changed: 191 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import express from 'express';
88
import http from 'http';
9-
import { Server } from 'socket.io';
9+
import { Server as SocketIOServer } from 'socket.io';
1010
import { AgentTARS } from '@agent-tars/core';
1111
import { EventStreamBridge } from './event-stream';
1212
import { EventType } from '@multimodal/agent';
@@ -78,96 +78,215 @@ export class AgentSession {
7878
}
7979
}
8080

81-
export async function startServer(options: ServerOptions): Promise<http.Server> {
82-
const { port } = options;
83-
const app = express();
84-
const server = http.createServer(app);
85-
const io = new Server(server);
81+
/**
82+
* Agent TARS Server class that provides an encapsulated interface
83+
* for creating and managing the server instance
84+
*/
85+
export class AgentTARSServer {
86+
private app: express.Application;
87+
private server: http.Server;
88+
private io: SocketIOServer;
89+
private sessions: Record<string, AgentSession> = {};
90+
private isRunning = false;
91+
private port: number;
8692

87-
// Store active agent sessions
88-
const sessions: Record<string, AgentSession> = {};
93+
/**
94+
* Create a new Agent TARS Server instance
95+
* @param options Server configuration options
96+
*/
97+
constructor(options: ServerOptions) {
98+
this.port = options.port;
99+
this.app = express();
100+
this.server = http.createServer(this.app);
101+
this.io = new SocketIOServer(this.server);
89102

90-
// Serve API endpoints
91-
app.use(express.json());
103+
this.setupServer();
104+
}
92105

93-
// Create new agent session
94-
app.post('/api/sessions', async (req, res) => {
95-
try {
96-
const sessionId = `session_${Date.now()}`;
97-
const workingDirectory = ensureWorkingDirectory(sessionId);
106+
/**
107+
* Get the Express application instance
108+
* @returns Express application
109+
*/
110+
getApp(): express.Application {
111+
return this.app;
112+
}
98113

99-
const session = new AgentSession(sessionId, workingDirectory);
100-
sessions[sessionId] = session;
114+
/**
115+
* Get the HTTP server instance
116+
* @returns HTTP server
117+
*/
118+
getHttpServer(): http.Server {
119+
return this.server;
120+
}
101121

102-
await session.initialize();
122+
/**
123+
* Get the Socket.IO server instance
124+
* @returns Socket.IO server
125+
*/
126+
getSocketIOServer(): SocketIOServer {
127+
return this.io;
128+
}
103129

104-
res.status(201).json({ sessionId });
105-
} catch (error) {
106-
console.error('Failed to create session:', error);
107-
res.status(500).json({ error: 'Failed to create session' });
108-
}
109-
});
130+
/**
131+
* Check if the server is currently running
132+
* @returns True if server is running
133+
*/
134+
isServerRunning(): boolean {
135+
return this.isRunning;
136+
}
110137

111-
// Send query to specified session
112-
app.post('/api/sessions/:sessionId/query', async (req, res) => {
113-
const { sessionId } = req.params;
114-
const { query } = req.body;
138+
/**
139+
* Get an active session by ID
140+
* @param sessionId The session ID to retrieve
141+
* @returns The agent session or undefined if not found
142+
*/
143+
getSession(sessionId: string): AgentSession | undefined {
144+
return this.sessions[sessionId];
145+
}
115146

116-
if (!sessions[sessionId]) {
117-
return res.status(404).json({ error: 'Session not found' });
118-
}
147+
/**
148+
* Get all active sessions
149+
* @returns Record of all sessions
150+
*/
151+
getAllSessions(): Record<string, AgentSession> {
152+
return { ...this.sessions };
153+
}
119154

120-
try {
121-
const result = await sessions[sessionId].runQuery(query);
122-
res.status(200).json({ result });
123-
} catch (error) {
124-
console.error(`Error processing query in session ${sessionId}:`, error);
125-
res.status(500).json({ error: 'Failed to process query' });
126-
}
127-
});
155+
/**
156+
* Set up server routes and socket handlers
157+
* @private
158+
*/
159+
private setupServer(): void {
160+
// Serve API endpoints
161+
this.app.use(express.json());
162+
163+
// Create new agent session
164+
this.app.post('/api/sessions', async (req, res) => {
165+
try {
166+
const sessionId = `session_${Date.now()}`;
167+
const workingDirectory = ensureWorkingDirectory(sessionId);
168+
169+
const session = new AgentSession(sessionId, workingDirectory);
170+
this.sessions[sessionId] = session;
128171

129-
// WebSocket connection handling
130-
io.on('connection', (socket) => {
131-
console.log('Client connected:', socket.id);
172+
await session.initialize();
132173

133-
socket.on('join-session', (sessionId) => {
134-
if (sessions[sessionId]) {
135-
socket.join(sessionId);
136-
console.log(`Client ${socket.id} joined session ${sessionId}`);
174+
res.status(201).json({ sessionId });
175+
} catch (error) {
176+
console.error('Failed to create session:', error);
177+
res.status(500).json({ error: 'Failed to create session' });
178+
}
179+
});
137180

138-
// Subscribe to session's event stream
139-
const eventHandler = (eventType: string, data: any) => {
140-
socket.emit('agent-event', { type: eventType, data });
141-
};
181+
// Send query to specified session
182+
this.app.post('/api/sessions/:sessionId/query', async (req, res) => {
183+
const { sessionId } = req.params;
184+
const { query } = req.body;
142185

143-
sessions[sessionId].eventBridge.subscribe(eventHandler);
186+
if (!this.sessions[sessionId]) {
187+
return res.status(404).json({ error: 'Session not found' });
188+
}
144189

145-
socket.on('disconnect', () => {
146-
sessions[sessionId].eventBridge.unsubscribe(eventHandler);
147-
});
148-
} else {
149-
socket.emit('error', 'Session not found');
190+
try {
191+
const result = await this.sessions[sessionId].runQuery(query);
192+
res.status(200).json({ result });
193+
} catch (error) {
194+
console.error(`Error processing query in session ${sessionId}:`, error);
195+
res.status(500).json({ error: 'Failed to process query' });
150196
}
151197
});
152198

153-
socket.on('send-query', async ({ sessionId, query }) => {
154-
if (sessions[sessionId]) {
155-
try {
156-
await sessions[sessionId].runQuery(query);
157-
} catch (error) {
158-
console.error('Error processing query:', error);
199+
// WebSocket connection handling
200+
this.io.on('connection', (socket) => {
201+
console.log('Client connected:', socket.id);
202+
203+
socket.on('join-session', (sessionId) => {
204+
if (this.sessions[sessionId]) {
205+
socket.join(sessionId);
206+
console.log(`Client ${socket.id} joined session ${sessionId}`);
207+
208+
// Subscribe to session's event stream
209+
const eventHandler = (eventType: string, data: any) => {
210+
socket.emit('agent-event', { type: eventType, data });
211+
};
212+
213+
this.sessions[sessionId].eventBridge.subscribe(eventHandler);
214+
215+
socket.on('disconnect', () => {
216+
if (this.sessions[sessionId]) {
217+
this.sessions[sessionId].eventBridge.unsubscribe(eventHandler);
218+
}
219+
});
220+
} else {
221+
socket.emit('error', 'Session not found');
159222
}
160-
} else {
161-
socket.emit('error', 'Session not found');
162-
}
223+
});
224+
225+
socket.on('send-query', async ({ sessionId, query }) => {
226+
if (this.sessions[sessionId]) {
227+
try {
228+
await this.sessions[sessionId].runQuery(query);
229+
} catch (error) {
230+
console.error('Error processing query:', error);
231+
}
232+
} else {
233+
socket.emit('error', 'Session not found');
234+
}
235+
});
163236
});
164-
});
237+
}
165238

166-
// Start server
167-
return new Promise((resolve) => {
168-
server.listen(port, () => {
169-
console.log(`🚀 Agent TARS Server is running at http://localhost:${port}`);
170-
resolve(server);
239+
/**
240+
* Start the server on the configured port
241+
* @returns Promise resolving with the server instance
242+
*/
243+
async start(): Promise<http.Server> {
244+
return new Promise((resolve) => {
245+
this.server.listen(this.port, () => {
246+
console.log(`🚀 Agent TARS Server is running at http://localhost:${this.port}`);
247+
this.isRunning = true;
248+
resolve(this.server);
249+
});
171250
});
172-
});
251+
}
252+
253+
/**
254+
* Stop the server and clean up all resources
255+
* @returns Promise resolving when server is stopped
256+
*/
257+
async stop(): Promise<void> {
258+
// Clean up all active sessions
259+
const sessionCleanup = Object.values(this.sessions).map((session) => session.cleanup());
260+
await Promise.all(sessionCleanup);
261+
262+
// Clear sessions
263+
this.sessions = {};
264+
265+
// Close server if running
266+
if (this.isRunning) {
267+
return new Promise((resolve, reject) => {
268+
this.server.close((err) => {
269+
if (err) {
270+
reject(err);
271+
return;
272+
}
273+
274+
this.isRunning = false;
275+
console.log('Server stopped');
276+
resolve();
277+
});
278+
});
279+
}
280+
281+
return Promise.resolve();
282+
}
283+
}
284+
285+
/**
286+
* Legacy function to maintain backward compatibility
287+
* @deprecated Use the `AgentTARSServer` class directly instead
288+
*/
289+
export async function startServer(options: ServerOptions): Promise<http.Server> {
290+
const server = new AgentTARSServer(options);
291+
return server.start();
173292
}

0 commit comments

Comments
 (0)