Files
cherry-studio/cagents/python-cli-web-poc/server.js
T
Vaayne 75766dbfdc feat: Add POC command page structure and routing
- Created CommandPocPage.tsx with basic layout structure
- Added POC-specific TypeScript interfaces and types
- Implemented basic UI components: PocHeader, PocMessageList, PocMessageBubble, PocCommandInput, PocStatusBar
- Added /command-poc route to Router.tsx
- Set up component folder structure following PRD specifications

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-03 11:43:02 +08:00

180 lines
4.1 KiB
JavaScript

const express = require('express')
const WebSocket = require('ws')
const { spawn } = require('child_process')
const path = require('path')
const http = require('http')
const app = express()
const server = http.createServer(app)
const wss = new WebSocket.Server({ server })
const PORT = process.env.PORT || 3000
// Serve static files from public directory
app.use(express.static('public'))
// Route for main page
app.get('/', (req, res) => {
res.redirect('/index.html')
})
// Store active Python processes
const activeSessions = new Map()
wss.on('connection', (ws) => {
console.log('Client connected')
ws.on('message', (message) => {
try {
const data = JSON.parse(message)
switch (data.type) {
case 'run_command':
handlePythonCommand(ws, data.command, data.sessionId)
break
case 'send_input':
handleUserInput(ws, data.input, data.sessionId)
break
case 'kill_session':
killSession(data.sessionId)
break
}
} catch (error) {
ws.send(
JSON.stringify({
type: 'error',
message: 'Invalid message format'
})
)
}
})
ws.on('close', () => {
console.log('Client disconnected')
// Clean up any active sessions for this client
for (let [sessionId, session] of activeSessions) {
if (session.ws === ws) {
killSession(sessionId)
}
}
})
})
function handlePythonCommand(ws, command, sessionId) {
// Kill existing session if any
if (activeSessions.has(sessionId)) {
killSession(sessionId)
}
// Parse command (simple split for POC)
const args = command.trim().split(' ')
const pythonCommand = args[0] === 'python' || args[0] === 'python3' ? args[0] : 'python3'
const scriptArgs = args[0] === 'python' || args[0] === 'python3' ? args.slice(1) : args
try {
// Spawn Python process
const pythonProcess = spawn(pythonCommand, scriptArgs, {
stdio: ['pipe', 'pipe', 'pipe']
})
// Store session
activeSessions.set(sessionId, {
process: pythonProcess,
ws: ws
})
// Send confirmation
ws.send(
JSON.stringify({
type: 'command_started',
sessionId: sessionId,
command: command
})
)
// Handle stdout
pythonProcess.stdout.on('data', (data) => {
ws.send(
JSON.stringify({
type: 'output',
sessionId: sessionId,
data: data.toString(),
stream: 'stdout'
})
)
})
// Handle stderr
pythonProcess.stderr.on('data', (data) => {
ws.send(
JSON.stringify({
type: 'output',
sessionId: sessionId,
data: data.toString(),
stream: 'stderr'
})
)
})
// Handle process exit
pythonProcess.on('close', (code) => {
ws.send(
JSON.stringify({
type: 'command_finished',
sessionId: sessionId,
exitCode: code
})
)
activeSessions.delete(sessionId)
})
// Handle process error
pythonProcess.on('error', (error) => {
ws.send(
JSON.stringify({
type: 'error',
sessionId: sessionId,
message: `Failed to start process: ${error.message}`
})
)
activeSessions.delete(sessionId)
})
} catch (error) {
ws.send(
JSON.stringify({
type: 'error',
sessionId: sessionId,
message: `Error running command: ${error.message}`
})
)
}
}
function handleUserInput(ws, input, sessionId) {
const session = activeSessions.get(sessionId)
if (session && session.process) {
session.process.stdin.write(input + '\n')
} else {
ws.send(
JSON.stringify({
type: 'error',
sessionId: sessionId,
message: 'No active session to send input to'
})
)
}
}
function killSession(sessionId) {
const session = activeSessions.get(sessionId)
if (session && session.process) {
session.process.kill()
activeSessions.delete(sessionId)
}
}
server.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`)
console.log('Open your browser and navigate to the URL above')
})