75766dbfdc
- 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>
180 lines
4.1 KiB
JavaScript
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')
|
|
})
|