quality of life improvements

Signed-off-by: mudler <mudler@localai.io>
This commit is contained in:
mudler
2025-04-23 18:39:35 +02:00
parent bd903a3f33
commit 608d7aba85
2 changed files with 82 additions and 11 deletions

View File

@@ -1,5 +1,6 @@
GOCMD?=go
IMAGE_NAME?=webui
MCPBOX_IMAGE_NAME?=mcpbox
ROOT_DIR:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
prepare-tests:
@@ -23,10 +24,16 @@ build: webui/react-ui/dist
.PHONY: run
run: webui/react-ui/dist
$(GOCMD) run ./
LOCALAGI_MCPBOX_URL="http://localhost:9090" $(GOCMD) run ./
build-image:
docker build -t $(IMAGE_NAME) -f Dockerfile.webui .
image-push:
docker push $(IMAGE_NAME)
build-mcpbox:
docker build -t $(MCPBOX_IMAGE_NAME) -f Dockerfile.mcpbox .
run-mcpbox:
docker run -v /var/run/docker.sock:/var/run/docker.sock --privileged -p 9090:8080 -ti mcpbox

View File

@@ -1,6 +1,7 @@
package stdio
import (
"bytes"
"context"
"encoding/json"
"fmt"
@@ -306,47 +307,110 @@ func (s *Server) handleWebSocket(w http.ResponseWriter, r *http.Request) {
log.Printf("WebSocket connection established for process: %s", id)
// Create a done channel to signal process completion
done := make(chan struct{})
// Create buffers to capture output
var stdoutBuf, stderrBuf bytes.Buffer
stdoutTee := io.TeeReader(process.Stdout, &stdoutBuf)
stderrTee := io.TeeReader(process.Stderr, &stderrBuf)
// Handle stdin
go func() {
defer func() {
select {
case <-done:
// Process already done, this is expected
default:
log.Printf("WebSocket stdin connection closed for process %s", id)
}
}()
for {
_, message, err := conn.ReadMessage()
if err != nil {
log.Printf("WebSocket stdin read error for process %s: %v", id, err)
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseNormalClosure) {
log.Printf("WebSocket stdin unexpected error for process %s: %v", id, err)
}
return
}
if _, err := process.Stdin.Write(message); err != nil {
if err != io.EOF {
log.Printf("WebSocket stdin write error for process %s: %v", id, err)
}
return
}
process.Stdin.Write(message)
}
}()
// Handle stdout
go func() {
defer func() {
select {
case <-done:
// Process already done, this is expected
default:
log.Printf("WebSocket stdout connection closed for process %s", id)
}
}()
buf := make([]byte, 1024)
for {
n, err := process.Stdout.Read(buf)
n, err := stdoutTee.Read(buf)
if err != nil {
if err != io.EOF {
log.Printf("WebSocket stdout read error for process %s: %v", id, err)
}
return
}
if err := conn.WriteMessage(websocket.TextMessage, buf[:n]); err != nil {
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseNormalClosure) {
log.Printf("WebSocket stdout write error for process %s: %v", id, err)
}
return
}
conn.WriteMessage(websocket.TextMessage, buf[:n])
}
}()
// Handle stderr
go func() {
defer func() {
select {
case <-done:
// Process already done, this is expected
default:
log.Printf("WebSocket stderr connection closed for process %s", id)
}
}()
buf := make([]byte, 1024)
for {
n, err := process.Stderr.Read(buf)
n, err := stderrTee.Read(buf)
if err != nil {
if err != io.EOF {
log.Printf("WebSocket stderr read error for process %s: %v", id, err)
}
return
}
if err := conn.WriteMessage(websocket.TextMessage, buf[:n]); err != nil {
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseNormalClosure) {
log.Printf("WebSocket stderr write error for process %s: %v", id, err)
}
return
}
conn.WriteMessage(websocket.TextMessage, buf[:n])
}
}()
// Wait for process to exit
process.Cmd.Wait()
log.Printf("Process %s exited", id)
err = process.Cmd.Wait()
close(done) // Signal that the process is done
if err != nil {
log.Printf("Process %s exited with error: %v\nstdout: %s\nstderr: %s",
id, err, stdoutBuf.String(), stderrBuf.String())
} else {
log.Printf("Process %s exited successfully", id)
}
}
// Add new handlers for group management