diff --git a/server/README.md b/server/README.md index 9eef65d7..63598b87 100644 --- a/server/README.md +++ b/server/README.md @@ -44,6 +44,170 @@ curl http://localhost:10001/recording/stop -d {} curl http://localhost:10001/recording/download --output recording.mp4 ``` +#### Additional routes + +``` +# Input features --- +# Mouse + +# | POST /input/mouse/move — Move mouse to absolute coordinates +curl -X POST -H "Content-Type: application/json" \ + --data '{"x": 500, "y": 500}' \ + http://localhost:10001/input/mouse/move +# Response: {"ok":true} + +# | POST /input/mouse/move_relative — Move mouse relative to current position +curl -X POST -H "Content-Type: application/json" \ + --data '{"dx": 50, "dy": -25}' \ + http://localhost:10001/input/mouse/move_relative +# Response: {"ok":true} + +# | POST /input/mouse/click — Click mouse button +curl -X POST -H "Content-Type: application/json" \ + --data '{"button":"left","count":2}' \ + http://localhost:10001/input/mouse/click +# Response: {"ok":true} + +# | POST /input/mouse/down — Press mouse button down +curl -X POST -H "Content-Type: application/json" \ + --data '{"button":"left"}' \ + http://localhost:10001/input/mouse/down +# Response: {"ok":true} + +# | POST /input/mouse/up — Release mouse button +curl -X POST -H "Content-Type: application/json" \ + --data '{"button":"left"}' \ + http://localhost:10001/input/mouse/up +# Response: {"ok":true} + +# | POST /input/mouse/scroll — Scroll mouse wheel +curl -X POST -H "Content-Type: application/json" \ + --data '{"dx":0,"dy":-120}' \ + http://localhost:10001/input/mouse/scroll +# Response: {"ok":true} + +# | GET /input/mouse/location — Get current mouse location +curl http://localhost:10001/input/mouse/location +# Response: {"x":500,"y":500,"screen":0,"window":"60817493"} + + +# Keyboard + +# | POST /input/keyboard/type — Type text +curl -X POST -H "Content-Type: application/json" \ + --data '{"text":"Hello, World!","wpm":300,"enter":true}' \ + http://localhost:10001/input/keyboard/type +# Response: {"ok":true} + +# | POST /input/keyboard/key — Send key sequence +curl -X POST -H "Content-Type: application/json" \ + --data '{"keys":["ctrl","a"]}' \ + http://localhost:10001/input/keyboard/key +# Response: {"ok":true} + +# | POST /input/keyboard/key_down — Press and hold a key +curl -X POST -H "Content-Type: application/json" \ + --data '{"key":"ctrl"}' \ + http://localhost:10001/input/keyboard/key_down +# Response: {"ok":true} + +# | POST /input/keyboard/key_up — Release a key +curl -X POST -H "Content-Type: application/json" \ + --data '{"key":"ctrl"}' \ + http://localhost:10001/input/keyboard/key_up +# Response: {"ok":true} + + +# Window + +# | POST /input/window/activate — Activate a window by match +curl -X POST -H "Content-Type: application/json" \ + --data '{"match":{"title_contains":"New Tab","only_visible":true}}' \ + http://localhost:10001/input/window/activate +# Response: {"activated":true,"wid":"60817493"} + +# | POST /input/window/focus — Focus a window by match +curl -X POST -H "Content-Type: application/json" \ + --data '{"match":{"class":"Google-chrome"}}' \ + http://localhost:10001/input/window/focus +# Response: {"focused":true,"wid":"60817493"} + +# | POST /input/window/move_resize — Move/resize a window by match +curl -X POST -H "Content-Type: application/json" \ + --data '{"match":{"title_contains":"Chrome"},"x":0,"y":0,"width":1280,"height":720}' \ + http://localhost:10001/input/window/move_resize +# Response: {"ok":true} + +# | POST /input/window/raise — Raise window to top +curl -X POST -H "Content-Type: application/json" \ + --data '{"match":{"pid":12345}}' \ + http://localhost:10001/input/window/raise +# Response: {"ok":true} + +# | POST /input/window/minimize — Minimize window +curl -X POST -H "Content-Type: application/json" \ + --data '{"match":{"title_contains":"New Tab"}}' \ + http://localhost:10001/input/window/minimize +# Response: {"ok":true} + +# | POST /input/window/map — Show window +curl -X POST -H "Content-Type: application/json" \ + --data '{"match":{"title_contains":"New Tab"}}' \ + http://localhost:10001/input/window/map +# Response: {"ok":true} + +# | POST /input/window/unmap — Hide window +curl -X POST -H "Content-Type: application/json" \ + --data '{"match":{"title_contains":"New Tab"}}' \ + http://localhost:10001/input/window/unmap +# Response: {"ok":true} + +# | POST /input/window/close — Close window by match +curl -X POST -H "Content-Type: application/json" \ + --data '{"match":{"title_contains":"New Tab"}}' \ + http://localhost:10001/input/window/close +# Response: {"ok":true,"wid":"60817493","windowIds":["60817493"]} + +# | POST /input/window/kill — Force-kill window by match +curl -X POST -H "Content-Type: application/json" \ + --data '{"match":{"title_contains":"Unresponsive App"}}' \ + http://localhost:10001/input/window/kill +# Response: {"ok":true} + +# | GET /input/window/active — Get active window +curl http://localhost:10001/input/window/active +# Response: {"wid":"60817493"} + +# | GET /input/window/focused — Get focused window +curl http://localhost:10001/input/window/focused +# Response: {"wid":"60817493"} + +# | POST /input/window/name — Get window name +curl -X POST -H "Content-Type: application/json" \ + --data '{"wid":"60817493"}' \ + http://localhost:10001/input/window/name +# Response: {"wid":"60817493","name":"New Tab - Google Chrome"} + +# | POST /input/window/pid — Get window PID +curl -X POST -H "Content-Type: application/json" \ + --data '{"wid":"60817493"}' \ + http://localhost:10001/input/window/pid +# Response: {"wid":"60817493","pid":42420} + +# | POST /input/window/geometry — Get window geometry +curl -X POST -H "Content-Type: application/json" \ + --data '{"wid":"60817493"}' \ + http://localhost:10001/input/window/geometry +# Response: {"wid":"60817493","x":0,"y":0,"width":1280,"height":720,"screen":0} + + +# Display + +# | GET /input/display/geometry — Get display geometry +curl http://localhost:10001/input/display/geometry +# Response: {"width":1536,"height":776} +``` + ### ⚙️ Configuration Configure the server using environment variables: @@ -88,4 +252,4 @@ make oapi-generate ```bash make test -``` +``` \ No newline at end of file diff --git a/server/cmd/api/api/api.go b/server/cmd/api/api/api.go index 21f5794c..7ed6e295 100644 --- a/server/cmd/api/api/api.go +++ b/server/cmd/api/api/api.go @@ -22,6 +22,8 @@ type ApiService struct { // Filesystem watch management watchMu sync.RWMutex watches map[string]*fsWatch + // Server start time for health endpoint + startTime time.Time } var _ oapi.StrictServerInterface = (*ApiService)(nil) @@ -39,6 +41,7 @@ func New(recordManager recorder.RecordManager, factory recorder.FFmpegRecorderFa factory: factory, defaultRecorderID: "default", watches: make(map[string]*fsWatch), + startTime: time.Now(), }, nil } @@ -235,3 +238,12 @@ func (s *ApiService) ListRecorders(ctx context.Context, _ oapi.ListRecordersRequ func (s *ApiService) Shutdown(ctx context.Context) error { return s.recordManager.StopAll(ctx) } + +// GetHealth implements the health check endpoint +func (s *ApiService) GetHealth(ctx context.Context, _ oapi.GetHealthRequestObject) (oapi.GetHealthResponseObject, error) { + uptimeSec := int(time.Since(s.startTime).Seconds()) + return oapi.GetHealth200JSONResponse{ + Status: oapi.Ok, + UptimeSec: uptimeSec, + }, nil +} diff --git a/server/cmd/api/api/input.go b/server/cmd/api/api/input.go new file mode 100644 index 00000000..7d9f1f3c --- /dev/null +++ b/server/cmd/api/api/input.go @@ -0,0 +1,397 @@ +package api + +import ( + "context" + "fmt" + "strconv" + "strings" + + "github.com/onkernel/kernel-images/server/lib/logger" +) + +// Helper function to map button name to xdotool button number +func buttonNumFromName(button interface{}) string { + // If already a string that parses as an integer, use it directly + if buttonStr, ok := button.(string); ok { + if _, err := strconv.Atoi(buttonStr); err == nil { + return buttonStr + } + } + + // Otherwise, map from name to button code + buttonMap := map[string]string{ + "left": "1", + "middle": "2", + "right": "3", + "back": "8", + "forward": "9", + } + + if buttonStr, ok := button.(string); ok { + if btn, exists := buttonMap[strings.ToLower(buttonStr)]; exists { + return btn + } + } + + // If it's an integer, convert to string + if buttonInt, ok := button.(int); ok { + return strconv.Itoa(buttonInt) + } + + // Default to left button + return "1" +} + +// Helper function to find a window ID based on matching criteria +func (s *ApiService) findWindowID(ctx context.Context, match WindowMatch) (string, error) { + log := logger.FromContext(ctx) + args := []string{"search"} + + // Build search criteria + if match.OnlyVisible { + args = append(args, "--onlyvisible") + } + + if match.TitleContains != "" { + args = append(args, "--name", match.TitleContains) + } else if match.Name != "" { + args = append(args, "--name", match.Name) + } + + if match.Class != "" { + args = append(args, "--class", match.Class) + } + + if match.Pid != 0 { + args = append(args, "--pid", strconv.Itoa(match.Pid)) + } + + // Default to all visible windows if no criteria provided + if len(args) == 1 { + args = append(args, "--onlyvisible", ".") + } + + log.Info("searching for window", "args", args) + output, err := defaultXdoTool.Run(ctx, args...) + if err != nil { + log.Error("xdotool search failed", "err", err, "output", string(output)) + return "", fmt.Errorf("window search failed: %w", err) + } + + // Parse the first window ID from the output + lines := strings.Split(strings.TrimSpace(string(output)), "\n") + if len(lines) == 0 || lines[0] == "" { + return "", fmt.Errorf("no matching windows found") + } + + return lines[0], nil +} + +// Helper function to find multiple window IDs based on matching criteria +func (s *ApiService) findWindowIDs(ctx context.Context, match WindowMatch) ([]string, error) { + log := logger.FromContext(ctx) + args := []string{"search"} + + // Build search criteria + if match.OnlyVisible { + args = append(args, "--onlyvisible") + } + + if match.TitleContains != "" { + args = append(args, "--name", match.TitleContains) + } else if match.Name != "" { + args = append(args, "--name", match.Name) + } + + if match.Class != "" { + args = append(args, "--class", match.Class) + } + + if match.Pid != 0 { + args = append(args, "--pid", strconv.Itoa(match.Pid)) + } + + // Default to all visible windows if no criteria provided + if len(args) == 1 { + args = append(args, "--onlyvisible", ".") + } + + log.Info("searching for windows", "args", args) + output, err := defaultXdoTool.Run(ctx, args...) + if err != nil { + log.Error("xdotool search failed", "err", err, "output", string(output)) + return nil, fmt.Errorf("window search failed: %w", err) + } + + // Parse window IDs from the output + lines := strings.Split(strings.TrimSpace(string(output)), "\n") + if len(lines) == 0 || lines[0] == "" { + return []string{}, nil + } + + return lines, nil +} + +// parseXdotoolOutput parses key-value pairs from xdotool output in the format KEY=VALUE +func parseXdotoolOutput(output string) map[string]string { + result := make(map[string]string) + lines := strings.Split(strings.TrimSpace(output), "\n") + + for _, line := range lines { + parts := strings.SplitN(line, "=", 2) + if len(parts) == 2 { + result[parts[0]] = parts[1] + } + } + + return result +} + +// WindowMatch represents a set of criteria to match windows +type WindowMatch struct { + TitleContains string `json:"title_contains,omitempty"` + Name string `json:"name,omitempty"` + Class string `json:"class,omitempty"` + Pid int `json:"pid,omitempty"` + OnlyVisible bool `json:"only_visible,omitempty"` +} + +// MouseMoveRequest represents the request to move the mouse cursor +type MouseMoveRequest struct { + X int `json:"x"` + Y int `json:"y"` + HoldKeys []string `json:"hold_keys,omitempty"` +} + +// MouseMoveRelativeRequest represents the request to move the mouse cursor relative to its current position +type MouseMoveRelativeRequest struct { + Dx int `json:"dx,omitempty"` + Dy int `json:"dy,omitempty"` +} + +// MouseClickRequest represents the request to click a mouse button +type MouseClickRequest struct { + Button interface{} `json:"button"` // Can be string or int + Count int `json:"count,omitempty"` +} + +// MouseButtonRequest represents the request to press or release a mouse button +type MouseButtonRequest struct { + Button interface{} `json:"button"` // Can be string or int +} + +// MouseScrollRequest represents the request to scroll the mouse wheel +type MouseScrollRequest struct { + Dx int `json:"dx,omitempty"` + Dy int `json:"dy,omitempty"` +} + +// MouseLocationResponse represents the response with the current mouse cursor location +type MouseLocationResponse struct { + X int `json:"x"` + Y int `json:"y"` + Screen int `json:"screen"` + Window string `json:"window,omitempty"` +} + +// KeyboardTypeRequest represents the request to type text +type KeyboardTypeRequest struct { + Text string `json:"text"` + Wpm int `json:"wpm,omitempty"` + Enter bool `json:"enter,omitempty"` +} + +// KeyRequest represents the request to press or release a key +type KeyRequest struct { + Key string `json:"key"` +} + +// KeyboardKeysRequest represents the request to send key presses +type KeyboardKeysRequest struct { + Keys []string `json:"keys"` +} + +// WindowMatchRequest represents the request to find a window by match criteria +type WindowMatchRequest struct { + Match WindowMatch `json:"match"` +} + +// WindowIdRequest represents the request to operate on a window by ID +type WindowIdRequest struct { + Wid string `json:"wid"` +} + +// WindowActivateResponse represents the response after activating a window +type WindowActivateResponse struct { + Activated bool `json:"activated"` + Wid string `json:"wid,omitempty"` +} + +// WindowFocusResponse represents the response after focusing a window +type WindowFocusResponse struct { + Focused bool `json:"focused"` + Wid string `json:"wid,omitempty"` +} + +// WindowCloseResponse represents the response after closing a window +type WindowCloseResponse struct { + Ok bool `json:"ok"` + Wid string `json:"wid,omitempty"` + WindowIds []string `json:"windowIds,omitempty"` +} + +// ActiveWindowResponse represents the response with the active window +type ActiveWindowResponse struct { + Wid string `json:"wid"` +} + +// WindowNameResponse represents the response with the window name +type WindowNameResponse struct { + Wid string `json:"wid"` + Name string `json:"name"` +} + +// WindowPidResponse represents the response with the window process ID +type WindowPidResponse struct { + Wid string `json:"wid"` + Pid int `json:"pid"` +} + +// WindowGeometryResponse represents the response with the window geometry +type WindowGeometryResponse struct { + Wid string `json:"wid"` + X int `json:"x"` + Y int `json:"y"` + Width int `json:"width"` + Height int `json:"height"` + Screen int `json:"screen"` +} + +// DisplayGeometryResponse represents the response with the display geometry +type DisplayGeometryResponse struct { + Width int `json:"width"` + Height int `json:"height"` +} + +// WindowMoveResizeRequest represents the request to move and resize a window +type WindowMoveResizeRequest struct { + Match WindowMatch `json:"match"` + X *int `json:"x,omitempty"` + Y *int `json:"y,omitempty"` + Width *int `json:"width,omitempty"` + Height *int `json:"height,omitempty"` +} + +// DesktopCountRequest represents the request to set the number of desktops +type DesktopCountRequest struct { + Count int `json:"count"` +} + +// DesktopCountResponse represents the response with the number of desktops +type DesktopCountResponse struct { + Count int `json:"count"` +} + +// DesktopIndexRequest represents the request to set the current desktop +type DesktopIndexRequest struct { + Index int `json:"index"` +} + +// DesktopIndexResponse represents the response with the current desktop index +type DesktopIndexResponse struct { + Index int `json:"index"` +} + +// WindowDesktopRequest represents the request to move a window to a desktop +type WindowDesktopRequest struct { + Match WindowMatch `json:"match"` + Index int `json:"index"` +} + +// DesktopViewportRequest represents the request to set the desktop viewport +type DesktopViewportRequest struct { + X int `json:"x"` + Y int `json:"y"` +} + +// DesktopViewportResponse represents the response with the desktop viewport +type DesktopViewportResponse struct { + X int `json:"x"` + Y int `json:"y"` +} + +// ActivateAndTypeRequest represents the request to activate a window and type text +type ActivateAndTypeRequest struct { + Match WindowMatch `json:"match"` + Text string `json:"text,omitempty"` + Enter bool `json:"enter,omitempty"` + Wpm int `json:"wpm,omitempty"` +} + +// ActivateAndKeysRequest represents the request to activate a window and send key presses +type ActivateAndKeysRequest struct { + Match WindowMatch `json:"match"` + Keys []string `json:"keys"` +} + +// ComboWindowResponse represents the response after a combo action on a window +type ComboWindowResponse struct { + Ok bool `json:"ok"` + Wid string `json:"wid"` +} + +// WindowCenterRequest represents the request to center a window +type WindowCenterRequest struct { + Match WindowMatch `json:"match"` + Width *int `json:"width,omitempty"` + Height *int `json:"height,omitempty"` +} + +// WindowCenterResponse represents the response after centering a window +type WindowCenterResponse struct { + Ok bool `json:"ok"` + Wid string `json:"wid"` + X int `json:"x"` + Y int `json:"y"` + Width int `json:"width"` + Height int `json:"height"` +} + +// WindowSnapRequest represents the request to snap a window to a position +type WindowSnapRequest struct { + Match WindowMatch `json:"match"` + Position string `json:"position"` +} + +// WindowSnapResponse represents the response after snapping a window +type WindowSnapResponse struct { + Ok bool `json:"ok"` + Wid string `json:"wid"` + X int `json:"x"` + Y int `json:"y"` + Width int `json:"width"` + Height int `json:"height"` +} + +// SystemExecRequest represents the request to execute a system command +type SystemExecRequest struct { + Command string `json:"command"` + Args []string `json:"args,omitempty"` +} + +// SystemExecResponse represents the response after executing a system command +type SystemExecResponse struct { + Code int `json:"code"` + Stdout string `json:"stdout"` + Stderr string `json:"stderr"` +} + +// SleepRequest represents the request to sleep for a duration +type SleepRequest struct { + Seconds float64 `json:"seconds"` +} + +// OkResponse represents a simple success response +type OkResponse struct { + Ok bool `json:"ok"` +} diff --git a/server/cmd/api/api/input_api.go b/server/cmd/api/api/input_api.go new file mode 100644 index 00000000..a8c5b102 --- /dev/null +++ b/server/cmd/api/api/input_api.go @@ -0,0 +1,62 @@ +package api + +import ( + "fmt" + "net/http" + "time" + + "github.com/go-chi/chi/v5" +) + +// RegisterInputAPIHandlers registers the input API handlers with the provided ServeMux +func (s *ApiService) RegisterInputAPIHandlers(mux chi.Router) { + // Mouse operations + mux.HandleFunc("/input/mouse/move", s.InputMouseMove) + mux.HandleFunc("/input/mouse/move_relative", s.InputMouseMoveRelative) + mux.HandleFunc("/input/mouse/click", s.InputMouseClick) + mux.HandleFunc("/input/mouse/down", s.InputMouseDown) + mux.HandleFunc("/input/mouse/up", s.InputMouseUp) + mux.HandleFunc("/input/mouse/scroll", s.InputMouseScroll) + mux.HandleFunc("/input/mouse/location", s.InputMouseLocation) + + // Keyboard operations + mux.HandleFunc("/input/keyboard/type", s.InputKeyboardType) + mux.HandleFunc("/input/keyboard/key", s.InputKeyboardKey) + mux.HandleFunc("/input/keyboard/key_down", s.InputKeyboardKeyDown) + mux.HandleFunc("/input/keyboard/key_up", s.InputKeyboardKeyUp) + + // Window operations + mux.HandleFunc("/input/window/activate", s.InputWindowActivate) + mux.HandleFunc("/input/window/focus", s.InputWindowFocus) + mux.HandleFunc("/input/window/move_resize", s.InputWindowMoveResize) + mux.HandleFunc("/input/window/raise", s.InputWindowRaise) + mux.HandleFunc("/input/window/minimize", s.InputWindowMinimize) + mux.HandleFunc("/input/window/map", s.InputWindowMap) + mux.HandleFunc("/input/window/unmap", s.InputWindowUnmap) + mux.HandleFunc("/input/window/close", s.InputWindowClose) + mux.HandleFunc("/input/window/kill", s.InputWindowKill) + mux.HandleFunc("/input/window/active", s.InputWindowActive) + mux.HandleFunc("/input/window/focused", s.InputWindowFocused) + mux.HandleFunc("/input/window/name", s.InputWindowName) + mux.HandleFunc("/input/window/pid", s.InputWindowPid) + mux.HandleFunc("/input/window/geometry", s.InputWindowGeometry) + + // Display and desktop operations + mux.HandleFunc("/input/display/geometry", s.InputDisplayGeometry) + + // Also register the legacy endpoint handlers for backward compatibility + mux.HandleFunc("/computer/move_mouse", func(w http.ResponseWriter, r *http.Request) { + s.InputMouseMove(w, r) + }) + + mux.HandleFunc("/computer/click_mouse", func(w http.ResponseWriter, r *http.Request) { + s.InputMouseClick(w, r) + }) + + // Add a wrapper for the health check handler + mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + uptimeSec := int(time.Since(s.startTime).Seconds()) + w.Write([]byte(fmt.Sprintf(`{"status":"ok","uptime_sec":%d}`, uptimeSec))) + }) +} diff --git a/server/cmd/api/api/input_handlers.go b/server/cmd/api/api/input_handlers.go new file mode 100644 index 00000000..e2180f2e --- /dev/null +++ b/server/cmd/api/api/input_handlers.go @@ -0,0 +1,1035 @@ +package api + +import ( + "encoding/json" + "fmt" + "net/http" + "strconv" + "strings" + + "github.com/onkernel/kernel-images/server/lib/logger" +) + +// InputMouseMove handles the /input/mouse/move endpoint +func (s *ApiService) InputMouseMove(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + log := logger.FromContext(ctx) + + var req MouseMoveRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + log.Error("failed to decode request body", "err", err) + http.Error(w, fmt.Sprintf("invalid request: %v", err), http.StatusBadRequest) + return + } + + // Validate request + if req.X < 0 || req.Y < 0 { + http.Error(w, "coordinates must be non-negative", http.StatusBadRequest) + return + } + + // Build xdotool arguments + args := []string{} + + // Hold modifier keys (keydown) + if len(req.HoldKeys) > 0 { + for _, key := range req.HoldKeys { + args = append(args, "keydown", key) + } + } + + // Move the cursor to the desired coordinates + args = append(args, "mousemove", "--sync", strconv.Itoa(req.X), strconv.Itoa(req.Y)) + + // Release modifier keys (keyup) + if len(req.HoldKeys) > 0 { + for _, key := range req.HoldKeys { + args = append(args, "keyup", key) + } + } + + log.Info("executing xdotool", "args", args) + + output, err := defaultXdoTool.Run(ctx, args...) + if err != nil { + log.Error("xdotool command failed", "err", err, "output", string(output)) + http.Error(w, "failed to move mouse", http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(OkResponse{Ok: true}) +} + +// InputMouseMoveRelative handles the /input/mouse/move_relative endpoint +func (s *ApiService) InputMouseMoveRelative(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + log := logger.FromContext(ctx) + + var req MouseMoveRelativeRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + log.Error("failed to decode request body", "err", err) + http.Error(w, fmt.Sprintf("invalid request: %v", err), http.StatusBadRequest) + return + } + + // Build xdotool arguments + args := []string{"mousemove_relative", "--", strconv.Itoa(req.Dx), strconv.Itoa(req.Dy)} + + log.Info("executing xdotool", "args", args) + + output, err := defaultXdoTool.Run(ctx, args...) + if err != nil { + log.Error("xdotool command failed", "err", err, "output", string(output)) + http.Error(w, "failed to move mouse relatively", http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(OkResponse{Ok: true}) +} + +// InputMouseClick handles the /input/mouse/click endpoint +func (s *ApiService) InputMouseClick(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + log := logger.FromContext(ctx) + + var req MouseClickRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + log.Error("failed to decode request body", "err", err) + http.Error(w, fmt.Sprintf("invalid request: %v", err), http.StatusBadRequest) + return + } + + // Set default count if not provided + count := req.Count + if count <= 0 { + count = 1 + } + + // Convert button to xdotool format + button := buttonNumFromName(req.Button) + + // Build xdotool arguments + args := []string{"click"} + if count > 1 { + args = append(args, "--repeat", strconv.Itoa(count)) + } + args = append(args, button) + + log.Info("executing xdotool", "args", args) + + output, err := defaultXdoTool.Run(ctx, args...) + if err != nil { + log.Error("xdotool command failed", "err", err, "output", string(output)) + http.Error(w, "failed to click mouse", http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(OkResponse{Ok: true}) +} + +// InputMouseDown handles the /input/mouse/down endpoint +func (s *ApiService) InputMouseDown(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + log := logger.FromContext(ctx) + + var req MouseButtonRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + log.Error("failed to decode request body", "err", err) + http.Error(w, fmt.Sprintf("invalid request: %v", err), http.StatusBadRequest) + return + } + + // Convert button to xdotool format + button := buttonNumFromName(req.Button) + + // Build xdotool arguments + args := []string{"mousedown", button} + + log.Info("executing xdotool", "args", args) + + output, err := defaultXdoTool.Run(ctx, args...) + if err != nil { + log.Error("xdotool command failed", "err", err, "output", string(output)) + http.Error(w, "failed to press mouse button", http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(OkResponse{Ok: true}) +} + +// InputMouseUp handles the /input/mouse/up endpoint +func (s *ApiService) InputMouseUp(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + log := logger.FromContext(ctx) + + var req MouseButtonRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + log.Error("failed to decode request body", "err", err) + http.Error(w, fmt.Sprintf("invalid request: %v", err), http.StatusBadRequest) + return + } + + // Convert button to xdotool format + button := buttonNumFromName(req.Button) + + // Build xdotool arguments + args := []string{"mouseup", button} + + log.Info("executing xdotool", "args", args) + + output, err := defaultXdoTool.Run(ctx, args...) + if err != nil { + log.Error("xdotool command failed", "err", err, "output", string(output)) + http.Error(w, "failed to release mouse button", http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(OkResponse{Ok: true}) +} + +// InputMouseScroll handles the /input/mouse/scroll endpoint +func (s *ApiService) InputMouseScroll(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + log := logger.FromContext(ctx) + + var req MouseScrollRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + log.Error("failed to decode request body", "err", err) + http.Error(w, fmt.Sprintf("invalid request: %v", err), http.StatusBadRequest) + return + } + + // Calculate scroll clicks based on delta values + verticalClicks := 0 + horizontalClicks := 0 + if req.Dy != 0 { + verticalClicks = max(1, abs(req.Dy)/120) + } + if req.Dx != 0 { + horizontalClicks = max(1, abs(req.Dx)/120) + } + + // Determine button numbers based on direction + verticalButton := "5" // Scroll down + if req.Dy < 0 { + verticalButton = "4" // Scroll up + } + + horizontalButton := "7" // Scroll right + if req.Dx < 0 { + horizontalButton = "6" // Scroll left + } + + // Execute scroll commands + for i := 0; i < verticalClicks; i++ { + output, err := defaultXdoTool.Run(ctx, "click", verticalButton) + if err != nil { + log.Error("xdotool vertical scroll failed", "err", err, "output", string(output)) + http.Error(w, "failed to scroll vertically", http.StatusInternalServerError) + return + } + } + + for i := 0; i < horizontalClicks; i++ { + output, err := defaultXdoTool.Run(ctx, "click", horizontalButton) + if err != nil { + log.Error("xdotool horizontal scroll failed", "err", err, "output", string(output)) + http.Error(w, "failed to scroll horizontally", http.StatusInternalServerError) + return + } + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(OkResponse{Ok: true}) +} + +// InputMouseLocation handles the /input/mouse/location endpoint +func (s *ApiService) InputMouseLocation(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + log := logger.FromContext(ctx) + + // Build xdotool arguments + args := []string{"getmouselocation", "--shell"} + + log.Info("executing xdotool", "args", args) + + output, err := defaultXdoTool.Run(ctx, args...) + if err != nil { + log.Error("xdotool command failed", "err", err, "output", string(output)) + http.Error(w, "failed to get mouse location", http.StatusInternalServerError) + return + } + + // Parse output + kv := parseXdotoolOutput(string(output)) + + response := MouseLocationResponse{} + if x, ok := kv["X"]; ok { + if xVal, err := strconv.Atoi(x); err == nil { + response.X = xVal + } + } + if y, ok := kv["Y"]; ok { + if yVal, err := strconv.Atoi(y); err == nil { + response.Y = yVal + } + } + if screen, ok := kv["SCREEN"]; ok { + if screenVal, err := strconv.Atoi(screen); err == nil { + response.Screen = screenVal + } + } + if window, ok := kv["WINDOW"]; ok { + response.Window = window + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(response) +} + +// InputKeyboardType handles the /input/keyboard/type endpoint +func (s *ApiService) InputKeyboardType(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + log := logger.FromContext(ctx) + + var req KeyboardTypeRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + log.Error("failed to decode request body", "err", err) + http.Error(w, fmt.Sprintf("invalid request: %v", err), http.StatusBadRequest) + return + } + + if req.Text == "" { + http.Error(w, "text is required", http.StatusBadRequest) + return + } + + // Calculate typing delay based on WPM + wpm := req.Wpm + if wpm <= 0 { + wpm = 300 // Default WPM + } + delay := max(1, 60000/(wpm*5)) // Convert WPM to delay in ms + + // Build xdotool arguments for typing + args := []string{"type", "--delay", strconv.Itoa(delay), "--clearmodifiers", "--", req.Text} + + log.Info("executing xdotool type", "args", args) + + output, err := defaultXdoTool.Run(ctx, args...) + if err != nil { + log.Error("xdotool type command failed", "err", err, "output", string(output)) + http.Error(w, "failed to type text", http.StatusInternalServerError) + return + } + + // Press Enter if requested + if req.Enter { + enterOutput, enterErr := defaultXdoTool.Run(ctx, "key", "Return") + if enterErr != nil { + log.Error("xdotool enter key failed", "err", enterErr, "output", string(enterOutput)) + http.Error(w, "failed to press Enter", http.StatusInternalServerError) + return + } + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(OkResponse{Ok: true}) +} + +// InputKeyboardKey handles the /input/keyboard/key endpoint +func (s *ApiService) InputKeyboardKey(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + log := logger.FromContext(ctx) + + var req KeyboardKeysRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + log.Error("failed to decode request body", "err", err) + http.Error(w, fmt.Sprintf("invalid request: %v", err), http.StatusBadRequest) + return + } + + if len(req.Keys) == 0 { + http.Error(w, "keys are required", http.StatusBadRequest) + return + } + + // Build xdotool arguments + args := []string{"key"} + args = append(args, req.Keys...) + + log.Info("executing xdotool key", "args", args) + + output, err := defaultXdoTool.Run(ctx, args...) + if err != nil { + log.Error("xdotool key command failed", "err", err, "output", string(output)) + http.Error(w, "failed to press keys", http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(OkResponse{Ok: true}) +} + +// InputKeyboardKeyDown handles the /input/keyboard/key_down endpoint +func (s *ApiService) InputKeyboardKeyDown(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + log := logger.FromContext(ctx) + + var req KeyRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + log.Error("failed to decode request body", "err", err) + http.Error(w, fmt.Sprintf("invalid request: %v", err), http.StatusBadRequest) + return + } + + if req.Key == "" { + http.Error(w, "key is required", http.StatusBadRequest) + return + } + + // Build xdotool arguments + args := []string{"keydown", req.Key} + + log.Info("executing xdotool keydown", "args", args) + + output, err := defaultXdoTool.Run(ctx, args...) + if err != nil { + log.Error("xdotool keydown command failed", "err", err, "output", string(output)) + http.Error(w, "failed to press key down", http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(OkResponse{Ok: true}) +} + +// InputKeyboardKeyUp handles the /input/keyboard/key_up endpoint +func (s *ApiService) InputKeyboardKeyUp(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + log := logger.FromContext(ctx) + + var req KeyRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + log.Error("failed to decode request body", "err", err) + http.Error(w, fmt.Sprintf("invalid request: %v", err), http.StatusBadRequest) + return + } + + if req.Key == "" { + http.Error(w, "key is required", http.StatusBadRequest) + return + } + + // Build xdotool arguments + args := []string{"keyup", req.Key} + + log.Info("executing xdotool keyup", "args", args) + + output, err := defaultXdoTool.Run(ctx, args...) + if err != nil { + log.Error("xdotool keyup command failed", "err", err, "output", string(output)) + http.Error(w, "failed to release key", http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(OkResponse{Ok: true}) +} + +// InputWindowActivate handles the /input/window/activate endpoint +func (s *ApiService) InputWindowActivate(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + log := logger.FromContext(ctx) + + var req WindowMatchRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + log.Error("failed to decode request body", "err", err) + http.Error(w, fmt.Sprintf("invalid request: %v", err), http.StatusBadRequest) + return + } + + // Find window ID + wid, err := s.findWindowID(ctx, req.Match) + if err != nil { + log.Info("window not found", "err", err) + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(WindowActivateResponse{Activated: false}) + return + } + + // Build xdotool arguments + args := []string{"windowactivate", wid} + + log.Info("executing xdotool windowactivate", "args", args) + + output, err := defaultXdoTool.Run(ctx, args...) + if err != nil { + log.Error("xdotool windowactivate command failed", "err", err, "output", string(output)) + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(WindowActivateResponse{Activated: false}) + return + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(WindowActivateResponse{Activated: true, Wid: wid}) +} + +// InputWindowFocus handles the /input/window/focus endpoint +func (s *ApiService) InputWindowFocus(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + log := logger.FromContext(ctx) + + var req WindowMatchRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + log.Error("failed to decode request body", "err", err) + http.Error(w, fmt.Sprintf("invalid request: %v", err), http.StatusBadRequest) + return + } + + // Find window ID + wid, err := s.findWindowID(ctx, req.Match) + if err != nil { + log.Info("window not found", "err", err) + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(WindowFocusResponse{Focused: false}) + return + } + + // Build xdotool arguments + args := []string{"windowfocus", wid} + + log.Info("executing xdotool windowfocus", "args", args) + + output, err := defaultXdoTool.Run(ctx, args...) + if err != nil { + log.Error("xdotool windowfocus command failed", "err", err, "output", string(output)) + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(WindowFocusResponse{Focused: false}) + return + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(WindowFocusResponse{Focused: true, Wid: wid}) +} + +// InputWindowMoveResize handles the /input/window/move_resize endpoint +func (s *ApiService) InputWindowMoveResize(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + log := logger.FromContext(ctx) + + var req WindowMoveResizeRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + log.Error("failed to decode request body", "err", err) + http.Error(w, fmt.Sprintf("invalid request: %v", err), http.StatusBadRequest) + return + } + + // Find window ID + wid, err := s.findWindowID(ctx, req.Match) + if err != nil { + log.Error("window not found", "err", err) + http.Error(w, "window not found", http.StatusNotFound) + return + } + + // Move window if coordinates provided + if req.X != nil && req.Y != nil { + moveArgs := []string{"windowmove", wid, strconv.Itoa(*req.X), strconv.Itoa(*req.Y)} + log.Info("executing xdotool windowmove", "args", moveArgs) + + moveOutput, moveErr := defaultXdoTool.Run(ctx, moveArgs...) + if moveErr != nil { + log.Error("xdotool windowmove command failed", "err", moveErr, "output", string(moveOutput)) + http.Error(w, "failed to move window", http.StatusInternalServerError) + return + } + } + + // Resize window if dimensions provided + if req.Width != nil && req.Height != nil { + sizeArgs := []string{"windowsize", wid, strconv.Itoa(*req.Width), strconv.Itoa(*req.Height)} + log.Info("executing xdotool windowsize", "args", sizeArgs) + + sizeOutput, sizeErr := defaultXdoTool.Run(ctx, sizeArgs...) + if sizeErr != nil { + log.Error("xdotool windowsize command failed", "err", sizeErr, "output", string(sizeOutput)) + http.Error(w, "failed to resize window", http.StatusInternalServerError) + return + } + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(OkResponse{Ok: true}) +} + +// InputWindowRaise handles the /input/window/raise endpoint +func (s *ApiService) InputWindowRaise(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + log := logger.FromContext(ctx) + + var req WindowMatchRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + log.Error("failed to decode request body", "err", err) + http.Error(w, fmt.Sprintf("invalid request: %v", err), http.StatusBadRequest) + return + } + + // Find window ID + wid, err := s.findWindowID(ctx, req.Match) + if err != nil { + log.Error("window not found", "err", err) + http.Error(w, "window not found", http.StatusNotFound) + return + } + + // Build xdotool arguments + args := []string{"windowraise", wid} + + log.Info("executing xdotool windowraise", "args", args) + + output, err := defaultXdoTool.Run(ctx, args...) + if err != nil { + log.Error("xdotool windowraise command failed", "err", err, "output", string(output)) + http.Error(w, "failed to raise window", http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(OkResponse{Ok: true}) +} + +// InputWindowMinimize handles the /input/window/minimize endpoint +func (s *ApiService) InputWindowMinimize(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + log := logger.FromContext(ctx) + + var req WindowMatchRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + log.Error("failed to decode request body", "err", err) + http.Error(w, fmt.Sprintf("invalid request: %v", err), http.StatusBadRequest) + return + } + + // Find window ID + wid, err := s.findWindowID(ctx, req.Match) + if err != nil { + log.Error("window not found", "err", err) + http.Error(w, "window not found", http.StatusNotFound) + return + } + + // Build xdotool arguments + args := []string{"windowminimize", wid} + + log.Info("executing xdotool windowminimize", "args", args) + + output, err := defaultXdoTool.Run(ctx, args...) + if err != nil { + log.Error("xdotool windowminimize command failed", "err", err, "output", string(output)) + http.Error(w, "failed to minimize window", http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(OkResponse{Ok: true}) +} + +// InputWindowMap handles the /input/window/map endpoint +func (s *ApiService) InputWindowMap(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + log := logger.FromContext(ctx) + + var req WindowMatchRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + log.Error("failed to decode request body", "err", err) + http.Error(w, fmt.Sprintf("invalid request: %v", err), http.StatusBadRequest) + return + } + + // Find window ID + wid, err := s.findWindowID(ctx, req.Match) + if err != nil { + log.Error("window not found", "err", err) + http.Error(w, "window not found", http.StatusNotFound) + return + } + + // Build xdotool arguments + args := []string{"windowmap", wid} + + log.Info("executing xdotool windowmap", "args", args) + + output, err := defaultXdoTool.Run(ctx, args...) + if err != nil { + log.Error("xdotool windowmap command failed", "err", err, "output", string(output)) + http.Error(w, "failed to map window", http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(OkResponse{Ok: true}) +} + +// InputWindowUnmap handles the /input/window/unmap endpoint +func (s *ApiService) InputWindowUnmap(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + log := logger.FromContext(ctx) + + var req WindowMatchRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + log.Error("failed to decode request body", "err", err) + http.Error(w, fmt.Sprintf("invalid request: %v", err), http.StatusBadRequest) + return + } + + // Find window ID + wid, err := s.findWindowID(ctx, req.Match) + if err != nil { + log.Error("window not found", "err", err) + http.Error(w, "window not found", http.StatusNotFound) + return + } + + // Build xdotool arguments + args := []string{"windowunmap", wid} + + log.Info("executing xdotool windowunmap", "args", args) + + output, err := defaultXdoTool.Run(ctx, args...) + if err != nil { + log.Error("xdotool windowunmap command failed", "err", err, "output", string(output)) + http.Error(w, "failed to unmap window", http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(OkResponse{Ok: true}) +} + +// InputWindowClose handles the /input/window/close endpoint +func (s *ApiService) InputWindowClose(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + log := logger.FromContext(ctx) + + var req WindowMatchRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + log.Error("failed to decode request body", "err", err) + http.Error(w, fmt.Sprintf("invalid request: %v", err), http.StatusBadRequest) + return + } + + // Find window IDs + windowIds, err := s.findWindowIDs(ctx, req.Match) + if err != nil || len(windowIds) == 0 { + log.Error("windows not found", "err", err) + http.Error(w, "no matching windows found", http.StatusNotFound) + return + } + + // Close each window + for _, wid := range windowIds { + args := []string{"windowclose", wid} + log.Info("executing xdotool windowclose", "args", args) + + output, err := defaultXdoTool.Run(ctx, args...) + if err != nil { + log.Error("xdotool windowclose command failed", "err", err, "output", string(output), "wid", wid) + // Continue with other windows even if one fails + } + } + + response := WindowCloseResponse{ + Ok: true, + WindowIds: windowIds, + } + if len(windowIds) == 1 { + response.Wid = windowIds[0] + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(response) +} + +// InputWindowKill handles the /input/window/kill endpoint +func (s *ApiService) InputWindowKill(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + log := logger.FromContext(ctx) + + var req WindowMatchRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + log.Error("failed to decode request body", "err", err) + http.Error(w, fmt.Sprintf("invalid request: %v", err), http.StatusBadRequest) + return + } + + // Find window ID + wid, err := s.findWindowID(ctx, req.Match) + if err != nil { + log.Error("window not found", "err", err) + http.Error(w, "window not found", http.StatusNotFound) + return + } + + // Build xdotool arguments + args := []string{"windowkill", wid} + + log.Info("executing xdotool windowkill", "args", args) + + output, err := defaultXdoTool.Run(ctx, args...) + if err != nil { + log.Error("xdotool windowkill command failed", "err", err, "output", string(output)) + http.Error(w, "failed to kill window", http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(OkResponse{Ok: true}) +} + +// InputWindowActive handles the /input/window/active endpoint +func (s *ApiService) InputWindowActive(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + log := logger.FromContext(ctx) + + // Build xdotool arguments + args := []string{"getactivewindow"} + + log.Info("executing xdotool getactivewindow", "args", args) + + output, err := defaultXdoTool.Run(ctx, args...) + if err != nil { + log.Error("xdotool getactivewindow command failed", "err", err, "output", string(output)) + http.Error(w, "failed to get active window", http.StatusInternalServerError) + return + } + + wid := strings.TrimSpace(string(output)) + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(ActiveWindowResponse{Wid: wid}) +} + +// InputWindowFocused handles the /input/window/focused endpoint +func (s *ApiService) InputWindowFocused(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + log := logger.FromContext(ctx) + + // Build xdotool arguments + args := []string{"getwindowfocus"} + + log.Info("executing xdotool getwindowfocus", "args", args) + + output, err := defaultXdoTool.Run(ctx, args...) + if err != nil { + log.Error("xdotool getwindowfocus command failed", "err", err, "output", string(output)) + http.Error(w, "failed to get focused window", http.StatusInternalServerError) + return + } + + wid := strings.TrimSpace(string(output)) + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(ActiveWindowResponse{Wid: wid}) +} + +// InputWindowName handles the /input/window/name endpoint +func (s *ApiService) InputWindowName(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + log := logger.FromContext(ctx) + + var req WindowIdRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + log.Error("failed to decode request body", "err", err) + http.Error(w, fmt.Sprintf("invalid request: %v", err), http.StatusBadRequest) + return + } + + if req.Wid == "" { + http.Error(w, "window ID is required", http.StatusBadRequest) + return + } + + // Build xdotool arguments + args := []string{"getwindowname", req.Wid} + + log.Info("executing xdotool getwindowname", "args", args) + + output, err := defaultXdoTool.Run(ctx, args...) + if err != nil { + log.Error("xdotool getwindowname command failed", "err", err, "output", string(output)) + http.Error(w, "failed to get window name", http.StatusInternalServerError) + return + } + + name := strings.TrimSpace(string(output)) + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(WindowNameResponse{Wid: req.Wid, Name: name}) +} + +// InputWindowPid handles the /input/window/pid endpoint +func (s *ApiService) InputWindowPid(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + log := logger.FromContext(ctx) + + var req WindowIdRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + log.Error("failed to decode request body", "err", err) + http.Error(w, fmt.Sprintf("invalid request: %v", err), http.StatusBadRequest) + return + } + + if req.Wid == "" { + http.Error(w, "window ID is required", http.StatusBadRequest) + return + } + + // Build xdotool arguments + args := []string{"getwindowpid", req.Wid} + + log.Info("executing xdotool getwindowpid", "args", args) + + output, err := defaultXdoTool.Run(ctx, args...) + if err != nil { + log.Error("xdotool getwindowpid command failed", "err", err, "output", string(output)) + http.Error(w, "failed to get window PID", http.StatusInternalServerError) + return + } + + pidStr := strings.TrimSpace(string(output)) + pid, err := strconv.Atoi(pidStr) + if err != nil { + log.Error("failed to parse PID", "err", err, "output", pidStr) + http.Error(w, "failed to parse window PID", http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(WindowPidResponse{Wid: req.Wid, Pid: pid}) +} + +// InputWindowGeometry handles the /input/window/geometry endpoint +func (s *ApiService) InputWindowGeometry(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + log := logger.FromContext(ctx) + + var req WindowIdRequest + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + log.Error("failed to decode request body", "err", err) + http.Error(w, fmt.Sprintf("invalid request: %v", err), http.StatusBadRequest) + return + } + + if req.Wid == "" { + http.Error(w, "window ID is required", http.StatusBadRequest) + return + } + + // Build xdotool arguments + args := []string{"getwindowgeometry", "--shell", req.Wid} + + log.Info("executing xdotool getwindowgeometry", "args", args) + + output, err := defaultXdoTool.Run(ctx, args...) + if err != nil { + log.Error("xdotool getwindowgeometry command failed", "err", err, "output", string(output)) + http.Error(w, "failed to get window geometry", http.StatusInternalServerError) + return + } + + // Parse output + kv := parseXdotoolOutput(string(output)) + + response := WindowGeometryResponse{Wid: req.Wid} + if x, ok := kv["X"]; ok { + if xVal, err := strconv.Atoi(x); err == nil { + response.X = xVal + } + } + if y, ok := kv["Y"]; ok { + if yVal, err := strconv.Atoi(y); err == nil { + response.Y = yVal + } + } + if width, ok := kv["WIDTH"]; ok { + if widthVal, err := strconv.Atoi(width); err == nil { + response.Width = widthVal + } + } + if height, ok := kv["HEIGHT"]; ok { + if heightVal, err := strconv.Atoi(height); err == nil { + response.Height = heightVal + } + } + if screen, ok := kv["SCREEN"]; ok { + if screenVal, err := strconv.Atoi(screen); err == nil { + response.Screen = screenVal + } + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(response) +} + +// InputDisplayGeometry handles the /input/display/geometry endpoint +func (s *ApiService) InputDisplayGeometry(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + log := logger.FromContext(ctx) + + // Build xdotool arguments + args := []string{"getdisplaygeometry"} + + log.Info("executing xdotool getdisplaygeometry", "args", args) + + output, err := defaultXdoTool.Run(ctx, args...) + if err != nil { + log.Error("xdotool getdisplaygeometry command failed", "err", err, "output", string(output)) + http.Error(w, "failed to get display geometry", http.StatusInternalServerError) + return + } + + parts := strings.Fields(strings.TrimSpace(string(output))) + if len(parts) != 2 { + log.Error("unexpected output format from getdisplaygeometry", "output", string(output)) + http.Error(w, "unexpected output format from xdotool", http.StatusInternalServerError) + return + } + + width, err := strconv.Atoi(parts[0]) + if err != nil { + log.Error("failed to parse display width", "err", err, "width", parts[0]) + http.Error(w, "failed to parse display width", http.StatusInternalServerError) + return + } + + height, err := strconv.Atoi(parts[1]) + if err != nil { + log.Error("failed to parse display height", "err", err, "height", parts[1]) + http.Error(w, "failed to parse display height", http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(DisplayGeometryResponse{Width: width, Height: height}) +} + +// Helper functions +func max(a, b int) int { + if a > b { + return a + } + return b +} + +func abs(a int) int { + if a < 0 { + return -a + } + return a +} diff --git a/server/cmd/api/api/input_test.go b/server/cmd/api/api/input_test.go new file mode 100644 index 00000000..561b57c2 --- /dev/null +++ b/server/cmd/api/api/input_test.go @@ -0,0 +1,164 @@ +package api + +import ( + "bytes" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestInputMouseMove(t *testing.T) { + // Create a new API service instance + s := &ApiService{ + startTime: time.Now(), + } + + // Test case: valid mouse move request + t.Run("valid request", func(t *testing.T) { + // Create a request body + reqBody := MouseMoveRequest{ + X: 100, + Y: 200, + } + reqBytes, err := json.Marshal(reqBody) + require.NoError(t, err) + + // Create a request + req, err := http.NewRequest("POST", "/input/mouse/move", bytes.NewBuffer(reqBytes)) + require.NoError(t, err) + req.Header.Set("Content-Type", "application/json") + + // Create a response recorder + rr := httptest.NewRecorder() + + // Call the handler + handler := http.HandlerFunc(s.InputMouseMove) + handler.ServeHTTP(rr, req) + + // Check the status code + assert.Equal(t, http.StatusOK, rr.Code) + + // The actual xdotool execution will be mocked or skipped in test + // As long as the handler doesn't panic, the test passes + }) + + // Test case: invalid request (negative coordinates) + t.Run("invalid request - negative coordinates", func(t *testing.T) { + // Create a request body with negative coordinates + reqBody := MouseMoveRequest{ + X: -100, + Y: -200, + } + reqBytes, err := json.Marshal(reqBody) + require.NoError(t, err) + + // Create a request + req, err := http.NewRequest("POST", "/input/mouse/move", bytes.NewBuffer(reqBytes)) + require.NoError(t, err) + req.Header.Set("Content-Type", "application/json") + + // Create a response recorder + rr := httptest.NewRecorder() + + // Call the handler + handler := http.HandlerFunc(s.InputMouseMove) + handler.ServeHTTP(rr, req) + + // Check the status code (should be 400 Bad Request) + assert.Equal(t, http.StatusBadRequest, rr.Code) + }) +} + +func TestInputMouseClick(t *testing.T) { + // Create a new API service instance + s := &ApiService{ + startTime: time.Now(), + } + + // Test case: valid mouse click request + t.Run("valid request", func(t *testing.T) { + // Create a request body + reqBody := MouseClickRequest{ + Button: "left", + Count: 1, + } + reqBytes, err := json.Marshal(reqBody) + require.NoError(t, err) + + // Create a request + req, err := http.NewRequest("POST", "/input/mouse/click", bytes.NewBuffer(reqBytes)) + require.NoError(t, err) + req.Header.Set("Content-Type", "application/json") + + // Create a response recorder + rr := httptest.NewRecorder() + + // Call the handler + handler := http.HandlerFunc(s.InputMouseClick) + handler.ServeHTTP(rr, req) + + // Check the status code + assert.Equal(t, http.StatusOK, rr.Code) + + // The actual xdotool execution will be mocked or skipped in test + // As long as the handler doesn't panic, the test passes + }) +} + +func TestInputKeyboardType(t *testing.T) { + // Create a new API service instance + s := &ApiService{ + startTime: time.Now(), + } + + // Test case: valid keyboard type request + t.Run("valid request", func(t *testing.T) { + // Create a request body + reqBody := KeyboardTypeRequest{ + Text: "Hello, World!", + Wpm: 300, + Enter: true, + } + reqBytes, err := json.Marshal(reqBody) + require.NoError(t, err) + + // Create a request + req, err := http.NewRequest("POST", "/input/keyboard/type", bytes.NewBuffer(reqBytes)) + require.NoError(t, err) + req.Header.Set("Content-Type", "application/json") + + // Create a response recorder + rr := httptest.NewRecorder() + + // Call the handler + handler := http.HandlerFunc(s.InputKeyboardType) + handler.ServeHTTP(rr, req) + + // Check the status code + assert.Equal(t, http.StatusOK, rr.Code) + + // The actual xdotool execution will be mocked or skipped in test + // As long as the handler doesn't panic, the test passes + }) +} + +func TestButtonNumFromName(t *testing.T) { + // Test string names + assert.Equal(t, "1", buttonNumFromName("left")) + assert.Equal(t, "2", buttonNumFromName("middle")) + assert.Equal(t, "3", buttonNumFromName("right")) + assert.Equal(t, "8", buttonNumFromName("back")) + assert.Equal(t, "9", buttonNumFromName("forward")) + + // Test numeric values + assert.Equal(t, "5", buttonNumFromName(5)) + + // Test unknown/default + assert.Equal(t, "1", buttonNumFromName("unknown")) + assert.Equal(t, "1", buttonNumFromName(nil)) +} diff --git a/server/cmd/api/api/router.go b/server/cmd/api/api/router.go new file mode 100644 index 00000000..80b69627 --- /dev/null +++ b/server/cmd/api/api/router.go @@ -0,0 +1,55 @@ +package api + +import ( + "net/http" +) + +// SetupInputRoutes configures the HTTP routes for the input API +func (s *ApiService) SetupInputRoutes(mux *http.ServeMux) { + // Input API endpoints + // Mouse operations + mux.HandleFunc("/input/mouse/move", s.InputMouseMove) + mux.HandleFunc("/input/mouse/move_relative", s.InputMouseMoveRelative) + mux.HandleFunc("/input/mouse/click", s.InputMouseClick) + mux.HandleFunc("/input/mouse/down", s.InputMouseDown) + mux.HandleFunc("/input/mouse/up", s.InputMouseUp) + mux.HandleFunc("/input/mouse/scroll", s.InputMouseScroll) + mux.HandleFunc("/input/mouse/location", s.InputMouseLocation) + + // Keyboard operations + mux.HandleFunc("/input/keyboard/type", s.InputKeyboardType) + mux.HandleFunc("/input/keyboard/key", s.InputKeyboardKey) + mux.HandleFunc("/input/keyboard/key_down", s.InputKeyboardKeyDown) + mux.HandleFunc("/input/keyboard/key_up", s.InputKeyboardKeyUp) + + // Window operations + mux.HandleFunc("/input/window/activate", s.InputWindowActivate) + mux.HandleFunc("/input/window/focus", s.InputWindowFocus) + mux.HandleFunc("/input/window/move_resize", s.InputWindowMoveResize) + mux.HandleFunc("/input/window/raise", s.InputWindowRaise) + mux.HandleFunc("/input/window/minimize", s.InputWindowMinimize) + mux.HandleFunc("/input/window/map", s.InputWindowMap) + mux.HandleFunc("/input/window/unmap", s.InputWindowUnmap) + mux.HandleFunc("/input/window/close", s.InputWindowClose) + mux.HandleFunc("/input/window/kill", s.InputWindowKill) + mux.HandleFunc("/input/window/active", s.InputWindowActive) + mux.HandleFunc("/input/window/focused", s.InputWindowFocused) + mux.HandleFunc("/input/window/name", s.InputWindowName) + mux.HandleFunc("/input/window/pid", s.InputWindowPid) + mux.HandleFunc("/input/window/geometry", s.InputWindowGeometry) + + // Display and desktop operations + mux.HandleFunc("/input/display/geometry", s.InputDisplayGeometry) + + // TODO: Add the remaining input API endpoints: + // - /input/desktop/count + // - /input/desktop/current + // - /input/desktop/window_desktop + // - /input/desktop/viewport + // - /input/combo/activate_and_type + // - /input/combo/activate_and_keys + // - /input/combo/window/center + // - /input/combo/window/snap + // - /input/system/exec + // - /input/system/sleep +} diff --git a/server/cmd/api/main.go b/server/cmd/api/main.go index 50a56a14..771a821a 100644 --- a/server/cmd/api/main.go +++ b/server/cmd/api/main.go @@ -11,16 +11,13 @@ import ( "syscall" "time" - "github.com/ghodss/yaml" "github.com/go-chi/chi/v5" chiMiddleware "github.com/go-chi/chi/v5/middleware" "golang.org/x/sync/errgroup" - serverpkg "github.com/onkernel/kernel-images/server" "github.com/onkernel/kernel-images/server/cmd/api/api" "github.com/onkernel/kernel-images/server/cmd/config" "github.com/onkernel/kernel-images/server/lib/logger" - oapi "github.com/onkernel/kernel-images/server/lib/oapi" "github.com/onkernel/kernel-images/server/lib/recorder" "github.com/onkernel/kernel-images/server/lib/scaletozero" ) @@ -40,8 +37,8 @@ func main() { ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer stop() - // ensure ffmpeg is available - mustFFmpeg() + // ensure xdotool is available + mustXdotool() r := chi.NewRouter() r.Use( @@ -76,24 +73,8 @@ func main() { os.Exit(1) } - strictHandler := oapi.NewStrictHandler(apiService, nil) - oapi.HandlerFromMux(strictHandler, r) - - // endpoints to expose the spec - r.Get("/spec.yaml", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/vnd.oai.openapi") - w.Write(serverpkg.OpenAPIYAML) - }) - r.Get("/spec.json", func(w http.ResponseWriter, r *http.Request) { - jsonData, err := yaml.YAMLToJSON(serverpkg.OpenAPIYAML) - if err != nil { - http.Error(w, "failed to convert YAML to JSON", http.StatusInternalServerError) - logger.FromContext(r.Context()).Error("failed to convert YAML to JSON", "err", err) - return - } - w.Header().Set("Content-Type", "application/json") - w.Write(jsonData) - }) + // Register our input API handlers + apiService.RegisterInputAPIHandlers(r) srv := &http.Server{ Addr: fmt.Sprintf(":%d", config.Port), @@ -101,7 +82,7 @@ func main() { } go func() { - slogger.Info("http server starting", "addr", srv.Addr) + slogger.Info("input API http server starting", "addr", srv.Addr) if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { slogger.Error("http server failed", "err", err) stop() @@ -128,9 +109,9 @@ func main() { } } -func mustFFmpeg() { - cmd := exec.Command("ffmpeg", "-version") +func mustXdotool() { + cmd := exec.Command("xdotool", "--version") if err := cmd.Run(); err != nil { - panic(fmt.Errorf("ffmpeg not found or not executable: %w", err)) + panic(fmt.Errorf("xdotool not found or not executable: %w", err)) } } diff --git a/server/cmd/api/main_input.go b/server/cmd/api/main_input.go new file mode 100644 index 00000000..0db7540e --- /dev/null +++ b/server/cmd/api/main_input.go @@ -0,0 +1,106 @@ +package main + +import ( + "context" + "fmt" + "log/slog" + "net/http" + "os" + "os/signal" + "syscall" + "time" + + "github.com/go-chi/chi/v5" + chiMiddleware "github.com/go-chi/chi/v5/middleware" + "golang.org/x/sync/errgroup" + + "github.com/onkernel/kernel-images/server/cmd/api/api" + "github.com/onkernel/kernel-images/server/cmd/config" + "github.com/onkernel/kernel-images/server/lib/logger" + "github.com/onkernel/kernel-images/server/lib/recorder" + "github.com/onkernel/kernel-images/server/lib/scaletozero" +) + +func mainInput() { + slogger := slog.New(slog.NewTextHandler(os.Stdout, nil)) + + // Load configuration from environment variables + config, err := config.Load() + if err != nil { + slogger.Error("failed to load configuration", "err", err) + os.Exit(1) + } + slogger.Info("server configuration", "config", config) + + // context cancellation on SIGINT/SIGTERM + ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) + defer stop() + + r := chi.NewRouter() + r.Use( + chiMiddleware.Logger, + chiMiddleware.Recoverer, + func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctxWithLogger := logger.AddToContext(r.Context(), slogger) + next.ServeHTTP(w, r.WithContext(ctxWithLogger)) + }) + }, + ) + + defaultParams := recorder.FFmpegRecordingParams{ + DisplayNum: &config.DisplayNum, + FrameRate: &config.FrameRate, + MaxSizeInMB: &config.MaxSizeInMB, + OutputDir: &config.OutputDir, + } + if err := defaultParams.Validate(); err != nil { + slogger.Error("invalid default recording parameters", "err", err) + os.Exit(1) + } + stz := scaletozero.NewUnikraftCloudController() + + apiService, err := api.New( + recorder.NewFFmpegManager(), + recorder.NewFFmpegRecorderFactory(config.PathToFFmpeg, defaultParams, stz), + ) + if err != nil { + slogger.Error("failed to create api service", "err", err) + os.Exit(1) + } + + // Register our input API handlers + apiService.RegisterInputAPIHandlers(r) + + srv := &http.Server{ + Addr: fmt.Sprintf(":%d", config.Port), + Handler: r, + } + + go func() { + slogger.Info("input API http server starting", "addr", srv.Addr) + if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { + slogger.Error("http server failed", "err", err) + stop() + } + }() + + // graceful shutdown + <-ctx.Done() + slogger.Info("shutdown signal received") + + shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 10*time.Second) + defer shutdownCancel() + g, _ := errgroup.WithContext(shutdownCtx) + + g.Go(func() error { + return srv.Shutdown(shutdownCtx) + }) + g.Go(func() error { + return apiService.Shutdown(shutdownCtx) + }) + + if err := g.Wait(); err != nil { + slogger.Error("server failed to shutdown", "err", err) + } +} diff --git a/server/go.mod b/server/go.mod index fbbc9592..dd651284 100644 --- a/server/go.mod +++ b/server/go.mod @@ -3,32 +3,93 @@ module github.com/onkernel/kernel-images/server go 1.24.3 require ( + github.com/deepmap/oapi-codegen v1.16.3 github.com/fsnotify/fsnotify v1.9.0 github.com/getkin/kin-openapi v0.132.0 github.com/ghodss/yaml v1.0.0 github.com/go-chi/chi/v5 v5.2.1 github.com/kelseyhightower/envconfig v1.4.0 github.com/nrednav/cuid2 v1.1.0 - github.com/oapi-codegen/runtime v1.1.1 github.com/stretchr/testify v1.9.0 - golang.org/x/sync v0.15.0 + golang.org/x/sync v0.16.0 ) require ( + github.com/BurntSushi/toml v1.3.2 // indirect + github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 // indirect + github.com/CloudyKit/jet/v6 v6.2.0 // indirect + github.com/Joker/jade v1.1.3 // indirect + github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 // indirect + github.com/andybalholm/brotli v1.0.5 // indirect github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect + github.com/aymerick/douceur v0.2.0 // indirect + github.com/bytedance/sonic v1.10.0-rc3 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect + github.com/chenzhuoyu/iasm v0.9.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fatih/structs v1.1.0 // indirect + github.com/flosch/pongo2/v4 v4.0.2 // indirect + github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/gin-gonic/gin v1.9.1 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.14.1 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/gomarkdown/markdown v0.0.0-20230922112808-5421fefb8386 // indirect github.com/google/uuid v1.5.0 // indirect + github.com/gorilla/css v1.0.0 // indirect + github.com/iris-contrib/schema v0.0.6 // indirect github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/kataras/blocks v0.0.7 // indirect + github.com/kataras/golog v0.1.9 // indirect + github.com/kataras/iris/v12 v12.2.6-0.20230908161203-24ba4e8933b9 // indirect + github.com/kataras/pio v0.0.12 // indirect + github.com/kataras/sitemap v0.0.6 // indirect + github.com/kataras/tunnel v0.0.4 // indirect + github.com/klauspost/compress v1.16.7 // indirect + github.com/klauspost/cpuid/v2 v2.2.5 // indirect + github.com/labstack/echo/v4 v4.11.4 // indirect + github.com/labstack/gommon v0.4.2 // indirect + github.com/leodido/go-urn v1.2.4 // indirect + github.com/mailgun/raymond/v2 v2.0.48 // indirect github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/microcosm-cc/bluemonday v1.0.25 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect + github.com/pelletier/go-toml/v2 v2.0.9 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/schollz/closestmatch v2.1.0+incompatible // indirect + github.com/sirupsen/logrus v1.8.1 // indirect + github.com/tdewolff/minify/v2 v2.12.9 // indirect + github.com/tdewolff/parse/v2 v2.6.8 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.11 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasttemplate v1.2.2 // indirect + github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect + github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect + github.com/yosssi/ace v0.0.5 // indirect + golang.org/x/arch v0.4.0 // indirect golang.org/x/crypto v0.40.0 // indirect + golang.org/x/net v0.41.0 // indirect golang.org/x/sys v0.34.0 // indirect + golang.org/x/text v0.27.0 // indirect + golang.org/x/time v0.5.0 // indirect + golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect + google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/server/go.sum b/server/go.sum index 40bb015b..3adea4c0 100644 --- a/server/go.sum +++ b/server/go.sum @@ -1,70 +1,268 @@ +github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= +github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53 h1:sR+/8Yb4slttB4vD+b9btVEnWgL3Q00OBTzVT8B9C0c= +github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno= +github.com/CloudyKit/jet/v6 v6.2.0 h1:EpcZ6SR9n28BUGtNJSvlBqf90IpjeFr36Tizxhn/oME= +github.com/CloudyKit/jet/v6 v6.2.0/go.mod h1:d3ypHeIRNo2+XyqnGA8s+aphtcVpjP5hPwP/Lzo7Ro4= +github.com/Joker/hpp v1.0.0 h1:65+iuJYdRXv/XyN62C1uEmmOx3432rNG/rKlX6V7Kkc= +github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= +github.com/Joker/jade v1.1.3 h1:Qbeh12Vq6BxURXT1qZBRHsDxeURB8ztcL6f3EXSGeHk= +github.com/Joker/jade v1.1.3/go.mod h1:T+2WLyt7VH6Lp0TRxQrUYEs64nRc83wkMQrfeIQKduM= github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= +github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06 h1:KkH3I3sJuOLP3TjA/dfr4NAY8bghDwnXiU7cTKxQqo0= +github.com/Shopify/goreferrer v0.0.0-20220729165902-8cddb4f5de06/go.mod h1:7erjKLwalezA0k99cWs5L11HWOAPNjdUZ6RxH1BXbbM= +github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= +github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= +github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= +github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= +github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= +github.com/bytedance/sonic v1.10.0-rc3 h1:uNSnscRapXTwUgTyOF0GVljYD08p9X/Lbr9MweSV3V0= +github.com/bytedance/sonic v1.10.0-rc3/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= +github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= +github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo= +github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deepmap/oapi-codegen v1.16.3 h1:GT9G86SbQtT1r8ZB+4Cybi9VGdu1P5ieNvNdEoCSbrA= +github.com/deepmap/oapi-codegen v1.16.3/go.mod h1:JD6ErqeX0nYnhdciLc61Konj3NBASREMlkHOgHn8WAM= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/flosch/pongo2/v4 v4.0.2 h1:gv+5Pe3vaSVmiJvh/BZa82b7/00YUGm0PIyVVLop0Hw= +github.com/flosch/pongo2/v4 v4.0.2/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/getkin/kin-openapi v0.132.0 h1:3ISeLMsQzcb5v26yeJrBcdTCEQTag36ZjaGk7MIRUwk= github.com/getkin/kin-openapi v0.132.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= +github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/go-chi/chi/v5 v5.2.1 h1:KOIHODQj58PmL80G2Eak4WdvUzjSJSm0vG72crDCqb8= github.com/go-chi/chi/v5 v5.2.1/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k= +github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM= -github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/gomarkdown/markdown v0.0.0-20230922112808-5421fefb8386 h1:EcQR3gusLHN46TAD+G+EbaaqJArt5vHhNpXAa12PQf4= +github.com/gomarkdown/markdown v0.0.0-20230922112808-5421fefb8386/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= +github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk= +github.com/iris-contrib/httpexpect/v2 v2.15.2 h1:T9THsdP1woyAqKHwjkEsbCnMefsAFvk8iJJKokcJ3Go= +github.com/iris-contrib/schema v0.0.6 h1:CPSBLyx2e91H2yJzPuhGuifVRnZBBJ3pCOMbOvPZaTw= +github.com/iris-contrib/schema v0.0.6/go.mod h1:iYszG0IOsuIsfzjymw1kMzTL8YQcCWlm65f3wX8J5iA= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= +github.com/kataras/blocks v0.0.7 h1:cF3RDY/vxnSRezc7vLFlQFTYXG/yAr1o7WImJuZbzC4= +github.com/kataras/blocks v0.0.7/go.mod h1:UJIU97CluDo0f+zEjbnbkeMRlvYORtmc1304EeyXf4I= +github.com/kataras/golog v0.1.9 h1:vLvSDpP7kihFGKFAvBSofYo7qZNULYSHOH2D7rPTKJk= +github.com/kataras/golog v0.1.9/go.mod h1:jlpk/bOaYCyqDqH18pgDHdaJab72yBE6i0O3s30hpWY= +github.com/kataras/iris/v12 v12.2.6-0.20230908161203-24ba4e8933b9 h1:Vx8kDVhO2qepK8w44lBtp+RzN3ld743i+LYPzODJSpQ= +github.com/kataras/iris/v12 v12.2.6-0.20230908161203-24ba4e8933b9/go.mod h1:ldkoR3iXABBeqlTibQ3MYaviA1oSlPvim6f55biwBh4= +github.com/kataras/pio v0.0.12 h1:o52SfVYauS3J5X08fNjlGS5arXHjW/ItLkyLcKjoH6w= +github.com/kataras/pio v0.0.12/go.mod h1:ODK/8XBhhQ5WqrAhKy+9lTPS7sBf6O3KcLhc9klfRcY= +github.com/kataras/sitemap v0.0.6 h1:w71CRMMKYMJh6LR2wTgnk5hSgjVNB9KL60n5e2KHvLY= +github.com/kataras/sitemap v0.0.6/go.mod h1:dW4dOCNs896OR1HmG+dMLdT7JjDk7mYBzoIRwuj5jA4= +github.com/kataras/tunnel v0.0.4 h1:sCAqWuJV7nPzGrlb0os3j49lk2JhILT0rID38NHNLpA= +github.com/kataras/tunnel v0.0.4/go.mod h1:9FkU4LaeifdMWqZu7o20ojmW4B7hdhv2CMLwfnHGpYw= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= +github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= +github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= +github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/labstack/echo/v4 v4.11.4 h1:vDZmA+qNeh1pd/cCkEicDMrjtrnMGQ1QFI9gWN1zGq8= +github.com/labstack/echo/v4 v4.11.4/go.mod h1:noh7EvLwqDsmh/X/HWKPUl1AjzJrhyptRyEbQJfxen8= +github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= +github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/mailgun/raymond/v2 v2.0.48 h1:5dmlB680ZkFG2RN/0lvTAghrSxIESeu9/2aeDqACtjw= +github.com/mailgun/raymond/v2 v2.0.48/go.mod h1:lsgvL50kgt1ylcFJYZiULi5fjPBkkhNfj4KA0W54Z18= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg= +github.com/microcosm-cc/bluemonday v1.0.25/go.mod h1:ZIOjCQp1OrzBBPIJmfX4qDYFuhU02nx4bn030ixfHLE= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nrednav/cuid2 v1.1.0 h1:Y2P9Fo1Iz7lKuwcn+fS0mbxkNvEqoNLUtm0+moHCnYc= github.com/nrednav/cuid2 v1.1.0/go.mod h1:jBjkJAI+QLM4EUGvtwGDHC1cP1QQrRNfLo/A7qJFDhA= -github.com/oapi-codegen/runtime v1.1.1 h1:EXLHh0DXIJnWhdRPN2w4MXAzFyE4CskzhNLUmtpMYro= -github.com/oapi-codegen/runtime v1.1.1/go.mod h1:SK9X900oXmPWilYR5/WKPzt3Kqxn/uS/+lbpREv+eCg= github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY= github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw= github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c= github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o= +github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0= +github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo= +github.com/schollz/closestmatch v2.1.0+incompatible h1:Uel2GXEpJqOWBrlyI+oY9LTiyyjYS17cCYRqP13/SHk= +github.com/schollz/closestmatch v2.1.0+incompatible/go.mod h1:RtP1ddjLong6gTkbtmuhtR2uUrrJOpYzYRvbcPAid+g= +github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tdewolff/minify/v2 v2.12.9 h1:dvn5MtmuQ/DFMwqf5j8QhEVpPX6fi3WGImhv8RUB4zA= +github.com/tdewolff/minify/v2 v2.12.9/go.mod h1:qOqdlDfL+7v0/fyymB+OP497nIxJYSvX4MQWA8OoiXU= +github.com/tdewolff/parse/v2 v2.6.8 h1:mhNZXYCx//xG7Yq2e/kVLNZw4YfYmeHbhx+Zc0OvFMA= +github.com/tdewolff/parse/v2 v2.6.8/go.mod h1:XHDhaU6IBgsryfdnpzUXBlT6leW/l25yrFBTEb4eIyM= +github.com/tdewolff/test v1.0.9 h1:SswqJCmeN4B+9gEAi/5uqT0qpi1y2/2O47V/1hhGZT0= +github.com/tdewolff/test v1.0.9/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= +github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU= +github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= +github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= +github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY= +github.com/yosssi/ace v0.0.5 h1:tUkIP/BLdKqrlrPwcmH0shwEEhTRHoGnc1wFIWmaBUA= +github.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0= +github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA= +github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc= +golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= -golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= -golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= +golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +moul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/server/lib/oapi/oapi.go b/server/lib/oapi/oapi.go index 14fa6ddf..afb62996 100644 --- a/server/lib/oapi/oapi.go +++ b/server/lib/oapi/oapi.go @@ -1,6 +1,6 @@ // Package oapi provides primitives to interact with the openapi HTTP API. // -// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.5.0 DO NOT EDIT. +// Code generated by github.com/deepmap/oapi-codegen version v1.12.4 DO NOT EDIT. package oapi import ( @@ -17,10 +17,9 @@ import ( "strings" "time" + "github.com/deepmap/oapi-codegen/pkg/runtime" "github.com/getkin/kin-openapi/openapi3" "github.com/go-chi/chi/v5" - "github.com/oapi-codegen/runtime" - strictnethttp "github.com/oapi-codegen/runtime/strictmiddleware/nethttp" ) // Defines values for ClickMouseRequestButton. @@ -47,6 +46,11 @@ const ( WRITE FileSystemEventType = "WRITE" ) +// Defines values for HealthResponseStatus. +const ( + Ok HealthResponseStatus = "ok" +) + // ClickMouseRequest defines model for ClickMouseRequest. type ClickMouseRequest struct { // Button Mouse button to interact with @@ -139,6 +143,18 @@ type FileSystemEvent struct { // FileSystemEventType Event type. type FileSystemEventType string +// HealthResponse defines model for HealthResponse. +type HealthResponse struct { + // Status Health status + Status HealthResponseStatus `json:"status"` + + // UptimeSec Uptime in seconds + UptimeSec int `json:"uptime_sec"` +} + +// HealthResponseStatus Health status +type HealthResponseStatus string + // ListFiles Array of file or directory information entries. type ListFiles = []FileInfo @@ -174,6 +190,21 @@ type RecorderInfo struct { StartedAt *time.Time `json:"started_at"` } +// ScreenshotRegionRequest defines model for ScreenshotRegionRequest. +type ScreenshotRegionRequest struct { + // Height Height of the region + Height int `json:"height"` + + // Width Width of the region + Width int `json:"width"` + + // X X coordinate of the top-left corner + X int `json:"x"` + + // Y Y coordinate of the top-left corner + Y int `json:"y"` +} + // SetFilePermissionsRequest defines model for SetFilePermissionsRequest. type SetFilePermissionsRequest struct { // Group New group name or GID. @@ -300,6 +331,9 @@ type StartRecordingJSONRequestBody = StartRecordingRequest // StopRecordingJSONRequestBody defines body for StopRecording for application/json ContentType. type StopRecordingJSONRequestBody = StopRecordingRequest +// CaptureScreenshotRegionJSONRequestBody defines body for CaptureScreenshotRegion for application/json ContentType. +type CaptureScreenshotRegionJSONRequestBody = ScreenshotRegionRequest + // RequestEditorFn is the function signature for the RequestEditor callback function type RequestEditorFn func(ctx context.Context, req *http.Request) error @@ -373,27 +407,27 @@ func WithRequestEditorFn(fn RequestEditorFn) ClientOption { // The interface specification for the client above. type ClientInterface interface { - // ClickMouseWithBody request with any body + // ClickMouse request with any body ClickMouseWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) ClickMouse(ctx context.Context, body ClickMouseJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // MoveMouseWithBody request with any body + // MoveMouse request with any body MoveMouseWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) MoveMouse(ctx context.Context, body MoveMouseJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // CreateDirectoryWithBody request with any body + // CreateDirectory request with any body CreateDirectoryWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) CreateDirectory(ctx context.Context, body CreateDirectoryJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // DeleteDirectoryWithBody request with any body + // DeleteDirectory request with any body DeleteDirectoryWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) DeleteDirectory(ctx context.Context, body DeleteDirectoryJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // DeleteFileWithBody request with any body + // DeleteFile request with any body DeleteFileWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) DeleteFile(ctx context.Context, body DeleteFileJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -404,7 +438,7 @@ type ClientInterface interface { // ListFiles request ListFiles(ctx context.Context, params *ListFilesParams, reqEditors ...RequestEditorFn) (*http.Response, error) - // MovePathWithBody request with any body + // MovePath request with any body MovePathWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) MovePath(ctx context.Context, body MovePathJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -412,12 +446,12 @@ type ClientInterface interface { // ReadFile request ReadFile(ctx context.Context, params *ReadFileParams, reqEditors ...RequestEditorFn) (*http.Response, error) - // SetFilePermissionsWithBody request with any body + // SetFilePermissions request with any body SetFilePermissionsWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) SetFilePermissions(ctx context.Context, body SetFilePermissionsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // StartFsWatchWithBody request with any body + // StartFsWatch request with any body StartFsWatchWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) StartFsWatch(ctx context.Context, body StartFsWatchJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -428,10 +462,13 @@ type ClientInterface interface { // StreamFsEvents request StreamFsEvents(ctx context.Context, watchId string, reqEditors ...RequestEditorFn) (*http.Response, error) - // WriteFileWithBody request with any body + // WriteFile request with any body WriteFileWithBody(ctx context.Context, params *WriteFileParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) - // DeleteRecordingWithBody request with any body + // GetHealth request + GetHealth(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // DeleteRecording request with any body DeleteRecordingWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) DeleteRecording(ctx context.Context, body DeleteRecordingJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -442,15 +479,23 @@ type ClientInterface interface { // ListRecorders request ListRecorders(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) - // StartRecordingWithBody request with any body + // StartRecording request with any body StartRecordingWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) StartRecording(ctx context.Context, body StartRecordingJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) - // StopRecordingWithBody request with any body + // StopRecording request with any body StopRecordingWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) StopRecording(ctx context.Context, body StopRecordingJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // CaptureScreenshot request + CaptureScreenshot(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // CaptureScreenshotRegion request with any body + CaptureScreenshotRegionWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + CaptureScreenshotRegion(ctx context.Context, body CaptureScreenshotRegionJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) } func (c *Client) ClickMouseWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { @@ -717,6 +762,18 @@ func (c *Client) WriteFileWithBody(ctx context.Context, params *WriteFileParams, return c.Client.Do(req) } +func (c *Client) GetHealth(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetHealthRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + func (c *Client) DeleteRecordingWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewDeleteRecordingRequestWithBody(c.Server, contentType, body) if err != nil { @@ -813,6 +870,42 @@ func (c *Client) StopRecording(ctx context.Context, body StopRecordingJSONReques return c.Client.Do(req) } +func (c *Client) CaptureScreenshot(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCaptureScreenshotRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) CaptureScreenshotRegionWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCaptureScreenshotRegionRequestWithBody(c.Server, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) CaptureScreenshotRegion(ctx context.Context, body CaptureScreenshotRegionJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCaptureScreenshotRegionRequest(c.Server, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + // NewClickMouseRequest calls the generic ClickMouse builder with application/json body func NewClickMouseRequest(server string, body ClickMouseJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader @@ -1032,24 +1125,22 @@ func NewFileInfoRequest(server string, params *FileInfoParams) (*http.Request, e return nil, err } - if params != nil { - queryValues := queryURL.Query() + queryValues := queryURL.Query() - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "path", runtime.ParamLocationQuery, params.Path); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { - return nil, err - } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "path", runtime.ParamLocationQuery, params.Path); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) } } - - queryURL.RawQuery = queryValues.Encode() } + queryURL.RawQuery = queryValues.Encode() + req, err := http.NewRequest("GET", queryURL.String(), nil) if err != nil { return nil, err @@ -1077,24 +1168,22 @@ func NewListFilesRequest(server string, params *ListFilesParams) (*http.Request, return nil, err } - if params != nil { - queryValues := queryURL.Query() + queryValues := queryURL.Query() - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "path", runtime.ParamLocationQuery, params.Path); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { - return nil, err - } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "path", runtime.ParamLocationQuery, params.Path); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) } } - - queryURL.RawQuery = queryValues.Encode() } + queryURL.RawQuery = queryValues.Encode() + req, err := http.NewRequest("GET", queryURL.String(), nil) if err != nil { return nil, err @@ -1162,24 +1251,22 @@ func NewReadFileRequest(server string, params *ReadFileParams) (*http.Request, e return nil, err } - if params != nil { - queryValues := queryURL.Query() + queryValues := queryURL.Query() - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "path", runtime.ParamLocationQuery, params.Path); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { - return nil, err - } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "path", runtime.ParamLocationQuery, params.Path); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) } } - - queryURL.RawQuery = queryValues.Encode() } + queryURL.RawQuery = queryValues.Encode() + req, err := http.NewRequest("GET", queryURL.String(), nil) if err != nil { return nil, err @@ -1355,10 +1442,23 @@ func NewWriteFileRequestWithBody(server string, params *WriteFileParams, content return nil, err } - if params != nil { - queryValues := queryURL.Query() + queryValues := queryURL.Query() - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "path", runtime.ParamLocationQuery, params.Path); err != nil { + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "path", runtime.ParamLocationQuery, params.Path); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) + } + } + } + + if params.Mode != nil { + + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "mode", runtime.ParamLocationQuery, *params.Mode); err != nil { return nil, err } else if parsed, err := url.ParseQuery(queryFrag); err != nil { return nil, err @@ -1370,25 +1470,10 @@ func NewWriteFileRequestWithBody(server string, params *WriteFileParams, content } } - if params.Mode != nil { - - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "mode", runtime.ParamLocationQuery, *params.Mode); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { - return nil, err - } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } - } - } - - } - - queryURL.RawQuery = queryValues.Encode() } + queryURL.RawQuery = queryValues.Encode() + req, err := http.NewRequest("PUT", queryURL.String(), body) if err != nil { return nil, err @@ -1399,6 +1484,33 @@ func NewWriteFileRequestWithBody(server string, params *WriteFileParams, content return req, nil } +// NewGetHealthRequest generates requests for GetHealth +func NewGetHealthRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/health") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + // NewDeleteRecordingRequest calls the generic DeleteRecording builder with application/json body func NewDeleteRecordingRequest(server string, body DeleteRecordingJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader @@ -1458,28 +1570,26 @@ func NewDownloadRecordingRequest(server string, params *DownloadRecordingParams) return nil, err } - if params != nil { - queryValues := queryURL.Query() + queryValues := queryURL.Query() - if params.Id != nil { + if params.Id != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "id", runtime.ParamLocationQuery, *params.Id); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { - return nil, err - } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "id", runtime.ParamLocationQuery, *params.Id); err != nil { + return nil, err + } else if parsed, err := url.ParseQuery(queryFrag); err != nil { + return nil, err + } else { + for k, v := range parsed { + for _, v2 := range v { + queryValues.Add(k, v2) } } - } - queryURL.RawQuery = queryValues.Encode() } + queryURL.RawQuery = queryValues.Encode() + req, err := http.NewRequest("GET", queryURL.String(), nil) if err != nil { return nil, err @@ -1595,6 +1705,73 @@ func NewStopRecordingRequestWithBody(server string, contentType string, body io. return req, nil } +// NewCaptureScreenshotRequest generates requests for CaptureScreenshot +func NewCaptureScreenshotRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/screenshot") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewCaptureScreenshotRegionRequest calls the generic CaptureScreenshotRegion builder with application/json body +func NewCaptureScreenshotRegionRequest(server string, body CaptureScreenshotRegionJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewCaptureScreenshotRegionRequestWithBody(server, "application/json", bodyReader) +} + +// NewCaptureScreenshotRegionRequestWithBody generates requests for CaptureScreenshotRegion with any type of body +func NewCaptureScreenshotRegionRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/screenshot") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + func (c *Client) applyEditors(ctx context.Context, req *http.Request, additionalEditors []RequestEditorFn) error { for _, r := range c.RequestEditors { if err := r(ctx, req); err != nil { @@ -1638,91 +1815,102 @@ func WithBaseURL(baseURL string) ClientOption { // ClientWithResponsesInterface is the interface specification for the client with responses above. type ClientWithResponsesInterface interface { - // ClickMouseWithBodyWithResponse request with any body + // ClickMouse request with any body ClickMouseWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ClickMouseResponse, error) ClickMouseWithResponse(ctx context.Context, body ClickMouseJSONRequestBody, reqEditors ...RequestEditorFn) (*ClickMouseResponse, error) - // MoveMouseWithBodyWithResponse request with any body + // MoveMouse request with any body MoveMouseWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*MoveMouseResponse, error) MoveMouseWithResponse(ctx context.Context, body MoveMouseJSONRequestBody, reqEditors ...RequestEditorFn) (*MoveMouseResponse, error) - // CreateDirectoryWithBodyWithResponse request with any body + // CreateDirectory request with any body CreateDirectoryWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateDirectoryResponse, error) CreateDirectoryWithResponse(ctx context.Context, body CreateDirectoryJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateDirectoryResponse, error) - // DeleteDirectoryWithBodyWithResponse request with any body + // DeleteDirectory request with any body DeleteDirectoryWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*DeleteDirectoryResponse, error) DeleteDirectoryWithResponse(ctx context.Context, body DeleteDirectoryJSONRequestBody, reqEditors ...RequestEditorFn) (*DeleteDirectoryResponse, error) - // DeleteFileWithBodyWithResponse request with any body + // DeleteFile request with any body DeleteFileWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*DeleteFileResponse, error) DeleteFileWithResponse(ctx context.Context, body DeleteFileJSONRequestBody, reqEditors ...RequestEditorFn) (*DeleteFileResponse, error) - // FileInfoWithResponse request + // FileInfo request FileInfoWithResponse(ctx context.Context, params *FileInfoParams, reqEditors ...RequestEditorFn) (*FileInfoResponse, error) - // ListFilesWithResponse request + // ListFiles request ListFilesWithResponse(ctx context.Context, params *ListFilesParams, reqEditors ...RequestEditorFn) (*ListFilesResponse, error) - // MovePathWithBodyWithResponse request with any body + // MovePath request with any body MovePathWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*MovePathResponse, error) MovePathWithResponse(ctx context.Context, body MovePathJSONRequestBody, reqEditors ...RequestEditorFn) (*MovePathResponse, error) - // ReadFileWithResponse request + // ReadFile request ReadFileWithResponse(ctx context.Context, params *ReadFileParams, reqEditors ...RequestEditorFn) (*ReadFileResponse, error) - // SetFilePermissionsWithBodyWithResponse request with any body + // SetFilePermissions request with any body SetFilePermissionsWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SetFilePermissionsResponse, error) SetFilePermissionsWithResponse(ctx context.Context, body SetFilePermissionsJSONRequestBody, reqEditors ...RequestEditorFn) (*SetFilePermissionsResponse, error) - // StartFsWatchWithBodyWithResponse request with any body + // StartFsWatch request with any body StartFsWatchWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*StartFsWatchResponse, error) StartFsWatchWithResponse(ctx context.Context, body StartFsWatchJSONRequestBody, reqEditors ...RequestEditorFn) (*StartFsWatchResponse, error) - // StopFsWatchWithResponse request + // StopFsWatch request StopFsWatchWithResponse(ctx context.Context, watchId string, reqEditors ...RequestEditorFn) (*StopFsWatchResponse, error) - // StreamFsEventsWithResponse request + // StreamFsEvents request StreamFsEventsWithResponse(ctx context.Context, watchId string, reqEditors ...RequestEditorFn) (*StreamFsEventsResponse, error) - // WriteFileWithBodyWithResponse request with any body + // WriteFile request with any body WriteFileWithBodyWithResponse(ctx context.Context, params *WriteFileParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*WriteFileResponse, error) - // DeleteRecordingWithBodyWithResponse request with any body + // GetHealth request + GetHealthWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetHealthResponse, error) + + // DeleteRecording request with any body DeleteRecordingWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*DeleteRecordingResponse, error) DeleteRecordingWithResponse(ctx context.Context, body DeleteRecordingJSONRequestBody, reqEditors ...RequestEditorFn) (*DeleteRecordingResponse, error) - // DownloadRecordingWithResponse request + // DownloadRecording request DownloadRecordingWithResponse(ctx context.Context, params *DownloadRecordingParams, reqEditors ...RequestEditorFn) (*DownloadRecordingResponse, error) - // ListRecordersWithResponse request + // ListRecorders request ListRecordersWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*ListRecordersResponse, error) - // StartRecordingWithBodyWithResponse request with any body + // StartRecording request with any body StartRecordingWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*StartRecordingResponse, error) StartRecordingWithResponse(ctx context.Context, body StartRecordingJSONRequestBody, reqEditors ...RequestEditorFn) (*StartRecordingResponse, error) - // StopRecordingWithBodyWithResponse request with any body + // StopRecording request with any body StopRecordingWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*StopRecordingResponse, error) StopRecordingWithResponse(ctx context.Context, body StopRecordingJSONRequestBody, reqEditors ...RequestEditorFn) (*StopRecordingResponse, error) + + // CaptureScreenshot request + CaptureScreenshotWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*CaptureScreenshotResponse, error) + + // CaptureScreenshotRegion request with any body + CaptureScreenshotRegionWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CaptureScreenshotRegionResponse, error) + + CaptureScreenshotRegionWithResponse(ctx context.Context, body CaptureScreenshotRegionJSONRequestBody, reqEditors ...RequestEditorFn) (*CaptureScreenshotRegionResponse, error) } type ClickMouseResponse struct { Body []byte HTTPResponse *http.Response - JSON400 *BadRequestError - JSON500 *InternalError + JSON400 *Error + JSON500 *Error } // Status returns HTTPResponse.Status @@ -1744,8 +1932,8 @@ func (r ClickMouseResponse) StatusCode() int { type MoveMouseResponse struct { Body []byte HTTPResponse *http.Response - JSON400 *BadRequestError - JSON500 *InternalError + JSON400 *Error + JSON500 *Error } // Status returns HTTPResponse.Status @@ -1767,8 +1955,8 @@ func (r MoveMouseResponse) StatusCode() int { type CreateDirectoryResponse struct { Body []byte HTTPResponse *http.Response - JSON400 *BadRequestError - JSON500 *InternalError + JSON400 *Error + JSON500 *Error } // Status returns HTTPResponse.Status @@ -1790,9 +1978,9 @@ func (r CreateDirectoryResponse) StatusCode() int { type DeleteDirectoryResponse struct { Body []byte HTTPResponse *http.Response - JSON400 *BadRequestError - JSON404 *NotFoundError - JSON500 *InternalError + JSON400 *Error + JSON404 *Error + JSON500 *Error } // Status returns HTTPResponse.Status @@ -1814,9 +2002,9 @@ func (r DeleteDirectoryResponse) StatusCode() int { type DeleteFileResponse struct { Body []byte HTTPResponse *http.Response - JSON400 *BadRequestError - JSON404 *NotFoundError - JSON500 *InternalError + JSON400 *Error + JSON404 *Error + JSON500 *Error } // Status returns HTTPResponse.Status @@ -1839,9 +2027,9 @@ type FileInfoResponse struct { Body []byte HTTPResponse *http.Response JSON200 *FileInfo - JSON400 *BadRequestError - JSON404 *NotFoundError - JSON500 *InternalError + JSON400 *Error + JSON404 *Error + JSON500 *Error } // Status returns HTTPResponse.Status @@ -1864,9 +2052,9 @@ type ListFilesResponse struct { Body []byte HTTPResponse *http.Response JSON200 *ListFiles - JSON400 *BadRequestError - JSON404 *NotFoundError - JSON500 *InternalError + JSON400 *Error + JSON404 *Error + JSON500 *Error } // Status returns HTTPResponse.Status @@ -1888,9 +2076,9 @@ func (r ListFilesResponse) StatusCode() int { type MovePathResponse struct { Body []byte HTTPResponse *http.Response - JSON400 *BadRequestError - JSON404 *NotFoundError - JSON500 *InternalError + JSON400 *Error + JSON404 *Error + JSON500 *Error } // Status returns HTTPResponse.Status @@ -1912,9 +2100,9 @@ func (r MovePathResponse) StatusCode() int { type ReadFileResponse struct { Body []byte HTTPResponse *http.Response - JSON400 *BadRequestError - JSON404 *NotFoundError - JSON500 *InternalError + JSON400 *Error + JSON404 *Error + JSON500 *Error } // Status returns HTTPResponse.Status @@ -1936,9 +2124,9 @@ func (r ReadFileResponse) StatusCode() int { type SetFilePermissionsResponse struct { Body []byte HTTPResponse *http.Response - JSON400 *BadRequestError - JSON404 *NotFoundError - JSON500 *InternalError + JSON400 *Error + JSON404 *Error + JSON500 *Error } // Status returns HTTPResponse.Status @@ -1964,9 +2152,9 @@ type StartFsWatchResponse struct { // WatchId Unique identifier for the directory watch WatchId *string `json:"watch_id,omitempty"` } - JSON400 *BadRequestError - JSON404 *NotFoundError - JSON500 *InternalError + JSON400 *Error + JSON404 *Error + JSON500 *Error } // Status returns HTTPResponse.Status @@ -1988,9 +2176,9 @@ func (r StartFsWatchResponse) StatusCode() int { type StopFsWatchResponse struct { Body []byte HTTPResponse *http.Response - JSON400 *BadRequestError - JSON404 *NotFoundError - JSON500 *InternalError + JSON400 *Error + JSON404 *Error + JSON500 *Error } // Status returns HTTPResponse.Status @@ -2012,9 +2200,9 @@ func (r StopFsWatchResponse) StatusCode() int { type StreamFsEventsResponse struct { Body []byte HTTPResponse *http.Response - JSON400 *BadRequestError - JSON404 *NotFoundError - JSON500 *InternalError + JSON400 *Error + JSON404 *Error + JSON500 *Error } // Status returns HTTPResponse.Status @@ -2036,9 +2224,9 @@ func (r StreamFsEventsResponse) StatusCode() int { type WriteFileResponse struct { Body []byte HTTPResponse *http.Response - JSON400 *BadRequestError - JSON404 *NotFoundError - JSON500 *InternalError + JSON400 *Error + JSON404 *Error + JSON500 *Error } // Status returns HTTPResponse.Status @@ -2057,12 +2245,35 @@ func (r WriteFileResponse) StatusCode() int { return 0 } +type GetHealthResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *HealthResponse + JSON500 *Error +} + +// Status returns HTTPResponse.Status +func (r GetHealthResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetHealthResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + type DeleteRecordingResponse struct { Body []byte HTTPResponse *http.Response - JSON400 *BadRequestError - JSON404 *NotFoundError - JSON500 *InternalError + JSON400 *Error + JSON404 *Error + JSON500 *Error } // Status returns HTTPResponse.Status @@ -2084,9 +2295,9 @@ func (r DeleteRecordingResponse) StatusCode() int { type DownloadRecordingResponse struct { Body []byte HTTPResponse *http.Response - JSON400 *BadRequestError - JSON404 *NotFoundError - JSON500 *InternalError + JSON400 *Error + JSON404 *Error + JSON500 *Error } // Status returns HTTPResponse.Status @@ -2109,7 +2320,7 @@ type ListRecordersResponse struct { Body []byte HTTPResponse *http.Response JSON200 *[]RecorderInfo - JSON500 *InternalError + JSON500 *Error } // Status returns HTTPResponse.Status @@ -2131,9 +2342,9 @@ func (r ListRecordersResponse) StatusCode() int { type StartRecordingResponse struct { Body []byte HTTPResponse *http.Response - JSON400 *BadRequestError - JSON409 *ConflictError - JSON500 *InternalError + JSON400 *Error + JSON409 *Error + JSON500 *Error } // Status returns HTTPResponse.Status @@ -2155,8 +2366,8 @@ func (r StartRecordingResponse) StatusCode() int { type StopRecordingResponse struct { Body []byte HTTPResponse *http.Response - JSON400 *BadRequestError - JSON500 *InternalError + JSON400 *Error + JSON500 *Error } // Status returns HTTPResponse.Status @@ -2175,6 +2386,51 @@ func (r StopRecordingResponse) StatusCode() int { return 0 } +type CaptureScreenshotResponse struct { + Body []byte + HTTPResponse *http.Response + JSON500 *Error +} + +// Status returns HTTPResponse.Status +func (r CaptureScreenshotResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r CaptureScreenshotResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type CaptureScreenshotRegionResponse struct { + Body []byte + HTTPResponse *http.Response + JSON400 *Error + JSON500 *Error +} + +// Status returns HTTPResponse.Status +func (r CaptureScreenshotRegionResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r CaptureScreenshotRegionResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + // ClickMouseWithBodyWithResponse request with arbitrary body returning *ClickMouseResponse func (c *ClientWithResponses) ClickMouseWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ClickMouseResponse, error) { rsp, err := c.ClickMouseWithBody(ctx, contentType, body, reqEditors...) @@ -2365,6 +2621,15 @@ func (c *ClientWithResponses) WriteFileWithBodyWithResponse(ctx context.Context, return ParseWriteFileResponse(rsp) } +// GetHealthWithResponse request returning *GetHealthResponse +func (c *ClientWithResponses) GetHealthWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetHealthResponse, error) { + rsp, err := c.GetHealth(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetHealthResponse(rsp) +} + // DeleteRecordingWithBodyWithResponse request with arbitrary body returning *DeleteRecordingResponse func (c *ClientWithResponses) DeleteRecordingWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*DeleteRecordingResponse, error) { rsp, err := c.DeleteRecordingWithBody(ctx, contentType, body, reqEditors...) @@ -2434,6 +2699,32 @@ func (c *ClientWithResponses) StopRecordingWithResponse(ctx context.Context, bod return ParseStopRecordingResponse(rsp) } +// CaptureScreenshotWithResponse request returning *CaptureScreenshotResponse +func (c *ClientWithResponses) CaptureScreenshotWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*CaptureScreenshotResponse, error) { + rsp, err := c.CaptureScreenshot(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseCaptureScreenshotResponse(rsp) +} + +// CaptureScreenshotRegionWithBodyWithResponse request with arbitrary body returning *CaptureScreenshotRegionResponse +func (c *ClientWithResponses) CaptureScreenshotRegionWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CaptureScreenshotRegionResponse, error) { + rsp, err := c.CaptureScreenshotRegionWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseCaptureScreenshotRegionResponse(rsp) +} + +func (c *ClientWithResponses) CaptureScreenshotRegionWithResponse(ctx context.Context, body CaptureScreenshotRegionJSONRequestBody, reqEditors ...RequestEditorFn) (*CaptureScreenshotRegionResponse, error) { + rsp, err := c.CaptureScreenshotRegion(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseCaptureScreenshotRegionResponse(rsp) +} + // ParseClickMouseResponse parses an HTTP response from a ClickMouseWithResponse call func ParseClickMouseResponse(rsp *http.Response) (*ClickMouseResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) @@ -2449,14 +2740,14 @@ func ParseClickMouseResponse(rsp *http.Response) (*ClickMouseResponse, error) { switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest BadRequestError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON400 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: - var dest InternalError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -2482,14 +2773,14 @@ func ParseMoveMouseResponse(rsp *http.Response) (*MoveMouseResponse, error) { switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest BadRequestError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON400 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: - var dest InternalError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -2515,14 +2806,14 @@ func ParseCreateDirectoryResponse(rsp *http.Response) (*CreateDirectoryResponse, switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest BadRequestError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON400 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: - var dest InternalError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -2548,21 +2839,21 @@ func ParseDeleteDirectoryResponse(rsp *http.Response) (*DeleteDirectoryResponse, switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest BadRequestError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON400 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest NotFoundError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON404 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: - var dest InternalError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -2588,21 +2879,21 @@ func ParseDeleteFileResponse(rsp *http.Response) (*DeleteFileResponse, error) { switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest BadRequestError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON400 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest NotFoundError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON404 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: - var dest InternalError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -2635,21 +2926,21 @@ func ParseFileInfoResponse(rsp *http.Response) (*FileInfoResponse, error) { response.JSON200 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest BadRequestError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON400 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest NotFoundError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON404 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: - var dest InternalError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -2682,21 +2973,21 @@ func ParseListFilesResponse(rsp *http.Response) (*ListFilesResponse, error) { response.JSON200 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest BadRequestError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON400 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest NotFoundError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON404 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: - var dest InternalError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -2722,21 +3013,21 @@ func ParseMovePathResponse(rsp *http.Response) (*MovePathResponse, error) { switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest BadRequestError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON400 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest NotFoundError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON404 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: - var dest InternalError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -2762,21 +3053,21 @@ func ParseReadFileResponse(rsp *http.Response) (*ReadFileResponse, error) { switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest BadRequestError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON400 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest NotFoundError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON404 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: - var dest InternalError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -2802,21 +3093,21 @@ func ParseSetFilePermissionsResponse(rsp *http.Response) (*SetFilePermissionsRes switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest BadRequestError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON400 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest NotFoundError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON404 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: - var dest InternalError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -2852,21 +3143,21 @@ func ParseStartFsWatchResponse(rsp *http.Response) (*StartFsWatchResponse, error response.JSON201 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest BadRequestError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON400 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest NotFoundError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON404 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: - var dest InternalError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -2885,28 +3176,68 @@ func ParseStopFsWatchResponse(rsp *http.Response) (*StopFsWatchResponse, error) return nil, err } - response := &StopFsWatchResponse{ + response := &StopFsWatchResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseStreamFsEventsResponse parses an HTTP response from a StreamFsEventsWithResponse call +func ParseStreamFsEventsResponse(rsp *http.Response) (*StreamFsEventsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &StreamFsEventsResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest BadRequestError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON400 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest NotFoundError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON404 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: - var dest InternalError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -2917,36 +3248,36 @@ func ParseStopFsWatchResponse(rsp *http.Response) (*StopFsWatchResponse, error) return response, nil } -// ParseStreamFsEventsResponse parses an HTTP response from a StreamFsEventsWithResponse call -func ParseStreamFsEventsResponse(rsp *http.Response) (*StreamFsEventsResponse, error) { +// ParseWriteFileResponse parses an HTTP response from a WriteFileWithResponse call +func ParseWriteFileResponse(rsp *http.Response) (*WriteFileResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &StreamFsEventsResponse{ + response := &WriteFileResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest BadRequestError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON400 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest NotFoundError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON404 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: - var dest InternalError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -2957,36 +3288,29 @@ func ParseStreamFsEventsResponse(rsp *http.Response) (*StreamFsEventsResponse, e return response, nil } -// ParseWriteFileResponse parses an HTTP response from a WriteFileWithResponse call -func ParseWriteFileResponse(rsp *http.Response) (*WriteFileResponse, error) { +// ParseGetHealthResponse parses an HTTP response from a GetHealthWithResponse call +func ParseGetHealthResponse(rsp *http.Response) (*GetHealthResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &WriteFileResponse{ + response := &GetHealthResponse{ Body: bodyBytes, HTTPResponse: rsp, } switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest BadRequestError - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON400 = &dest - - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest NotFoundError + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest HealthResponse if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } - response.JSON404 = &dest + response.JSON200 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: - var dest InternalError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -3012,21 +3336,21 @@ func ParseDeleteRecordingResponse(rsp *http.Response) (*DeleteRecordingResponse, switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest BadRequestError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON400 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest NotFoundError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON404 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: - var dest InternalError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -3052,21 +3376,21 @@ func ParseDownloadRecordingResponse(rsp *http.Response) (*DownloadRecordingRespo switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest BadRequestError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON400 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest NotFoundError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON404 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: - var dest InternalError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -3099,7 +3423,7 @@ func ParseListRecordersResponse(rsp *http.Response) (*ListRecordersResponse, err response.JSON200 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: - var dest InternalError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -3125,21 +3449,21 @@ func ParseStartRecordingResponse(rsp *http.Response) (*StartRecordingResponse, e switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest BadRequestError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON400 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 409: - var dest ConflictError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON409 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: - var dest InternalError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -3165,14 +3489,73 @@ func ParseStopRecordingResponse(rsp *http.Response) (*StopRecordingResponse, err switch { case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: - var dest BadRequestError + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseCaptureScreenshotResponse parses an HTTP response from a CaptureScreenshotWithResponse call +func ParseCaptureScreenshotResponse(rsp *http.Response) (*CaptureScreenshotResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &CaptureScreenshotResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest Error + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseCaptureScreenshotRegionResponse parses an HTTP response from a CaptureScreenshotRegionWithResponse call +func ParseCaptureScreenshotRegionResponse(rsp *http.Response) (*CaptureScreenshotRegionResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &CaptureScreenshotRegionResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } response.JSON400 = &dest case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: - var dest InternalError + var dest Error if err := json.Unmarshal(bodyBytes, &dest); err != nil { return nil, err } @@ -3227,6 +3610,9 @@ type ServerInterface interface { // Write or create a file // (PUT /fs/write_file) WriteFile(w http.ResponseWriter, r *http.Request, params WriteFileParams) + // Check API health status + // (GET /health) + GetHealth(w http.ResponseWriter, r *http.Request) // Delete a previously recorded video file // (POST /recording/delete) DeleteRecording(w http.ResponseWriter, r *http.Request) @@ -3244,124 +3630,6 @@ type ServerInterface interface { StopRecording(w http.ResponseWriter, r *http.Request) } -// Unimplemented server implementation that returns http.StatusNotImplemented for each endpoint. - -type Unimplemented struct{} - -// Simulate a mouse click action on the host computer -// (POST /computer/click_mouse) -func (_ Unimplemented) ClickMouse(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNotImplemented) -} - -// Move the mouse cursor to the specified coordinates on the host computer -// (POST /computer/move_mouse) -func (_ Unimplemented) MoveMouse(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNotImplemented) -} - -// Create a new directory -// (PUT /fs/create_directory) -func (_ Unimplemented) CreateDirectory(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNotImplemented) -} - -// Delete a directory -// (PUT /fs/delete_directory) -func (_ Unimplemented) DeleteDirectory(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNotImplemented) -} - -// Delete a file -// (PUT /fs/delete_file) -func (_ Unimplemented) DeleteFile(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNotImplemented) -} - -// Get information about a file or directory -// (GET /fs/file_info) -func (_ Unimplemented) FileInfo(w http.ResponseWriter, r *http.Request, params FileInfoParams) { - w.WriteHeader(http.StatusNotImplemented) -} - -// List files in a directory -// (GET /fs/list_files) -func (_ Unimplemented) ListFiles(w http.ResponseWriter, r *http.Request, params ListFilesParams) { - w.WriteHeader(http.StatusNotImplemented) -} - -// Move or rename a file or directory -// (PUT /fs/move) -func (_ Unimplemented) MovePath(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNotImplemented) -} - -// Read file contents -// (GET /fs/read_file) -func (_ Unimplemented) ReadFile(w http.ResponseWriter, r *http.Request, params ReadFileParams) { - w.WriteHeader(http.StatusNotImplemented) -} - -// Set file or directory permissions/ownership -// (PUT /fs/set_file_permissions) -func (_ Unimplemented) SetFilePermissions(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNotImplemented) -} - -// Watch a directory for changes -// (POST /fs/watch) -func (_ Unimplemented) StartFsWatch(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNotImplemented) -} - -// Stop watching a directory -// (DELETE /fs/watch/{watch_id}) -func (_ Unimplemented) StopFsWatch(w http.ResponseWriter, r *http.Request, watchId string) { - w.WriteHeader(http.StatusNotImplemented) -} - -// Stream filesystem events for a watch -// (GET /fs/watch/{watch_id}/events) -func (_ Unimplemented) StreamFsEvents(w http.ResponseWriter, r *http.Request, watchId string) { - w.WriteHeader(http.StatusNotImplemented) -} - -// Write or create a file -// (PUT /fs/write_file) -func (_ Unimplemented) WriteFile(w http.ResponseWriter, r *http.Request, params WriteFileParams) { - w.WriteHeader(http.StatusNotImplemented) -} - -// Delete a previously recorded video file -// (POST /recording/delete) -func (_ Unimplemented) DeleteRecording(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNotImplemented) -} - -// Download the most recently recorded video file -// (GET /recording/download) -func (_ Unimplemented) DownloadRecording(w http.ResponseWriter, r *http.Request, params DownloadRecordingParams) { - w.WriteHeader(http.StatusNotImplemented) -} - -// List all recorders -// (GET /recording/list) -func (_ Unimplemented) ListRecorders(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNotImplemented) -} - -// Start a screen recording. Only one recording per ID can be registered at a time. -// (POST /recording/start) -func (_ Unimplemented) StartRecording(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNotImplemented) -} - -// Stop the recording -// (POST /recording/stop) -func (_ Unimplemented) StopRecording(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNotImplemented) -} - // ServerInterfaceWrapper converts contexts to parameters. type ServerInterfaceWrapper struct { Handler ServerInterface @@ -3373,76 +3641,82 @@ type MiddlewareFunc func(http.Handler) http.Handler // ClickMouse operation middleware func (siw *ServerInterfaceWrapper) ClickMouse(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { siw.Handler.ClickMouse(w, r) - })) + }) for _, middleware := range siw.HandlerMiddlewares { handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // MoveMouse operation middleware func (siw *ServerInterfaceWrapper) MoveMouse(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { siw.Handler.MoveMouse(w, r) - })) + }) for _, middleware := range siw.HandlerMiddlewares { handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // CreateDirectory operation middleware func (siw *ServerInterfaceWrapper) CreateDirectory(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { siw.Handler.CreateDirectory(w, r) - })) + }) for _, middleware := range siw.HandlerMiddlewares { handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // DeleteDirectory operation middleware func (siw *ServerInterfaceWrapper) DeleteDirectory(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { siw.Handler.DeleteDirectory(w, r) - })) + }) for _, middleware := range siw.HandlerMiddlewares { handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // DeleteFile operation middleware func (siw *ServerInterfaceWrapper) DeleteFile(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { siw.Handler.DeleteFile(w, r) - })) + }) for _, middleware := range siw.HandlerMiddlewares { handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // FileInfo operation middleware func (siw *ServerInterfaceWrapper) FileInfo(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -3464,19 +3738,20 @@ func (siw *ServerInterfaceWrapper) FileInfo(w http.ResponseWriter, r *http.Reque return } - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { siw.Handler.FileInfo(w, r, params) - })) + }) for _, middleware := range siw.HandlerMiddlewares { handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // ListFiles operation middleware func (siw *ServerInterfaceWrapper) ListFiles(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -3498,33 +3773,35 @@ func (siw *ServerInterfaceWrapper) ListFiles(w http.ResponseWriter, r *http.Requ return } - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { siw.Handler.ListFiles(w, r, params) - })) + }) for _, middleware := range siw.HandlerMiddlewares { handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // MovePath operation middleware func (siw *ServerInterfaceWrapper) MovePath(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { siw.Handler.MovePath(w, r) - })) + }) for _, middleware := range siw.HandlerMiddlewares { handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // ReadFile operation middleware func (siw *ServerInterfaceWrapper) ReadFile(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -3546,97 +3823,102 @@ func (siw *ServerInterfaceWrapper) ReadFile(w http.ResponseWriter, r *http.Reque return } - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { siw.Handler.ReadFile(w, r, params) - })) + }) for _, middleware := range siw.HandlerMiddlewares { handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // SetFilePermissions operation middleware func (siw *ServerInterfaceWrapper) SetFilePermissions(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { siw.Handler.SetFilePermissions(w, r) - })) + }) for _, middleware := range siw.HandlerMiddlewares { handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // StartFsWatch operation middleware func (siw *ServerInterfaceWrapper) StartFsWatch(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { siw.Handler.StartFsWatch(w, r) - })) + }) for _, middleware := range siw.HandlerMiddlewares { handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // StopFsWatch operation middleware func (siw *ServerInterfaceWrapper) StopFsWatch(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error // ------------- Path parameter "watch_id" ------------- var watchId string - err = runtime.BindStyledParameterWithOptions("simple", "watch_id", chi.URLParam(r, "watch_id"), &watchId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithLocation("simple", false, "watch_id", runtime.ParamLocationPath, chi.URLParam(r, "watch_id"), &watchId) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "watch_id", Err: err}) return } - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { siw.Handler.StopFsWatch(w, r, watchId) - })) + }) for _, middleware := range siw.HandlerMiddlewares { handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // StreamFsEvents operation middleware func (siw *ServerInterfaceWrapper) StreamFsEvents(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error // ------------- Path parameter "watch_id" ------------- var watchId string - err = runtime.BindStyledParameterWithOptions("simple", "watch_id", chi.URLParam(r, "watch_id"), &watchId, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + err = runtime.BindStyledParameterWithLocation("simple", false, "watch_id", runtime.ParamLocationPath, chi.URLParam(r, "watch_id"), &watchId) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "watch_id", Err: err}) return } - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { siw.Handler.StreamFsEvents(w, r, watchId) - })) + }) for _, middleware := range siw.HandlerMiddlewares { handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // WriteFile operation middleware func (siw *ServerInterfaceWrapper) WriteFile(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -3666,33 +3948,50 @@ func (siw *ServerInterfaceWrapper) WriteFile(w http.ResponseWriter, r *http.Requ return } - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { siw.Handler.WriteFile(w, r, params) - })) + }) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r.WithContext(ctx)) +} + +// GetHealth operation middleware +func (siw *ServerInterfaceWrapper) GetHealth(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GetHealth(w, r) + }) for _, middleware := range siw.HandlerMiddlewares { handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // DeleteRecording operation middleware func (siw *ServerInterfaceWrapper) DeleteRecording(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { siw.Handler.DeleteRecording(w, r) - })) + }) for _, middleware := range siw.HandlerMiddlewares { handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // DownloadRecording operation middleware func (siw *ServerInterfaceWrapper) DownloadRecording(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() var err error @@ -3707,57 +4006,60 @@ func (siw *ServerInterfaceWrapper) DownloadRecording(w http.ResponseWriter, r *h return } - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { siw.Handler.DownloadRecording(w, r, params) - })) + }) for _, middleware := range siw.HandlerMiddlewares { handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // ListRecorders operation middleware func (siw *ServerInterfaceWrapper) ListRecorders(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { siw.Handler.ListRecorders(w, r) - })) + }) for _, middleware := range siw.HandlerMiddlewares { handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // StartRecording operation middleware func (siw *ServerInterfaceWrapper) StartRecording(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { siw.Handler.StartRecording(w, r) - })) + }) for _, middleware := range siw.HandlerMiddlewares { handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } // StopRecording operation middleware func (siw *ServerInterfaceWrapper) StopRecording(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + var handler http.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { siw.Handler.StopRecording(w, r) - })) + }) for _, middleware := range siw.HandlerMiddlewares { handler = middleware(handler) } - handler.ServeHTTP(w, r) + handler.ServeHTTP(w, r.WithContext(ctx)) } type UnescapedCookieParamError struct { @@ -3773,16 +4075,16 @@ func (e *UnescapedCookieParamError) Unwrap() error { return e.Err } -type UnmarshalingParamError struct { +type UnmarshallingParamError struct { ParamName string Err error } -func (e *UnmarshalingParamError) Error() string { - return fmt.Sprintf("Error unmarshaling parameter %s as JSON: %s", e.ParamName, e.Err.Error()) +func (e *UnmarshallingParamError) Error() string { + return fmt.Sprintf("Error unmarshalling parameter %s as JSON: %s", e.ParamName, e.Err.Error()) } -func (e *UnmarshalingParamError) Unwrap() error { +func (e *UnmarshallingParamError) Unwrap() error { return e.Err } @@ -3915,6 +4217,9 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl r.Group(func(r chi.Router) { r.Put(options.BaseURL+"/fs/write_file", wrapper.WriteFile) }) + r.Group(func(r chi.Router) { + r.Get(options.BaseURL+"/health", wrapper.GetHealth) + }) r.Group(func(r chi.Router) { r.Post(options.BaseURL+"/recording/delete", wrapper.DeleteRecording) }) @@ -4467,40 +4772,18 @@ type StreamFsEvents200TexteventStreamResponse struct { } func (response StreamFsEvents200TexteventStreamResponse) VisitStreamFsEventsResponse(w http.ResponseWriter) error { + w.Header().Set("X-SSE-Content-Type", fmt.Sprint(response.Headers.XSSEContentType)) w.Header().Set("Content-Type", "text/event-stream") if response.ContentLength != 0 { w.Header().Set("Content-Length", fmt.Sprint(response.ContentLength)) } - w.Header().Set("X-SSE-Content-Type", fmt.Sprint(response.Headers.XSSEContentType)) w.WriteHeader(200) if closer, ok := response.Body.(io.ReadCloser); ok { defer closer.Close() } - flusher, ok := w.(http.Flusher) - if !ok { - // If w doesn't support flushing, might as well use io.Copy - _, err := io.Copy(w, response.Body) - return err - } - - // Use a buffer for efficient copying and flushing - buf := make([]byte, 4096) // text/event-stream are usually very small messages - for { - n, err := response.Body.Read(buf) - if n > 0 { - if _, werr := w.Write(buf[:n]); werr != nil { - return werr - } - flusher.Flush() // Flush after each write - } - if err != nil { - if err == io.EOF { - return nil // End of file, no error - } - return err - } - } + _, err := io.Copy(w, response.Body) + return err } type StreamFsEvents400JSONResponse struct{ BadRequestErrorJSONResponse } @@ -4574,6 +4857,31 @@ func (response WriteFile500JSONResponse) VisitWriteFileResponse(w http.ResponseW return json.NewEncoder(w).Encode(response) } +type GetHealthRequestObject struct { +} + +type GetHealthResponseObject interface { + VisitGetHealthResponse(w http.ResponseWriter) error +} + +type GetHealth200JSONResponse HealthResponse + +func (response GetHealth200JSONResponse) VisitGetHealthResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetHealth500JSONResponse struct{ InternalErrorJSONResponse } + +func (response GetHealth500JSONResponse) VisitGetHealthResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + type DeleteRecordingRequestObject struct { Body *DeleteRecordingJSONRequestBody } @@ -4637,12 +4945,12 @@ type DownloadRecording200Videomp4Response struct { } func (response DownloadRecording200Videomp4Response) VisitDownloadRecordingResponse(w http.ResponseWriter) error { + w.Header().Set("X-Recording-Finished-At", fmt.Sprint(response.Headers.XRecordingFinishedAt)) + w.Header().Set("X-Recording-Started-At", fmt.Sprint(response.Headers.XRecordingStartedAt)) w.Header().Set("Content-Type", "video/mp4") if response.ContentLength != 0 { w.Header().Set("Content-Length", fmt.Sprint(response.ContentLength)) } - w.Header().Set("X-Recording-Finished-At", fmt.Sprint(response.Headers.XRecordingFinishedAt)) - w.Header().Set("X-Recording-Started-At", fmt.Sprint(response.Headers.XRecordingStartedAt)) w.WriteHeader(200) if closer, ok := response.Body.(io.ReadCloser); ok { @@ -4795,6 +5103,86 @@ func (response StopRecording500JSONResponse) VisitStopRecordingResponse(w http.R return json.NewEncoder(w).Encode(response) } +type CaptureScreenshotRequestObject struct { +} + +type CaptureScreenshotResponseObject interface { + VisitCaptureScreenshotResponse(w http.ResponseWriter) error +} + +type CaptureScreenshot200ImagepngResponse struct { + Body io.Reader + ContentLength int64 +} + +func (response CaptureScreenshot200ImagepngResponse) VisitCaptureScreenshotResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "image/png") + if response.ContentLength != 0 { + w.Header().Set("Content-Length", fmt.Sprint(response.ContentLength)) + } + w.WriteHeader(200) + + if closer, ok := response.Body.(io.ReadCloser); ok { + defer closer.Close() + } + _, err := io.Copy(w, response.Body) + return err +} + +type CaptureScreenshot500JSONResponse struct{ InternalErrorJSONResponse } + +func (response CaptureScreenshot500JSONResponse) VisitCaptureScreenshotResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + +type CaptureScreenshotRegionRequestObject struct { + Body *CaptureScreenshotRegionJSONRequestBody +} + +type CaptureScreenshotRegionResponseObject interface { + VisitCaptureScreenshotRegionResponse(w http.ResponseWriter) error +} + +type CaptureScreenshotRegion200ImagepngResponse struct { + Body io.Reader + ContentLength int64 +} + +func (response CaptureScreenshotRegion200ImagepngResponse) VisitCaptureScreenshotRegionResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "image/png") + if response.ContentLength != 0 { + w.Header().Set("Content-Length", fmt.Sprint(response.ContentLength)) + } + w.WriteHeader(200) + + if closer, ok := response.Body.(io.ReadCloser); ok { + defer closer.Close() + } + _, err := io.Copy(w, response.Body) + return err +} + +type CaptureScreenshotRegion400JSONResponse struct{ BadRequestErrorJSONResponse } + +func (response CaptureScreenshotRegion400JSONResponse) VisitCaptureScreenshotRegionResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type CaptureScreenshotRegion500JSONResponse struct{ InternalErrorJSONResponse } + +func (response CaptureScreenshotRegion500JSONResponse) VisitCaptureScreenshotRegionResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + // StrictServerInterface represents all server handlers. type StrictServerInterface interface { // Simulate a mouse click action on the host computer @@ -4839,6 +5227,9 @@ type StrictServerInterface interface { // Write or create a file // (PUT /fs/write_file) WriteFile(ctx context.Context, request WriteFileRequestObject) (WriteFileResponseObject, error) + // Check API health status + // (GET /health) + GetHealth(ctx context.Context, request GetHealthRequestObject) (GetHealthResponseObject, error) // Delete a previously recorded video file // (POST /recording/delete) DeleteRecording(ctx context.Context, request DeleteRecordingRequestObject) (DeleteRecordingResponseObject, error) @@ -4856,8 +5247,9 @@ type StrictServerInterface interface { StopRecording(ctx context.Context, request StopRecordingRequestObject) (StopRecordingResponseObject, error) } -type StrictHandlerFunc = strictnethttp.StrictHTTPHandlerFunc -type StrictMiddlewareFunc = strictnethttp.StrictHTTPMiddlewareFunc +type StrictHandlerFunc func(ctx context.Context, w http.ResponseWriter, r *http.Request, args interface{}) (interface{}, error) + +type StrictMiddlewareFunc func(f StrictHandlerFunc, operationID string) StrictHandlerFunc type StrictHTTPServerOptions struct { RequestErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error) @@ -4912,7 +5304,7 @@ func (sh *strictHandler) ClickMouse(w http.ResponseWriter, r *http.Request) { sh.options.ResponseErrorHandlerFunc(w, r, err) } } else if response != nil { - sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("Unexpected response type: %T", response)) } } @@ -4943,7 +5335,7 @@ func (sh *strictHandler) MoveMouse(w http.ResponseWriter, r *http.Request) { sh.options.ResponseErrorHandlerFunc(w, r, err) } } else if response != nil { - sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("Unexpected response type: %T", response)) } } @@ -4974,7 +5366,7 @@ func (sh *strictHandler) CreateDirectory(w http.ResponseWriter, r *http.Request) sh.options.ResponseErrorHandlerFunc(w, r, err) } } else if response != nil { - sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("Unexpected response type: %T", response)) } } @@ -5005,7 +5397,7 @@ func (sh *strictHandler) DeleteDirectory(w http.ResponseWriter, r *http.Request) sh.options.ResponseErrorHandlerFunc(w, r, err) } } else if response != nil { - sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("Unexpected response type: %T", response)) } } @@ -5036,7 +5428,7 @@ func (sh *strictHandler) DeleteFile(w http.ResponseWriter, r *http.Request) { sh.options.ResponseErrorHandlerFunc(w, r, err) } } else if response != nil { - sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("Unexpected response type: %T", response)) } } @@ -5062,7 +5454,7 @@ func (sh *strictHandler) FileInfo(w http.ResponseWriter, r *http.Request, params sh.options.ResponseErrorHandlerFunc(w, r, err) } } else if response != nil { - sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("Unexpected response type: %T", response)) } } @@ -5088,7 +5480,7 @@ func (sh *strictHandler) ListFiles(w http.ResponseWriter, r *http.Request, param sh.options.ResponseErrorHandlerFunc(w, r, err) } } else if response != nil { - sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("Unexpected response type: %T", response)) } } @@ -5119,7 +5511,7 @@ func (sh *strictHandler) MovePath(w http.ResponseWriter, r *http.Request) { sh.options.ResponseErrorHandlerFunc(w, r, err) } } else if response != nil { - sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("Unexpected response type: %T", response)) } } @@ -5145,7 +5537,7 @@ func (sh *strictHandler) ReadFile(w http.ResponseWriter, r *http.Request, params sh.options.ResponseErrorHandlerFunc(w, r, err) } } else if response != nil { - sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("Unexpected response type: %T", response)) } } @@ -5176,7 +5568,7 @@ func (sh *strictHandler) SetFilePermissions(w http.ResponseWriter, r *http.Reque sh.options.ResponseErrorHandlerFunc(w, r, err) } } else if response != nil { - sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("Unexpected response type: %T", response)) } } @@ -5207,7 +5599,7 @@ func (sh *strictHandler) StartFsWatch(w http.ResponseWriter, r *http.Request) { sh.options.ResponseErrorHandlerFunc(w, r, err) } } else if response != nil { - sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("Unexpected response type: %T", response)) } } @@ -5233,7 +5625,7 @@ func (sh *strictHandler) StopFsWatch(w http.ResponseWriter, r *http.Request, wat sh.options.ResponseErrorHandlerFunc(w, r, err) } } else if response != nil { - sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("Unexpected response type: %T", response)) } } @@ -5259,7 +5651,7 @@ func (sh *strictHandler) StreamFsEvents(w http.ResponseWriter, r *http.Request, sh.options.ResponseErrorHandlerFunc(w, r, err) } } else if response != nil { - sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("Unexpected response type: %T", response)) } } @@ -5287,7 +5679,31 @@ func (sh *strictHandler) WriteFile(w http.ResponseWriter, r *http.Request, param sh.options.ResponseErrorHandlerFunc(w, r, err) } } else if response != nil { - sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("Unexpected response type: %T", response)) + } +} + +// GetHealth operation middleware +func (sh *strictHandler) GetHealth(w http.ResponseWriter, r *http.Request) { + var request GetHealthRequestObject + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.GetHealth(ctx, request.(GetHealthRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetHealth") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(GetHealthResponseObject); ok { + if err := validResponse.VisitGetHealthResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("Unexpected response type: %T", response)) } } @@ -5318,7 +5734,7 @@ func (sh *strictHandler) DeleteRecording(w http.ResponseWriter, r *http.Request) sh.options.ResponseErrorHandlerFunc(w, r, err) } } else if response != nil { - sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("Unexpected response type: %T", response)) } } @@ -5344,7 +5760,7 @@ func (sh *strictHandler) DownloadRecording(w http.ResponseWriter, r *http.Reques sh.options.ResponseErrorHandlerFunc(w, r, err) } } else if response != nil { - sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("Unexpected response type: %T", response)) } } @@ -5368,7 +5784,7 @@ func (sh *strictHandler) ListRecorders(w http.ResponseWriter, r *http.Request) { sh.options.ResponseErrorHandlerFunc(w, r, err) } } else if response != nil { - sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("Unexpected response type: %T", response)) } } @@ -5399,7 +5815,7 @@ func (sh *strictHandler) StartRecording(w http.ResponseWriter, r *http.Request) sh.options.ResponseErrorHandlerFunc(w, r, err) } } else if response != nil { - sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("Unexpected response type: %T", response)) } } @@ -5430,61 +5846,65 @@ func (sh *strictHandler) StopRecording(w http.ResponseWriter, r *http.Request) { sh.options.ResponseErrorHandlerFunc(w, r, err) } } else if response != nil { - sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("Unexpected response type: %T", response)) } } // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "H4sIAAAAAAAC/9xbe3PbNhL/Khhc/2juqEcSp53qPyd2Op6r04yVTnrX5DQQsZTQkAADgJYVj7/7zQIk", - "RYrQw7Kd1J3JTCKSAPa9v11srmmsslxJkNbQ0TXVYHIlDbgfLxm/gM8FGHuqtdL4KFbSgrT4T5bnqYiZ", - "FUoO/jRK4jMTzyFj+K/vNCR0RP8xWO0/8G/NwO92c3MTUQ4m1iLHTegIDyTlifQmoq+UTFIRf63Tq+Pw", - "6DNpQUuWfqWjq+PIGPQlaFJ+GNE3yr5WheRfiY43yhJ3HsV35ee426tUxJ/OVWGg0g8SwLnAhSx9q1UO", - "2gq0m4SlBiKaNx5d02lhraewfaDbkvi3xCoiUBAstmQh7JxGFGSR0dEfNIXE0ohqMZvj35ngPAUa0SmL", - "P9GIJkovmOb0Y0TtMgc6osZqIWcowhhJn/jH68e/W+ZAVELcN4TF7vHqVK4W+LPIablN8IC5SvnkEyxN", - "iD0uEgGa4GvkD78lvMClxM7BH0wjKixkbn1n9/IB05ot8bcssolbVR6XsCK1dPS0o8oim4JG5qzIwB2u", - "IQdmW+eWu6PYZ+As7qrLxe8kVkpzIZl10qo3ILkyopRZd6dld6f/HLLTTUQ1fC6EBo5KuaK49UoRavon", - "eKd9pYFZOBEaYqv08jBLzRQPGMqvuV9OeLU7wQ/J9yq2LCVeXRGB/qxPfnzx4kmfnHjNOMH/+OJFn0Y0", - "ZxbdnI7o//4Y9n78eP08Orr5jgZMKmd23iXieGpUWlhoEIEf4gmxY33tkEH/n93N16TpTgoJ8wRSsPCW", - "2flhctzBQkU4d8fcP+EXEDtDmx1GveBd2s84SOvduTRdXR3S4IQcp/mcySIDLWKiNJkv8znIdf2z3pfj", - "3n+HvZ96H//1XZDZDmN1DlgzWDCGzSAQPNYkVn0YEtprkcKZTFR3e2EmXOiuNN7Pwc5BOzk4ZQpD2Moy", - "+yuepkqlwCQekyk+wXDU3e4XZiy6lEjKlObCVt/H9oxZOqKcWei51QGPCbstsuUddSqsId+jf0bkA+V6", - "caV7+OcDRR19oD296Oke/vlAn/RDJ0gWovslM0DwVWUTCR6pdFASezs4vg6uM+ILTKZLC4FkMxZfgAhJ", - "3Os+GZKkQYYA098dWx2PJXWtw6LKDho6LIW+yZzGS2MhO70s0UpXMcZ9QOI5kzMggB86L7m1+bEkgdgC", - "398OD9VlfdShSr2dlYRBixMpwXf9BlZ5dXF6/O6URvT9xZn7++T0l1P3j4vTN8fnpwHosqZ89zbaHFh/", - "EcY6vQV4RHSCvHUlJqR3YHRpkLYyxBrwbMOpdVQK4KBzdQl3AKR3AW2ZuoRbYbZdmMoqt6eHQ4U2ShOr", - "DsJU++60N6ZCMR8OAjgYO9kFZsBYJB4NpIp7u7BARI2Od21sVKFj2HvPNZHUB0QNLkIS8kgDdDh9JkIK", - "Mwc+YYEo+A6RuWVZThZzkA04Ua3alP5kkaZsmgIdWV1AQDwev3QfmxoXNd43AqOxTNvbUlsuOpDYNbkL", - "Ttt0hmQ+BheJ3oLOhDFCSXOYfc60KvIup29gQdyrMhto8vPZSf9w2BEoEn44Onpyu5pALSToMK3uFSkM", - "6Ire3zbQu0+KWsyVAZKvZEuYdpFlCmWy5ofi9S2QYYxG9Nq8Zza+14qjLgeRgwXuHhSMBgyX4hJaVXV5", - "zgboUe5H6rVpEG7sW7g4Cdyxbkk0y0AzGzDKi1V0qT5CtJjkaKCXoLXgYIjxDahSAk9QY+xKZIgxng0j", - "mgnpfzwNZadQ1VRXzmJVPiEwbddPBpyp3VP15Ig+KbRLKmdyDLGSPJTpPWsNOni5CCVj/LId0tkqkIxd", - "OSgsvsCZPH+5mQKHm0wJ4M9f7qmRp8PhsKWUYTDTByxN5Xc1NKVjwH12+8tZlgEXzEK6JMaq3PX2VGHJ", - "TLMYkiIlZl5YrhayT97NhSEZWxINpkgtSoORWGld5AjwLwUH5YQVxvW3Kdu9ByNBD1az4yNRwgIrLKZA", - "+m/QElJylrEZGHL89oxG9BK08cQO+0/7Qxftc5AsF3REn/eH/eclLneid0i5sKAHvrWZIQp2AVB5NaKe", - "vOlzOmq0bqkPRGDsS8WX99ZN7vaGb9oxD9O+e9C4W3g2HG7qBvs2LCYghBPAURxH/vMQGfW2g/X7ipuI", - "vthnXbvZ7zrfRZYxvXRFdVakGCoZcXJutYqJks6g5spYUmnFbbDSEcLxXSqqa5kH0lCnVrqbgsrCAjn7", - "tso5r0qdrEmXVe6ZySFGt+eN+shs0VhiBr6LOqmLV6exIuRT7U7zQzlWuJ+9l/KebkNCnk9OTBHHYExS", - "pOnymyrSc0oYkbBY9Q5qvfjW6h568b3fh9ZLtzV+qD+tVOJZvJM7HQ2Pdq9rXyjeh+68NJo9t3W9Yb7e", - "oTJESX95bbmy7m+gKKePSkf4Y1KBlBkENFR34RCDYOlgQRs6+uPwRqfAzz8X4DzU92Kr+rCtlqih4531", - "5sewDu/FiFadyO6luTOLRpvzEZrGz2BbjVo2RXzOutqrzSYVxjrHNhvtZtUv3tdw2veaj9NSVlwHTGUV", - "71F+Za36yGwFGXSGYXx11rUN1x/fFO+rhvIDQt37iPUOWq7w0SPUk+NAaaLBNQW3ObMGxussHfTlC2C8", - "zNH7ubI7rLrkx/3/Kt6sYgu2Z6wGlrXNqm5eT4VkjsT1k8KhH7m7Nyj9jYwF9et1VorN1MZhwAf6SaMj", - "vNG7u435B/LzzTcAh3p8YytS5Jw9TpA3Bhu4hG2obuAuC8xc5LWGXRd7c3ei2Zl/KG0Gmv/7l7l7k9Bu", - "YTq2J6GW4W9SfC4g1LFeiXRRimOvJuDaBYK7NShvzR575PDMNGCAk5W/JzJtExtcVyK/8TLHeiRkbypf", - "mdtaunEppMwZZQap9bgti+xOGkeBIZNSUSrPH7+ixq71jhwJOQvitnUlDdxMzmaAP3ZJ9LU59Z99RV2t", - "J3gLV9ZTG8zsuyq75qhSwF/H41Pit61GXMrRJagYnwPjjutr+ntvPD7tvfK09d4FJ3jOgQvmJnhwQ9ye", - "M8vK7cj360HsCW1Kpxr46YS6wIDPzWM0UyfojpRdWGFl2K0tVotd/aX3+Mk+0PWkMYfCOjD24eBrtPHG", - "NKnHCDZOELTGjH84OtpEprt230DW1rkD73z7ZPw7AusDGtoOfaMJWJCPPo2imSJoi6tW+KpLV19PD1Yp", - "MwzV1mafH7Sj2rlAvin1uAtorwYR/ga91FzDpVCFSZfVtXLzlrqjP7WQqWJ8Y0o9KT9oqnBr1KqDRX2p", - "vUKtffJ+DpKoDD2ER/5WzE8TFAaMB7Q+ftTLNwUQl7LD4WPXtfju9O0ENsjyozsX5I0hFx/yW5m5ftt7", - "XQ7Y9Y63DrqpxM+6tYdVqum8Pvm5YJpJC8DL+aiL16+eP3/+U59uwzNRi5SxrwMOoqSsIQ4lBEl5Nny2", - "zUWFIcaKNCVCklyrmQZjIpKnwAwQq5eEzZiQJGUWdFvcF2D1snec2NDU2riYzcBg+bNgwrpZ/+bIzRQS", - "pZFRq5feCVZMbJu4eYyAp3L58iLbOF8EafeLKKnweWBjB74aT/WNmDs0vfea2G4Nw3Ymobv+6prJKqnD", - "j7m/FjVL0+a2bbE5x9nR8njoNBqe9wtm0afbXLQav72T6f+0e137v+PeD9Zn2hJGTKyhOVHcJ7/KdEmU", - "bMa6HDQ5OyExkxjfNMyEsaCBE4Zb+P8t1NGyn0/bpOTGFNyD6TgwaXd7oFS2IL7tJJRVeTv9OEb+HwAA", - "//+ThhMPQj4AAA==", + "H4sIAAAAAAAC/9xba3PUONb+Kyq982F4130BwkxNvoUksKmdMFSaKWYX2C61fdzWYEtGktM0VP771pF8", + "basv6aRhMlVUkfbl6Ojc9JyLv9JQZrkUIIymx1+pAp1LocH+eM6iK/hUgDbnSkmFl0IpDAiDf7I8T3nI", + "DJdi9KeWAq/pMIGM4V8/KIjpMf2/UUN/5O7qkaN2c3MT0Ah0qHiOROgxLkjKFelNQE+liFMefqvVq+Vw", + "6QthQAmWfqOlq+XIBNQ1KFI+GNBX0ryQhYi+ER+vpCF2PYr3yseR2mnKw4+XstBQ6QcZiCKOL7L0tZI5", + "KMPRbmKWagho3rr0lc4KYxyH3QUtSeLuEiMJR0Gw0JAFNwkNKIgio8fvaAqxoQFVfJ7g/xmPohRoQGcs", + "/EgDGku1YCqiHwJqljnQY6qN4mKOIgyR9am7vLr8m2UORMbEPkNYaC83q0ZygT+LnJZkvAskMo2mH2Gp", + "fduLeMxBEbyN+8NnSVTgq8Qk4BamAeUGMvt+j3p5gSnFlvhbFNnUvlUuF7MiNfT4cU+VRTYDhZszPAO7", + "uIIcmOmsW1JHsc/BWtzn/i7+IKGUKuKCGSutmgDJpealzPqUln1K/96H0k1AFXwquIIIlfKZIulGEXL2", + "JzinPVXADJxxBaGRarmfpWYy8hjKb7l7nUQVdYIPkh9laFhKnLoCAsP5kPz87NmjITlzmrGC//nZsyEN", + "aM4Mujk9pv99Nx78/OHr0+Do5gfqMamcmaTPxMlMy7Qw0GICH8QVQrv1lUVGw//vE1+Rpl3JJ8wzSMHA", + "a2aS/eS4ZQsV45Fd5v4Zv4LQGtp8P+551Of9IgJhnDuXpquqRVo7ISdpnjBRZKB4SKQiyTJPQKzqnw2+", + "nAz+Mx78Mvjwjx+8m+1trD4DVgwWtGZz8ASPFYlVD/qE9oKncCFi2SfP9TTiqi+NtwmYBJSVg1Um14Q1", + "ljls9jSTMgUmcJlMRlMMR31yvzJt0KV4XB5pNmwNXWzPmKHHNGIGBvZtj8f43Ra35Rx1xo0mP6J/BuQ9", + "jdTisxrgv/cUdfSeDtRioAb47z19NPStIJiP7+dMA8FblU3EuKRUXkns7OB42/ue5l9gOlsa8Bw2E/4F", + "CBfE3h6SMYlbbHDQw+2x1e6x5K6zWFDZQUuHpdDXmdNkqQ1k59clWukrRtsHSJgwMQcC+KD1klubH4tj", + "CA1Eu9vhvrqsl9pXqbezEj9osSIleG/YwiqnV+cnb85pQN9eXdj/z85/Pbd/XJ2/Ork890CXFeXbu8H6", + "wPpPYCmeBi5D6EcKbZgpPHbp3iPl7YZj6YdTRY7WNdUQ9kn9bu+hkWsIpYi0xYKCZ0hxvNW8axZaa/h2", + "+ivXxlqoR5uIw1CLfdvgwoUqDF4gTOVyNbTbhMjr+OtBfJfyGu4Ave8CTzN5DbdCp9vQo5GWpgN+hdJS", + "ESP3Qo+7UtoZPaKY94c7EWgz3QbbQBtkHg2kivDbUE9AtQq3EdayUCHsTHPVK6oFgtYufBJymAqUHyjE", + "XHCdQDRlnnj/BnMQw7KcLBIQLeBUvbXuoBdFmrJZCvTYqAI84nFIrX9Z1wiwdb91BGjDlLktt+VLezK7", + "Ince0S6fPplPQgUgdCLNFcy5FHvGALCJsyc04/UGzs5dBlaH1Mc+z1zwyGeNb/HyLUntmGwamQ9SiA0J", + "pRKgtgT9nTPP25H1BpJKGkElYq8OwZ4mr0FlXGsuhd5Pi3Mli7y/tVewIPZWiV0UeXlxNtwfJHtS2p+O", + "jh7dLoOVC5Sol1d7ixQaVMXv72v43QVQLRKpgeSNbAlT9nSYQQkto32zyw0Ad4KB4IV+y0x4r/lxXbzA", + "HSyQulcwCvDI49fQqQGV66wByiU9Ur+besHxrmm2lcAds+xYsQwUMx6jvGpOiOohhH1xjgZ6DUrxCDTR", + "rlxaSuARaox9dh78ZLwt+Phy/LrOw5tkH9OobravwZraPeX6lumzQllgcCEmJbLtozW3tRYfUflSCxBv", + "kc5GgWTss03c+Be4EJfP13Ngsa8u083L5ztq5PF4PO4oxR9kPZYm87samlQhIJ3t/nKRZRBxZiBdEm1k", + "bivRsjBkrlgIcZESnRQmkgsxJG8SrknGlkSBLlKD0mB4mKgix3T0mkcgrbD8WehtikzOg5Ghg1WY8BIv", + "oZ3hBmEM/RcoASm5yNgcNDl5fUEDeg1KO2bHw8fDsY32OQiWc3pMnw7Hw6dlFmlFb7OdwoAauUJ8hpmM", + "DYDSqRH15Ew/osetRgN1gQi0eS6j5b31PvqdjJtuzEPoZi+0OmFPxuN1vQvXNMADCCEhRCiOI/e4j42a", + "7Gi1u3YT0Ge7vNdtTdk+TZFlTC1tCSgrUgyVjFg5dxobRAprUInUCHmcViyBRkeYUm1TUZ2PHkhDvXz3", + "bgoqk0Pc2fdVzmWVrmZtvoy013QOIbp91MKpeoPGYj1yNf9pXYCwGit8PtXtixzKsfzdl52U93gTEnL7", + "jIguwhC0jos0XX5XRbqdEkYELJr6T60X1wjYQS+uU3FovfQbOfv6U6MSt8U7udPR+Gj7e932933ozkmj", + "XSFe1Rue11tUhijpL68tm9b9DRRl9VHpCH9MK5AyB4+G6koqYhBMHQwoTY/f7V+W5/j4pwKsh7rOQZUf", + "dtUStHS8Nd/84NfhvRhRU03uj3hYs2iVqh+gabwE0ym2sxnic9bXXm02KdfGOrZeazdNzX9Xw+l24R+m", + "pTS79phKE+9RfmWu+sBsBTdoDUO77KxvG7bHsS7eV02BA0Ld+4j1Flo2+OgB6snuQCqiwBYFNzmzAhbV", + "p7TXl6+AReUZvZsr28WqkRSk/1fxZhkaMANtFLCsa1Z1A2LGBbMsrq7kD/24u3uD0t/JWFC/Tmel2HRt", + "HBpcoJ+2KsJrvbtfmD+Qn6/vAOzr8S1SpMgj9jBB3gSMp5HeUt3INgt0wvNaw7aKvb460a7MH0qbnuL/", + "7mnuzix0S5h221NfyfB3wT8V4KtYNyJdlOLYqQi40kCwXYOy8/nQI4fbTAsGWFm5PpHumtjoayXyGydz", + "zEd89ibzxtxWjht7hJRnRnmC1HrcdIpsPzSOPA3YUlEyzx++oia29I474mLuxW2rShrZCbL1AH9iD9EX", + "+tw99g11tXrAG/hsHLfek31bZtcerPP462RyThzZakypHLSDauMJsMju+iv9YzCZnA9OHW+DN955s0uI", + "OLPzZkgQyUfMsJIc+XE1iD2ibelUw169UOcZR7t5iGZqBd2Tsg0rrAy7tcUqvq2+9BYf2QW6nrVmiVgP", + "xh4OvgZrO6ZxPUawdoKgMxT/09HROjZt230NWxvnDpzz7XLi3xFY71HQtugbTcCAePDHKJopgrawKoU3", + "VbrEjnuuDcMvwbiBUHrAssbKqKonSp68viBcE8fs8n7aAgmEHwkSTjoTr1YqddN+1AAJP4Bd+X7hoHXm", + "Xlv9prTubelHM57xN6gw5wquuSx0uqya7e3efU9/ciFSyaK1Fn5WPtBW4cZYXofQutXfYPkheZuAIDLD", + "uBEFrlfoZiwKDdrBfBdV69fXhVULZPxBdduwwHZQYwU2yvKjO5cpWqM/7iDs4JX67uBFOTo6ONk4wilj", + "N8XZHeGp5k6H5GXBFBMGICqnxq5enD59+vSXId2E8oIOKxOXHe3FSZlZ7csIsvJk/GSTi3JNtOFpSrgg", + "uZJzBVoHJE+BaSBGLQmbMy5IyowdgmyJ+wqMWg5OYuOb5ZsU8zloTAoXjBuyMplPZhBLhRs1aumcoNnE", + "pjmkhwgDK5cv2/va+iIIs1tESbk7B9b2JarBa33XM3OnbxE6Y969Gf++v9oSu4zr8KPvr3DP0rRNtis2", + "6zhbCkGHPkb9U5DeU/TxJhetBsvvZPq/bH+v+0n9/WRATBnCiLaD6k1cG5LfRLokUrRjXQ6KXJyRkAmM", + "bwrmXBtQEBGGJNwXfz0tu6m9dUpuzQYeTMee+cPbA6WyMPN958OMzLvHjxO3rr8yWBuHTlluCgXN9wjb", + "YxHP2BxGufsC406QoFmVhI6PfoHr7vjdUa5t2S5XDgYgHFNQ3rCj6f7pxVUhXVXfQRzEMNd8G7J7T+G7", + "Kev7zW6tUTKrJvDC6tsV9PD/BQAA//+501KJH0UAAA==", } // GetSwagger returns the content of the embedded swagger specification file @@ -5492,16 +5912,16 @@ var swaggerSpec = []string{ func decodeSpec() ([]byte, error) { zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, "")) if err != nil { - return nil, fmt.Errorf("error base64 decoding spec: %w", err) + return nil, fmt.Errorf("error base64 decoding spec: %s", err) } zr, err := gzip.NewReader(bytes.NewReader(zipped)) if err != nil { - return nil, fmt.Errorf("error decompressing spec: %w", err) + return nil, fmt.Errorf("error decompressing spec: %s", err) } var buf bytes.Buffer _, err = buf.ReadFrom(zr) if err != nil { - return nil, fmt.Errorf("error decompressing spec: %w", err) + return nil, fmt.Errorf("error decompressing spec: %s", err) } return buf.Bytes(), nil @@ -5519,7 +5939,7 @@ func decodeSpecCached() func() ([]byte, error) { // Constructs a synthetic filesystem for resolving external references when loading openapi specifications. func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) { - res := make(map[string]func() ([]byte, error)) + var res = make(map[string]func() ([]byte, error)) if len(pathToFile) > 0 { res[pathToFile] = rawSpec } @@ -5533,12 +5953,12 @@ func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) { // Externally referenced files must be embedded in the corresponding golang packages. // Urls can be supported but this task was out of the scope. func GetSwagger() (swagger *openapi3.T, err error) { - resolvePath := PathToRawSpec("") + var resolvePath = PathToRawSpec("") loader := openapi3.NewLoader() loader.IsExternalRefsAllowed = true loader.ReadFromURIFunc = func(loader *openapi3.Loader, url *url.URL) ([]byte, error) { - pathToFile := url.String() + var pathToFile = url.String() pathToFile = path.Clean(pathToFile) getSpec, ok := resolvePath[pathToFile] if !ok { diff --git a/server/openapi.yaml b/server/openapi.yaml index d56dbaa5..cd1492ef 100644 --- a/server/openapi.yaml +++ b/server/openapi.yaml @@ -3,6 +3,20 @@ info: title: Kernel Images API version: 0.1.0 paths: + /health: + get: + summary: Check API health status + operationId: getHealth + responses: + "200": + description: API is healthy + content: + application/json: + schema: + $ref: "#/components/schemas/HealthResponse" + "500": + $ref: "#/components/responses/InternalError" + /recording/start: post: summary: Start a screen recording. Only one recording per ID can be registered at a time. @@ -55,10 +69,6 @@ paths: "200": description: Recording file headers: - # Note: using a `format: date-time` here doesn't work as intended as the generated code - # calls a `fmt.Sprint` on the value when setting the header. time.String is a - # non-standard format that most parses will barf on, making everyone's life harder, so - # we're skipping the `format` in favor of an explicit description. X-Recording-Started-At: description: Timestamp of when the recording started. Guaranteed to be RFC3339. schema: @@ -120,40 +130,886 @@ paths: $ref: "#/components/responses/NotFoundError" "500": $ref: "#/components/responses/InternalError" - /computer/click_mouse: + + # Compatibility alias endpoints + /computer/click_mouse: + post: + summary: Simulate a mouse click action on the host computer (compatibility alias) + operationId: clickMouse + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/ClickMouseRequest" + responses: + "200": + description: Mouse action performed + content: + application/json: + schema: + $ref: "#/components/schemas/OkResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "500": + $ref: "#/components/responses/InternalError" + /computer/move_mouse: + post: + summary: Move the mouse cursor to the specified coordinates on the host computer (compatibility alias) + operationId: moveMouse + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/MoveMouseRequest" + responses: + "200": + description: Mouse cursor moved + content: + application/json: + schema: + $ref: "#/components/schemas/OkResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "500": + $ref: "#/components/responses/InternalError" + + # Input API endpoints + /input/mouse/move: + post: + summary: Move mouse cursor to absolute coordinates + operationId: inputMouseMove + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/MouseMoveRequest" + responses: + "200": + description: Mouse moved successfully + content: + application/json: + schema: + $ref: "#/components/schemas/OkResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "500": + $ref: "#/components/responses/InternalError" + + /input/mouse/move_relative: + post: + summary: Move mouse cursor relative to current position + operationId: inputMouseMoveRelative + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/MouseMoveRelativeRequest" + responses: + "200": + description: Mouse moved successfully + content: + application/json: + schema: + $ref: "#/components/schemas/OkResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "500": + $ref: "#/components/responses/InternalError" + + /input/mouse/click: + post: + summary: Click mouse button + operationId: inputMouseClick + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/MouseClickRequest" + responses: + "200": + description: Mouse clicked successfully + content: + application/json: + schema: + $ref: "#/components/schemas/OkResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "500": + $ref: "#/components/responses/InternalError" + + /input/mouse/down: + post: + summary: Press mouse button down + operationId: inputMouseDown + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/MouseButtonRequest" + responses: + "200": + description: Mouse button pressed + content: + application/json: + schema: + $ref: "#/components/schemas/OkResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "500": + $ref: "#/components/responses/InternalError" + + /input/mouse/up: + post: + summary: Release mouse button + operationId: inputMouseUp + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/MouseButtonRequest" + responses: + "200": + description: Mouse button released + content: + application/json: + schema: + $ref: "#/components/schemas/OkResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "500": + $ref: "#/components/responses/InternalError" + + /input/mouse/scroll: + post: + summary: Scroll mouse wheel + operationId: inputMouseScroll + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/MouseScrollRequest" + responses: + "200": + description: Mouse scrolled + content: + application/json: + schema: + $ref: "#/components/schemas/OkResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "500": + $ref: "#/components/responses/InternalError" + + /input/mouse/location: + get: + summary: Get current mouse cursor location + operationId: inputMouseLocation + responses: + "200": + description: Mouse location + content: + application/json: + schema: + $ref: "#/components/schemas/MouseLocationResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "500": + $ref: "#/components/responses/InternalError" + + /input/keyboard/type: + post: + summary: Type text + operationId: inputKeyboardType + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/KeyboardTypeRequest" + responses: + "200": + description: Text typed successfully + content: + application/json: + schema: + $ref: "#/components/schemas/OkResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "500": + $ref: "#/components/responses/InternalError" + + /input/keyboard/key: + post: + summary: Send key presses + operationId: inputKeyboardKey + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/KeyboardKeysRequest" + responses: + "200": + description: Keys pressed successfully + content: + application/json: + schema: + $ref: "#/components/schemas/OkResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "500": + $ref: "#/components/responses/InternalError" + + /input/keyboard/key_down: + post: + summary: Press and hold a key + operationId: inputKeyboardKeyDown + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/KeyRequest" + responses: + "200": + description: Key pressed successfully + content: + application/json: + schema: + $ref: "#/components/schemas/OkResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "500": + $ref: "#/components/responses/InternalError" + + /input/keyboard/key_up: + post: + summary: Release a key + operationId: inputKeyboardKeyUp + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/KeyRequest" + responses: + "200": + description: Key released successfully + content: + application/json: + schema: + $ref: "#/components/schemas/OkResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "500": + $ref: "#/components/responses/InternalError" + + /input/window/activate: + post: + summary: Activate a window + operationId: inputWindowActivate + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/WindowMatchRequest" + responses: + "200": + description: Window activation result + content: + application/json: + schema: + $ref: "#/components/schemas/WindowActivateResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "500": + $ref: "#/components/responses/InternalError" + + /input/window/focus: + post: + summary: Focus a window + operationId: inputWindowFocus + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/WindowMatchRequest" + responses: + "200": + description: Window focus result + content: + application/json: + schema: + $ref: "#/components/schemas/WindowFocusResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "500": + $ref: "#/components/responses/InternalError" + + /input/window/move_resize: + post: + summary: Move and resize a window + operationId: inputWindowMoveResize + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/WindowMoveResizeRequest" + responses: + "200": + description: Window moved/resized successfully + content: + application/json: + schema: + $ref: "#/components/schemas/OkResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "404": + $ref: "#/components/responses/NotFoundError" + "500": + $ref: "#/components/responses/InternalError" + + /input/window/raise: + post: + summary: Raise window to top + operationId: inputWindowRaise + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/WindowMatchRequest" + responses: + "200": + description: Window raised successfully + content: + application/json: + schema: + $ref: "#/components/schemas/OkResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "404": + $ref: "#/components/responses/NotFoundError" + "500": + $ref: "#/components/responses/InternalError" + + /input/window/minimize: + post: + summary: Minimize window + operationId: inputWindowMinimize + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/WindowMatchRequest" + responses: + "200": + description: Window minimized successfully + content: + application/json: + schema: + $ref: "#/components/schemas/OkResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "404": + $ref: "#/components/responses/NotFoundError" + "500": + $ref: "#/components/responses/InternalError" + + /input/window/map: + post: + summary: Map (show) window + operationId: inputWindowMap + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/WindowMatchRequest" + responses: + "200": + description: Window mapped successfully + content: + application/json: + schema: + $ref: "#/components/schemas/OkResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "404": + $ref: "#/components/responses/NotFoundError" + "500": + $ref: "#/components/responses/InternalError" + + /input/window/unmap: + post: + summary: Unmap (hide) window + operationId: inputWindowUnmap + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/WindowMatchRequest" + responses: + "200": + description: Window unmapped successfully + content: + application/json: + schema: + $ref: "#/components/schemas/OkResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "404": + $ref: "#/components/responses/NotFoundError" + "500": + $ref: "#/components/responses/InternalError" + + /input/window/close: + post: + summary: Close window + operationId: inputWindowClose + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/WindowMatchRequest" + responses: + "200": + description: Window close result + content: + application/json: + schema: + $ref: "#/components/schemas/WindowCloseResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "404": + $ref: "#/components/responses/NotFoundError" + "500": + $ref: "#/components/responses/InternalError" + + /input/window/kill: + post: + summary: Kill window + operationId: inputWindowKill + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/WindowMatchRequest" + responses: + "200": + description: Window killed successfully + content: + application/json: + schema: + $ref: "#/components/schemas/OkResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "404": + $ref: "#/components/responses/NotFoundError" + "500": + $ref: "#/components/responses/InternalError" + + /input/window/active: + get: + summary: Get active window + operationId: inputWindowActive + responses: + "200": + description: Active window information + content: + application/json: + schema: + $ref: "#/components/schemas/ActiveWindowResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "500": + $ref: "#/components/responses/InternalError" + + /input/window/focused: + get: + summary: Get focused window + operationId: inputWindowFocused + responses: + "200": + description: Focused window information + content: + application/json: + schema: + $ref: "#/components/schemas/ActiveWindowResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "500": + $ref: "#/components/responses/InternalError" + + /input/window/name: + post: + summary: Get window name + operationId: inputWindowName + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/WindowIdRequest" + responses: + "200": + description: Window name + content: + application/json: + schema: + $ref: "#/components/schemas/WindowNameResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "500": + $ref: "#/components/responses/InternalError" + + /input/window/pid: + post: + summary: Get window PID + operationId: inputWindowPid + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/WindowIdRequest" + responses: + "200": + description: Window PID + content: + application/json: + schema: + $ref: "#/components/schemas/WindowPidResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "500": + $ref: "#/components/responses/InternalError" + + /input/window/geometry: + post: + summary: Get window geometry + operationId: inputWindowGeometry + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/WindowIdRequest" + responses: + "200": + description: Window geometry information + content: + application/json: + schema: + $ref: "#/components/schemas/WindowGeometryResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "500": + $ref: "#/components/responses/InternalError" + + /input/display/geometry: + get: + summary: Get display geometry + operationId: inputDisplayGeometry + responses: + "200": + description: Display geometry information + content: + application/json: + schema: + $ref: "#/components/schemas/DisplayGeometryResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "500": + $ref: "#/components/responses/InternalError" + + /input/desktop/count: + get: + summary: Get number of desktops + operationId: inputDesktopCountGet + responses: + "200": + description: Desktop count + content: + application/json: + schema: + $ref: "#/components/schemas/DesktopCountResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "500": + $ref: "#/components/responses/InternalError" + post: + summary: Set number of desktops + operationId: inputDesktopCountSet + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/DesktopCountRequest" + responses: + "200": + description: Desktop count set successfully + content: + application/json: + schema: + $ref: "#/components/schemas/OkResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "500": + $ref: "#/components/responses/InternalError" + + /input/desktop/current: + get: + summary: Get current desktop + operationId: inputDesktopCurrentGet + responses: + "200": + description: Current desktop index + content: + application/json: + schema: + $ref: "#/components/schemas/DesktopIndexResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "500": + $ref: "#/components/responses/InternalError" + post: + summary: Set current desktop + operationId: inputDesktopCurrentSet + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/DesktopIndexRequest" + responses: + "200": + description: Current desktop set successfully + content: + application/json: + schema: + $ref: "#/components/schemas/OkResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "500": + $ref: "#/components/responses/InternalError" + + /input/desktop/window_desktop: + post: + summary: Move window to a different desktop + operationId: inputDesktopWindowDesktop + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/WindowDesktopRequest" + responses: + "200": + description: Window moved to desktop successfully + content: + application/json: + schema: + $ref: "#/components/schemas/OkResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "404": + $ref: "#/components/responses/NotFoundError" + "500": + $ref: "#/components/responses/InternalError" + + /input/desktop/viewport: + get: + summary: Get desktop viewport + operationId: inputDesktopViewportGet + responses: + "200": + description: Desktop viewport information + content: + application/json: + schema: + $ref: "#/components/schemas/DesktopViewportResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "500": + $ref: "#/components/responses/InternalError" + post: + summary: Set desktop viewport + operationId: inputDesktopViewportSet + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/DesktopViewportRequest" + responses: + "200": + description: Desktop viewport set successfully + content: + application/json: + schema: + $ref: "#/components/schemas/OkResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "500": + $ref: "#/components/responses/InternalError" + + /input/combo/activate_and_type: + post: + summary: Activate window and type text + operationId: inputComboActivateAndType + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/ActivateAndTypeRequest" + responses: + "200": + description: Window activated and text typed successfully + content: + application/json: + schema: + $ref: "#/components/schemas/ComboWindowResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "404": + $ref: "#/components/responses/NotFoundError" + "500": + $ref: "#/components/responses/InternalError" + + /input/combo/activate_and_keys: + post: + summary: Activate window and send key sequence + operationId: inputComboActivateAndKeys + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/ActivateAndKeysRequest" + responses: + "200": + description: Window activated and keys sent successfully + content: + application/json: + schema: + $ref: "#/components/schemas/ComboWindowResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "404": + $ref: "#/components/responses/NotFoundError" + "500": + $ref: "#/components/responses/InternalError" + + /input/combo/window/center: + post: + summary: Center window on screen + operationId: inputComboWindowCenter + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/WindowCenterRequest" + responses: + "200": + description: Window centered successfully + content: + application/json: + schema: + $ref: "#/components/schemas/WindowCenterResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "404": + $ref: "#/components/responses/NotFoundError" + "500": + $ref: "#/components/responses/InternalError" + + /input/combo/window/snap: + post: + summary: Snap window to position + operationId: inputComboWindowSnap + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/WindowSnapRequest" + responses: + "200": + description: Window snapped successfully + content: + application/json: + schema: + $ref: "#/components/schemas/WindowSnapResponse" + "400": + $ref: "#/components/responses/BadRequestError" + "404": + $ref: "#/components/responses/NotFoundError" + "500": + $ref: "#/components/responses/InternalError" + + /input/system/exec: post: - summary: Simulate a mouse click action on the host computer - operationId: clickMouse + summary: Execute a system command + operationId: inputSystemExec requestBody: required: true content: application/json: schema: - $ref: "#/components/schemas/ClickMouseRequest" + $ref: "#/components/schemas/SystemExecRequest" responses: "200": - description: Mouse action performed + description: Command executed successfully + content: + application/json: + schema: + $ref: "#/components/schemas/SystemExecResponse" "400": $ref: "#/components/responses/BadRequestError" "500": $ref: "#/components/responses/InternalError" - /computer/move_mouse: + + /input/system/sleep: post: - summary: Move the mouse cursor to the specified coordinates on the host computer - operationId: moveMouse + summary: Sleep for a specified duration + operationId: inputSystemSleep requestBody: required: true content: application/json: schema: - $ref: "#/components/schemas/MoveMouseRequest" + $ref: "#/components/schemas/SleepRequest" responses: "200": - description: Mouse cursor moved + description: Sleep completed successfully + content: + application/json: + schema: + $ref: "#/components/schemas/OkResponse" "400": $ref: "#/components/responses/BadRequestError" "500": $ref: "#/components/responses/InternalError" + # File system operations /fs/read_file: get: @@ -445,8 +1301,31 @@ paths: $ref: "#/components/responses/NotFoundError" "500": $ref: "#/components/responses/InternalError" + components: schemas: + HealthResponse: + type: object + required: [status, uptime_sec] + properties: + status: + type: string + description: Health status + enum: [ok] + uptime_sec: + type: integer + description: Uptime in seconds + minimum: 0 + + OkResponse: + type: object + required: [ok] + properties: + ok: + type: boolean + description: Success indicator + enum: [true] + StartRecordingRequest: type: object properties: @@ -553,6 +1432,530 @@ components: items: type: string additionalProperties: false + + # New input API schema definitions + MouseMoveRelativeRequest: + type: object + properties: + dx: + type: integer + description: Relative X movement distance + default: 0 + dy: + type: integer + description: Relative Y movement distance + default: 0 + additionalProperties: false + + MouseClickRequest: + type: object + required: + - button + properties: + button: + oneOf: + - type: string + enum: [left, middle, right, back, forward] + - type: integer + description: Numeric button identifier + count: + type: integer + description: Number of clicks + default: 1 + additionalProperties: false + + MouseButtonRequest: + type: object + required: + - button + properties: + button: + oneOf: + - type: string + enum: [left, middle, right, back, forward] + - type: integer + description: Numeric button identifier + additionalProperties: false + + MouseScrollRequest: + type: object + properties: + dx: + type: integer + description: Horizontal scroll amount + default: 0 + dy: + type: integer + description: Vertical scroll amount + default: -120 + additionalProperties: false + + MouseLocationResponse: + type: object + required: [x, y] + properties: + x: + type: integer + description: X coordinate + y: + type: integer + description: Y coordinate + screen: + type: integer + description: Screen number + window: + type: string + description: Window ID under cursor + + KeyboardTypeRequest: + type: object + required: + - text + properties: + text: + type: string + description: Text to type + wpm: + type: integer + description: Words per minute typing speed + default: 300 + enter: + type: boolean + description: Whether to press Enter after typing + default: false + additionalProperties: false + + KeyboardKeysRequest: + type: object + required: + - keys + properties: + keys: + type: array + description: Key sequence to send + items: + type: string + additionalProperties: false + + KeyRequest: + type: object + required: + - key + properties: + key: + type: string + description: Key to press/release + additionalProperties: false + + WindowMatch: + type: object + properties: + title_contains: + type: string + description: Window title substring to match + nullable: true + name: + type: string + description: Alias for title_contains + nullable: true + class: + type: string + description: Window class to match + nullable: true + pid: + type: integer + description: Process ID to match + nullable: true + only_visible: + type: boolean + description: Only match visible windows + default: false + additionalProperties: false + + WindowMatchRequest: + type: object + required: + - match + properties: + match: + $ref: "#/components/schemas/WindowMatch" + additionalProperties: false + + WindowIdRequest: + type: object + required: + - wid + properties: + wid: + type: string + description: Window ID + additionalProperties: false + + WindowActivateResponse: + type: object + required: [activated] + properties: + activated: + type: boolean + description: Whether window was activated + wid: + type: string + description: Window ID if found + + WindowFocusResponse: + type: object + required: [focused] + properties: + focused: + type: boolean + description: Whether window was focused + wid: + type: string + description: Window ID if found + + WindowCloseResponse: + type: object + required: [ok] + properties: + ok: + type: boolean + description: Success indicator + wid: + type: string + description: Window ID if found + windowIds: + type: array + description: Multiple window IDs when multiple windows are closed + items: + type: string + + WindowMoveResizeRequest: + type: object + required: + - match + properties: + match: + $ref: "#/components/schemas/WindowMatch" + x: + type: integer + description: New X position + y: + type: integer + description: New Y position + width: + type: integer + description: New width + height: + type: integer + description: New height + additionalProperties: false + + ActiveWindowResponse: + type: object + properties: + wid: + type: string + description: Window ID + + WindowNameResponse: + type: object + required: [wid, name] + properties: + wid: + type: string + description: Window ID + name: + type: string + description: Window name/title + + WindowPidResponse: + type: object + required: [wid, pid] + properties: + wid: + type: string + description: Window ID + pid: + type: integer + description: Process ID + + WindowGeometryResponse: + type: object + required: [wid, x, y, width, height] + properties: + wid: + type: string + description: Window ID + x: + type: integer + description: X position + y: + type: integer + description: Y position + width: + type: integer + description: Width + height: + type: integer + description: Height + screen: + type: integer + description: Screen number + + DisplayGeometryResponse: + type: object + required: [width, height] + properties: + width: + type: integer + description: Display width + height: + type: integer + description: Display height + + DesktopCountRequest: + type: object + required: + - count + properties: + count: + type: integer + description: Number of desktops to set + minimum: 1 + additionalProperties: false + + DesktopCountResponse: + type: object + required: [count] + properties: + count: + type: integer + description: Number of desktops + + DesktopIndexRequest: + type: object + required: + - index + properties: + index: + type: integer + description: Desktop index to switch to + minimum: 0 + additionalProperties: false + + DesktopIndexResponse: + type: object + required: [index] + properties: + index: + type: integer + description: Current desktop index + + WindowDesktopRequest: + type: object + required: + - match + - index + properties: + match: + $ref: "#/components/schemas/WindowMatch" + index: + type: integer + description: Desktop index to move window to + minimum: 0 + additionalProperties: false + + DesktopViewportRequest: + type: object + required: + - x + - y + properties: + x: + type: integer + description: X coordinate of viewport + y: + type: integer + description: Y coordinate of viewport + additionalProperties: false + + DesktopViewportResponse: + type: object + required: [x, y] + properties: + x: + type: integer + description: X coordinate of viewport + y: + type: integer + description: Y coordinate of viewport + + ActivateAndTypeRequest: + type: object + required: + - match + properties: + match: + $ref: "#/components/schemas/WindowMatch" + text: + type: string + description: Text to type + default: "" + enter: + type: boolean + description: Whether to press Enter after typing + default: false + wpm: + type: integer + description: Words per minute typing speed + default: 300 + additionalProperties: false + + ActivateAndKeysRequest: + type: object + required: + - match + - keys + properties: + match: + $ref: "#/components/schemas/WindowMatch" + keys: + type: array + description: Key sequence to send + items: + type: string + additionalProperties: false + + ComboWindowResponse: + type: object + required: [ok, wid] + properties: + ok: + type: boolean + description: Success indicator + wid: + type: string + description: Window ID + + WindowCenterRequest: + type: object + required: + - match + properties: + match: + $ref: "#/components/schemas/WindowMatch" + width: + type: integer + description: Optional width to set + nullable: true + height: + type: integer + description: Optional height to set + nullable: true + additionalProperties: false + + WindowCenterResponse: + type: object + required: [ok, wid, x, y, width, height] + properties: + ok: + type: boolean + description: Success indicator + wid: + type: string + description: Window ID + x: + type: integer + description: New X position + y: + type: integer + description: New Y position + width: + type: integer + description: Width + height: + type: integer + description: Height + + WindowSnapRequest: + type: object + required: + - match + - position + properties: + match: + $ref: "#/components/schemas/WindowMatch" + position: + type: string + description: Position to snap window to + enum: [left, right, top, bottom, topleft, topright, bottomleft, bottomright, maximize] + additionalProperties: false + + WindowSnapResponse: + type: object + required: [ok, wid, x, y, width, height] + properties: + ok: + type: boolean + description: Success indicator + wid: + type: string + description: Window ID + x: + type: integer + description: New X position + y: + type: integer + description: New Y position + width: + type: integer + description: Width + height: + type: integer + description: Height + + SystemExecRequest: + type: object + required: + - command + properties: + command: + type: string + description: Command to execute + args: + type: array + description: Command arguments + items: + type: string + default: [] + additionalProperties: false + + SystemExecResponse: + type: object + required: [code, stdout, stderr] + properties: + code: + type: integer + description: Exit code + stdout: + type: string + description: Standard output + stderr: + type: string + description: Standard error + + SleepRequest: + type: object + required: + - seconds + properties: + seconds: + type: number + description: Seconds to sleep + minimum: 0 + additionalProperties: false + + # Existing schema definitions StartFsWatchRequest: type: object required: @@ -703,4 +2106,4 @@ components: content: application/json: schema: - $ref: "#/components/schemas/Error" + $ref: "#/components/schemas/Error" \ No newline at end of file