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>
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
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')
|
||||
})
|
||||
Reference in New Issue
Block a user