diff --git a/images/chromium-headless/image/Dockerfile b/images/chromium-headless/image/Dockerfile index e1b16ab9..f38b20f4 100644 --- a/images/chromium-headless/image/Dockerfile +++ b/images/chromium-headless/image/Dockerfile @@ -136,6 +136,7 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=$CACHEIDPREFIX-ap dbus-x11 \ xvfb \ x11-utils \ + xclip \ xdotool \ fontconfig \ fonts-noto-cjk \ diff --git a/server/cmd/api/api/computer.go b/server/cmd/api/api/computer.go index b5a8090a..d477a950 100644 --- a/server/cmd/api/api/computer.go +++ b/server/cmd/api/api/computer.go @@ -1136,6 +1136,54 @@ func clampPoints(points [][2]int, screenWidth, screenHeight int) { } } +func (s *ApiService) ReadClipboard(ctx context.Context, request oapi.ReadClipboardRequestObject) (oapi.ReadClipboardResponseObject, error) { + log := logger.FromContext(ctx) + + s.inputMu.Lock() + defer s.inputMu.Unlock() + + display := s.resolveDisplayFromEnv() + cmd := exec.CommandContext(ctx, "xclip", "-selection", "clipboard", "-o") + cmd.Env = append(os.Environ(), fmt.Sprintf("DISPLAY=%s", display)) + output, err := cmd.Output() + if err != nil { + var exitErr *exec.ExitError + if errors.As(err, &exitErr) && exitErr.ExitCode() == 1 { + return oapi.ReadClipboard200JSONResponse{Text: ""}, nil + } + log.Error("xclip read failed", "err", err) + return oapi.ReadClipboard500JSONResponse{InternalErrorJSONResponse: oapi.InternalErrorJSONResponse{ + Message: fmt.Sprintf("failed to read clipboard: %v", err)}, + }, nil + } + return oapi.ReadClipboard200JSONResponse{Text: string(output)}, nil +} + +func (s *ApiService) WriteClipboard(ctx context.Context, request oapi.WriteClipboardRequestObject) (oapi.WriteClipboardResponseObject, error) { + log := logger.FromContext(ctx) + + s.inputMu.Lock() + defer s.inputMu.Unlock() + + if request.Body == nil { + return oapi.WriteClipboard400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{ + Message: "request body is required"}, + }, nil + } + + display := s.resolveDisplayFromEnv() + cmd := exec.CommandContext(ctx, "xclip", "-selection", "clipboard") + cmd.Env = append(os.Environ(), fmt.Sprintf("DISPLAY=%s", display)) + cmd.Stdin = strings.NewReader(request.Body.Text) + if err := cmd.Run(); err != nil { + log.Error("xclip write failed", "err", err) + return oapi.WriteClipboard500JSONResponse{InternalErrorJSONResponse: oapi.InternalErrorJSONResponse{ + Message: fmt.Sprintf("failed to write to clipboard: %v", err)}, + }, nil + } + return oapi.WriteClipboard200Response{}, nil +} + // generateRelativeSteps produces a sequence of relative steps that approximate a // straight line from (0,0) to (dx,dy) using at most the provided number of // steps. Each returned element is a pair {stepX, stepY}. The steps are diff --git a/server/e2e/e2e_chromium_test.go b/server/e2e/e2e_chromium_test.go index 7c61147f..1aa5f8be 100644 --- a/server/e2e/e2e_chromium_test.go +++ b/server/e2e/e2e_chromium_test.go @@ -169,6 +169,43 @@ func TestDisplayResolutionChange(t *testing.T) { t.Log("all resolution changes verified successfully") } +func TestClipboardHeadless(t *testing.T) { + t.Parallel() + + if _, err := exec.LookPath("docker"); err != nil { + t.Skipf("docker not available: %v", err) + } + + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute) + defer cancel() + + c := NewTestContainer(t, headlessImage) + require.NoError(t, c.Start(ctx, ContainerConfig{}), "failed to start container") + defer c.Stop(ctx) + + require.NoError(t, c.WaitReady(ctx), "api not ready") + + specResp, err := http.Get(c.APIBaseURL() + "/spec.yaml") + require.NoError(t, err) + specBody, _ := io.ReadAll(specResp.Body) + specResp.Body.Close() + require.True(t, strings.Contains(string(specBody), "/computer/clipboard/write"), + "API spec does not include clipboard routes - rebuild the image with: cd kernel-images/images/chromium-headless && docker build --no-cache -f image/Dockerfile -t %s ../..", headlessImage) + + client, err := c.APIClient() + require.NoError(t, err, "failed to create API client") + + writeResp, err := client.WriteClipboardWithResponse(ctx, instanceoapi.WriteClipboardRequest{Text: "e2e-clipboard-test"}) + require.NoError(t, err, "WriteClipboard request failed") + require.Equal(t, http.StatusOK, writeResp.StatusCode(), "unexpected write status: %s body=%s", writeResp.Status(), string(writeResp.Body)) + + readResp, err := client.ReadClipboardWithResponse(ctx) + require.NoError(t, err, "ReadClipboard request failed") + require.Equal(t, http.StatusOK, readResp.StatusCode(), "unexpected read status: %s body=%s", readResp.Status(), string(readResp.Body)) + require.NotNil(t, readResp.JSON200, "expected JSON200 response") + require.Equal(t, "e2e-clipboard-test", readResp.JSON200.Text, "clipboard content mismatch") +} + func TestExtensionUploadAndActivation(t *testing.T) { t.Parallel() ensurePlaywrightDeps(t) diff --git a/server/lib/oapi/oapi.go b/server/lib/oapi/oapi.go index 4d92b0b6..b7750e9b 100644 --- a/server/lib/oapi/oapi.go +++ b/server/lib/oapi/oapi.go @@ -148,6 +148,12 @@ type ClickMouseRequestButton string // ClickMouseRequestClickType Type of click action type ClickMouseRequestClickType string +// ClipboardContent defines model for ClipboardContent. +type ClipboardContent struct { + // Text Current clipboard text content + Text string `json:"text"` +} + // ComputerAction A single computer action to execute as part of a batch. The `type` field selects which // action to perform, and the corresponding field contains the action parameters. // Exactly one action field matching the type must be provided. @@ -322,13 +328,13 @@ type MousePositionResponse struct { // MoveMouseRequest defines model for MoveMouseRequest. type MoveMouseRequest struct { - // DurationMs Target total duration in milliseconds for the mouse movement when smooth=true. Steps and per-step delay are auto-computed to achieve this duration. Ignored when smooth=false. Omit for automatic timing based on distance. + // DurationMs Target total duration in milliseconds for the mouse movement when smooth=true. Omit for automatic timing based on distance. DurationMs *int `json:"duration_ms,omitempty"` // HoldKeys Modifier keys to hold during the move HoldKeys *[]string `json:"hold_keys,omitempty"` - // Smooth Use human-like Bezier curve path instead of instant teleport (recommended for bot detection evasion) + // Smooth Use human-like Bezier curve path instead of instant mouse movement. Smooth *bool `json:"smooth,omitempty"` // X X coordinate to move the cursor to @@ -659,6 +665,12 @@ type TypeTextRequest struct { Text string `json:"text"` } +// WriteClipboardRequest defines model for WriteClipboardRequest. +type WriteClipboardRequest struct { + // Text Text to write to the system clipboard + Text string `json:"text"` +} + // BadRequestError defines model for BadRequestError. type BadRequestError = Error @@ -799,6 +811,9 @@ type BatchComputerActionJSONRequestBody = BatchComputerActionRequest // ClickMouseJSONRequestBody defines body for ClickMouse for application/json ContentType. type ClickMouseJSONRequestBody = ClickMouseRequest +// WriteClipboardJSONRequestBody defines body for WriteClipboard for application/json ContentType. +type WriteClipboardJSONRequestBody = WriteClipboardRequest + // SetCursorJSONRequestBody defines body for SetCursor for application/json ContentType. type SetCursorJSONRequestBody = SetCursorRequest @@ -968,6 +983,14 @@ type ClientInterface interface { ClickMouse(ctx context.Context, body ClickMouseJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // ReadClipboard request + ReadClipboard(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // WriteClipboardWithBody request with any body + WriteClipboardWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + WriteClipboard(ctx context.Context, body WriteClipboardJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // SetCursorWithBody request with any body SetCursorWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -1219,6 +1242,42 @@ func (c *Client) ClickMouse(ctx context.Context, body ClickMouseJSONRequestBody, return c.Client.Do(req) } +func (c *Client) ReadClipboard(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewReadClipboardRequest(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) WriteClipboardWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewWriteClipboardRequestWithBody(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) WriteClipboard(ctx context.Context, body WriteClipboardJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewWriteClipboardRequest(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) +} + func (c *Client) SetCursorWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewSetCursorRequestWithBody(c.Server, contentType, body) if err != nil { @@ -2124,6 +2183,73 @@ func NewClickMouseRequestWithBody(server string, contentType string, body io.Rea return req, nil } +// NewReadClipboardRequest generates requests for ReadClipboard +func NewReadClipboardRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/computer/clipboard/read") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("POST", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewWriteClipboardRequest calls the generic WriteClipboard builder with application/json body +func NewWriteClipboardRequest(server string, body WriteClipboardJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewWriteClipboardRequestWithBody(server, "application/json", bodyReader) +} + +// NewWriteClipboardRequestWithBody generates requests for WriteClipboard with any type of body +func NewWriteClipboardRequestWithBody(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("/computer/clipboard/write") + 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 +} + // NewSetCursorRequest calls the generic SetCursor builder with application/json body func NewSetCursorRequest(server string, body SetCursorJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader @@ -3849,6 +3975,14 @@ type ClientWithResponsesInterface interface { ClickMouseWithResponse(ctx context.Context, body ClickMouseJSONRequestBody, reqEditors ...RequestEditorFn) (*ClickMouseResponse, error) + // ReadClipboardWithResponse request + ReadClipboardWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*ReadClipboardResponse, error) + + // WriteClipboardWithBodyWithResponse request with any body + WriteClipboardWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*WriteClipboardResponse, error) + + WriteClipboardWithResponse(ctx context.Context, body WriteClipboardJSONRequestBody, reqEditors ...RequestEditorFn) (*WriteClipboardResponse, error) + // SetCursorWithBodyWithResponse request with any body SetCursorWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SetCursorResponse, error) @@ -4108,6 +4242,52 @@ func (r ClickMouseResponse) StatusCode() int { return 0 } +type ReadClipboardResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *ClipboardContent + JSON500 *InternalError +} + +// Status returns HTTPResponse.Status +func (r ReadClipboardResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r ReadClipboardResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type WriteClipboardResponse struct { + Body []byte + HTTPResponse *http.Response + JSON400 *BadRequestError + JSON500 *InternalError +} + +// Status returns HTTPResponse.Status +func (r WriteClipboardResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r WriteClipboardResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + type SetCursorResponse struct { Body []byte HTTPResponse *http.Response @@ -5127,6 +5307,32 @@ func (c *ClientWithResponses) ClickMouseWithResponse(ctx context.Context, body C return ParseClickMouseResponse(rsp) } +// ReadClipboardWithResponse request returning *ReadClipboardResponse +func (c *ClientWithResponses) ReadClipboardWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*ReadClipboardResponse, error) { + rsp, err := c.ReadClipboard(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseReadClipboardResponse(rsp) +} + +// WriteClipboardWithBodyWithResponse request with arbitrary body returning *WriteClipboardResponse +func (c *ClientWithResponses) WriteClipboardWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*WriteClipboardResponse, error) { + rsp, err := c.WriteClipboardWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseWriteClipboardResponse(rsp) +} + +func (c *ClientWithResponses) WriteClipboardWithResponse(ctx context.Context, body WriteClipboardJSONRequestBody, reqEditors ...RequestEditorFn) (*WriteClipboardResponse, error) { + rsp, err := c.WriteClipboard(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseWriteClipboardResponse(rsp) +} + // SetCursorWithBodyWithResponse request with arbitrary body returning *SetCursorResponse func (c *ClientWithResponses) SetCursorWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SetCursorResponse, error) { rsp, err := c.SetCursorWithBody(ctx, contentType, body, reqEditors...) @@ -5803,6 +6009,72 @@ func ParseClickMouseResponse(rsp *http.Response) (*ClickMouseResponse, error) { return response, nil } +// ParseReadClipboardResponse parses an HTTP response from a ReadClipboardWithResponse call +func ParseReadClipboardResponse(rsp *http.Response) (*ReadClipboardResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &ReadClipboardResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ClipboardContent + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest InternalError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseWriteClipboardResponse parses an HTTP response from a WriteClipboardWithResponse call +func ParseWriteClipboardResponse(rsp *http.Response) (*WriteClipboardResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &WriteClipboardResponse{ + 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 == 500: + var dest InternalError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + // ParseSetCursorResponse parses an HTTP response from a SetCursorWithResponse call func ParseSetCursorResponse(rsp *http.Response) (*SetCursorResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) @@ -7389,6 +7661,12 @@ type ServerInterface interface { // Simulate a mouse click action on the host computer // (POST /computer/click_mouse) ClickMouse(w http.ResponseWriter, r *http.Request) + // Read text from the system clipboard + // (POST /computer/clipboard/read) + ReadClipboard(w http.ResponseWriter, r *http.Request) + // Write text to the system clipboard + // (POST /computer/clipboard/write) + WriteClipboard(w http.ResponseWriter, r *http.Request) // Hide or show the cursor // (POST /computer/cursor) SetCursor(w http.ResponseWriter, r *http.Request) @@ -7539,6 +7817,18 @@ func (_ Unimplemented) ClickMouse(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotImplemented) } +// Read text from the system clipboard +// (POST /computer/clipboard/read) +func (_ Unimplemented) ReadClipboard(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + +// Write text to the system clipboard +// (POST /computer/clipboard/write) +func (_ Unimplemented) WriteClipboard(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNotImplemented) +} + // Hide or show the cursor // (POST /computer/cursor) func (_ Unimplemented) SetCursor(w http.ResponseWriter, r *http.Request) { @@ -7844,6 +8134,34 @@ func (siw *ServerInterfaceWrapper) ClickMouse(w http.ResponseWriter, r *http.Req handler.ServeHTTP(w, r) } +// ReadClipboard operation middleware +func (siw *ServerInterfaceWrapper) ReadClipboard(w http.ResponseWriter, r *http.Request) { + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.ReadClipboard(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// WriteClipboard operation middleware +func (siw *ServerInterfaceWrapper) WriteClipboard(w http.ResponseWriter, r *http.Request) { + + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.WriteClipboard(w, r) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + // SetCursor operation middleware func (siw *ServerInterfaceWrapper) SetCursor(w http.ResponseWriter, r *http.Request) { @@ -8799,6 +9117,12 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl r.Group(func(r chi.Router) { r.Post(options.BaseURL+"/computer/click_mouse", wrapper.ClickMouse) }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/computer/clipboard/read", wrapper.ReadClipboard) + }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/computer/clipboard/write", wrapper.WriteClipboard) + }) r.Group(func(r chi.Router) { r.Post(options.BaseURL+"/computer/cursor", wrapper.SetCursor) }) @@ -9067,6 +9391,65 @@ func (response ClickMouse500JSONResponse) VisitClickMouseResponse(w http.Respons return json.NewEncoder(w).Encode(response) } +type ReadClipboardRequestObject struct { +} + +type ReadClipboardResponseObject interface { + VisitReadClipboardResponse(w http.ResponseWriter) error +} + +type ReadClipboard200JSONResponse ClipboardContent + +func (response ReadClipboard200JSONResponse) VisitReadClipboardResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type ReadClipboard500JSONResponse struct{ InternalErrorJSONResponse } + +func (response ReadClipboard500JSONResponse) VisitReadClipboardResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + +type WriteClipboardRequestObject struct { + Body *WriteClipboardJSONRequestBody +} + +type WriteClipboardResponseObject interface { + VisitWriteClipboardResponse(w http.ResponseWriter) error +} + +type WriteClipboard200Response struct { +} + +func (response WriteClipboard200Response) VisitWriteClipboardResponse(w http.ResponseWriter) error { + w.WriteHeader(200) + return nil +} + +type WriteClipboard400JSONResponse struct{ BadRequestErrorJSONResponse } + +func (response WriteClipboard400JSONResponse) VisitWriteClipboardResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type WriteClipboard500JSONResponse struct{ InternalErrorJSONResponse } + +func (response WriteClipboard500JSONResponse) VisitWriteClipboardResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + type SetCursorRequestObject struct { Body *SetCursorJSONRequestBody } @@ -10852,6 +11235,12 @@ type StrictServerInterface interface { // Simulate a mouse click action on the host computer // (POST /computer/click_mouse) ClickMouse(ctx context.Context, request ClickMouseRequestObject) (ClickMouseResponseObject, error) + // Read text from the system clipboard + // (POST /computer/clipboard/read) + ReadClipboard(ctx context.Context, request ReadClipboardRequestObject) (ReadClipboardResponseObject, error) + // Write text to the system clipboard + // (POST /computer/clipboard/write) + WriteClipboard(ctx context.Context, request WriteClipboardRequestObject) (WriteClipboardResponseObject, error) // Hide or show the cursor // (POST /computer/cursor) SetCursor(ctx context.Context, request SetCursorRequestObject) (SetCursorResponseObject, error) @@ -11127,6 +11516,61 @@ func (sh *strictHandler) ClickMouse(w http.ResponseWriter, r *http.Request) { } } +// ReadClipboard operation middleware +func (sh *strictHandler) ReadClipboard(w http.ResponseWriter, r *http.Request) { + var request ReadClipboardRequestObject + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.ReadClipboard(ctx, request.(ReadClipboardRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "ReadClipboard") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(ReadClipboardResponseObject); ok { + if err := validResponse.VisitReadClipboardResponse(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)) + } +} + +// WriteClipboard operation middleware +func (sh *strictHandler) WriteClipboard(w http.ResponseWriter, r *http.Request) { + var request WriteClipboardRequestObject + + var body WriteClipboardJSONRequestBody + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + sh.options.RequestErrorHandlerFunc(w, r, fmt.Errorf("can't decode JSON body: %w", err)) + return + } + request.Body = &body + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.WriteClipboard(ctx, request.(WriteClipboardRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "WriteClipboard") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(WriteClipboardResponseObject); ok { + if err := validResponse.VisitWriteClipboardResponse(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)) + } +} + // SetCursor operation middleware func (sh *strictHandler) SetCursor(w http.ResponseWriter, r *http.Request) { var request SetCursorRequestObject @@ -12304,147 +12748,148 @@ func (sh *strictHandler) StopRecording(w http.ResponseWriter, r *http.Request) { // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", - "wn3ZYnxapPXjBxfy/wMAAP//G9cbfrq0AAA=", + "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", + "fdlifFqk9eMHF/L/AwAA//9p0GK1mLgAAA==", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/server/openapi.yaml b/server/openapi.yaml index 2a2b7a59..99244a4a 100644 --- a/server/openapi.yaml +++ b/server/openapi.yaml @@ -497,7 +497,37 @@ paths: $ref: "#/components/schemas/BatchComputerActionRequest" responses: "200": - description: All actions executed successfully + description: Batch executed successfully + "400": + $ref: "#/components/responses/BadRequestError" + "500": + $ref: "#/components/responses/InternalError" + /computer/clipboard/read: + post: + summary: Read text from the system clipboard + operationId: readClipboard + responses: + "200": + description: Clipboard content read successfully + content: + application/json: + schema: + $ref: "#/components/schemas/ClipboardContent" + "500": + $ref: "#/components/responses/InternalError" + /computer/clipboard/write: + post: + summary: Write text to the system clipboard + operationId: writeClipboard + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/WriteClipboardRequest" + responses: + "200": + description: Text written to clipboard successfully "400": $ref: "#/components/responses/BadRequestError" "500": @@ -1304,6 +1334,24 @@ components: minimum: 0 default: 0 additionalProperties: false + ClipboardContent: + type: object + required: + - text + properties: + text: + type: string + description: Current clipboard text content + additionalProperties: false + WriteClipboardRequest: + type: object + required: + - text + properties: + text: + type: string + description: Text to write to the system clipboard + additionalProperties: false PressKeyRequest: type: object required: