diff --git a/images/chromium-headful/build-docker.sh b/images/chromium-headful/build-docker.sh index ff92a6f8..5f1db257 100755 --- a/images/chromium-headful/build-docker.sh +++ b/images/chromium-headful/build-docker.sh @@ -4,8 +4,7 @@ set -e -o pipefail # Move to the script's directory so relative paths work regardless of the caller CWD SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) cd "$SCRIPT_DIR" - -IMAGE="${IMAGE:-onkernel/kernel-cu-test:latest}" +source ../../shared/ensure-common-build-run-vars.sh chromium-headful source ../../shared/start-buildkit.sh diff --git a/images/chromium-headful/build-unikernel.sh b/images/chromium-headful/build-unikernel.sh index ce7d26df..0b16bf99 100755 --- a/images/chromium-headful/build-unikernel.sh +++ b/images/chromium-headful/build-unikernel.sh @@ -1,7 +1,10 @@ #!/usr/bin/env bash -source common.sh -source ../../shared/erofs-utils.sh +# Move to the script's directory so relative paths work regardless of the caller CWD +SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) +cd "$SCRIPT_DIR" +source "$SCRIPT_DIR/../../shared/ensure-common-build-run-vars.sh" chromium-headful +source "$SCRIPT_DIR/../../shared/erofs-utils.sh" # Ensure the mkfs.erofs tool is available if ! check_mkfs_erofs; then diff --git a/images/chromium-headful/common.sh b/images/chromium-headful/common.sh deleted file mode 100755 index 5d2f1715..00000000 --- a/images/chromium-headful/common.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bash -set -e -o pipefail - -# Default UKC_INDEX to index.unikraft.io if not set -UKC_INDEX="${UKC_INDEX:-index.unikraft.io}" - -# fail if IMAGE, UKC_TOKEN, UKC_METRO are not set -errormsg="" -for var in IMAGE UKC_TOKEN UKC_METRO; do - if [ -z "${!var}" ]; then - errormsg+="$var " - fi -done -if [ -n "$errormsg" ]; then - echo "Required variables not set: $errormsg" - exit 1 -fi diff --git a/images/chromium-headful/run-docker.sh b/images/chromium-headful/run-docker.sh index 2a46b90e..5adbd8a8 100755 --- a/images/chromium-headful/run-docker.sh +++ b/images/chromium-headful/run-docker.sh @@ -4,9 +4,7 @@ set -ex -o pipefail # Move to the script's directory so relative paths work regardless of the caller CWD SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) cd "$SCRIPT_DIR" - -IMAGE="${IMAGE:-onkernel/kernel-cu-test:latest}" -NAME="${NAME:-kernel-cu-test}" +source ../../shared/ensure-common-build-run-vars.sh chromium-headful # Directory on host where recordings will be saved HOST_RECORDINGS_DIR="$SCRIPT_DIR/recordings" @@ -45,7 +43,7 @@ RUN_ARGS=( ) if [[ "${WITH_KERNEL_IMAGES_API:-}" == "true" ]]; then - RUN_ARGS+=( -p 10001:10001 ) + RUN_ARGS+=( -p 444:10001 ) RUN_ARGS+=( -e WITH_KERNEL_IMAGES_API=true ) fi diff --git a/images/chromium-headful/run-unikernel.sh b/images/chromium-headful/run-unikernel.sh index a16e7c88..5911d653 100755 --- a/images/chromium-headful/run-unikernel.sh +++ b/images/chromium-headful/run-unikernel.sh @@ -1,11 +1,15 @@ #!/usr/bin/env bash +set -euo pipefail -source common.sh -name=chromium-headful-test -kraft cloud inst rm $name || true +# Move to the script's directory so relative paths work regardless of the caller CWD +SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) +cd "$SCRIPT_DIR" +source ../../shared/ensure-common-build-run-vars.sh chromium-headful + +kraft cloud inst rm $NAME || true # Name for the Kraft Cloud volume that will carry Chromium flags -volume_name="${name}-flags" +volume_name="${NAME}-flags" # ------------------------------------------------------------------------------ # Prepare Kraft Cloud volume containing Chromium flags @@ -45,7 +49,7 @@ deploy_args=( -e HOME=/ -e RUN_AS_ROOT="$RUN_AS_ROOT" \ -v "$volume_name":/chromium - -n "$name" + -n "$NAME" ) if [[ "${WITH_KERNEL_IMAGES_API:-}" == "true" ]]; then diff --git a/images/chromium-headless/.gitignore b/images/chromium-headless/.gitignore index 4f1859e9..face8273 100644 --- a/images/chromium-headless/.gitignore +++ b/images/chromium-headless/.gitignore @@ -1 +1,2 @@ image/.rootfs +image/bin diff --git a/images/chromium-headless/README.md b/images/chromium-headless/README.md index c3523c8f..b627145a 100644 --- a/images/chromium-headless/README.md +++ b/images/chromium-headless/README.md @@ -5,7 +5,6 @@ 1. Build and run the image, tagging it with a name you'd like to use: ```bash -export IMAGE=onkernel/chromium-headless ./build-docker.sh ./run-docker.sh ``` @@ -25,7 +24,6 @@ uv run python main.py http://localhost:9222 1. Build and run the image, tagging it with a name you'd like to use: ```bash -export IMAGE=onkernel/chromium-headless export UKC_TOKEN= export UKC_METRO= # latest UKC also allows pushing to metro-specific indexes diff --git a/images/chromium-headless/build-docker.sh b/images/chromium-headless/build-docker.sh index e1112801..6e45ce1a 100755 --- a/images/chromium-headless/build-docker.sh +++ b/images/chromium-headless/build-docker.sh @@ -1,4 +1,15 @@ #!/usr/bin/env bash +set -e -o pipefail -source common.sh -(cd image && docker build -t $IMAGE .) +# Move to the script's directory so relative paths work regardless of the caller CWD +SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) +cd "$SCRIPT_DIR" +source ../../shared/ensure-common-build-run-vars.sh chromium-headless + +source ../../shared/start-buildkit.sh + +# Build the kernel-images API binary and place it into the Docker build context +source ../../shared/build-server.sh "$SCRIPT_DIR/image/bin" + +# Build (and optionally push) the Docker image +(cd image && docker build -t "$IMAGE" .) diff --git a/images/chromium-headless/build-unikernel.sh b/images/chromium-headless/build-unikernel.sh index ab7bf64f..a54c930c 100755 --- a/images/chromium-headless/build-unikernel.sh +++ b/images/chromium-headless/build-unikernel.sh @@ -1,6 +1,9 @@ #!/usr/bin/env bash -source common.sh +# Move to the script's directory so relative paths work regardless of the caller CWD +SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) +cd "$SCRIPT_DIR" +source ../../shared/ensure-common-build-run-vars.sh chromium-headless source ../../shared/erofs-utils.sh # Ensure the mkfs.erofs tool is present @@ -14,8 +17,10 @@ set -euo pipefail cd image/ # Build the root file system -source ../../shared/start-buildkit.sh +source ../../../shared/start-buildkit.sh rm -rf ./.rootfs || true +# Build the API binary +source ../../../shared/build-server.sh "$(pwd)/bin" app_name=chromium-headless-build docker build --platform linux/amd64 -t "$IMAGE" . docker rm cnt-"$app_name" || true diff --git a/images/chromium-headless/common.sh b/images/chromium-headless/common.sh deleted file mode 100755 index e155b7a4..00000000 --- a/images/chromium-headless/common.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash -set -e -o pipefail - -# fail if IMAGE, UKC_TOKEN, UKC_METRO, UKC_INDEX are not set -errormsg="" -for var in IMAGE UKC_TOKEN UKC_METRO UKC_INDEX; do - if [ -z "${!var}" ]; then - errormsg+="$var " - fi -done -if [ -n "$errormsg" ]; then - echo "Required variables not set: $errormsg" - exit 1 -fi diff --git a/images/chromium-headless/image/Dockerfile b/images/chromium-headless/image/Dockerfile index 23340eb1..86a41f0c 100644 --- a/images/chromium-headless/image/Dockerfile +++ b/images/chromium-headless/image/Dockerfile @@ -34,6 +34,16 @@ RUN set -xe; \ RUN add-apt-repository -y ppa:xtradeb/apps RUN apt update -y && apt install -y chromium ncat +# Install FFmpeg (latest static build) for the recording server +RUN set -eux; \ + URL="https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz"; \ + echo "Downloading FFmpeg static build from $URL"; \ + curl -fsSL "$URL" -o /tmp/ffmpeg.tar.xz; \ + tar -xJf /tmp/ffmpeg.tar.xz -C /tmp; \ + install -m755 /tmp/ffmpeg-*/ffmpeg /usr/local/bin/ffmpeg; \ + install -m755 /tmp/ffmpeg-*/ffprobe /usr/local/bin/ffprobe; \ + rm -rf /tmp/ffmpeg* + # Remove upower to prevent spurious D-Bus activations and logs RUN apt-get -yqq purge upower || true && rm -rf /var/lib/apt/lists/* @@ -44,3 +54,7 @@ COPY ./xvfb_startup.sh /usr/bin/xvfb_startup.sh # Wrapper script set environment COPY ./wrapper.sh /usr/bin/wrapper.sh + +# Copy the kernel-images API binary built during the build process +COPY bin/kernel-images-api /usr/local/bin/kernel-images-api +ENV WITH_KERNEL_IMAGES_API=false diff --git a/images/chromium-headless/image/wrapper.sh b/images/chromium-headless/image/wrapper.sh index a2033a29..51baec59 100755 --- a/images/chromium-headless/image/wrapper.sh +++ b/images/chromium-headless/image/wrapper.sh @@ -108,6 +108,9 @@ cleanup () { echo "Cleaning up..." kill -TERM $pid 2>/dev/null || true kill -TERM $pid2 2>/dev/null || true + if [[ -n "${pid3:-}" ]]; then + kill -TERM $pid3 2>/dev/null || true + fi if [ -n "${dbus_pid:-}" ]; then kill -TERM $dbus_pid 2>/dev/null || true fi @@ -115,6 +118,7 @@ cleanup () { trap cleanup TERM INT pid= pid2= +pid3= INTERNAL_PORT=9223 CHROME_PORT=9222 # External port mapped to host echo "Starting Chromium on internal port $INTERNAL_PORT" @@ -138,11 +142,34 @@ ncat \ -l "$CHROME_PORT" \ --keep-open & pid2=$! +# Optionally start the kernel-images API server file i/o +if [[ "${WITH_KERNEL_IMAGES_API:-}" == "true" ]]; then + echo "✨ Starting kernel-images API." + API_PORT="${KERNEL_IMAGES_API_PORT:-10001}" + API_FRAME_RATE="${KERNEL_IMAGES_API_FRAME_RATE:-10}" + API_DISPLAY_NUM="${KERNEL_IMAGES_API_DISPLAY_NUM:-${DISPLAY_NUM:-1}}" + API_MAX_SIZE_MB="${KERNEL_IMAGES_API_MAX_SIZE_MB:-500}" + API_OUTPUT_DIR="${KERNEL_IMAGES_API_OUTPUT_DIR:-/recordings}" + + mkdir -p "$API_OUTPUT_DIR" + + PORT="$API_PORT" \ + FRAME_RATE="$API_FRAME_RATE" \ + DISPLAY_NUM="$API_DISPLAY_NUM" \ + MAX_SIZE_MB="$API_MAX_SIZE_MB" \ + OUTPUT_DIR="$API_OUTPUT_DIR" \ + /usr/local/bin/kernel-images-api & pid3=$! +fi + # Wait for Chromium to exit; propagate its exit code wait "$pid" exit_code=$? echo "Chromium exited with code $exit_code" # Ensure ncat proxy is terminated kill -TERM "$pid2" 2>/dev/null || true +# Ensure kernel-images API server is terminated +if [[ -n "${pid3:-}" ]]; then + kill -TERM "$pid3" 2>/dev/null || true +fi exit "$exit_code" diff --git a/images/chromium-headless/run-docker.sh b/images/chromium-headless/run-docker.sh index eb79adfc..1057b434 100755 --- a/images/chromium-headless/run-docker.sh +++ b/images/chromium-headless/run-docker.sh @@ -1,8 +1,28 @@ #!/usr/bin/env bash +set -ex -o pipefail -source common.sh +# Move to the script's directory so relative paths work regardless of the caller CWD +SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) +cd "$SCRIPT_DIR" +source ../../shared/ensure-common-build-run-vars.sh chromium-headless -docker run -it --rm \ - -p 9222:9222 \ - -e WITH_DOCKER=true \ - $IMAGE /usr/bin/wrapper.sh +# Directory on host where recordings will be saved when the API is enabled +HOST_RECORDINGS_DIR="$SCRIPT_DIR/recordings" +mkdir -p "$HOST_RECORDINGS_DIR" + +RUN_ARGS=( + --name "$NAME" + --privileged + --tmpfs /dev/shm:size=2g + -p 9222:9222 + -e WITH_DOCKER=true +) + +if [[ "${WITH_KERNEL_IMAGES_API:-}" == "true" ]]; then + RUN_ARGS+=( -p 444:10001 ) + RUN_ARGS+=( -e WITH_KERNEL_IMAGES_API=true ) + RUN_ARGS+=( -v "$HOST_RECORDINGS_DIR:/recordings" ) +fi + +docker rm -f "$NAME" 2>/dev/null || true +docker run -it --rm "${RUN_ARGS[@]}" "$IMAGE" /usr/bin/wrapper.sh diff --git a/images/chromium-headless/run-unikernel.sh b/images/chromium-headless/run-unikernel.sh index 913bc0ab..9d5d6282 100755 --- a/images/chromium-headless/run-unikernel.sh +++ b/images/chromium-headless/run-unikernel.sh @@ -1,14 +1,24 @@ #!/usr/bin/env bash +set -euo pipefail -source common.sh -name="chromium-headless-test" +# Move to the script's directory so relative paths work regardless of the caller CWD +SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) +cd "$SCRIPT_DIR" +source ../../shared/ensure-common-build-run-vars.sh chromium-headless -kraft cloud inst rm "$name" || true +kraft cloud inst rm "$NAME" || true -kraft cloud inst create \ - --start \ - -M 1G \ - -p 9222:9222/tls \ - --vcpus 1 \ - -n "$name" \ - $IMAGE +deploy_args=( + --start + -M 1G + -p 9222:9222/tls + --vcpus 1 + -n "$NAME" +) + +if [[ "${WITH_KERNEL_IMAGES_API:-}" == "true" ]]; then + deploy_args+=( -p 444:10001/tls ) + deploy_args+=( -e WITH_KERNEL_IMAGES_API=true ) +fi + +kraft cloud inst create "${deploy_args[@]}" "$IMAGE" diff --git a/server/Makefile b/server/Makefile index f4fc5d8f..a45d3203 100644 --- a/server/Makefile +++ b/server/Makefile @@ -22,6 +22,9 @@ oapi-generate: $(OAPI_CODEGEN) pnpm i -g @apiture/openapi-down-convert openapi-down-convert --input openapi.yaml --output openapi-3.0.yaml $(OAPI_CODEGEN) -config ./oapi-codegen.yaml ./openapi-3.0.yaml + @echo "Fixing oapi-codegen issue https://github.com/oapi-codegen/oapi-codegen/issues/1764..." + go run ./scripts/oapi/patch_sse_methods.go -file ./lib/oapi/oapi.go -expected-replacements 1 + go fmt ./lib/oapi/oapi.go go mod tidy build: | $(BIN_DIR) diff --git a/server/cmd/api/api/api.go b/server/cmd/api/api/api.go index eeed31b8..21f5794c 100644 --- a/server/cmd/api/api/api.go +++ b/server/cmd/api/api/api.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "os" + "sync" "time" "github.com/onkernel/kernel-images/server/lib/logger" @@ -18,6 +19,9 @@ type ApiService struct { recordManager recorder.RecordManager factory recorder.FFmpegRecorderFactory + // Filesystem watch management + watchMu sync.RWMutex + watches map[string]*fsWatch } var _ oapi.StrictServerInterface = (*ApiService)(nil) @@ -34,6 +38,7 @@ func New(recordManager recorder.RecordManager, factory recorder.FFmpegRecorderFa recordManager: recordManager, factory: factory, defaultRecorderID: "default", + watches: make(map[string]*fsWatch), }, nil } diff --git a/server/cmd/api/api/fs.go b/server/cmd/api/api/fs.go new file mode 100644 index 00000000..3ba23fc1 --- /dev/null +++ b/server/cmd/api/api/fs.go @@ -0,0 +1,538 @@ +package api + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "io" + "os" + "path/filepath" + "strconv" + + "sync" + + "os/user" + + "github.com/fsnotify/fsnotify" + "github.com/nrednav/cuid2" + "github.com/onkernel/kernel-images/server/lib/logger" + oapi "github.com/onkernel/kernel-images/server/lib/oapi" +) + +// fsWatch represents an in-memory directory watch. +type fsWatch struct { + path string + recursive bool + events chan oapi.FileSystemEvent + watcher *fsnotify.Watcher + closeOnce sync.Once +} + +// addRecursive walks the directory and registers all subdirectories when recursive=true. +func addRecursive(w *fsnotify.Watcher, root string) error { + return filepath.WalkDir(root, func(path string, d os.DirEntry, err error) error { + if err != nil { + return err + } + if d.IsDir() { + return w.Add(path) + } + return nil + }) +} + +// Close safely shuts down the underlying fsnotify.Watcher exactly once. +func (fw *fsWatch) Close() { + fw.closeOnce.Do(func() { + _ = fw.watcher.Close() + }) +} + +// ReadFile returns the contents of a file specified by the path param. +func (s *ApiService) ReadFile(ctx context.Context, req oapi.ReadFileRequestObject) (oapi.ReadFileResponseObject, error) { + log := logger.FromContext(ctx) + path := req.Params.Path + if path == "" { + return oapi.ReadFile400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: "path cannot be empty"}}, nil + } + + f, err := os.Open(path) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + return oapi.ReadFile404JSONResponse{NotFoundErrorJSONResponse: oapi.NotFoundErrorJSONResponse{Message: "file not found"}}, nil + } + log.Error("failed to open file", "err", err, "path", path) + return oapi.ReadFile500JSONResponse{InternalErrorJSONResponse: oapi.InternalErrorJSONResponse{Message: "unable to open file"}}, nil + } + + stat, err := f.Stat() + if err != nil { + f.Close() + log.Error("failed to stat file", "err", err, "path", path) + return oapi.ReadFile500JSONResponse{InternalErrorJSONResponse: oapi.InternalErrorJSONResponse{Message: "unable to stat file"}}, nil + } + + return oapi.ReadFile200ApplicationoctetStreamResponse{ + Body: f, + ContentLength: stat.Size(), + }, nil +} + +// WriteFile creates or overwrites a file with the supplied data stream. +func (s *ApiService) WriteFile(ctx context.Context, req oapi.WriteFileRequestObject) (oapi.WriteFileResponseObject, error) { + log := logger.FromContext(ctx) + path := req.Params.Path + if path == "" { + return oapi.WriteFile400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: "path cannot be empty"}}, nil + } + if req.Body == nil { + return oapi.WriteFile400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: "empty request body"}}, nil + } + + // create parent directories if necessary + if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { + log.Error("failed to create directories", "err", err, "path", path) + return oapi.WriteFile500JSONResponse{InternalErrorJSONResponse: oapi.InternalErrorJSONResponse{Message: "unable to create directories"}}, nil + } + + // determine desired file mode (default 0o644) + perm := os.FileMode(0o644) + if req.Params.Mode != nil { + if v, err := strconv.ParseUint(*req.Params.Mode, 8, 32); err == nil { + perm = os.FileMode(v) + } else { + log.Error("invalid mode", "mode", *req.Params.Mode) + return oapi.WriteFile400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: "invalid mode"}}, nil + } + } + + // open the file with the specified permissions + f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, perm) + if err != nil { + log.Error("failed to create file", "err", err, "path", path) + return oapi.WriteFile500JSONResponse{InternalErrorJSONResponse: oapi.InternalErrorJSONResponse{Message: "unable to create file"}}, nil + } + defer f.Close() + + if _, err := io.Copy(f, req.Body); err != nil { + log.Error("failed to write file", "err", err, "path", path) + return oapi.WriteFile500JSONResponse{InternalErrorJSONResponse: oapi.InternalErrorJSONResponse{Message: "failed to write data"}}, nil + } + + return oapi.WriteFile201Response{}, nil +} + +// CreateDirectory creates a new directory (recursively) with an optional mode. +func (s *ApiService) CreateDirectory(ctx context.Context, req oapi.CreateDirectoryRequestObject) (oapi.CreateDirectoryResponseObject, error) { + log := logger.FromContext(ctx) + if req.Body == nil { + return oapi.CreateDirectory400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: "request body required"}}, nil + } + path := req.Body.Path + if path == "" { + return oapi.CreateDirectory400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: "path cannot be empty"}}, nil + } + // default to 0o755 + perm := os.FileMode(0o755) + if req.Body.Mode != nil { + if v, err := strconv.ParseUint(*req.Body.Mode, 8, 32); err == nil { + perm = os.FileMode(v) + } else { + log.Error("invalid mode", "mode", *req.Body.Mode) + return oapi.CreateDirectory400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: "invalid mode"}}, nil + } + } + if err := os.MkdirAll(path, perm); err != nil { + log.Error("failed to create directory", "err", err, "path", path) + return oapi.CreateDirectory500JSONResponse{InternalErrorJSONResponse: oapi.InternalErrorJSONResponse{Message: "failed to create directory"}}, nil + } + return oapi.CreateDirectory201Response{}, nil +} + +// DeleteFile removes a single file. +func (s *ApiService) DeleteFile(ctx context.Context, req oapi.DeleteFileRequestObject) (oapi.DeleteFileResponseObject, error) { + log := logger.FromContext(ctx) + if req.Body == nil { + return oapi.DeleteFile400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: "request body required"}}, nil + } + path := req.Body.Path + if path == "" { + return oapi.DeleteFile400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: "path cannot be empty"}}, nil + } + if err := os.Remove(path); err != nil { + if errors.Is(err, os.ErrNotExist) { + return oapi.DeleteFile404JSONResponse{NotFoundErrorJSONResponse: oapi.NotFoundErrorJSONResponse{Message: "file not found"}}, nil + } + log.Error("failed to delete file", "err", err, "path", path) + return oapi.DeleteFile500JSONResponse{InternalErrorJSONResponse: oapi.InternalErrorJSONResponse{Message: "failed to delete file"}}, nil + } + return oapi.DeleteFile200Response{}, nil +} + +// DeleteDirectory removes a directory and its contents. +func (s *ApiService) DeleteDirectory(ctx context.Context, req oapi.DeleteDirectoryRequestObject) (oapi.DeleteDirectoryResponseObject, error) { + log := logger.FromContext(ctx) + if req.Body == nil { + return oapi.DeleteDirectory400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: "request body required"}}, nil + } + path := req.Body.Path + if path == "" { + return oapi.DeleteDirectory400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: "path cannot be empty"}}, nil + } + if err := os.RemoveAll(path); err != nil { + if errors.Is(err, os.ErrNotExist) { + return oapi.DeleteDirectory404JSONResponse{NotFoundErrorJSONResponse: oapi.NotFoundErrorJSONResponse{Message: "directory not found"}}, nil + } + log.Error("failed to delete directory", "err", err, "path", path) + return oapi.DeleteDirectory500JSONResponse{InternalErrorJSONResponse: oapi.InternalErrorJSONResponse{Message: "failed to delete directory"}}, nil + } + return oapi.DeleteDirectory200Response{}, nil +} + +// ListFiles returns FileInfo entries for the contents of a directory. +func (s *ApiService) ListFiles(ctx context.Context, req oapi.ListFilesRequestObject) (oapi.ListFilesResponseObject, error) { + log := logger.FromContext(ctx) + path := req.Params.Path + if path == "" { + return oapi.ListFiles400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: "path cannot be empty"}}, nil + } + entries, err := os.ReadDir(path) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + return oapi.ListFiles404JSONResponse{NotFoundErrorJSONResponse: oapi.NotFoundErrorJSONResponse{Message: "directory not found"}}, nil + } + log.Error("failed to read directory", "err", err, "path", path) + return oapi.ListFiles500JSONResponse{InternalErrorJSONResponse: oapi.InternalErrorJSONResponse{Message: "failed to read directory"}}, nil + } + var list oapi.ListFiles + for _, entry := range entries { + // Retrieve FileInfo for each entry. If this fails (e.g. broken symlink, permission + // error) we surface the failure to the client instead of silently ignoring it so + // that consumers do not unknowingly operate on incomplete or unreliable metadata. + info, err := entry.Info() + if err != nil { + log.Error("failed to stat directory entry", "err", err, "dir", path, "entry", entry.Name()) + return oapi.ListFiles500JSONResponse{InternalErrorJSONResponse: oapi.InternalErrorJSONResponse{Message: "failed to stat directory entry"}}, nil + } + + // By specification SizeBytes should be 0 for directories. + size := 0 + if !info.IsDir() { + size = int(info.Size()) + } + + fi := oapi.FileInfo{ + Name: entry.Name(), + Path: filepath.Join(path, entry.Name()), + IsDir: entry.IsDir(), + SizeBytes: size, + ModTime: info.ModTime(), + Mode: info.Mode().String(), + } + + list = append(list, fi) + } + return oapi.ListFiles200JSONResponse(list), nil +} + +// FileInfo returns metadata about a file or directory. +func (s *ApiService) FileInfo(ctx context.Context, req oapi.FileInfoRequestObject) (oapi.FileInfoResponseObject, error) { + log := logger.FromContext(ctx) + path := req.Params.Path + if path == "" { + return oapi.FileInfo400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: "path cannot be empty"}}, nil + } + stat, err := os.Stat(path) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + return oapi.FileInfo404JSONResponse{NotFoundErrorJSONResponse: oapi.NotFoundErrorJSONResponse{Message: "path not found"}}, nil + } + log.Error("failed to stat path", "err", err, "path", path) + return oapi.FileInfo500JSONResponse{InternalErrorJSONResponse: oapi.InternalErrorJSONResponse{Message: "failed to stat path"}}, nil + } + // By specification SizeBytes should be 0 for directories. + // Match behaviour of ListFiles for consistency. + size := 0 + if !stat.IsDir() { + size = int(stat.Size()) + } + fi := oapi.FileInfo{ + Name: filepath.Base(path), + Path: path, + IsDir: stat.IsDir(), + SizeBytes: size, + ModTime: stat.ModTime(), + Mode: stat.Mode().String(), + } + return oapi.FileInfo200JSONResponse(fi), nil +} + +// MovePath renames or moves a file/directory. +func (s *ApiService) MovePath(ctx context.Context, req oapi.MovePathRequestObject) (oapi.MovePathResponseObject, error) { + log := logger.FromContext(ctx) + if req.Body == nil { + return oapi.MovePath400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: "request body required"}}, nil + } + src := req.Body.SrcPath + dst := req.Body.DestPath + if src == "" || dst == "" { + return oapi.MovePath400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: "src_path and dest_path required"}}, nil + } + if err := os.Rename(src, dst); err != nil { + if errors.Is(err, os.ErrNotExist) { + return oapi.MovePath404JSONResponse{NotFoundErrorJSONResponse: oapi.NotFoundErrorJSONResponse{Message: "source not found"}}, nil + } + log.Error("failed to move path", "err", err, "src", src, "dst", dst) + return oapi.MovePath500JSONResponse{InternalErrorJSONResponse: oapi.InternalErrorJSONResponse{Message: "failed to move path"}}, nil + } + return oapi.MovePath200Response{}, nil +} + +// SetFilePermissions changes mode (and optionally owner/group) of a path. +func (s *ApiService) SetFilePermissions(ctx context.Context, req oapi.SetFilePermissionsRequestObject) (oapi.SetFilePermissionsResponseObject, error) { + log := logger.FromContext(ctx) + if req.Body == nil { + return oapi.SetFilePermissions400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: "request body required"}}, nil + } + path := req.Body.Path + if path == "" { + return oapi.SetFilePermissions400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: "path cannot be empty"}}, nil + } + // parse mode + modeVal, err := strconv.ParseUint(req.Body.Mode, 8, 32) + if err != nil { + return oapi.SetFilePermissions400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: "invalid mode"}}, nil + } + if err := os.Chmod(path, os.FileMode(modeVal)); err != nil { + if errors.Is(err, os.ErrNotExist) { + return oapi.SetFilePermissions404JSONResponse{NotFoundErrorJSONResponse: oapi.NotFoundErrorJSONResponse{Message: "path not found"}}, nil + } + log.Error("failed to chmod", "err", err, "path", path) + return oapi.SetFilePermissions500JSONResponse{InternalErrorJSONResponse: oapi.InternalErrorJSONResponse{Message: "failed to chmod"}}, nil + } + // chown if owner/group provided (best effort) + if req.Body.Owner != nil || req.Body.Group != nil { + uid := -1 + gid := -1 + // Handle owner (uid) + if req.Body.Owner != nil { + ownerStr := *req.Body.Owner + // 1. Try parsing as a numeric UID directly + if id, err := strconv.Atoi(ownerStr); err == nil && id >= 0 { + uid = id + } else { + // 2. Fall back to name lookup + if u, err := user.Lookup(ownerStr); err == nil { + if id, err := strconv.Atoi(u.Uid); err == nil && id >= 0 { + uid = id + } + } + } + } + + // Handle group (gid) + if req.Body.Group != nil { + groupStr := *req.Body.Group + // 1. Try parsing as a numeric GID directly + if id, err := strconv.Atoi(groupStr); err == nil && id >= 0 { + gid = id + } else { + // 2. Fall back to name lookup + if g, err := user.LookupGroup(groupStr); err == nil { + if id, err := strconv.Atoi(g.Gid); err == nil && id >= 0 { + gid = id + } + } + } + } + // only attempt if at least one resolved + if uid != -1 || gid != -1 { + _ = os.Chown(path, uid, gid) // ignore error (likely EPERM) to keep API simpler + } + } + return oapi.SetFilePermissions200Response{}, nil +} + +// StartFsWatch is not implemented in this basic filesystem handler. It returns a 400 error to the client. +func (s *ApiService) StartFsWatch(ctx context.Context, req oapi.StartFsWatchRequestObject) (oapi.StartFsWatchResponseObject, error) { + log := logger.FromContext(ctx) + if req.Body == nil { + return oapi.StartFsWatch400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: "request body required"}}, nil + } + + path := req.Body.Path + if path == "" { + return oapi.StartFsWatch400JSONResponse{BadRequestErrorJSONResponse: oapi.BadRequestErrorJSONResponse{Message: "path cannot be empty"}}, nil + } + // Ensure path exists + if _, err := os.Stat(path); err != nil { + if errors.Is(err, os.ErrNotExist) { + return oapi.StartFsWatch404JSONResponse{NotFoundErrorJSONResponse: oapi.NotFoundErrorJSONResponse{Message: "path not found"}}, nil + } + log.Error("failed to stat path", "err", err, "path", path) + return oapi.StartFsWatch500JSONResponse{InternalErrorJSONResponse: oapi.InternalErrorJSONResponse{Message: "unable to stat path"}}, nil + } + + watcher, err := fsnotify.NewWatcher() + if err != nil { + log.Error("failed to create fsnotify watcher", "err", err) + return oapi.StartFsWatch500JSONResponse{InternalErrorJSONResponse: oapi.InternalErrorJSONResponse{Message: "internal error"}}, nil + } + + recursive := req.Body.Recursive != nil && *req.Body.Recursive + if recursive { + if err := addRecursive(watcher, path); err != nil { + log.Error("failed to add directories recursively", "err", err) + watcher.Close() + return oapi.StartFsWatch500JSONResponse{InternalErrorJSONResponse: oapi.InternalErrorJSONResponse{Message: "internal error"}}, nil + } + } else { + if err := watcher.Add(path); err != nil { + log.Error("failed to watch path", "err", err, "path", path) + watcher.Close() + return oapi.StartFsWatch500JSONResponse{InternalErrorJSONResponse: oapi.InternalErrorJSONResponse{Message: "internal error"}}, nil + } + } + + watchID := cuid2.Generate() + w := &fsWatch{ + path: path, + recursive: recursive, + events: make(chan oapi.FileSystemEvent, 100), + watcher: watcher, + } + + // Register the watch before starting the forwarding goroutine to avoid a + // race where the goroutine might exit before it is added to the map. + s.watchMu.Lock() + s.watches[watchID] = w + s.watchMu.Unlock() + + // Start background goroutine to forward events. We intentionally decouple + // its lifetime from the HTTP request context so that the watch continues + // to run until it is explicitly stopped via StopFsWatch or until watcher + // channels are closed. + go func(s *ApiService, id string) { + // Ensure resources are cleaned up no matter how the goroutine exits. + defer func() { + // Best-effort close (idempotent). + w.Close() + + // Remove stale entry to avoid map/chan leak if the watch stops on + // its own (e.g. underlying fs error, watcher overflow, etc.). It + // is safe to call delete even if StopFsWatch already removed it. + s.watchMu.Lock() + delete(s.watches, id) + s.watchMu.Unlock() + + close(w.events) // close after map cleanup so readers can finish + }() + + for { + select { + case ev, ok := <-watcher.Events: + if !ok { + return + } + var evType oapi.FileSystemEventType + switch { + case ev.Op&fsnotify.Create != 0: + evType = "CREATE" + case ev.Op&fsnotify.Write != 0: + evType = "WRITE" + case ev.Op&fsnotify.Remove != 0: + evType = "DELETE" + case ev.Op&fsnotify.Rename != 0: + evType = "RENAME" + default: + continue + } + info, _ := os.Stat(ev.Name) + isDir := info != nil && info.IsDir() + name := filepath.Base(ev.Name) + // Attempt a non-blocking send so that event production never blocks + // even if the consumer is slow or absent. When the buffer is full we + // simply drop the event, preferring liveness over completeness. + select { + case w.events <- oapi.FileSystemEvent{Type: evType, Path: ev.Name, Name: &name, IsDir: &isDir}: + default: + } + + // If recursive and new directory created, add watch recursively so that + // any nested sub-directories are also monitored. + if recursive && evType == "CREATE" && isDir { + if err := addRecursive(watcher, ev.Name); err != nil { + log.Error("failed to recursively watch new directory", "err", err, "path", ev.Name) + } + } + case err, ok := <-watcher.Errors: + if !ok { + return + } + log.Error("fsnotify error", "err", err) + } + } + }(s, watchID) + + return oapi.StartFsWatch201JSONResponse{WatchId: &watchID}, nil +} + +func (s *ApiService) StopFsWatch(ctx context.Context, req oapi.StopFsWatchRequestObject) (oapi.StopFsWatchResponseObject, error) { + log := logger.FromContext(ctx) + id := req.WatchId + s.watchMu.Lock() + w, ok := s.watches[id] + if ok { + delete(s.watches, id) + w.Close() + // channel will be closed by the event forwarding goroutine + } + s.watchMu.Unlock() + + if !ok { + log.Warn("stop requested for unknown watch", "watch_id", id) + return oapi.StopFsWatch404JSONResponse{NotFoundErrorJSONResponse: oapi.NotFoundErrorJSONResponse{Message: "watch not found"}}, nil + } + + return oapi.StopFsWatch204Response{}, nil +} + +func (s *ApiService) StreamFsEvents(ctx context.Context, req oapi.StreamFsEventsRequestObject) (oapi.StreamFsEventsResponseObject, error) { + log := logger.FromContext(ctx) + id := req.WatchId + s.watchMu.RLock() + w, ok := s.watches[id] + s.watchMu.RUnlock() + if !ok { + log.Warn("stream requested for unknown watch", "watch_id", id) + return oapi.StreamFsEvents404JSONResponse{NotFoundErrorJSONResponse: oapi.NotFoundErrorJSONResponse{Message: "watch not found"}}, nil + } + + pr, pw := io.Pipe() + go func() { + defer pw.Close() + for ev := range w.events { + // Build SSE formatted event: data: \n\n using a buffer and write in a single call + data, err := json.Marshal(ev) + if err != nil { + log.Error("failed to marshal fs event", "err", err) + return + } + + var buf bytes.Buffer + buf.Grow(len("data: ") + len(data) + 2) // 2 for the separating newlines + buf.WriteString("data: ") + buf.Write(data) + buf.WriteString("\n\n") + + if _, err := pw.Write(buf.Bytes()); err != nil { + log.Error("failed to write SSE event", "err", err) + return + } + } + }() + + headers := oapi.StreamFsEvents200ResponseHeaders{XSSEContentType: "application/json"} + return oapi.StreamFsEvents200TexteventStreamResponse{Body: pr, Headers: headers, ContentLength: 0}, nil +} diff --git a/server/cmd/api/api/fs_test.go b/server/cmd/api/api/fs_test.go new file mode 100644 index 00000000..c4a8a70f --- /dev/null +++ b/server/cmd/api/api/fs_test.go @@ -0,0 +1,234 @@ +package api + +import ( + "bufio" + "context" + "io" + "os" + "path/filepath" + "strings" + "testing" + + oapi "github.com/onkernel/kernel-images/server/lib/oapi" +) + +// TestWriteReadFile verifies that files can be written and read back successfully. +func TestWriteReadFile(t *testing.T) { + t.Parallel() + + ctx := context.Background() + svc := &ApiService{defaultRecorderID: "default"} + + tmpDir := t.TempDir() + filePath := filepath.Join(tmpDir, "test.txt") + content := "hello world" + + // Write the file + if resp, err := svc.WriteFile(ctx, oapi.WriteFileRequestObject{ + Params: oapi.WriteFileParams{Path: filePath}, + Body: strings.NewReader(content), + }); err != nil { + t.Fatalf("WriteFile returned error: %v", err) + } else { + if _, ok := resp.(oapi.WriteFile201Response); !ok { + t.Fatalf("unexpected response type from WriteFile: %T", resp) + } + } + + // Read the file + readResp, err := svc.ReadFile(ctx, oapi.ReadFileRequestObject{Params: oapi.ReadFileParams{Path: filePath}}) + if err != nil { + t.Fatalf("ReadFile returned error: %v", err) + } + r200, ok := readResp.(oapi.ReadFile200ApplicationoctetStreamResponse) + if !ok { + t.Fatalf("unexpected response type from ReadFile: %T", readResp) + } + data, _ := io.ReadAll(r200.Body) + if got := string(data); got != content { + t.Fatalf("ReadFile content mismatch: got %q want %q", got, content) + } + + // (Download functionality removed) + + // Attempt to read non-existent file + missingResp, err := svc.ReadFile(ctx, oapi.ReadFileRequestObject{Params: oapi.ReadFileParams{Path: filepath.Join(tmpDir, "missing.txt")}}) + if err != nil { + t.Fatalf("ReadFile missing file returned error: %v", err) + } + if _, ok := missingResp.(oapi.ReadFile404JSONResponse); !ok { + t.Fatalf("expected 404 response for missing file, got %T", missingResp) + } + + // Attempt to write with empty path + badResp, err := svc.WriteFile(ctx, oapi.WriteFileRequestObject{Params: oapi.WriteFileParams{Path: ""}, Body: strings.NewReader("data")}) + if err != nil { + t.Fatalf("WriteFile bad path returned error: %v", err) + } + if _, ok := badResp.(oapi.WriteFile400JSONResponse); !ok { + t.Fatalf("expected 400 response for empty path, got %T", badResp) + } +} + +// TestWriteFileAndWatch verifies WriteFile operation and filesystem watch event generation. +func TestWriteFileAndWatch(t *testing.T) { + t.Parallel() + + ctx := context.Background() + svc := &ApiService{defaultRecorderID: "default", watches: make(map[string]*fsWatch)} + + // Prepare watch + dir := t.TempDir() + recursive := true + startReq := oapi.StartFsWatchRequestObject{Body: &oapi.StartFsWatchRequest{Path: dir, Recursive: &recursive}} + startResp, err := svc.StartFsWatch(ctx, startReq) + if err != nil { + t.Fatalf("StartFsWatch error: %v", err) + } + sr201, ok := startResp.(oapi.StartFsWatch201JSONResponse) + if !ok { + t.Fatalf("unexpected response type from StartFsWatch: %T", startResp) + } + if sr201.WatchId == nil { + t.Fatalf("watch id nil") + } + watchID := *sr201.WatchId + + destPath := filepath.Join(dir, "write.txt") + content := "write content" + + // Perform WriteFile to trigger watch events + if resp, err := svc.WriteFile(ctx, oapi.WriteFileRequestObject{ + Params: oapi.WriteFileParams{Path: destPath}, + Body: strings.NewReader(content), + }); err != nil { + t.Fatalf("WriteFile error: %v", err) + } else { + if _, ok := resp.(oapi.WriteFile201Response); !ok { + t.Fatalf("unexpected response type from WriteFile: %T", resp) + } + } + + // Verify file exists + data, err := os.ReadFile(destPath) + if err != nil || string(data) != content { + t.Fatalf("written file mismatch: %v", err) + } + + // Stream events (should at least receive one) + streamReq := oapi.StreamFsEventsRequestObject{WatchId: watchID} + streamResp, err := svc.StreamFsEvents(ctx, streamReq) + if err != nil { + t.Fatalf("StreamFsEvents error: %v", err) + } + st200, ok := streamResp.(oapi.StreamFsEvents200TexteventStreamResponse) + if !ok { + t.Fatalf("unexpected response type from StreamFsEvents: %T", streamResp) + } + + reader := bufio.NewReader(st200.Body) + line, err := reader.ReadString('\n') + if err != nil { + t.Fatalf("failed to read SSE line: %v", err) + } + if !strings.HasPrefix(line, "data: ") { + t.Fatalf("unexpected SSE format: %s", line) + } + + // Cleanup + stopResp, err := svc.StopFsWatch(ctx, oapi.StopFsWatchRequestObject{WatchId: watchID}) + if err != nil { + t.Fatalf("StopFsWatch error: %v", err) + } + if _, ok := stopResp.(oapi.StopFsWatch204Response); !ok { + t.Fatalf("unexpected response type from StopFsWatch: %T", stopResp) + } +} + +// TestFileDirOperations covers the new filesystem management endpoints. +func TestFileDirOperations(t *testing.T) { + t.Parallel() + + ctx := context.Background() + svc := &ApiService{} + + tmp := t.TempDir() + dirPath := filepath.Join(tmp, "mydir") + + // Create directory + modeStr := "755" + createReq := oapi.CreateDirectoryRequestObject{Body: &oapi.CreateDirectoryRequest{Path: dirPath, Mode: &modeStr}} + if resp, err := svc.CreateDirectory(ctx, createReq); err != nil { + t.Fatalf("CreateDirectory error: %v", err) + } else { + if _, ok := resp.(oapi.CreateDirectory201Response); !ok { + t.Fatalf("unexpected response type from CreateDirectory: %T", resp) + } + } + + // Write a file inside the directory + filePath := filepath.Join(dirPath, "file.txt") + content := "data" + if resp, err := svc.WriteFile(ctx, oapi.WriteFileRequestObject{Params: oapi.WriteFileParams{Path: filePath}, Body: strings.NewReader(content)}); err != nil { + t.Fatalf("WriteFile error: %v", err) + } else if _, ok := resp.(oapi.WriteFile201Response); !ok { + t.Fatalf("unexpected WriteFile resp type: %T", resp) + } + + // List files + listResp, err := svc.ListFiles(ctx, oapi.ListFilesRequestObject{Params: oapi.ListFilesParams{Path: dirPath}}) + if err != nil { + t.Fatalf("ListFiles error: %v", err) + } + lf200, ok := listResp.(oapi.ListFiles200JSONResponse) + if !ok { + t.Fatalf("unexpected ListFiles resp type: %T", listResp) + } + if len(lf200) != 1 || lf200[0].Name != "file.txt" { + t.Fatalf("ListFiles unexpected content: %+v", lf200) + } + + // FileInfo + fiResp, err := svc.FileInfo(ctx, oapi.FileInfoRequestObject{Params: oapi.FileInfoParams{Path: filePath}}) + if err != nil { + t.Fatalf("FileInfo error: %v", err) + } + fi200, ok := fiResp.(oapi.FileInfo200JSONResponse) + if !ok { + t.Fatalf("unexpected FileInfo resp: %T", fiResp) + } + if fi200.Name != "file.txt" || fi200.SizeBytes == 0 { + t.Fatalf("FileInfo unexpected: %+v", fi200) + } + + // Move file + newFilePath := filepath.Join(dirPath, "moved.txt") + moveReq := oapi.MovePathRequestObject{Body: &oapi.MovePathRequest{SrcPath: filePath, DestPath: newFilePath}} + if resp, err := svc.MovePath(ctx, moveReq); err != nil { + t.Fatalf("MovePath error: %v", err) + } else if _, ok := resp.(oapi.MovePath200Response); !ok { + t.Fatalf("unexpected MovePath resp type: %T", resp) + } + + // Set permissions + chmodReq := oapi.SetFilePermissionsRequestObject{Body: &oapi.SetFilePermissionsRequest{Path: newFilePath, Mode: "600"}} + if resp, err := svc.SetFilePermissions(ctx, chmodReq); err != nil { + t.Fatalf("SetFilePermissions error: %v", err) + } else if _, ok := resp.(oapi.SetFilePermissions200Response); !ok { + t.Fatalf("unexpected SetFilePermissions resp: %T", resp) + } + + // Delete file + if resp, err := svc.DeleteFile(ctx, oapi.DeleteFileRequestObject{Body: &oapi.DeletePathRequest{Path: newFilePath}}); err != nil { + t.Fatalf("DeleteFile error: %v", err) + } else if _, ok := resp.(oapi.DeleteFile200Response); !ok { + t.Fatalf("unexpected DeleteFile resp: %T", resp) + } + + // Delete directory + if resp, err := svc.DeleteDirectory(ctx, oapi.DeleteDirectoryRequestObject{Body: &oapi.DeletePathRequest{Path: dirPath}}); err != nil { + t.Fatalf("DeleteDirectory error: %v", err) + } else if _, ok := resp.(oapi.DeleteDirectory200Response); !ok { + t.Fatalf("unexpected DeleteDirectory resp: %T", resp) + } +} diff --git a/server/go.mod b/server/go.mod index 3ed05aeb..fbbc9592 100644 --- a/server/go.mod +++ b/server/go.mod @@ -3,10 +3,12 @@ module github.com/onkernel/kernel-images/server go 1.24.3 require ( + 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 @@ -25,6 +27,8 @@ require ( github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + golang.org/x/crypto v0.40.0 // indirect + golang.org/x/sys v0.34.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 2adcffee..40bb015b 100644 --- a/server/go.sum +++ b/server/go.sum @@ -5,6 +5,8 @@ github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvF 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/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= 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= @@ -32,6 +34,8 @@ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0 github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= 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/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= @@ -51,8 +55,12 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +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/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= +golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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= diff --git a/server/lib/oapi/oapi.go b/server/lib/oapi/oapi.go index 5adf665d..14fa6ddf 100644 --- a/server/lib/oapi/oapi.go +++ b/server/lib/oapi/oapi.go @@ -39,6 +39,14 @@ const ( Up ClickMouseRequestClickType = "up" ) +// Defines values for FileSystemEventType. +const ( + CREATE FileSystemEventType = "CREATE" + DELETE FileSystemEventType = "DELETE" + RENAME FileSystemEventType = "RENAME" + WRITE FileSystemEventType = "WRITE" +) + // ClickMouseRequest defines model for ClickMouseRequest. type ClickMouseRequest struct { // Button Mouse button to interact with @@ -66,6 +74,21 @@ type ClickMouseRequestButton string // ClickMouseRequestClickType Type of click action type ClickMouseRequestClickType string +// CreateDirectoryRequest defines model for CreateDirectoryRequest. +type CreateDirectoryRequest struct { + // Mode Optional directory mode (octal string, e.g. 755). Defaults to 755. + Mode *string `json:"mode,omitempty"` + + // Path Absolute directory path to create. + Path string `json:"path"` +} + +// DeletePathRequest defines model for DeletePathRequest. +type DeletePathRequest struct { + // Path Absolute path to delete. + Path string `json:"path"` +} + // DeleteRecordingRequest defines model for DeleteRecordingRequest. type DeleteRecordingRequest struct { // Id Identifier of the recording to delete. Alphanumeric or hyphen. @@ -77,6 +100,48 @@ type Error struct { Message string `json:"message"` } +// FileInfo defines model for FileInfo. +type FileInfo struct { + // IsDir Whether the path is a directory. + IsDir bool `json:"is_dir"` + + // ModTime Last modification time. + ModTime time.Time `json:"mod_time"` + + // Mode File mode bits (e.g., "drwxr-xr-x" or "-rw-r--r--"). + Mode string `json:"mode"` + + // Name Base name of the file or directory. + Name string `json:"name"` + + // Path Absolute path. + Path string `json:"path"` + + // SizeBytes Size in bytes. 0 for directories. + SizeBytes int `json:"size_bytes"` +} + +// FileSystemEvent Filesystem change event. +type FileSystemEvent struct { + // IsDir Whether the affected path is a directory. + IsDir *bool `json:"is_dir,omitempty"` + + // Name Base name of the file or directory affected. + Name *string `json:"name,omitempty"` + + // Path Absolute path of the file or directory. + Path string `json:"path"` + + // Type Event type. + Type FileSystemEventType `json:"type"` +} + +// FileSystemEventType Event type. +type FileSystemEventType string + +// ListFiles Array of file or directory information entries. +type ListFiles = []FileInfo + // MoveMouseRequest defines model for MoveMouseRequest. type MoveMouseRequest struct { // HoldKeys Modifier keys to hold during the move @@ -89,6 +154,15 @@ type MoveMouseRequest struct { Y int `json:"y"` } +// MovePathRequest defines model for MovePathRequest. +type MovePathRequest struct { + // DestPath Absolute destination path. + DestPath string `json:"dest_path"` + + // SrcPath Absolute source path. + SrcPath string `json:"src_path"` +} + // RecorderInfo defines model for RecorderInfo. type RecorderInfo struct { // FinishedAt Timestamp when recording finished @@ -100,6 +174,30 @@ type RecorderInfo struct { StartedAt *time.Time `json:"started_at"` } +// SetFilePermissionsRequest defines model for SetFilePermissionsRequest. +type SetFilePermissionsRequest struct { + // Group New group name or GID. + Group *string `json:"group,omitempty"` + + // Mode File mode bits (octal string, e.g. 644). + Mode string `json:"mode"` + + // Owner New owner username or UID. + Owner *string `json:"owner,omitempty"` + + // Path Absolute path whose permissions are to be changed. + Path string `json:"path"` +} + +// StartFsWatchRequest defines model for StartFsWatchRequest. +type StartFsWatchRequest struct { + // Path Directory to watch. + Path string `json:"path"` + + // Recursive Whether to watch recursively. + Recursive *bool `json:"recursive,omitempty"` +} + // StartRecordingRequest defines model for StartRecordingRequest. type StartRecordingRequest struct { // Framerate Recording framerate in fps (overrides server default) @@ -136,6 +234,33 @@ type InternalError = Error // NotFoundError defines model for NotFoundError. type NotFoundError = Error +// FileInfoParams defines parameters for FileInfo. +type FileInfoParams struct { + // Path Absolute path of the file or directory. + Path string `form:"path" json:"path"` +} + +// ListFilesParams defines parameters for ListFiles. +type ListFilesParams struct { + // Path Absolute directory path. + Path string `form:"path" json:"path"` +} + +// ReadFileParams defines parameters for ReadFile. +type ReadFileParams struct { + // Path Absolute file path to read. + Path string `form:"path" json:"path"` +} + +// WriteFileParams defines parameters for WriteFile. +type WriteFileParams struct { + // Path Destination absolute file path. + Path string `form:"path" json:"path"` + + // Mode Optional file mode (octal string, e.g. 644). Defaults to 644. + Mode *string `form:"mode,omitempty" json:"mode,omitempty"` +} + // DownloadRecordingParams defines parameters for DownloadRecording. type DownloadRecordingParams struct { // Id Optional recorder identifier. When omitted, the server uses the default recorder. @@ -148,6 +273,24 @@ type ClickMouseJSONRequestBody = ClickMouseRequest // MoveMouseJSONRequestBody defines body for MoveMouse for application/json ContentType. type MoveMouseJSONRequestBody = MoveMouseRequest +// CreateDirectoryJSONRequestBody defines body for CreateDirectory for application/json ContentType. +type CreateDirectoryJSONRequestBody = CreateDirectoryRequest + +// DeleteDirectoryJSONRequestBody defines body for DeleteDirectory for application/json ContentType. +type DeleteDirectoryJSONRequestBody = DeletePathRequest + +// DeleteFileJSONRequestBody defines body for DeleteFile for application/json ContentType. +type DeleteFileJSONRequestBody = DeletePathRequest + +// MovePathJSONRequestBody defines body for MovePath for application/json ContentType. +type MovePathJSONRequestBody = MovePathRequest + +// SetFilePermissionsJSONRequestBody defines body for SetFilePermissions for application/json ContentType. +type SetFilePermissionsJSONRequestBody = SetFilePermissionsRequest + +// StartFsWatchJSONRequestBody defines body for StartFsWatch for application/json ContentType. +type StartFsWatchJSONRequestBody = StartFsWatchRequest + // DeleteRecordingJSONRequestBody defines body for DeleteRecording for application/json ContentType. type DeleteRecordingJSONRequestBody = DeleteRecordingRequest @@ -240,6 +383,54 @@ type ClientInterface interface { MoveMouse(ctx context.Context, body MoveMouseJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // CreateDirectoryWithBody 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 + 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 + 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) + + // FileInfo request + FileInfo(ctx context.Context, params *FileInfoParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // ListFiles request + ListFiles(ctx context.Context, params *ListFilesParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // MovePathWithBody 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) + + // ReadFile request + ReadFile(ctx context.Context, params *ReadFileParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // SetFilePermissionsWithBody 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 + 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) + + // StopFsWatch request + StopFsWatch(ctx context.Context, watchId string, reqEditors ...RequestEditorFn) (*http.Response, error) + + // StreamFsEvents request + StreamFsEvents(ctx context.Context, watchId string, reqEditors ...RequestEditorFn) (*http.Response, error) + + // WriteFileWithBody 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 DeleteRecordingWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -310,6 +501,222 @@ func (c *Client) MoveMouse(ctx context.Context, body MoveMouseJSONRequestBody, r return c.Client.Do(req) } +func (c *Client) CreateDirectoryWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCreateDirectoryRequestWithBody(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) CreateDirectory(ctx context.Context, body CreateDirectoryJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewCreateDirectoryRequest(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) DeleteDirectoryWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteDirectoryRequestWithBody(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) DeleteDirectory(ctx context.Context, body DeleteDirectoryJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteDirectoryRequest(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) DeleteFileWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteFileRequestWithBody(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) DeleteFile(ctx context.Context, body DeleteFileJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteFileRequest(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) FileInfo(ctx context.Context, params *FileInfoParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewFileInfoRequest(c.Server, params) + 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) ListFiles(ctx context.Context, params *ListFilesParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewListFilesRequest(c.Server, params) + 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) MovePathWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewMovePathRequestWithBody(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) MovePath(ctx context.Context, body MovePathJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewMovePathRequest(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) ReadFile(ctx context.Context, params *ReadFileParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewReadFileRequest(c.Server, params) + 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) SetFilePermissionsWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewSetFilePermissionsRequestWithBody(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) SetFilePermissions(ctx context.Context, body SetFilePermissionsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewSetFilePermissionsRequest(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) StartFsWatchWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewStartFsWatchRequestWithBody(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) StartFsWatch(ctx context.Context, body StartFsWatchJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewStartFsWatchRequest(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) StopFsWatch(ctx context.Context, watchId string, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewStopFsWatchRequest(c.Server, watchId) + 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) StreamFsEvents(ctx context.Context, watchId string, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewStreamFsEventsRequest(c.Server, watchId) + 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) WriteFileWithBody(ctx context.Context, params *WriteFileParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewWriteFileRequestWithBody(c.Server, params, 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) DeleteRecordingWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewDeleteRecordingRequestWithBody(c.Server, contentType, body) if err != nil { @@ -486,19 +893,19 @@ func NewMoveMouseRequestWithBody(server string, contentType string, body io.Read return req, nil } -// NewDeleteRecordingRequest calls the generic DeleteRecording builder with application/json body -func NewDeleteRecordingRequest(server string, body DeleteRecordingJSONRequestBody) (*http.Request, error) { +// NewCreateDirectoryRequest calls the generic CreateDirectory builder with application/json body +func NewCreateDirectoryRequest(server string, body CreateDirectoryJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewDeleteRecordingRequestWithBody(server, "application/json", bodyReader) + return NewCreateDirectoryRequestWithBody(server, "application/json", bodyReader) } -// NewDeleteRecordingRequestWithBody generates requests for DeleteRecording with any type of body -func NewDeleteRecordingRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { +// NewCreateDirectoryRequestWithBody generates requests for CreateDirectory with any type of body +func NewCreateDirectoryRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { var err error serverURL, err := url.Parse(server) @@ -506,7 +913,7 @@ func NewDeleteRecordingRequestWithBody(server string, contentType string, body i return nil, err } - operationPath := fmt.Sprintf("/recording/delete") + operationPath := fmt.Sprintf("/fs/create_directory") if operationPath[0] == '/' { operationPath = "." + operationPath } @@ -516,7 +923,7 @@ func NewDeleteRecordingRequestWithBody(server string, contentType string, body i return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), body) + req, err := http.NewRequest("PUT", queryURL.String(), body) if err != nil { return nil, err } @@ -526,16 +933,96 @@ func NewDeleteRecordingRequestWithBody(server string, contentType string, body i return req, nil } -// NewDownloadRecordingRequest generates requests for DownloadRecording -func NewDownloadRecordingRequest(server string, params *DownloadRecordingParams) (*http.Request, error) { - var err error +// NewDeleteDirectoryRequest calls the generic DeleteDirectory builder with application/json body +func NewDeleteDirectoryRequest(server string, body DeleteDirectoryJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewDeleteDirectoryRequestWithBody(server, "application/json", bodyReader) +} + +// NewDeleteDirectoryRequestWithBody generates requests for DeleteDirectory with any type of body +func NewDeleteDirectoryRequestWithBody(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("/recording/download") + operationPath := fmt.Sprintf("/fs/delete_directory") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("PUT", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewDeleteFileRequest calls the generic DeleteFile builder with application/json body +func NewDeleteFileRequest(server string, body DeleteFileJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewDeleteFileRequestWithBody(server, "application/json", bodyReader) +} + +// NewDeleteFileRequestWithBody generates requests for DeleteFile with any type of body +func NewDeleteFileRequestWithBody(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("/fs/delete_file") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("PUT", queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewFileInfoRequest generates requests for FileInfo +func NewFileInfoRequest(server string, params *FileInfoParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/fs/file_info") if operationPath[0] == '/' { operationPath = "." + operationPath } @@ -548,20 +1035,16 @@ func NewDownloadRecordingRequest(server string, params *DownloadRecordingParams) if params != nil { queryValues := queryURL.Query() - 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, "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() @@ -575,8 +1058,8 @@ func NewDownloadRecordingRequest(server string, params *DownloadRecordingParams) return req, nil } -// NewListRecordersRequest generates requests for ListRecorders -func NewListRecordersRequest(server string) (*http.Request, error) { +// NewListFilesRequest generates requests for ListFiles +func NewListFilesRequest(server string, params *ListFilesParams) (*http.Request, error) { var err error serverURL, err := url.Parse(server) @@ -584,7 +1067,7 @@ func NewListRecordersRequest(server string) (*http.Request, error) { return nil, err } - operationPath := fmt.Sprintf("/recording/list") + operationPath := fmt.Sprintf("/fs/list_files") if operationPath[0] == '/' { operationPath = "." + operationPath } @@ -594,6 +1077,24 @@ func NewListRecordersRequest(server string) (*http.Request, error) { return nil, err } + if params != nil { + 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) + } + } + } + + queryURL.RawQuery = queryValues.Encode() + } + req, err := http.NewRequest("GET", queryURL.String(), nil) if err != nil { return nil, err @@ -602,19 +1103,19 @@ func NewListRecordersRequest(server string) (*http.Request, error) { return req, nil } -// NewStartRecordingRequest calls the generic StartRecording builder with application/json body -func NewStartRecordingRequest(server string, body StartRecordingJSONRequestBody) (*http.Request, error) { +// NewMovePathRequest calls the generic MovePath builder with application/json body +func NewMovePathRequest(server string, body MovePathJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewStartRecordingRequestWithBody(server, "application/json", bodyReader) + return NewMovePathRequestWithBody(server, "application/json", bodyReader) } -// NewStartRecordingRequestWithBody generates requests for StartRecording with any type of body -func NewStartRecordingRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { +// NewMovePathRequestWithBody generates requests for MovePath with any type of body +func NewMovePathRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { var err error serverURL, err := url.Parse(server) @@ -622,7 +1123,7 @@ func NewStartRecordingRequestWithBody(server string, contentType string, body io return nil, err } - operationPath := fmt.Sprintf("/recording/start") + operationPath := fmt.Sprintf("/fs/move") if operationPath[0] == '/' { operationPath = "." + operationPath } @@ -632,7 +1133,7 @@ func NewStartRecordingRequestWithBody(server string, contentType string, body io return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), body) + req, err := http.NewRequest("PUT", queryURL.String(), body) if err != nil { return nil, err } @@ -642,19 +1143,64 @@ func NewStartRecordingRequestWithBody(server string, contentType string, body io return req, nil } -// NewStopRecordingRequest calls the generic StopRecording builder with application/json body -func NewStopRecordingRequest(server string, body StopRecordingJSONRequestBody) (*http.Request, error) { +// NewReadFileRequest generates requests for ReadFile +func NewReadFileRequest(server string, params *ReadFileParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/fs/read_file") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + 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) + } + } + } + + queryURL.RawQuery = queryValues.Encode() + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewSetFilePermissionsRequest calls the generic SetFilePermissions builder with application/json body +func NewSetFilePermissionsRequest(server string, body SetFilePermissionsJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader buf, err := json.Marshal(body) if err != nil { return nil, err } bodyReader = bytes.NewReader(buf) - return NewStopRecordingRequestWithBody(server, "application/json", bodyReader) + return NewSetFilePermissionsRequestWithBody(server, "application/json", bodyReader) } -// NewStopRecordingRequestWithBody generates requests for StopRecording with any type of body -func NewStopRecordingRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { +// NewSetFilePermissionsRequestWithBody generates requests for SetFilePermissions with any type of body +func NewSetFilePermissionsRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { var err error serverURL, err := url.Parse(server) @@ -662,7 +1208,7 @@ func NewStopRecordingRequestWithBody(server string, contentType string, body io. return nil, err } - operationPath := fmt.Sprintf("/recording/stop") + operationPath := fmt.Sprintf("/fs/set_file_permissions") if operationPath[0] == '/' { operationPath = "." + operationPath } @@ -672,7 +1218,7 @@ func NewStopRecordingRequestWithBody(server string, contentType string, body io. return nil, err } - req, err := http.NewRequest("POST", queryURL.String(), body) + req, err := http.NewRequest("PUT", queryURL.String(), body) if err != nil { return nil, err } @@ -682,999 +1228,3346 @@ func NewStopRecordingRequestWithBody(server string, contentType string, body io. 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 { - return err - } - } - for _, r := range additionalEditors { - if err := r(ctx, req); err != nil { - return err - } +// NewStartFsWatchRequest calls the generic StartFsWatch builder with application/json body +func NewStartFsWatchRequest(server string, body StartFsWatchJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err } - return nil + bodyReader = bytes.NewReader(buf) + return NewStartFsWatchRequestWithBody(server, "application/json", bodyReader) } -// ClientWithResponses builds on ClientInterface to offer response payloads -type ClientWithResponses struct { - ClientInterface -} +// NewStartFsWatchRequestWithBody generates requests for StartFsWatch with any type of body +func NewStartFsWatchRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error -// NewClientWithResponses creates a new ClientWithResponses, which wraps -// Client with return type handling -func NewClientWithResponses(server string, opts ...ClientOption) (*ClientWithResponses, error) { - client, err := NewClient(server, opts...) + serverURL, err := url.Parse(server) if err != nil { return nil, err } - return &ClientWithResponses{client}, nil -} -// WithBaseURL overrides the baseURL. -func WithBaseURL(baseURL string) ClientOption { - return func(c *Client) error { - newBaseURL, err := url.Parse(baseURL) - if err != nil { - return err - } - c.Server = newBaseURL.String() - return nil + operationPath := fmt.Sprintf("/fs/watch") + if operationPath[0] == '/' { + operationPath = "." + operationPath } -} -// ClientWithResponsesInterface is the interface specification for the client with responses above. -type ClientWithResponsesInterface interface { - // ClickMouseWithBodyWithResponse request with any body - ClickMouseWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*ClickMouseResponse, error) + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } - ClickMouseWithResponse(ctx context.Context, body ClickMouseJSONRequestBody, reqEditors ...RequestEditorFn) (*ClickMouseResponse, error) + req, err := http.NewRequest("POST", queryURL.String(), body) + if err != nil { + return nil, err + } - // MoveMouseWithBodyWithResponse request with any body - MoveMouseWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*MoveMouseResponse, error) + req.Header.Add("Content-Type", contentType) - MoveMouseWithResponse(ctx context.Context, body MoveMouseJSONRequestBody, reqEditors ...RequestEditorFn) (*MoveMouseResponse, error) + return req, nil +} - // DeleteRecordingWithBodyWithResponse request with any body - DeleteRecordingWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*DeleteRecordingResponse, error) +// NewStopFsWatchRequest generates requests for StopFsWatch +func NewStopFsWatchRequest(server string, watchId string) (*http.Request, error) { + var err error - DeleteRecordingWithResponse(ctx context.Context, body DeleteRecordingJSONRequestBody, reqEditors ...RequestEditorFn) (*DeleteRecordingResponse, error) + var pathParam0 string - // DownloadRecordingWithResponse request - DownloadRecordingWithResponse(ctx context.Context, params *DownloadRecordingParams, reqEditors ...RequestEditorFn) (*DownloadRecordingResponse, error) + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "watch_id", runtime.ParamLocationPath, watchId) + if err != nil { + return nil, err + } - // ListRecordersWithResponse request - ListRecordersWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*ListRecordersResponse, error) + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } - // StartRecordingWithBodyWithResponse request with any body - StartRecordingWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*StartRecordingResponse, error) + operationPath := fmt.Sprintf("/fs/watch/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } - StartRecordingWithResponse(ctx context.Context, body StartRecordingJSONRequestBody, reqEditors ...RequestEditorFn) (*StartRecordingResponse, error) + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } - // StopRecordingWithBodyWithResponse request with any body - StopRecordingWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*StopRecordingResponse, error) + req, err := http.NewRequest("DELETE", queryURL.String(), nil) + if err != nil { + return nil, err + } - StopRecordingWithResponse(ctx context.Context, body StopRecordingJSONRequestBody, reqEditors ...RequestEditorFn) (*StopRecordingResponse, error) + return req, nil } -type ClickMouseResponse struct { - Body []byte - HTTPResponse *http.Response - JSON400 *BadRequestError - JSON500 *InternalError -} +// NewStreamFsEventsRequest generates requests for StreamFsEvents +func NewStreamFsEventsRequest(server string, watchId string) (*http.Request, error) { + var err error -// Status returns HTTPResponse.Status -func (r ClickMouseResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "watch_id", runtime.ParamLocationPath, watchId) + if err != nil { + return nil, err } - return http.StatusText(0) -} -// StatusCode returns HTTPResponse.StatusCode -func (r ClickMouseResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} - -type MoveMouseResponse struct { - Body []byte - HTTPResponse *http.Response - JSON400 *BadRequestError - JSON500 *InternalError -} - -// Status returns HTTPResponse.Status -func (r MoveMouseResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status + serverURL, err := url.Parse(server) + if err != nil { + return nil, err } - return http.StatusText(0) -} -// StatusCode returns HTTPResponse.StatusCode -func (r MoveMouseResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode + operationPath := fmt.Sprintf("/fs/watch/%s/events", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath } - return 0 -} - -type DeleteRecordingResponse struct { - Body []byte - HTTPResponse *http.Response - JSON400 *BadRequestError - JSON404 *NotFoundError - JSON500 *InternalError -} -// Status returns HTTPResponse.Status -func (r DeleteRecordingResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err } - return http.StatusText(0) -} -// StatusCode returns HTTPResponse.StatusCode -func (r DeleteRecordingResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err } - return 0 -} -type DownloadRecordingResponse struct { - Body []byte - HTTPResponse *http.Response - JSON400 *BadRequestError - JSON404 *NotFoundError - JSON500 *InternalError + return req, nil } -// Status returns HTTPResponse.Status -func (r DownloadRecordingResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} +// NewWriteFileRequestWithBody generates requests for WriteFile with any type of body +func NewWriteFileRequestWithBody(server string, params *WriteFileParams, contentType string, body io.Reader) (*http.Request, error) { + var err error -// StatusCode returns HTTPResponse.StatusCode -func (r DownloadRecordingResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode + serverURL, err := url.Parse(server) + if err != nil { + return nil, err } - return 0 -} -type ListRecordersResponse struct { - Body []byte - HTTPResponse *http.Response - JSON200 *[]RecorderInfo - JSON500 *InternalError -} - -// Status returns HTTPResponse.Status -func (r ListRecordersResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status + operationPath := fmt.Sprintf("/fs/write_file") + if operationPath[0] == '/' { + operationPath = "." + operationPath } - return http.StatusText(0) -} -// StatusCode returns HTTPResponse.StatusCode -func (r ListRecordersResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err } - return 0 -} -type StartRecordingResponse struct { - Body []byte - HTTPResponse *http.Response - JSON400 *BadRequestError - JSON409 *ConflictError - JSON500 *InternalError -} + if params != nil { + queryValues := queryURL.Query() -// Status returns HTTPResponse.Status -func (r StartRecordingResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} + 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) + } + } + } -// StatusCode returns HTTPResponse.StatusCode -func (r StartRecordingResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode - } - return 0 -} + if params.Mode != nil { -type StopRecordingResponse struct { - Body []byte - HTTPResponse *http.Response - JSON400 *BadRequestError - JSON500 *InternalError -} + 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) + } + } + } -// Status returns HTTPResponse.Status -func (r StopRecordingResponse) Status() string { - if r.HTTPResponse != nil { - return r.HTTPResponse.Status - } - return http.StatusText(0) -} + } -// StatusCode returns HTTPResponse.StatusCode -func (r StopRecordingResponse) StatusCode() int { - if r.HTTPResponse != nil { - return r.HTTPResponse.StatusCode + queryURL.RawQuery = queryValues.Encode() } - 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...) + req, err := http.NewRequest("PUT", queryURL.String(), body) if err != nil { return nil, err } - return ParseClickMouseResponse(rsp) -} -func (c *ClientWithResponses) ClickMouseWithResponse(ctx context.Context, body ClickMouseJSONRequestBody, reqEditors ...RequestEditorFn) (*ClickMouseResponse, error) { - rsp, err := c.ClickMouse(ctx, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseClickMouseResponse(rsp) -} + req.Header.Add("Content-Type", contentType) -// MoveMouseWithBodyWithResponse request with arbitrary body returning *MoveMouseResponse -func (c *ClientWithResponses) MoveMouseWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*MoveMouseResponse, error) { - rsp, err := c.MoveMouseWithBody(ctx, contentType, body, reqEditors...) - if err != nil { - return nil, err - } - return ParseMoveMouseResponse(rsp) + return req, nil } -func (c *ClientWithResponses) MoveMouseWithResponse(ctx context.Context, body MoveMouseJSONRequestBody, reqEditors ...RequestEditorFn) (*MoveMouseResponse, error) { - rsp, err := c.MoveMouse(ctx, body, reqEditors...) +// NewDeleteRecordingRequest calls the generic DeleteRecording builder with application/json body +func NewDeleteRecordingRequest(server string, body DeleteRecordingJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) if err != nil { return nil, err } - return ParseMoveMouseResponse(rsp) + bodyReader = bytes.NewReader(buf) + return NewDeleteRecordingRequestWithBody(server, "application/json", bodyReader) } -// 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...) - if err != nil { - return nil, err - } - return ParseDeleteRecordingResponse(rsp) -} +// NewDeleteRecordingRequestWithBody generates requests for DeleteRecording with any type of body +func NewDeleteRecordingRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error -func (c *ClientWithResponses) DeleteRecordingWithResponse(ctx context.Context, body DeleteRecordingJSONRequestBody, reqEditors ...RequestEditorFn) (*DeleteRecordingResponse, error) { - rsp, err := c.DeleteRecording(ctx, body, reqEditors...) + serverURL, err := url.Parse(server) if err != nil { return nil, err } - return ParseDeleteRecordingResponse(rsp) -} -// DownloadRecordingWithResponse request returning *DownloadRecordingResponse -func (c *ClientWithResponses) DownloadRecordingWithResponse(ctx context.Context, params *DownloadRecordingParams, reqEditors ...RequestEditorFn) (*DownloadRecordingResponse, error) { - rsp, err := c.DownloadRecording(ctx, params, reqEditors...) - if err != nil { - return nil, err + operationPath := fmt.Sprintf("/recording/delete") + if operationPath[0] == '/' { + operationPath = "." + operationPath } - return ParseDownloadRecordingResponse(rsp) -} -// ListRecordersWithResponse request returning *ListRecordersResponse -func (c *ClientWithResponses) ListRecordersWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*ListRecordersResponse, error) { - rsp, err := c.ListRecorders(ctx, reqEditors...) + queryURL, err := serverURL.Parse(operationPath) if err != nil { return nil, err } - return ParseListRecordersResponse(rsp) -} -// StartRecordingWithBodyWithResponse request with arbitrary body returning *StartRecordingResponse -func (c *ClientWithResponses) StartRecordingWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*StartRecordingResponse, error) { - rsp, err := c.StartRecordingWithBody(ctx, contentType, body, reqEditors...) + req, err := http.NewRequest("POST", queryURL.String(), body) if err != nil { return nil, err } - return ParseStartRecordingResponse(rsp) + + req.Header.Add("Content-Type", contentType) + + return req, nil } -func (c *ClientWithResponses) StartRecordingWithResponse(ctx context.Context, body StartRecordingJSONRequestBody, reqEditors ...RequestEditorFn) (*StartRecordingResponse, error) { - rsp, err := c.StartRecording(ctx, body, reqEditors...) +// NewDownloadRecordingRequest generates requests for DownloadRecording +func NewDownloadRecordingRequest(server string, params *DownloadRecordingParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) if err != nil { return nil, err } - return ParseStartRecordingResponse(rsp) -} -// StopRecordingWithBodyWithResponse request with arbitrary body returning *StopRecordingResponse -func (c *ClientWithResponses) StopRecordingWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*StopRecordingResponse, error) { - rsp, err := c.StopRecordingWithBody(ctx, contentType, body, reqEditors...) - if err != nil { - return nil, err + operationPath := fmt.Sprintf("/recording/download") + if operationPath[0] == '/' { + operationPath = "." + operationPath } - return ParseStopRecordingResponse(rsp) -} -func (c *ClientWithResponses) StopRecordingWithResponse(ctx context.Context, body StopRecordingJSONRequestBody, reqEditors ...RequestEditorFn) (*StopRecordingResponse, error) { - rsp, err := c.StopRecording(ctx, body, reqEditors...) + queryURL, err := serverURL.Parse(operationPath) if err != nil { return nil, err } - return ParseStopRecordingResponse(rsp) -} -// ParseClickMouseResponse parses an HTTP response from a ClickMouseWithResponse call -func ParseClickMouseResponse(rsp *http.Response) (*ClickMouseResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } + if params != nil { + queryValues := queryURL.Query() - response := &ClickMouseResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } + if params.Id != nil { - 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 + 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) + } + } + } - 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 + queryURL.RawQuery = queryValues.Encode() + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err } - return response, nil + return req, nil } -// ParseMoveMouseResponse parses an HTTP response from a MoveMouseWithResponse call -func ParseMoveMouseResponse(rsp *http.Response) (*MoveMouseResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() +// NewListRecordersRequest generates requests for ListRecorders +func NewListRecordersRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) if err != nil { return nil, err } - response := &MoveMouseResponse{ - Body: bodyBytes, - HTTPResponse: rsp, + operationPath := fmt.Sprintf("/recording/list") + if operationPath[0] == '/' { + operationPath = "." + operationPath } - 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 + 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 response, nil + return req, nil } -// ParseDeleteRecordingResponse parses an HTTP response from a DeleteRecordingWithResponse call -func ParseDeleteRecordingResponse(rsp *http.Response) (*DeleteRecordingResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() +// NewStartRecordingRequest calls the generic StartRecording builder with application/json body +func NewStartRecordingRequest(server string, body StartRecordingJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) if err != nil { return nil, err } + bodyReader = bytes.NewReader(buf) + return NewStartRecordingRequestWithBody(server, "application/json", bodyReader) +} - response := &DeleteRecordingResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } +// NewStartRecordingRequestWithBody generates requests for StartRecording with any type of body +func NewStartRecordingRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error - 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 + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest NotFoundError - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON404 = &dest + operationPath := fmt.Sprintf("/recording/start") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } - 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 + 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 } - return response, nil + req.Header.Add("Content-Type", contentType) + + return req, nil } -// ParseDownloadRecordingResponse parses an HTTP response from a DownloadRecordingWithResponse call -func ParseDownloadRecordingResponse(rsp *http.Response) (*DownloadRecordingResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() +// NewStopRecordingRequest calls the generic StopRecording builder with application/json body +func NewStopRecordingRequest(server string, body StopRecordingJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) if err != nil { return nil, err } + bodyReader = bytes.NewReader(buf) + return NewStopRecordingRequestWithBody(server, "application/json", bodyReader) +} - response := &DownloadRecordingResponse{ - Body: bodyBytes, - HTTPResponse: rsp, +// NewStopRecordingRequestWithBody generates requests for StopRecording with any type of body +func NewStopRecordingRequestWithBody(server string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err } - 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 + operationPath := fmt.Sprintf("/recording/stop") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: - var dest NotFoundError - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON404 = &dest + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } - 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 + 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 { + return err + } + } + for _, r := range additionalEditors { + if err := r(ctx, req); err != nil { + return err + } } + return nil +} - return response, nil +// ClientWithResponses builds on ClientInterface to offer response payloads +type ClientWithResponses struct { + ClientInterface } -// ParseListRecordersResponse parses an HTTP response from a ListRecordersWithResponse call -func ParseListRecordersResponse(rsp *http.Response) (*ListRecordersResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() +// NewClientWithResponses creates a new ClientWithResponses, which wraps +// Client with return type handling +func NewClientWithResponses(server string, opts ...ClientOption) (*ClientWithResponses, error) { + client, err := NewClient(server, opts...) if err != nil { return nil, err } + return &ClientWithResponses{client}, nil +} - response := &ListRecordersResponse{ - Body: bodyBytes, - HTTPResponse: rsp, +// WithBaseURL overrides the baseURL. +func WithBaseURL(baseURL string) ClientOption { + return func(c *Client) error { + newBaseURL, err := url.Parse(baseURL) + if err != nil { + return err + } + c.Server = newBaseURL.String() + return nil } +} - switch { - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: - var dest []RecorderInfo - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON200 = &dest +// ClientWithResponsesInterface is the interface specification for the client with responses above. +type ClientWithResponsesInterface interface { + // ClickMouseWithBodyWithResponse 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 + 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 + 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 + 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 + 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 + FileInfoWithResponse(ctx context.Context, params *FileInfoParams, reqEditors ...RequestEditorFn) (*FileInfoResponse, error) + + // ListFilesWithResponse request + ListFilesWithResponse(ctx context.Context, params *ListFilesParams, reqEditors ...RequestEditorFn) (*ListFilesResponse, error) + + // MovePathWithBodyWithResponse 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 + ReadFileWithResponse(ctx context.Context, params *ReadFileParams, reqEditors ...RequestEditorFn) (*ReadFileResponse, error) + + // SetFilePermissionsWithBodyWithResponse 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 + 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 + StopFsWatchWithResponse(ctx context.Context, watchId string, reqEditors ...RequestEditorFn) (*StopFsWatchResponse, error) + + // StreamFsEventsWithResponse request + StreamFsEventsWithResponse(ctx context.Context, watchId string, reqEditors ...RequestEditorFn) (*StreamFsEventsResponse, error) + + // WriteFileWithBodyWithResponse request with any body + WriteFileWithBodyWithResponse(ctx context.Context, params *WriteFileParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*WriteFileResponse, error) + + // DeleteRecordingWithBodyWithResponse 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 + DownloadRecordingWithResponse(ctx context.Context, params *DownloadRecordingParams, reqEditors ...RequestEditorFn) (*DownloadRecordingResponse, error) + + // ListRecordersWithResponse request + ListRecordersWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*ListRecordersResponse, error) + + // StartRecordingWithBodyWithResponse 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 + StopRecordingWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*StopRecordingResponse, error) + + StopRecordingWithResponse(ctx context.Context, body StopRecordingJSONRequestBody, reqEditors ...RequestEditorFn) (*StopRecordingResponse, error) +} + +type ClickMouseResponse struct { + Body []byte + HTTPResponse *http.Response + JSON400 *BadRequestError + JSON500 *InternalError +} + +// Status returns HTTPResponse.Status +func (r ClickMouseResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r ClickMouseResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type MoveMouseResponse struct { + Body []byte + HTTPResponse *http.Response + JSON400 *BadRequestError + JSON500 *InternalError +} + +// Status returns HTTPResponse.Status +func (r MoveMouseResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r MoveMouseResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type CreateDirectoryResponse struct { + Body []byte + HTTPResponse *http.Response + JSON400 *BadRequestError + JSON500 *InternalError +} + +// Status returns HTTPResponse.Status +func (r CreateDirectoryResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r CreateDirectoryResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type DeleteDirectoryResponse struct { + Body []byte + HTTPResponse *http.Response + JSON400 *BadRequestError + JSON404 *NotFoundError + JSON500 *InternalError +} + +// Status returns HTTPResponse.Status +func (r DeleteDirectoryResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r DeleteDirectoryResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type DeleteFileResponse struct { + Body []byte + HTTPResponse *http.Response + JSON400 *BadRequestError + JSON404 *NotFoundError + JSON500 *InternalError +} + +// Status returns HTTPResponse.Status +func (r DeleteFileResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r DeleteFileResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type FileInfoResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *FileInfo + JSON400 *BadRequestError + JSON404 *NotFoundError + JSON500 *InternalError +} + +// Status returns HTTPResponse.Status +func (r FileInfoResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r FileInfoResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type ListFilesResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *ListFiles + JSON400 *BadRequestError + JSON404 *NotFoundError + JSON500 *InternalError +} + +// Status returns HTTPResponse.Status +func (r ListFilesResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r ListFilesResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type MovePathResponse struct { + Body []byte + HTTPResponse *http.Response + JSON400 *BadRequestError + JSON404 *NotFoundError + JSON500 *InternalError +} + +// Status returns HTTPResponse.Status +func (r MovePathResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r MovePathResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type ReadFileResponse struct { + Body []byte + HTTPResponse *http.Response + JSON400 *BadRequestError + JSON404 *NotFoundError + JSON500 *InternalError +} + +// Status returns HTTPResponse.Status +func (r ReadFileResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r ReadFileResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type SetFilePermissionsResponse struct { + Body []byte + HTTPResponse *http.Response + JSON400 *BadRequestError + JSON404 *NotFoundError + JSON500 *InternalError +} + +// Status returns HTTPResponse.Status +func (r SetFilePermissionsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r SetFilePermissionsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type StartFsWatchResponse struct { + Body []byte + HTTPResponse *http.Response + JSON201 *struct { + // WatchId Unique identifier for the directory watch + WatchId *string `json:"watch_id,omitempty"` + } + JSON400 *BadRequestError + JSON404 *NotFoundError + JSON500 *InternalError +} + +// Status returns HTTPResponse.Status +func (r StartFsWatchResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r StartFsWatchResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type StopFsWatchResponse struct { + Body []byte + HTTPResponse *http.Response + JSON400 *BadRequestError + JSON404 *NotFoundError + JSON500 *InternalError +} + +// Status returns HTTPResponse.Status +func (r StopFsWatchResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r StopFsWatchResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type StreamFsEventsResponse struct { + Body []byte + HTTPResponse *http.Response + JSON400 *BadRequestError + JSON404 *NotFoundError + JSON500 *InternalError +} + +// Status returns HTTPResponse.Status +func (r StreamFsEventsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r StreamFsEventsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type WriteFileResponse struct { + Body []byte + HTTPResponse *http.Response + JSON400 *BadRequestError + JSON404 *NotFoundError + JSON500 *InternalError +} + +// Status returns HTTPResponse.Status +func (r WriteFileResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r WriteFileResponse) 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 +} + +// Status returns HTTPResponse.Status +func (r DeleteRecordingResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r DeleteRecordingResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type DownloadRecordingResponse struct { + Body []byte + HTTPResponse *http.Response + JSON400 *BadRequestError + JSON404 *NotFoundError + JSON500 *InternalError +} + +// Status returns HTTPResponse.Status +func (r DownloadRecordingResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r DownloadRecordingResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type ListRecordersResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *[]RecorderInfo + JSON500 *InternalError +} + +// Status returns HTTPResponse.Status +func (r ListRecordersResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r ListRecordersResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type StartRecordingResponse struct { + Body []byte + HTTPResponse *http.Response + JSON400 *BadRequestError + JSON409 *ConflictError + JSON500 *InternalError +} + +// Status returns HTTPResponse.Status +func (r StartRecordingResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r StartRecordingResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type StopRecordingResponse struct { + Body []byte + HTTPResponse *http.Response + JSON400 *BadRequestError + JSON500 *InternalError +} + +// Status returns HTTPResponse.Status +func (r StopRecordingResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r StopRecordingResponse) 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...) + if err != nil { + return nil, err + } + return ParseClickMouseResponse(rsp) +} + +func (c *ClientWithResponses) ClickMouseWithResponse(ctx context.Context, body ClickMouseJSONRequestBody, reqEditors ...RequestEditorFn) (*ClickMouseResponse, error) { + rsp, err := c.ClickMouse(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseClickMouseResponse(rsp) +} + +// MoveMouseWithBodyWithResponse request with arbitrary body returning *MoveMouseResponse +func (c *ClientWithResponses) MoveMouseWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*MoveMouseResponse, error) { + rsp, err := c.MoveMouseWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseMoveMouseResponse(rsp) +} + +func (c *ClientWithResponses) MoveMouseWithResponse(ctx context.Context, body MoveMouseJSONRequestBody, reqEditors ...RequestEditorFn) (*MoveMouseResponse, error) { + rsp, err := c.MoveMouse(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseMoveMouseResponse(rsp) +} + +// CreateDirectoryWithBodyWithResponse request with arbitrary body returning *CreateDirectoryResponse +func (c *ClientWithResponses) CreateDirectoryWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*CreateDirectoryResponse, error) { + rsp, err := c.CreateDirectoryWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseCreateDirectoryResponse(rsp) +} + +func (c *ClientWithResponses) CreateDirectoryWithResponse(ctx context.Context, body CreateDirectoryJSONRequestBody, reqEditors ...RequestEditorFn) (*CreateDirectoryResponse, error) { + rsp, err := c.CreateDirectory(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseCreateDirectoryResponse(rsp) +} + +// DeleteDirectoryWithBodyWithResponse request with arbitrary body returning *DeleteDirectoryResponse +func (c *ClientWithResponses) DeleteDirectoryWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*DeleteDirectoryResponse, error) { + rsp, err := c.DeleteDirectoryWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseDeleteDirectoryResponse(rsp) +} + +func (c *ClientWithResponses) DeleteDirectoryWithResponse(ctx context.Context, body DeleteDirectoryJSONRequestBody, reqEditors ...RequestEditorFn) (*DeleteDirectoryResponse, error) { + rsp, err := c.DeleteDirectory(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseDeleteDirectoryResponse(rsp) +} + +// DeleteFileWithBodyWithResponse request with arbitrary body returning *DeleteFileResponse +func (c *ClientWithResponses) DeleteFileWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*DeleteFileResponse, error) { + rsp, err := c.DeleteFileWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseDeleteFileResponse(rsp) +} + +func (c *ClientWithResponses) DeleteFileWithResponse(ctx context.Context, body DeleteFileJSONRequestBody, reqEditors ...RequestEditorFn) (*DeleteFileResponse, error) { + rsp, err := c.DeleteFile(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseDeleteFileResponse(rsp) +} + +// FileInfoWithResponse request returning *FileInfoResponse +func (c *ClientWithResponses) FileInfoWithResponse(ctx context.Context, params *FileInfoParams, reqEditors ...RequestEditorFn) (*FileInfoResponse, error) { + rsp, err := c.FileInfo(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseFileInfoResponse(rsp) +} + +// ListFilesWithResponse request returning *ListFilesResponse +func (c *ClientWithResponses) ListFilesWithResponse(ctx context.Context, params *ListFilesParams, reqEditors ...RequestEditorFn) (*ListFilesResponse, error) { + rsp, err := c.ListFiles(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseListFilesResponse(rsp) +} + +// MovePathWithBodyWithResponse request with arbitrary body returning *MovePathResponse +func (c *ClientWithResponses) MovePathWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*MovePathResponse, error) { + rsp, err := c.MovePathWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseMovePathResponse(rsp) +} + +func (c *ClientWithResponses) MovePathWithResponse(ctx context.Context, body MovePathJSONRequestBody, reqEditors ...RequestEditorFn) (*MovePathResponse, error) { + rsp, err := c.MovePath(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseMovePathResponse(rsp) +} + +// ReadFileWithResponse request returning *ReadFileResponse +func (c *ClientWithResponses) ReadFileWithResponse(ctx context.Context, params *ReadFileParams, reqEditors ...RequestEditorFn) (*ReadFileResponse, error) { + rsp, err := c.ReadFile(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseReadFileResponse(rsp) +} + +// SetFilePermissionsWithBodyWithResponse request with arbitrary body returning *SetFilePermissionsResponse +func (c *ClientWithResponses) SetFilePermissionsWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*SetFilePermissionsResponse, error) { + rsp, err := c.SetFilePermissionsWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseSetFilePermissionsResponse(rsp) +} + +func (c *ClientWithResponses) SetFilePermissionsWithResponse(ctx context.Context, body SetFilePermissionsJSONRequestBody, reqEditors ...RequestEditorFn) (*SetFilePermissionsResponse, error) { + rsp, err := c.SetFilePermissions(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseSetFilePermissionsResponse(rsp) +} + +// StartFsWatchWithBodyWithResponse request with arbitrary body returning *StartFsWatchResponse +func (c *ClientWithResponses) StartFsWatchWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*StartFsWatchResponse, error) { + rsp, err := c.StartFsWatchWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseStartFsWatchResponse(rsp) +} + +func (c *ClientWithResponses) StartFsWatchWithResponse(ctx context.Context, body StartFsWatchJSONRequestBody, reqEditors ...RequestEditorFn) (*StartFsWatchResponse, error) { + rsp, err := c.StartFsWatch(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseStartFsWatchResponse(rsp) +} + +// StopFsWatchWithResponse request returning *StopFsWatchResponse +func (c *ClientWithResponses) StopFsWatchWithResponse(ctx context.Context, watchId string, reqEditors ...RequestEditorFn) (*StopFsWatchResponse, error) { + rsp, err := c.StopFsWatch(ctx, watchId, reqEditors...) + if err != nil { + return nil, err + } + return ParseStopFsWatchResponse(rsp) +} + +// StreamFsEventsWithResponse request returning *StreamFsEventsResponse +func (c *ClientWithResponses) StreamFsEventsWithResponse(ctx context.Context, watchId string, reqEditors ...RequestEditorFn) (*StreamFsEventsResponse, error) { + rsp, err := c.StreamFsEvents(ctx, watchId, reqEditors...) + if err != nil { + return nil, err + } + return ParseStreamFsEventsResponse(rsp) +} + +// WriteFileWithBodyWithResponse request with arbitrary body returning *WriteFileResponse +func (c *ClientWithResponses) WriteFileWithBodyWithResponse(ctx context.Context, params *WriteFileParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*WriteFileResponse, error) { + rsp, err := c.WriteFileWithBody(ctx, params, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseWriteFileResponse(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...) + if err != nil { + return nil, err + } + return ParseDeleteRecordingResponse(rsp) +} + +func (c *ClientWithResponses) DeleteRecordingWithResponse(ctx context.Context, body DeleteRecordingJSONRequestBody, reqEditors ...RequestEditorFn) (*DeleteRecordingResponse, error) { + rsp, err := c.DeleteRecording(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseDeleteRecordingResponse(rsp) +} + +// DownloadRecordingWithResponse request returning *DownloadRecordingResponse +func (c *ClientWithResponses) DownloadRecordingWithResponse(ctx context.Context, params *DownloadRecordingParams, reqEditors ...RequestEditorFn) (*DownloadRecordingResponse, error) { + rsp, err := c.DownloadRecording(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseDownloadRecordingResponse(rsp) +} + +// ListRecordersWithResponse request returning *ListRecordersResponse +func (c *ClientWithResponses) ListRecordersWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*ListRecordersResponse, error) { + rsp, err := c.ListRecorders(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseListRecordersResponse(rsp) +} + +// StartRecordingWithBodyWithResponse request with arbitrary body returning *StartRecordingResponse +func (c *ClientWithResponses) StartRecordingWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*StartRecordingResponse, error) { + rsp, err := c.StartRecordingWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseStartRecordingResponse(rsp) +} + +func (c *ClientWithResponses) StartRecordingWithResponse(ctx context.Context, body StartRecordingJSONRequestBody, reqEditors ...RequestEditorFn) (*StartRecordingResponse, error) { + rsp, err := c.StartRecording(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseStartRecordingResponse(rsp) +} + +// StopRecordingWithBodyWithResponse request with arbitrary body returning *StopRecordingResponse +func (c *ClientWithResponses) StopRecordingWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*StopRecordingResponse, error) { + rsp, err := c.StopRecordingWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseStopRecordingResponse(rsp) +} + +func (c *ClientWithResponses) StopRecordingWithResponse(ctx context.Context, body StopRecordingJSONRequestBody, reqEditors ...RequestEditorFn) (*StopRecordingResponse, error) { + rsp, err := c.StopRecording(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseStopRecordingResponse(rsp) +} + +// ParseClickMouseResponse parses an HTTP response from a ClickMouseWithResponse call +func ParseClickMouseResponse(rsp *http.Response) (*ClickMouseResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &ClickMouseResponse{ + 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 +} + +// ParseMoveMouseResponse parses an HTTP response from a MoveMouseWithResponse call +func ParseMoveMouseResponse(rsp *http.Response) (*MoveMouseResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &MoveMouseResponse{ + 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 +} + +// ParseCreateDirectoryResponse parses an HTTP response from a CreateDirectoryWithResponse call +func ParseCreateDirectoryResponse(rsp *http.Response) (*CreateDirectoryResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &CreateDirectoryResponse{ + 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 +} + +// ParseDeleteDirectoryResponse parses an HTTP response from a DeleteDirectoryWithResponse call +func ParseDeleteDirectoryResponse(rsp *http.Response) (*DeleteDirectoryResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DeleteDirectoryResponse{ + 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 + 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 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseDeleteFileResponse parses an HTTP response from a DeleteFileWithResponse call +func ParseDeleteFileResponse(rsp *http.Response) (*DeleteFileResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DeleteFileResponse{ + 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 + 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 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseFileInfoResponse parses an HTTP response from a FileInfoWithResponse call +func ParseFileInfoResponse(rsp *http.Response) (*FileInfoResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &FileInfoResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest FileInfo + 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 == 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 + 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 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseListFilesResponse parses an HTTP response from a ListFilesWithResponse call +func ParseListFilesResponse(rsp *http.Response) (*ListFilesResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &ListFilesResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest ListFiles + 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 == 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 + 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 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseMovePathResponse parses an HTTP response from a MovePathWithResponse call +func ParseMovePathResponse(rsp *http.Response) (*MovePathResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &MovePathResponse{ + 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 + 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 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseReadFileResponse parses an HTTP response from a ReadFileWithResponse call +func ParseReadFileResponse(rsp *http.Response) (*ReadFileResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &ReadFileResponse{ + 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 + 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 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseSetFilePermissionsResponse parses an HTTP response from a SetFilePermissionsWithResponse call +func ParseSetFilePermissionsResponse(rsp *http.Response) (*SetFilePermissionsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &SetFilePermissionsResponse{ + 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 + 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 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseStartFsWatchResponse parses an HTTP response from a StartFsWatchWithResponse call +func ParseStartFsWatchResponse(rsp *http.Response) (*StartFsWatchResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &StartFsWatchResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: + var dest struct { + // WatchId Unique identifier for the directory watch + WatchId *string `json:"watch_id,omitempty"` + } + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON201 = &dest + + 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 + 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 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseStopFsWatchResponse parses an HTTP response from a StopFsWatchWithResponse call +func ParseStopFsWatchResponse(rsp *http.Response) (*StopFsWatchResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &StopFsWatchResponse{ + 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 + 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 + 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 + 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 + 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 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// 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 := &WriteFileResponse{ + 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 + 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 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseDeleteRecordingResponse parses an HTTP response from a DeleteRecordingWithResponse call +func ParseDeleteRecordingResponse(rsp *http.Response) (*DeleteRecordingResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DeleteRecordingResponse{ + 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 + 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 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseDownloadRecordingResponse parses an HTTP response from a DownloadRecordingWithResponse call +func ParseDownloadRecordingResponse(rsp *http.Response) (*DownloadRecordingResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DownloadRecordingResponse{ + 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 + 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 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseListRecordersResponse parses an HTTP response from a ListRecordersWithResponse call +func ParseListRecordersResponse(rsp *http.Response) (*ListRecordersResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &ListRecordersResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest []RecorderInfo + 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 +} + +// ParseStartRecordingResponse parses an HTTP response from a StartRecordingWithResponse call +func ParseStartRecordingResponse(rsp *http.Response) (*StartRecordingResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &StartRecordingResponse{ + 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 == 409: + var dest ConflictError + 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 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseStopRecordingResponse parses an HTTP response from a StopRecordingWithResponse call +func ParseStopRecordingResponse(rsp *http.Response) (*StopRecordingResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &StopRecordingResponse{ + 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 +} + +// ServerInterface represents all server handlers. +type ServerInterface interface { + // Simulate a mouse click action on the host computer + // (POST /computer/click_mouse) + ClickMouse(w http.ResponseWriter, r *http.Request) + // Move the mouse cursor to the specified coordinates on the host computer + // (POST /computer/move_mouse) + MoveMouse(w http.ResponseWriter, r *http.Request) + // Create a new directory + // (PUT /fs/create_directory) + CreateDirectory(w http.ResponseWriter, r *http.Request) + // Delete a directory + // (PUT /fs/delete_directory) + DeleteDirectory(w http.ResponseWriter, r *http.Request) + // Delete a file + // (PUT /fs/delete_file) + DeleteFile(w http.ResponseWriter, r *http.Request) + // Get information about a file or directory + // (GET /fs/file_info) + FileInfo(w http.ResponseWriter, r *http.Request, params FileInfoParams) + // List files in a directory + // (GET /fs/list_files) + ListFiles(w http.ResponseWriter, r *http.Request, params ListFilesParams) + // Move or rename a file or directory + // (PUT /fs/move) + MovePath(w http.ResponseWriter, r *http.Request) + // Read file contents + // (GET /fs/read_file) + ReadFile(w http.ResponseWriter, r *http.Request, params ReadFileParams) + // Set file or directory permissions/ownership + // (PUT /fs/set_file_permissions) + SetFilePermissions(w http.ResponseWriter, r *http.Request) + // Watch a directory for changes + // (POST /fs/watch) + StartFsWatch(w http.ResponseWriter, r *http.Request) + // Stop watching a directory + // (DELETE /fs/watch/{watch_id}) + StopFsWatch(w http.ResponseWriter, r *http.Request, watchId string) + // Stream filesystem events for a watch + // (GET /fs/watch/{watch_id}/events) + StreamFsEvents(w http.ResponseWriter, r *http.Request, watchId string) + // Write or create a file + // (PUT /fs/write_file) + WriteFile(w http.ResponseWriter, r *http.Request, params WriteFileParams) + // Delete a previously recorded video file + // (POST /recording/delete) + DeleteRecording(w http.ResponseWriter, r *http.Request) + // Download the most recently recorded video file + // (GET /recording/download) + DownloadRecording(w http.ResponseWriter, r *http.Request, params DownloadRecordingParams) + // List all recorders + // (GET /recording/list) + ListRecorders(w http.ResponseWriter, r *http.Request) + // Start a screen recording. Only one recording per ID can be registered at a time. + // (POST /recording/start) + StartRecording(w http.ResponseWriter, r *http.Request) + // Stop the recording + // (POST /recording/stop) + 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 + HandlerMiddlewares []MiddlewareFunc + ErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error) +} + +type MiddlewareFunc func(http.Handler) http.Handler + +// ClickMouse operation middleware +func (siw *ServerInterfaceWrapper) ClickMouse(w http.ResponseWriter, r *http.Request) { + + 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) +} + +// MoveMouse operation middleware +func (siw *ServerInterfaceWrapper) MoveMouse(w http.ResponseWriter, r *http.Request) { + + 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) +} + +// CreateDirectory operation middleware +func (siw *ServerInterfaceWrapper) CreateDirectory(w http.ResponseWriter, r *http.Request) { + + 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) +} + +// DeleteDirectory operation middleware +func (siw *ServerInterfaceWrapper) DeleteDirectory(w http.ResponseWriter, r *http.Request) { + + 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) +} + +// DeleteFile operation middleware +func (siw *ServerInterfaceWrapper) DeleteFile(w http.ResponseWriter, r *http.Request) { + + 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) +} + +// FileInfo operation middleware +func (siw *ServerInterfaceWrapper) FileInfo(w http.ResponseWriter, r *http.Request) { + + var err error + + // Parameter object where we will unmarshal all parameters from the context + var params FileInfoParams + + // ------------- Required query parameter "path" ------------- + + if paramValue := r.URL.Query().Get("path"); paramValue != "" { + + } else { + siw.ErrorHandlerFunc(w, r, &RequiredParamError{ParamName: "path"}) + return + } + + err = runtime.BindQueryParameter("form", true, true, "path", r.URL.Query(), ¶ms.Path) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "path", Err: err}) + return + } + + 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) +} + +// ListFiles operation middleware +func (siw *ServerInterfaceWrapper) ListFiles(w http.ResponseWriter, r *http.Request) { + + var err error + + // Parameter object where we will unmarshal all parameters from the context + var params ListFilesParams + + // ------------- Required query parameter "path" ------------- + + if paramValue := r.URL.Query().Get("path"); paramValue != "" { + + } else { + siw.ErrorHandlerFunc(w, r, &RequiredParamError{ParamName: "path"}) + return + } + + err = runtime.BindQueryParameter("form", true, true, "path", r.URL.Query(), ¶ms.Path) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "path", Err: err}) + return + } + + 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) +} + +// MovePath operation middleware +func (siw *ServerInterfaceWrapper) MovePath(w http.ResponseWriter, r *http.Request) { + + 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) +} + +// ReadFile operation middleware +func (siw *ServerInterfaceWrapper) ReadFile(w http.ResponseWriter, r *http.Request) { + + var err error + + // Parameter object where we will unmarshal all parameters from the context + var params ReadFileParams + + // ------------- Required query parameter "path" ------------- + + if paramValue := r.URL.Query().Get("path"); paramValue != "" { + + } else { + siw.ErrorHandlerFunc(w, r, &RequiredParamError{ParamName: "path"}) + return + } + + err = runtime.BindQueryParameter("form", true, true, "path", r.URL.Query(), ¶ms.Path) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "path", Err: err}) + return + } + + 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) +} + +// SetFilePermissions operation middleware +func (siw *ServerInterfaceWrapper) SetFilePermissions(w http.ResponseWriter, r *http.Request) { + + 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) +} + +// StartFsWatch operation middleware +func (siw *ServerInterfaceWrapper) StartFsWatch(w http.ResponseWriter, r *http.Request) { + + 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) +} + +// StopFsWatch operation middleware +func (siw *ServerInterfaceWrapper) StopFsWatch(w http.ResponseWriter, r *http.Request) { + + 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}) + 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) { + siw.Handler.StopFsWatch(w, r, watchId) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// StreamFsEvents operation middleware +func (siw *ServerInterfaceWrapper) StreamFsEvents(w http.ResponseWriter, r *http.Request) { + + 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}) + 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) { + siw.Handler.StreamFsEvents(w, r, watchId) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// WriteFile operation middleware +func (siw *ServerInterfaceWrapper) WriteFile(w http.ResponseWriter, r *http.Request) { + + var err error + + // Parameter object where we will unmarshal all parameters from the context + var params WriteFileParams + + // ------------- Required query parameter "path" ------------- + + if paramValue := r.URL.Query().Get("path"); paramValue != "" { + + } else { + siw.ErrorHandlerFunc(w, r, &RequiredParamError{ParamName: "path"}) + return + } + + err = runtime.BindQueryParameter("form", true, true, "path", r.URL.Query(), ¶ms.Path) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "path", Err: err}) + return + } + + // ------------- Optional query parameter "mode" ------------- + + err = runtime.BindQueryParameter("form", true, false, "mode", r.URL.Query(), ¶ms.Mode) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "mode", Err: err}) + return + } + + 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) +} + +// DeleteRecording operation middleware +func (siw *ServerInterfaceWrapper) DeleteRecording(w http.ResponseWriter, r *http.Request) { + + 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) +} + +// DownloadRecording operation middleware +func (siw *ServerInterfaceWrapper) DownloadRecording(w http.ResponseWriter, r *http.Request) { + + var err error + + // Parameter object where we will unmarshal all parameters from the context + var params DownloadRecordingParams + + // ------------- Optional query parameter "id" ------------- + + err = runtime.BindQueryParameter("form", true, false, "id", r.URL.Query(), ¶ms.Id) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err}) + return + } + + 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) +} + +// ListRecorders operation middleware +func (siw *ServerInterfaceWrapper) ListRecorders(w http.ResponseWriter, r *http.Request) { + + 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) +} + +// StartRecording operation middleware +func (siw *ServerInterfaceWrapper) StartRecording(w http.ResponseWriter, r *http.Request) { + + 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) +} + +// StopRecording operation middleware +func (siw *ServerInterfaceWrapper) StopRecording(w http.ResponseWriter, r *http.Request) { + + 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) +} + +type UnescapedCookieParamError struct { + ParamName string + Err error +} + +func (e *UnescapedCookieParamError) Error() string { + return fmt.Sprintf("error unescaping cookie parameter '%s'", e.ParamName) +} + +func (e *UnescapedCookieParamError) Unwrap() error { + return e.Err +} + +type UnmarshalingParamError 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 *UnmarshalingParamError) Unwrap() error { + return e.Err +} + +type RequiredParamError struct { + ParamName string +} + +func (e *RequiredParamError) Error() string { + return fmt.Sprintf("Query argument %s is required, but not found", e.ParamName) +} + +type RequiredHeaderError struct { + ParamName string + Err error +} + +func (e *RequiredHeaderError) Error() string { + return fmt.Sprintf("Header parameter %s is required, but not found", e.ParamName) +} + +func (e *RequiredHeaderError) Unwrap() error { + return e.Err +} + +type InvalidParamFormatError struct { + ParamName string + Err error +} + +func (e *InvalidParamFormatError) Error() string { + return fmt.Sprintf("Invalid format for parameter %s: %s", e.ParamName, e.Err.Error()) +} + +func (e *InvalidParamFormatError) Unwrap() error { + return e.Err +} + +type TooManyValuesForParamError struct { + ParamName string + Count int +} + +func (e *TooManyValuesForParamError) Error() string { + return fmt.Sprintf("Expected one value for %s, got %d", e.ParamName, e.Count) +} + +// Handler creates http.Handler with routing matching OpenAPI spec. +func Handler(si ServerInterface) http.Handler { + return HandlerWithOptions(si, ChiServerOptions{}) +} + +type ChiServerOptions struct { + BaseURL string + BaseRouter chi.Router + Middlewares []MiddlewareFunc + ErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error) +} + +// HandlerFromMux creates http.Handler with routing matching OpenAPI spec based on the provided mux. +func HandlerFromMux(si ServerInterface, r chi.Router) http.Handler { + return HandlerWithOptions(si, ChiServerOptions{ + BaseRouter: r, + }) +} + +func HandlerFromMuxWithBaseURL(si ServerInterface, r chi.Router, baseURL string) http.Handler { + return HandlerWithOptions(si, ChiServerOptions{ + BaseURL: baseURL, + BaseRouter: r, + }) +} + +// HandlerWithOptions creates http.Handler with additional options +func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handler { + r := options.BaseRouter + + if r == nil { + r = chi.NewRouter() + } + if options.ErrorHandlerFunc == nil { + options.ErrorHandlerFunc = func(w http.ResponseWriter, r *http.Request, err error) { + http.Error(w, err.Error(), http.StatusBadRequest) + } + } + wrapper := ServerInterfaceWrapper{ + Handler: si, + HandlerMiddlewares: options.Middlewares, + ErrorHandlerFunc: options.ErrorHandlerFunc, + } + + 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/move_mouse", wrapper.MoveMouse) + }) + r.Group(func(r chi.Router) { + r.Put(options.BaseURL+"/fs/create_directory", wrapper.CreateDirectory) + }) + r.Group(func(r chi.Router) { + r.Put(options.BaseURL+"/fs/delete_directory", wrapper.DeleteDirectory) + }) + r.Group(func(r chi.Router) { + r.Put(options.BaseURL+"/fs/delete_file", wrapper.DeleteFile) + }) + r.Group(func(r chi.Router) { + r.Get(options.BaseURL+"/fs/file_info", wrapper.FileInfo) + }) + r.Group(func(r chi.Router) { + r.Get(options.BaseURL+"/fs/list_files", wrapper.ListFiles) + }) + r.Group(func(r chi.Router) { + r.Put(options.BaseURL+"/fs/move", wrapper.MovePath) + }) + r.Group(func(r chi.Router) { + r.Get(options.BaseURL+"/fs/read_file", wrapper.ReadFile) + }) + r.Group(func(r chi.Router) { + r.Put(options.BaseURL+"/fs/set_file_permissions", wrapper.SetFilePermissions) + }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/fs/watch", wrapper.StartFsWatch) + }) + r.Group(func(r chi.Router) { + r.Delete(options.BaseURL+"/fs/watch/{watch_id}", wrapper.StopFsWatch) + }) + r.Group(func(r chi.Router) { + r.Get(options.BaseURL+"/fs/watch/{watch_id}/events", wrapper.StreamFsEvents) + }) + r.Group(func(r chi.Router) { + r.Put(options.BaseURL+"/fs/write_file", wrapper.WriteFile) + }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/recording/delete", wrapper.DeleteRecording) + }) + r.Group(func(r chi.Router) { + r.Get(options.BaseURL+"/recording/download", wrapper.DownloadRecording) + }) + r.Group(func(r chi.Router) { + r.Get(options.BaseURL+"/recording/list", wrapper.ListRecorders) + }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/recording/start", wrapper.StartRecording) + }) + r.Group(func(r chi.Router) { + r.Post(options.BaseURL+"/recording/stop", wrapper.StopRecording) + }) + + return r +} + +type BadRequestErrorJSONResponse Error + +type ConflictErrorJSONResponse Error + +type InternalErrorJSONResponse Error + +type NotFoundErrorJSONResponse Error + +type ClickMouseRequestObject struct { + Body *ClickMouseJSONRequestBody +} + +type ClickMouseResponseObject interface { + VisitClickMouseResponse(w http.ResponseWriter) error +} + +type ClickMouse200Response struct { +} + +func (response ClickMouse200Response) VisitClickMouseResponse(w http.ResponseWriter) error { + w.WriteHeader(200) + return nil +} + +type ClickMouse400JSONResponse struct{ BadRequestErrorJSONResponse } + +func (response ClickMouse400JSONResponse) VisitClickMouseResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type ClickMouse500JSONResponse struct{ InternalErrorJSONResponse } + +func (response ClickMouse500JSONResponse) VisitClickMouseResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + +type MoveMouseRequestObject struct { + Body *MoveMouseJSONRequestBody +} + +type MoveMouseResponseObject interface { + VisitMoveMouseResponse(w http.ResponseWriter) error +} + +type MoveMouse200Response struct { +} + +func (response MoveMouse200Response) VisitMoveMouseResponse(w http.ResponseWriter) error { + w.WriteHeader(200) + return nil +} + +type MoveMouse400JSONResponse struct{ BadRequestErrorJSONResponse } + +func (response MoveMouse400JSONResponse) VisitMoveMouseResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type MoveMouse500JSONResponse struct{ InternalErrorJSONResponse } + +func (response MoveMouse500JSONResponse) VisitMoveMouseResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + +type CreateDirectoryRequestObject struct { + Body *CreateDirectoryJSONRequestBody +} + +type CreateDirectoryResponseObject interface { + VisitCreateDirectoryResponse(w http.ResponseWriter) error +} + +type CreateDirectory201Response struct { +} + +func (response CreateDirectory201Response) VisitCreateDirectoryResponse(w http.ResponseWriter) error { + w.WriteHeader(201) + return nil +} + +type CreateDirectory400JSONResponse struct{ BadRequestErrorJSONResponse } + +func (response CreateDirectory400JSONResponse) VisitCreateDirectoryResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type CreateDirectory500JSONResponse struct{ InternalErrorJSONResponse } + +func (response CreateDirectory500JSONResponse) VisitCreateDirectoryResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} - 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 +type DeleteDirectoryRequestObject struct { + Body *DeleteDirectoryJSONRequestBody +} - } +type DeleteDirectoryResponseObject interface { + VisitDeleteDirectoryResponse(w http.ResponseWriter) error +} - return response, nil +type DeleteDirectory200Response struct { } -// ParseStartRecordingResponse parses an HTTP response from a StartRecordingWithResponse call -func ParseStartRecordingResponse(rsp *http.Response) (*StartRecordingResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } +func (response DeleteDirectory200Response) VisitDeleteDirectoryResponse(w http.ResponseWriter) error { + w.WriteHeader(200) + return nil +} - response := &StartRecordingResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } +type DeleteDirectory400JSONResponse struct{ BadRequestErrorJSONResponse } - 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 +func (response DeleteDirectory400JSONResponse) VisitDeleteDirectoryResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) - case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 409: - var dest ConflictError - if err := json.Unmarshal(bodyBytes, &dest); err != nil { - return nil, err - } - response.JSON409 = &dest + return json.NewEncoder(w).Encode(response) +} - 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 +type DeleteDirectory404JSONResponse struct{ NotFoundErrorJSONResponse } - } +func (response DeleteDirectory404JSONResponse) VisitDeleteDirectoryResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(404) - return response, nil + return json.NewEncoder(w).Encode(response) } -// ParseStopRecordingResponse parses an HTTP response from a StopRecordingWithResponse call -func ParseStopRecordingResponse(rsp *http.Response) (*StopRecordingResponse, error) { - bodyBytes, err := io.ReadAll(rsp.Body) - defer func() { _ = rsp.Body.Close() }() - if err != nil { - return nil, err - } +type DeleteDirectory500JSONResponse struct{ InternalErrorJSONResponse } - response := &StopRecordingResponse{ - Body: bodyBytes, - HTTPResponse: rsp, - } +func (response DeleteDirectory500JSONResponse) VisitDeleteDirectoryResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) - 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 + return json.NewEncoder(w).Encode(response) +} - 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 +type DeleteFileRequestObject struct { + Body *DeleteFileJSONRequestBody +} - } +type DeleteFileResponseObject interface { + VisitDeleteFileResponse(w http.ResponseWriter) error +} - return response, nil +type DeleteFile200Response struct { } -// ServerInterface represents all server handlers. -type ServerInterface interface { - // Simulate a mouse click action on the host computer - // (POST /computer/click_mouse) - ClickMouse(w http.ResponseWriter, r *http.Request) - // Move the mouse cursor to the specified coordinates on the host computer - // (POST /computer/move_mouse) - MoveMouse(w http.ResponseWriter, r *http.Request) - // Delete a previously recorded video file - // (POST /recording/delete) - DeleteRecording(w http.ResponseWriter, r *http.Request) - // Download the most recently recorded video file - // (GET /recording/download) - DownloadRecording(w http.ResponseWriter, r *http.Request, params DownloadRecordingParams) - // List all recorders - // (GET /recording/list) - ListRecorders(w http.ResponseWriter, r *http.Request) - // Start a screen recording. Only one recording per ID can be registered at a time. - // (POST /recording/start) - StartRecording(w http.ResponseWriter, r *http.Request) - // Stop the recording - // (POST /recording/stop) - StopRecording(w http.ResponseWriter, r *http.Request) +func (response DeleteFile200Response) VisitDeleteFileResponse(w http.ResponseWriter) error { + w.WriteHeader(200) + return nil } -// Unimplemented server implementation that returns http.StatusNotImplemented for each endpoint. +type DeleteFile400JSONResponse struct{ BadRequestErrorJSONResponse } -type Unimplemented struct{} +func (response DeleteFile400JSONResponse) VisitDeleteFileResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) -// 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) + return json.NewEncoder(w).Encode(response) } -// 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) +type DeleteFile404JSONResponse struct{ NotFoundErrorJSONResponse } + +func (response DeleteFile404JSONResponse) VisitDeleteFileResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(404) + + return json.NewEncoder(w).Encode(response) } -// Delete a previously recorded video file -// (POST /recording/delete) -func (_ Unimplemented) DeleteRecording(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNotImplemented) +type DeleteFile500JSONResponse struct{ InternalErrorJSONResponse } + +func (response DeleteFile500JSONResponse) VisitDeleteFileResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) } -// 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) +type FileInfoRequestObject struct { + Params FileInfoParams } -// List all recorders -// (GET /recording/list) -func (_ Unimplemented) ListRecorders(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNotImplemented) +type FileInfoResponseObject interface { + VisitFileInfoResponse(w http.ResponseWriter) error } -// 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) +type FileInfo200JSONResponse FileInfo + +func (response FileInfo200JSONResponse) VisitFileInfoResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) } -// Stop the recording -// (POST /recording/stop) -func (_ Unimplemented) StopRecording(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNotImplemented) +type FileInfo400JSONResponse struct{ BadRequestErrorJSONResponse } + +func (response FileInfo400JSONResponse) VisitFileInfoResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) } -// ServerInterfaceWrapper converts contexts to parameters. -type ServerInterfaceWrapper struct { - Handler ServerInterface - HandlerMiddlewares []MiddlewareFunc - ErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error) +type FileInfo404JSONResponse struct{ NotFoundErrorJSONResponse } + +func (response FileInfo404JSONResponse) VisitFileInfoResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(404) + + return json.NewEncoder(w).Encode(response) } -type MiddlewareFunc func(http.Handler) http.Handler +type FileInfo500JSONResponse struct{ InternalErrorJSONResponse } -// ClickMouse operation middleware -func (siw *ServerInterfaceWrapper) ClickMouse(w http.ResponseWriter, r *http.Request) { +func (response FileInfo500JSONResponse) VisitFileInfoResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - siw.Handler.ClickMouse(w, r) - })) + return json.NewEncoder(w).Encode(response) +} - for _, middleware := range siw.HandlerMiddlewares { - handler = middleware(handler) - } +type ListFilesRequestObject struct { + Params ListFilesParams +} - handler.ServeHTTP(w, r) +type ListFilesResponseObject interface { + VisitListFilesResponse(w http.ResponseWriter) error } -// MoveMouse operation middleware -func (siw *ServerInterfaceWrapper) MoveMouse(w http.ResponseWriter, r *http.Request) { +type ListFiles200JSONResponse ListFiles - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - siw.Handler.MoveMouse(w, r) - })) +func (response ListFiles200JSONResponse) VisitListFilesResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) - for _, middleware := range siw.HandlerMiddlewares { - handler = middleware(handler) - } + return json.NewEncoder(w).Encode(response) +} - handler.ServeHTTP(w, r) +type ListFiles400JSONResponse struct{ BadRequestErrorJSONResponse } + +func (response ListFiles400JSONResponse) VisitListFilesResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) } -// DeleteRecording operation middleware -func (siw *ServerInterfaceWrapper) DeleteRecording(w http.ResponseWriter, r *http.Request) { +type ListFiles404JSONResponse struct{ NotFoundErrorJSONResponse } - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - siw.Handler.DeleteRecording(w, r) - })) +func (response ListFiles404JSONResponse) VisitListFilesResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(404) - for _, middleware := range siw.HandlerMiddlewares { - handler = middleware(handler) - } + return json.NewEncoder(w).Encode(response) +} - handler.ServeHTTP(w, r) +type ListFiles500JSONResponse struct{ InternalErrorJSONResponse } + +func (response ListFiles500JSONResponse) VisitListFilesResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) } -// DownloadRecording operation middleware -func (siw *ServerInterfaceWrapper) DownloadRecording(w http.ResponseWriter, r *http.Request) { +type MovePathRequestObject struct { + Body *MovePathJSONRequestBody +} - var err error +type MovePathResponseObject interface { + VisitMovePathResponse(w http.ResponseWriter) error +} - // Parameter object where we will unmarshal all parameters from the context - var params DownloadRecordingParams +type MovePath200Response struct { +} - // ------------- Optional query parameter "id" ------------- +func (response MovePath200Response) VisitMovePathResponse(w http.ResponseWriter) error { + w.WriteHeader(200) + return nil +} - err = runtime.BindQueryParameter("form", true, false, "id", r.URL.Query(), ¶ms.Id) - if err != nil { - siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "id", Err: err}) - return - } +type MovePath400JSONResponse struct{ BadRequestErrorJSONResponse } - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - siw.Handler.DownloadRecording(w, r, params) - })) +func (response MovePath400JSONResponse) VisitMovePathResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) - for _, middleware := range siw.HandlerMiddlewares { - handler = middleware(handler) - } + return json.NewEncoder(w).Encode(response) +} - handler.ServeHTTP(w, r) +type MovePath404JSONResponse struct{ NotFoundErrorJSONResponse } + +func (response MovePath404JSONResponse) VisitMovePathResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(404) + + return json.NewEncoder(w).Encode(response) } -// ListRecorders operation middleware -func (siw *ServerInterfaceWrapper) ListRecorders(w http.ResponseWriter, r *http.Request) { +type MovePath500JSONResponse struct{ InternalErrorJSONResponse } - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - siw.Handler.ListRecorders(w, r) - })) +func (response MovePath500JSONResponse) VisitMovePathResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) - for _, middleware := range siw.HandlerMiddlewares { - handler = middleware(handler) - } + return json.NewEncoder(w).Encode(response) +} - handler.ServeHTTP(w, r) +type ReadFileRequestObject struct { + Params ReadFileParams } -// StartRecording operation middleware -func (siw *ServerInterfaceWrapper) StartRecording(w http.ResponseWriter, r *http.Request) { +type ReadFileResponseObject interface { + VisitReadFileResponse(w http.ResponseWriter) error +} - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - siw.Handler.StartRecording(w, r) - })) +type ReadFile200ApplicationoctetStreamResponse struct { + Body io.Reader + ContentLength int64 +} - for _, middleware := range siw.HandlerMiddlewares { - handler = middleware(handler) +func (response ReadFile200ApplicationoctetStreamResponse) VisitReadFileResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/octet-stream") + if response.ContentLength != 0 { + w.Header().Set("Content-Length", fmt.Sprint(response.ContentLength)) } + w.WriteHeader(200) - handler.ServeHTTP(w, r) + if closer, ok := response.Body.(io.ReadCloser); ok { + defer closer.Close() + } + _, err := io.Copy(w, response.Body) + return err } -// StopRecording operation middleware -func (siw *ServerInterfaceWrapper) StopRecording(w http.ResponseWriter, r *http.Request) { - - handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - siw.Handler.StopRecording(w, r) - })) +type ReadFile400JSONResponse struct{ BadRequestErrorJSONResponse } - for _, middleware := range siw.HandlerMiddlewares { - handler = middleware(handler) - } +func (response ReadFile400JSONResponse) VisitReadFileResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) - handler.ServeHTTP(w, r) + return json.NewEncoder(w).Encode(response) } -type UnescapedCookieParamError struct { - ParamName string - Err error +type ReadFile404JSONResponse struct{ NotFoundErrorJSONResponse } + +func (response ReadFile404JSONResponse) VisitReadFileResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(404) + + return json.NewEncoder(w).Encode(response) } -func (e *UnescapedCookieParamError) Error() string { - return fmt.Sprintf("error unescaping cookie parameter '%s'", e.ParamName) +type ReadFile500JSONResponse struct{ InternalErrorJSONResponse } + +func (response ReadFile500JSONResponse) VisitReadFileResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) } -func (e *UnescapedCookieParamError) Unwrap() error { - return e.Err +type SetFilePermissionsRequestObject struct { + Body *SetFilePermissionsJSONRequestBody } -type UnmarshalingParamError struct { - ParamName string - Err error +type SetFilePermissionsResponseObject interface { + VisitSetFilePermissionsResponse(w http.ResponseWriter) error } -func (e *UnmarshalingParamError) Error() string { - return fmt.Sprintf("Error unmarshaling parameter %s as JSON: %s", e.ParamName, e.Err.Error()) +type SetFilePermissions200Response struct { } -func (e *UnmarshalingParamError) Unwrap() error { - return e.Err +func (response SetFilePermissions200Response) VisitSetFilePermissionsResponse(w http.ResponseWriter) error { + w.WriteHeader(200) + return nil } -type RequiredParamError struct { - ParamName string +type SetFilePermissions400JSONResponse struct{ BadRequestErrorJSONResponse } + +func (response SetFilePermissions400JSONResponse) VisitSetFilePermissionsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) } -func (e *RequiredParamError) Error() string { - return fmt.Sprintf("Query argument %s is required, but not found", e.ParamName) +type SetFilePermissions404JSONResponse struct{ NotFoundErrorJSONResponse } + +func (response SetFilePermissions404JSONResponse) VisitSetFilePermissionsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(404) + + return json.NewEncoder(w).Encode(response) } -type RequiredHeaderError struct { - ParamName string - Err error +type SetFilePermissions500JSONResponse struct{ InternalErrorJSONResponse } + +func (response SetFilePermissions500JSONResponse) VisitSetFilePermissionsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) } -func (e *RequiredHeaderError) Error() string { - return fmt.Sprintf("Header parameter %s is required, but not found", e.ParamName) +type StartFsWatchRequestObject struct { + Body *StartFsWatchJSONRequestBody } -func (e *RequiredHeaderError) Unwrap() error { - return e.Err +type StartFsWatchResponseObject interface { + VisitStartFsWatchResponse(w http.ResponseWriter) error } -type InvalidParamFormatError struct { - ParamName string - Err error +type StartFsWatch201JSONResponse struct { + // WatchId Unique identifier for the directory watch + WatchId *string `json:"watch_id,omitempty"` } -func (e *InvalidParamFormatError) Error() string { - return fmt.Sprintf("Invalid format for parameter %s: %s", e.ParamName, e.Err.Error()) +func (response StartFsWatch201JSONResponse) VisitStartFsWatchResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(201) + + return json.NewEncoder(w).Encode(response) } -func (e *InvalidParamFormatError) Unwrap() error { - return e.Err +type StartFsWatch400JSONResponse struct{ BadRequestErrorJSONResponse } + +func (response StartFsWatch400JSONResponse) VisitStartFsWatchResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) } -type TooManyValuesForParamError struct { - ParamName string - Count int +type StartFsWatch404JSONResponse struct{ NotFoundErrorJSONResponse } + +func (response StartFsWatch404JSONResponse) VisitStartFsWatchResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(404) + + return json.NewEncoder(w).Encode(response) } -func (e *TooManyValuesForParamError) Error() string { - return fmt.Sprintf("Expected one value for %s, got %d", e.ParamName, e.Count) +type StartFsWatch500JSONResponse struct{ InternalErrorJSONResponse } + +func (response StartFsWatch500JSONResponse) VisitStartFsWatchResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) } -// Handler creates http.Handler with routing matching OpenAPI spec. -func Handler(si ServerInterface) http.Handler { - return HandlerWithOptions(si, ChiServerOptions{}) +type StopFsWatchRequestObject struct { + WatchId string `json:"watch_id"` } -type ChiServerOptions struct { - BaseURL string - BaseRouter chi.Router - Middlewares []MiddlewareFunc - ErrorHandlerFunc func(w http.ResponseWriter, r *http.Request, err error) +type StopFsWatchResponseObject interface { + VisitStopFsWatchResponse(w http.ResponseWriter) error } -// HandlerFromMux creates http.Handler with routing matching OpenAPI spec based on the provided mux. -func HandlerFromMux(si ServerInterface, r chi.Router) http.Handler { - return HandlerWithOptions(si, ChiServerOptions{ - BaseRouter: r, - }) +type StopFsWatch204Response struct { } -func HandlerFromMuxWithBaseURL(si ServerInterface, r chi.Router, baseURL string) http.Handler { - return HandlerWithOptions(si, ChiServerOptions{ - BaseURL: baseURL, - BaseRouter: r, - }) +func (response StopFsWatch204Response) VisitStopFsWatchResponse(w http.ResponseWriter) error { + w.WriteHeader(204) + return nil } -// HandlerWithOptions creates http.Handler with additional options -func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handler { - r := options.BaseRouter +type StopFsWatch400JSONResponse struct{ BadRequestErrorJSONResponse } - if r == nil { - r = chi.NewRouter() - } - if options.ErrorHandlerFunc == nil { - options.ErrorHandlerFunc = func(w http.ResponseWriter, r *http.Request, err error) { - http.Error(w, err.Error(), http.StatusBadRequest) - } - } - wrapper := ServerInterfaceWrapper{ - Handler: si, - HandlerMiddlewares: options.Middlewares, - ErrorHandlerFunc: options.ErrorHandlerFunc, - } +func (response StopFsWatch400JSONResponse) VisitStopFsWatchResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) - 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/move_mouse", wrapper.MoveMouse) - }) - r.Group(func(r chi.Router) { - r.Post(options.BaseURL+"/recording/delete", wrapper.DeleteRecording) - }) - r.Group(func(r chi.Router) { - r.Get(options.BaseURL+"/recording/download", wrapper.DownloadRecording) - }) - r.Group(func(r chi.Router) { - r.Get(options.BaseURL+"/recording/list", wrapper.ListRecorders) - }) - r.Group(func(r chi.Router) { - r.Post(options.BaseURL+"/recording/start", wrapper.StartRecording) - }) - r.Group(func(r chi.Router) { - r.Post(options.BaseURL+"/recording/stop", wrapper.StopRecording) - }) + return json.NewEncoder(w).Encode(response) +} - return r +type StopFsWatch404JSONResponse struct{ NotFoundErrorJSONResponse } + +func (response StopFsWatch404JSONResponse) VisitStopFsWatchResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(404) + + return json.NewEncoder(w).Encode(response) } -type BadRequestErrorJSONResponse Error +type StopFsWatch500JSONResponse struct{ InternalErrorJSONResponse } -type ConflictErrorJSONResponse Error +func (response StopFsWatch500JSONResponse) VisitStopFsWatchResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) -type InternalErrorJSONResponse Error + return json.NewEncoder(w).Encode(response) +} -type NotFoundErrorJSONResponse Error +type StreamFsEventsRequestObject struct { + WatchId string `json:"watch_id"` +} -type ClickMouseRequestObject struct { - Body *ClickMouseJSONRequestBody +type StreamFsEventsResponseObject interface { + VisitStreamFsEventsResponse(w http.ResponseWriter) error } -type ClickMouseResponseObject interface { - VisitClickMouseResponse(w http.ResponseWriter) error +type StreamFsEvents200ResponseHeaders struct { + XSSEContentType string } -type ClickMouse200Response struct { +type StreamFsEvents200TexteventStreamResponse struct { + Body io.Reader + Headers StreamFsEvents200ResponseHeaders + ContentLength int64 } -func (response ClickMouse200Response) VisitClickMouseResponse(w http.ResponseWriter) error { +func (response StreamFsEvents200TexteventStreamResponse) VisitStreamFsEventsResponse(w http.ResponseWriter) error { + 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) - return nil + + 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 + } + } } -type ClickMouse400JSONResponse struct{ BadRequestErrorJSONResponse } +type StreamFsEvents400JSONResponse struct{ BadRequestErrorJSONResponse } -func (response ClickMouse400JSONResponse) VisitClickMouseResponse(w http.ResponseWriter) error { +func (response StreamFsEvents400JSONResponse) VisitStreamFsEventsResponse(w http.ResponseWriter) error { w.Header().Set("Content-Type", "application/json") w.WriteHeader(400) return json.NewEncoder(w).Encode(response) } -type ClickMouse500JSONResponse struct{ InternalErrorJSONResponse } +type StreamFsEvents404JSONResponse struct{ NotFoundErrorJSONResponse } -func (response ClickMouse500JSONResponse) VisitClickMouseResponse(w http.ResponseWriter) error { +func (response StreamFsEvents404JSONResponse) VisitStreamFsEventsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(404) + + return json.NewEncoder(w).Encode(response) +} + +type StreamFsEvents500JSONResponse struct{ InternalErrorJSONResponse } + +func (response StreamFsEvents500JSONResponse) VisitStreamFsEventsResponse(w http.ResponseWriter) error { w.Header().Set("Content-Type", "application/json") w.WriteHeader(500) return json.NewEncoder(w).Encode(response) } -type MoveMouseRequestObject struct { - Body *MoveMouseJSONRequestBody +type WriteFileRequestObject struct { + Params WriteFileParams + Body io.Reader } -type MoveMouseResponseObject interface { - VisitMoveMouseResponse(w http.ResponseWriter) error +type WriteFileResponseObject interface { + VisitWriteFileResponse(w http.ResponseWriter) error } -type MoveMouse200Response struct { +type WriteFile201Response struct { } -func (response MoveMouse200Response) VisitMoveMouseResponse(w http.ResponseWriter) error { - w.WriteHeader(200) +func (response WriteFile201Response) VisitWriteFileResponse(w http.ResponseWriter) error { + w.WriteHeader(201) return nil } -type MoveMouse400JSONResponse struct{ BadRequestErrorJSONResponse } +type WriteFile400JSONResponse struct{ BadRequestErrorJSONResponse } + +func (response WriteFile400JSONResponse) VisitWriteFileResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type WriteFile404JSONResponse struct{ NotFoundErrorJSONResponse } -func (response MoveMouse400JSONResponse) VisitMoveMouseResponse(w http.ResponseWriter) error { +func (response WriteFile404JSONResponse) VisitWriteFileResponse(w http.ResponseWriter) error { w.Header().Set("Content-Type", "application/json") - w.WriteHeader(400) + w.WriteHeader(404) return json.NewEncoder(w).Encode(response) } -type MoveMouse500JSONResponse struct{ InternalErrorJSONResponse } +type WriteFile500JSONResponse struct{ InternalErrorJSONResponse } -func (response MoveMouse500JSONResponse) VisitMoveMouseResponse(w http.ResponseWriter) error { +func (response WriteFile500JSONResponse) VisitWriteFileResponse(w http.ResponseWriter) error { w.Header().Set("Content-Type", "application/json") w.WriteHeader(500) @@ -1910,6 +4803,42 @@ type StrictServerInterface interface { // Move the mouse cursor to the specified coordinates on the host computer // (POST /computer/move_mouse) MoveMouse(ctx context.Context, request MoveMouseRequestObject) (MoveMouseResponseObject, error) + // Create a new directory + // (PUT /fs/create_directory) + CreateDirectory(ctx context.Context, request CreateDirectoryRequestObject) (CreateDirectoryResponseObject, error) + // Delete a directory + // (PUT /fs/delete_directory) + DeleteDirectory(ctx context.Context, request DeleteDirectoryRequestObject) (DeleteDirectoryResponseObject, error) + // Delete a file + // (PUT /fs/delete_file) + DeleteFile(ctx context.Context, request DeleteFileRequestObject) (DeleteFileResponseObject, error) + // Get information about a file or directory + // (GET /fs/file_info) + FileInfo(ctx context.Context, request FileInfoRequestObject) (FileInfoResponseObject, error) + // List files in a directory + // (GET /fs/list_files) + ListFiles(ctx context.Context, request ListFilesRequestObject) (ListFilesResponseObject, error) + // Move or rename a file or directory + // (PUT /fs/move) + MovePath(ctx context.Context, request MovePathRequestObject) (MovePathResponseObject, error) + // Read file contents + // (GET /fs/read_file) + ReadFile(ctx context.Context, request ReadFileRequestObject) (ReadFileResponseObject, error) + // Set file or directory permissions/ownership + // (PUT /fs/set_file_permissions) + SetFilePermissions(ctx context.Context, request SetFilePermissionsRequestObject) (SetFilePermissionsResponseObject, error) + // Watch a directory for changes + // (POST /fs/watch) + StartFsWatch(ctx context.Context, request StartFsWatchRequestObject) (StartFsWatchResponseObject, error) + // Stop watching a directory + // (DELETE /fs/watch/{watch_id}) + StopFsWatch(ctx context.Context, request StopFsWatchRequestObject) (StopFsWatchResponseObject, error) + // Stream filesystem events for a watch + // (GET /fs/watch/{watch_id}/events) + StreamFsEvents(ctx context.Context, request StreamFsEventsRequestObject) (StreamFsEventsResponseObject, error) + // Write or create a file + // (PUT /fs/write_file) + WriteFile(ctx context.Context, request WriteFileRequestObject) (WriteFileResponseObject, error) // Delete a previously recorded video file // (POST /recording/delete) DeleteRecording(ctx context.Context, request DeleteRecordingRequestObject) (DeleteRecordingResponseObject, error) @@ -2018,6 +4947,350 @@ func (sh *strictHandler) MoveMouse(w http.ResponseWriter, r *http.Request) { } } +// CreateDirectory operation middleware +func (sh *strictHandler) CreateDirectory(w http.ResponseWriter, r *http.Request) { + var request CreateDirectoryRequestObject + + var body CreateDirectoryJSONRequestBody + 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.CreateDirectory(ctx, request.(CreateDirectoryRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "CreateDirectory") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(CreateDirectoryResponseObject); ok { + if err := validResponse.VisitCreateDirectoryResponse(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)) + } +} + +// DeleteDirectory operation middleware +func (sh *strictHandler) DeleteDirectory(w http.ResponseWriter, r *http.Request) { + var request DeleteDirectoryRequestObject + + var body DeleteDirectoryJSONRequestBody + 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.DeleteDirectory(ctx, request.(DeleteDirectoryRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "DeleteDirectory") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(DeleteDirectoryResponseObject); ok { + if err := validResponse.VisitDeleteDirectoryResponse(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)) + } +} + +// DeleteFile operation middleware +func (sh *strictHandler) DeleteFile(w http.ResponseWriter, r *http.Request) { + var request DeleteFileRequestObject + + var body DeleteFileJSONRequestBody + 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.DeleteFile(ctx, request.(DeleteFileRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "DeleteFile") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(DeleteFileResponseObject); ok { + if err := validResponse.VisitDeleteFileResponse(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)) + } +} + +// FileInfo operation middleware +func (sh *strictHandler) FileInfo(w http.ResponseWriter, r *http.Request, params FileInfoParams) { + var request FileInfoRequestObject + + request.Params = params + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.FileInfo(ctx, request.(FileInfoRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "FileInfo") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(FileInfoResponseObject); ok { + if err := validResponse.VisitFileInfoResponse(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)) + } +} + +// ListFiles operation middleware +func (sh *strictHandler) ListFiles(w http.ResponseWriter, r *http.Request, params ListFilesParams) { + var request ListFilesRequestObject + + request.Params = params + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.ListFiles(ctx, request.(ListFilesRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "ListFiles") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(ListFilesResponseObject); ok { + if err := validResponse.VisitListFilesResponse(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)) + } +} + +// MovePath operation middleware +func (sh *strictHandler) MovePath(w http.ResponseWriter, r *http.Request) { + var request MovePathRequestObject + + var body MovePathJSONRequestBody + 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.MovePath(ctx, request.(MovePathRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "MovePath") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(MovePathResponseObject); ok { + if err := validResponse.VisitMovePathResponse(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)) + } +} + +// ReadFile operation middleware +func (sh *strictHandler) ReadFile(w http.ResponseWriter, r *http.Request, params ReadFileParams) { + var request ReadFileRequestObject + + request.Params = params + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.ReadFile(ctx, request.(ReadFileRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "ReadFile") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(ReadFileResponseObject); ok { + if err := validResponse.VisitReadFileResponse(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)) + } +} + +// SetFilePermissions operation middleware +func (sh *strictHandler) SetFilePermissions(w http.ResponseWriter, r *http.Request) { + var request SetFilePermissionsRequestObject + + var body SetFilePermissionsJSONRequestBody + 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.SetFilePermissions(ctx, request.(SetFilePermissionsRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "SetFilePermissions") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(SetFilePermissionsResponseObject); ok { + if err := validResponse.VisitSetFilePermissionsResponse(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)) + } +} + +// StartFsWatch operation middleware +func (sh *strictHandler) StartFsWatch(w http.ResponseWriter, r *http.Request) { + var request StartFsWatchRequestObject + + var body StartFsWatchJSONRequestBody + 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.StartFsWatch(ctx, request.(StartFsWatchRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "StartFsWatch") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(StartFsWatchResponseObject); ok { + if err := validResponse.VisitStartFsWatchResponse(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)) + } +} + +// StopFsWatch operation middleware +func (sh *strictHandler) StopFsWatch(w http.ResponseWriter, r *http.Request, watchId string) { + var request StopFsWatchRequestObject + + request.WatchId = watchId + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.StopFsWatch(ctx, request.(StopFsWatchRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "StopFsWatch") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(StopFsWatchResponseObject); ok { + if err := validResponse.VisitStopFsWatchResponse(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)) + } +} + +// StreamFsEvents operation middleware +func (sh *strictHandler) StreamFsEvents(w http.ResponseWriter, r *http.Request, watchId string) { + var request StreamFsEventsRequestObject + + request.WatchId = watchId + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.StreamFsEvents(ctx, request.(StreamFsEventsRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "StreamFsEvents") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(StreamFsEventsResponseObject); ok { + if err := validResponse.VisitStreamFsEventsResponse(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)) + } +} + +// WriteFile operation middleware +func (sh *strictHandler) WriteFile(w http.ResponseWriter, r *http.Request, params WriteFileParams) { + var request WriteFileRequestObject + + request.Params = params + + request.Body = r.Body + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.WriteFile(ctx, request.(WriteFileRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "WriteFile") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(WriteFileResponseObject); ok { + if err := validResponse.VisitWriteFileResponse(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)) + } +} + // DeleteRecording operation middleware func (sh *strictHandler) DeleteRecording(w http.ResponseWriter, r *http.Request) { var request DeleteRecordingRequestObject @@ -2164,34 +5437,54 @@ func (sh *strictHandler) StopRecording(w http.ResponseWriter, r *http.Request) { // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "H4sIAAAAAAAC/9RZ3W/cuBH/Vwj2HlpU++E4fci+OUlTLFrfHewDem2QBlxxtOKFIpkhZVsx9n8vhtTu", - "Spa8/s7hnryiyJnh/ObjN/I1z23lrAETPF9ccwTvrPEQH94KeQZfa/Dh74gWaSm3JoAJ9FM4p1UugrJm", - "9pu3htZ8XkIl6NcPCAVf8D/N9vJn6a2fJWmbzSbjEnyOypEQviCFrNXINxl/Z02hVf69tG/VkeqlCYBG", - "6O+kequOnQNeALJ2Y8Z/tOGDrY38Tnb8aAOL+ji9a7eTtHda5V9Obe1hiw8ZIKWig0L/jNYBBkVxUwjt", - "IeOus3TNV3UIycK+wiiSpbcsWKbIESIP7FKFkmccTF3xxUeuoQg846jWJf2tlJQaeMZXIv/CM15YvBQo", - "+aeMh8YBX3AfUJk1uTAn0z+n5Zvqf2kcMFuwuIeJPC7vtUp7SY+1462YUQWl1fLzF2j82PWkKhQgo9d0", - "P9rLZE1HWSghKeYZVwGqeH4gvV0QiKKhZ1NXn+OpVl0hah344mgAZV2tAOlyQVUQlSM4EKGnt5VObl9D", - "jLir4S1+Zbm1KJURIXprJ4A561Xrs6GkZijpP4+RtMk4wtdaIUgC5YqT6D0QdvUbpKR9DxoCnEEeVawf", - "F6lKDs1eSjAhAdkajVsl5FcZ9U7ZiXalMHUFqHJmkZWNK8FMecadCJTgfMH/91FMvp1M/jufvJl8+usP", - "fBBPm5GL7bK/b2oF3os1jITNDZdtN4457dRewBMS+ynBX9kLeFDs3xWbwUaZKaxq9BZZsI+KzftKunds", - "pqgEXJrCDpEslFG+BPlZhJEiRfkbROXYZQmmE3rbU6kAVnSWSxFgQhnPqVRoLVYa+CJgDSOVK8X6cNnv", - "cqjzfmWtBmFogw8Cw0OtbQ890tgbjlYkp2vnmM/PSeMTy0GBogIUYaR5nO2B2G5iyrDCefZnewGISoJn", - "PnX0tlD/hZqXuFIVNZhXc+pkJj0cjYXpWDH6ySXTmdpXpYLis1eWPHivrHmmohSNfl9jJBtLcw65NXIs", - "5dPVOnbI9hB5xqdjd3jnoEMqcfVBaThX32BpTt/ebkGhNDCvvkVITt/eE5Gj+XzeA2U+mvIjkWbdUwPN", - "Yg4kp9fW26032lFVgVQigG6YD9ZFsmTrwNYocihqzXxZByIvU/ZLqTyrRMMQfK0DeUOw3CLWLoBkF0qC", - "jc6a7lHvZPpDuiFQjYwGvVgrpCXVVtCgAlUL/k9AA5otK7EGz05+XvKMXwD6ZOx8ejSd002sAyOc4gt+", - "PJ1Pj5MlZXR9pMh1AJwlrlhRO4xF2iYYCacU+pLmhB0X5qkogQ9vrWyejZ4PyfamX/+oQsaFzrD2aj6/", - "jV4nXsscIFVekOSO12n7mBk7sbObA+Am43+7z7n+9BRHibqqBDZ8wc9VVWsqlYJFP/e4N6MpoARWWh/Y", - "FpUoYI8R9eW7INqRmhdCaECangZQyzDoZr8vOKdbzlN17Qo2rnkHOaW97BAlfwCxXROYJYJ8O1w3iPsL", - "gXbLeLBpsbsLqn27T9d5ElKv56/vPtcf/p8D3+QCJphDuFC29rrZFu9uLxjgZy+NtiL2gjWMAdhu6ELo", - "BLGiAOj54uOtJGbXOvZsZsr+TczRVioEkFmKvdSza0+TbAnb5r07Tp1FkeCvNWBDdFJUsWsTT9yHx0Oa", - "z6fxoOiEYnTYrHKv+zG4o7crZUQ05qbowXeXDpVU8btGCUJGz13zXye7t5MPLeOfnBxk3rZI5LtPCbfj", - "wpT9oxYoTACQlNsrYGcf3h0fH7+Z9pw17MZdU84TnX+UJe0o8FhDyJRX81eHUlR55oPSmviOQ7tG8D5j", - "ToPwwAI2TKyFMowaEfbdfQYBm8lJQS8GCs7r9Ro8EadLoUL8stIltisoLNJFAzYpCfaXOMRr443+aKWk", - "Tfm2XfiYi2DC/SqKVqkPjFaTfykftvOy53em4eGOsPu6cKg19KbzwYeHYb6ShRTbuLPyOVwapQqtu2L7", - "bouJc3sf7Q+8L9RGx6fq0S56dChFt98DnhT6b+4+1/8vwrNQWLKcCeZzhO4njin7yeiGWdOtdQ6QLd+z", - "XBiqbwhr5QMgSCZIBFWQ6RDlNAXeBnJn1nwxjEfm2YcTJbqI+73nDRqQe+0nXuT/AQAA//9WTA/u+RoA", - "AA==", + "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==", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/server/openapi.yaml b/server/openapi.yaml index 6f9584e0..d56dbaa5 100644 --- a/server/openapi.yaml +++ b/server/openapi.yaml @@ -154,6 +154,297 @@ paths: $ref: "#/components/responses/BadRequestError" "500": $ref: "#/components/responses/InternalError" + # File system operations + /fs/read_file: + get: + summary: Read file contents + operationId: readFile + parameters: + - name: path + in: query + required: true + schema: + type: string + pattern: "^/.*" + description: Absolute file path to read. + responses: + "200": + description: File read successfully + content: + application/octet-stream: + schema: + type: string + format: binary + "400": + $ref: "#/components/responses/BadRequestError" + "404": + $ref: "#/components/responses/NotFoundError" + "500": + $ref: "#/components/responses/InternalError" + + /fs/write_file: + put: + summary: Write or create a file + operationId: writeFile + parameters: + - name: path + in: query + required: true + schema: + type: string + pattern: "^/.*" + description: Destination absolute file path. + - name: mode + in: query + required: false + schema: + type: string + pattern: "^[0-7]{3,4}$" + description: Optional file mode (octal string, e.g. 644). Defaults to 644. + requestBody: + required: true + content: + application/octet-stream: + schema: + type: string + format: binary + responses: + "201": + description: File written successfully + "400": + $ref: "#/components/responses/BadRequestError" + "404": + $ref: "#/components/responses/NotFoundError" + "500": + $ref: "#/components/responses/InternalError" + + /fs/list_files: + get: + summary: List files in a directory + operationId: listFiles + parameters: + - name: path + in: query + required: true + schema: + type: string + pattern: "^/.*" + description: Absolute directory path. + responses: + "200": + description: Directory listing + content: + application/json: + schema: + $ref: "#/components/schemas/ListFiles" + "400": + $ref: "#/components/responses/BadRequestError" + "404": + $ref: "#/components/responses/NotFoundError" + "500": + $ref: "#/components/responses/InternalError" + + /fs/create_directory: + put: + summary: Create a new directory + operationId: createDirectory + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/CreateDirectoryRequest" + responses: + "201": + description: Directory created successfully + "400": + $ref: "#/components/responses/BadRequestError" + "500": + $ref: "#/components/responses/InternalError" + + /fs/delete_file: + put: + summary: Delete a file + operationId: deleteFile + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/DeletePathRequest" + responses: + "200": + description: File deleted + "400": + $ref: "#/components/responses/BadRequestError" + "404": + $ref: "#/components/responses/NotFoundError" + "500": + $ref: "#/components/responses/InternalError" + + /fs/delete_directory: + put: + summary: Delete a directory + operationId: deleteDirectory + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/DeletePathRequest" + responses: + "200": + description: Directory deleted + "400": + $ref: "#/components/responses/BadRequestError" + "404": + $ref: "#/components/responses/NotFoundError" + "500": + $ref: "#/components/responses/InternalError" + + /fs/set_file_permissions: + put: + summary: Set file or directory permissions/ownership + operationId: setFilePermissions + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/SetFilePermissionsRequest" + responses: + "200": + description: Permissions updated + "400": + $ref: "#/components/responses/BadRequestError" + "404": + $ref: "#/components/responses/NotFoundError" + "500": + $ref: "#/components/responses/InternalError" + + /fs/file_info: + get: + summary: Get information about a file or directory + operationId: fileInfo + parameters: + - name: path + in: query + required: true + schema: + type: string + pattern: "^/.*" + description: Absolute path of the file or directory. + responses: + "200": + description: File information + content: + application/json: + schema: + $ref: "#/components/schemas/FileInfo" + "400": + $ref: "#/components/responses/BadRequestError" + "404": + $ref: "#/components/responses/NotFoundError" + "500": + $ref: "#/components/responses/InternalError" + + /fs/move: + put: + summary: Move or rename a file or directory + operationId: movePath + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/MovePathRequest" + responses: + "200": + description: Move successful + "400": + $ref: "#/components/responses/BadRequestError" + "404": + $ref: "#/components/responses/NotFoundError" + "500": + $ref: "#/components/responses/InternalError" + + /fs/watch: + post: + summary: Watch a directory for changes + operationId: startFsWatch + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/StartFsWatchRequest" + responses: + "201": + description: Watch started successfully + content: + application/json: + schema: + type: object + properties: + watch_id: + type: string + description: Unique identifier for the directory watch + "400": + $ref: "#/components/responses/BadRequestError" + "404": + $ref: "#/components/responses/NotFoundError" + "500": + $ref: "#/components/responses/InternalError" + + /fs/watch/{watch_id}/events: + get: + summary: Stream filesystem events for a watch + operationId: streamFsEvents + parameters: + - name: watch_id + in: path + required: true + schema: + type: string + responses: + "200": + description: SSE stream of filesystem events + headers: + X-SSE-Content-Type: + description: Media type of SSE data events (application/json) + schema: + type: string + const: application/json + content: + text/event-stream: + schema: + $ref: "#/components/schemas/FileSystemEvent" + "400": + $ref: "#/components/responses/BadRequestError" + "404": + $ref: "#/components/responses/NotFoundError" + "500": + $ref: "#/components/responses/InternalError" + + /fs/watch/{watch_id}: + delete: + summary: Stop watching a directory + operationId: stopFsWatch + parameters: + - name: watch_id + in: path + required: true + schema: + type: string + responses: + "204": + description: Watch stopped successfully + "400": + $ref: "#/components/responses/BadRequestError" + "404": + $ref: "#/components/responses/NotFoundError" + "500": + $ref: "#/components/responses/InternalError" components: schemas: StartRecordingRequest: @@ -262,6 +553,37 @@ components: items: type: string additionalProperties: false + StartFsWatchRequest: + type: object + required: + - path + properties: + path: + type: string + description: Directory to watch. + recursive: + type: boolean + description: Whether to watch recursively. + default: false + additionalProperties: false + FileSystemEvent: + type: object + description: Filesystem change event. + required: [type, path] + properties: + type: + type: string + enum: [CREATE, WRITE, DELETE, RENAME] + description: Event type. + name: + type: string + description: Base name of the file or directory affected. + path: + type: string + description: Absolute path of the file or directory. + is_dir: + type: boolean + description: Whether the affected path is a directory. DeleteRecordingRequest: type: object properties: @@ -270,6 +592,93 @@ components: description: Identifier of the recording to delete. Alphanumeric or hyphen. pattern: "^[a-zA-Z0-9-]+$" additionalProperties: false + FileInfo: + type: object + required: [name, path, size_bytes, is_dir, mod_time, mode] + properties: + name: + type: string + description: Base name of the file or directory. + path: + type: string + description: Absolute path. + size_bytes: + type: integer + description: Size in bytes. 0 for directories. + is_dir: + type: boolean + description: Whether the path is a directory. + mod_time: + type: string + format: date-time + description: Last modification time. + mode: + type: string + description: File mode bits (e.g., "drwxr-xr-x" or "-rw-r--r--"). + + ListFiles: + type: array + description: Array of file or directory information entries. + items: + $ref: "#/components/schemas/FileInfo" + + CreateDirectoryRequest: + type: object + required: [path] + properties: + path: + type: string + description: Absolute directory path to create. + pattern: "^/.*" + mode: + type: string + description: Optional directory mode (octal string, e.g. 755). Defaults to 755. + pattern: "^[0-7]{3,4}$" + additionalProperties: false + + DeletePathRequest: + type: object + required: [path] + properties: + path: + type: string + description: Absolute path to delete. + pattern: "^/.*" + additionalProperties: false + + SetFilePermissionsRequest: + type: object + required: [path, mode] + properties: + path: + type: string + description: Absolute path whose permissions are to be changed. + pattern: "^/.*" + owner: + type: string + description: New owner username or UID. + group: + type: string + description: New group name or GID. + mode: + type: string + description: File mode bits (octal string, e.g. 644). + pattern: "^[0-7]{3,4}$" + additionalProperties: false + + MovePathRequest: + type: object + required: [src_path, dest_path] + properties: + src_path: + type: string + description: Absolute source path. + pattern: "^/.*" + dest_path: + type: string + description: Absolute destination path. + pattern: "^/.*" + additionalProperties: false responses: BadRequestError: description: Bad Request diff --git a/server/scripts/oapi/patch_sse_methods.go b/server/scripts/oapi/patch_sse_methods.go new file mode 100644 index 00000000..50db1447 --- /dev/null +++ b/server/scripts/oapi/patch_sse_methods.go @@ -0,0 +1,83 @@ +package main + +import ( + "bytes" + "flag" + "fmt" + "log" + "os" +) + +const oldBlock = ` + 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() + } + _, err := io.Copy(w, response.Body) + return err` + +const newBlock = ` + 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 + } + }` + +func main() { + filePath := flag.String("file", "", "path to oapi.go to rewrite") + expectedReplacements := flag.Int("expected-replacements", 0, "expected number of SSE code blocks to replace") + flag.Parse() + + if *filePath == "" || *expectedReplacements <= 0 { + log.Fatal("usage: -file= -expected-replacements=") + } + path := *filePath + + data, err := os.ReadFile(path) + if err != nil { + log.Fatalf("fix_sse: failed to read %s: %v", path, err) + } + + // count occurrences of the old block before replacement + occurrences := bytes.Count(data, []byte(oldBlock)) + if occurrences != *expectedReplacements { + log.Fatalf("expected exactly %d SSE code blocks to replace, found %d in %s", *expectedReplacements, occurrences, path) + } + + updated := bytes.ReplaceAll(data, []byte(oldBlock), []byte(newBlock)) + if bytes.Equal(data, updated) { + log.Fatalf("no changes made to %s - expected to find and replace SSE code blocks", path) + } + + if err := os.WriteFile(path, updated, 0644); err != nil { + log.Fatalf("failed to write %s: %v", path, err) + } + fmt.Printf("✓ SSE flush fix applied to %s\n", path) +} diff --git a/shared/cdp-test/.gitignore b/shared/cdp-test/.gitignore index aed7c280..b0081399 100644 --- a/shared/cdp-test/.gitignore +++ b/shared/cdp-test/.gitignore @@ -1,2 +1,2 @@ -screenshot.png -screenshot-before.png +screenshot* +downloads/* diff --git a/shared/cdp-test/main.py b/shared/cdp-test/main.py index dfd35172..e4d720ed 100644 --- a/shared/cdp-test/main.py +++ b/shared/cdp-test/main.py @@ -3,18 +3,109 @@ import json import re import socket +import argparse from pathlib import Path from urllib.parse import urljoin, urlparse from urllib.request import urlopen, Request -from playwright.async_api import async_playwright, TimeoutError as PlaywrightTimeoutError # type: ignore +from playwright.async_api import Route, async_playwright, TimeoutError as PlaywrightTimeoutError # type: ignore import aiohttp # type: ignore import contextlib +from typing import Optional -async def run(cdp_url: str) -> None: - """Connect to an existing Chromium instance via CDP, navigate, and screenshot.""" +# -------------- helper functions -------------- # + +async def _click_and_await_download(page, client, click_func, timeout: int = 30): + """Trigger *click_func* and wait until the browser download completes. + + Returns the suggested filename or None if unavailable/timed out. + """ + download_completed = asyncio.Event() + download_filename: str | None = None + + def _on_download_begin(event): + nonlocal download_filename + download_filename = event.get("suggestedFilename", "unknown") + print(f"Download started: {download_filename}", file=sys.stderr) + + def _on_download_progress(event): + if event.get("state") in ["completed", "canceled"]: + download_completed.set() + + client.on("Browser.downloadWillBegin", _on_download_begin) + client.on("Browser.downloadProgress", _on_download_progress) + + # Trigger the click that initiates the download. + await click_func() + + try: + await asyncio.wait_for(download_completed.wait(), timeout=timeout) + print("Download completed", file=sys.stderr) + except asyncio.TimeoutError: + print(f"Download timed out after {timeout} seconds", file=sys.stderr) + finally: + # Best-effort removal of listeners (not critical). + try: + client.off("Browser.downloadWillBegin", _on_download_begin) # type: ignore[attr-defined] + client.off("Browser.downloadProgress", _on_download_progress) # type: ignore[attr-defined] + except Exception: + pass + + return download_filename + + +async def _fetch_remote_downloads(cdp_url: str, remote_dir: str = "/tmp/downloads", local_dir: str = "downloads") -> None: + """Fetch all files from *remote_dir* (over the filesystem API) and save them to *local_dir*.""" + parsed = urlparse(cdp_url) + fs_base_url = f"https://{parsed.hostname}:444" + + async with aiohttp.ClientSession() as session: + list_url = f"{fs_base_url}/fs/list_files?path={remote_dir}" + print(f"Listing files in {remote_dir} from {list_url}", file=sys.stderr) + + async with session.get(list_url) as resp: + if resp.status == 200: + files = await resp.json() + print(f"Found {len(files)} items in {remote_dir}", file=sys.stderr) + elif resp.status == 404: + print(f"{remote_dir} directory not found or empty", file=sys.stderr) + files = [] + else: + error_text = await resp.text() + print(f"Failed to list files: {resp.status} - {error_text}", file=sys.stderr) + files = [] + + local_download_path = Path(local_dir) + local_download_path.mkdir(exist_ok=True) + + for file_info in files: + if not file_info.get("is_dir", False): + file_path = file_info.get("path") + file_name = file_info.get("name") + + if file_path and file_name: + file_url = f"{fs_base_url}/fs/read_file?path={file_path}" + print(f"Fetching {file_name} from {file_url}", file=sys.stderr) + + async with session.get(file_url) as file_resp: + if file_resp.status == 200: + content = await file_resp.read() + local_file_path = local_download_path / file_name + with open(local_file_path, "wb") as f: + f.write(content) + print(f"Downloaded file saved to {local_file_path.resolve()}") + else: + error_text = await file_resp.text() + print(f"Failed to fetch {file_name}: {file_resp.status} - {error_text}", file=sys.stderr) + +async def run(cdp_url: str | None = None) -> None: + """Run browser automation either by connecting to existing instance or launching new one.""" async with async_playwright() as p: - # Connect to the running browser exposed via the CDP websocket URL. - browser = await p.chromium.connect_over_cdp(cdp_url) + if cdp_url: + # Connect to the running browser exposed via the CDP websocket URL. + browser = await p.chromium.connect_over_cdp(cdp_url) + else: + # Launch a new browser instance locally with GUI + browser = await p.chromium.launch(headless=False) # Re-use the first context if present, otherwise create a fresh one. if browser.contexts: @@ -25,6 +116,34 @@ async def run(cdp_url: str) -> None: # Re-use the first page if present, otherwise create a fresh one. page = context.pages[0] if context.pages else await context.new_page() + # Get CDP client from page + client = await page.context.new_cdp_session(page) + + # Configure download behavior + await client.send( + "Browser.setDownloadBehavior", + { + "behavior": "allow", + "downloadPath": "/tmp/downloads", + "eventsEnabled": True, + }, + ) + print("Set download behavior via CDP", file=sys.stderr) + + # Intercept PDF requests to ensure they're downloaded as attachments + async def handle_pdf_route(route: Route): + response = await route.fetch() + headers = response.headers + headers['content-disposition'] = 'attachment' + await route.fulfill( + response=response, + headers=headers + ) + + # Set up route interception for PDF files + await page.route('**/*.pdf', handle_pdf_route) + print("Set up PDF download interception", file=sys.stderr) + # Snapshot the page as-is for debugging purposes. print(f"Page URL: {page.url}") print(f"Taking screenshot before navigation") @@ -61,6 +180,122 @@ async def run(cdp_url: str) -> None: await page.screenshot(path=str(out_path), full_page=True) print(f"Screenshot saved to {out_path.resolve()}") + # Navigate to the test page + print(f"Navigating to download test page...", file=sys.stderr) + try: + await page.goto("https://browser-tests-alpha.vercel.app/api/download-test", wait_until="domcontentloaded", timeout=30_000) + + # Wait for network to settle + try: + await page.wait_for_load_state("networkidle", timeout=10_000) + except PlaywrightTimeoutError: + print("networkidle state not reached within 10 s – proceeding", file=sys.stderr) + + # Trigger the first test download via helper + await _click_and_await_download( + page, + client, + lambda: page.click("#download"), + ) + + # Fetch the downloaded files from the filesystem API (remote mode only) + if cdp_url: + await _fetch_remote_downloads(cdp_url) + + # --------------- Second download: IRS Form 1040 --------------- # + print("Navigating to IRS Forms & Instructions page...", file=sys.stderr) + try: + await page.goto("https://www.irs.gov/forms-instructions", wait_until="domcontentloaded", timeout=30_000) + try: + await page.wait_for_load_state("networkidle", timeout=10_000) + except PlaywrightTimeoutError: + print("networkidle state not reached within 10 s – proceeding", file=sys.stderr) + + print("Looking for Form 1040 PDF link and triggering download…", file=sys.stderr) + await _click_and_await_download( + page, + client, + lambda: page.click('a[href*="f1040.pdf"]'), + ) + + # Fetch again to get the newly downloaded file + if cdp_url: + await _fetch_remote_downloads(cdp_url) + + except PlaywrightTimeoutError: + print("Navigation to IRS Forms & Instructions timed out", file=sys.stderr) + await page.screenshot(path="screenshot-irs-error.png", full_page=True) + except Exception as e: + print(f"Error during IRS download test: {e}", file=sys.stderr) + await page.screenshot(path="screenshot-irs-error.png", full_page=True) + + except PlaywrightTimeoutError: + print(f"Navigation to download test page timed out", file=sys.stderr) + await page.screenshot(path="screenshot-download-error.png", full_page=True) + except Exception as e: + print(f"Error during download test: {e}", file=sys.stderr) + await page.screenshot(path="screenshot-download-error.png", full_page=True) + + # --------------- Upload test --------------- # + print(f"Navigating to upload test page...", file=sys.stderr) + try: + await page.goto("https://browser-tests-alpha.vercel.app/api/upload-test", wait_until="domcontentloaded", timeout=30_000) + try: + await page.wait_for_load_state("networkidle", timeout=10_000) + except PlaywrightTimeoutError: + print("networkidle state not reached within 10 s – proceeding", file=sys.stderr) + + # Determine file path to upload + local_file_path = Path("downloads") / "sandstorm.mp3" + if not local_file_path.exists(): + raise FileNotFoundError(f"File {local_file_path} not found") + + print(f"Uploading {local_file_path} …", file=sys.stderr) + file_input = page.locator("#fileUpload") + await file_input.set_input_files(str(local_file_path)) + print("Upload completed", file=sys.stderr) + + # --------------- Second upload using CDP (remote path) --------------- # + print("Navigating to upload test page for CDP upload...", file=sys.stderr) + try: + await page.goto("https://browser-tests-alpha.vercel.app/api/upload-test", wait_until="domcontentloaded", timeout=30_000) + try: + await page.wait_for_load_state("networkidle", timeout=10_000) + except PlaywrightTimeoutError: + print("networkidle state not reached within 10 s – proceeding", file=sys.stderr) + + # Use CDP commands to set the remote file path in the file input + root = await client.send("DOM.getDocument", {"depth": 1, "pierce": True}) + input_node = await client.send("DOM.querySelector", { + "nodeId": root["root"]["nodeId"], + "selector": "#fileUpload" + }) + + remote_file_path = "/tmp/downloads/f1040.pdf" + await client.send("DOM.setFileInputFiles", { + "files": [remote_file_path], + "nodeId": input_node["nodeId"] + }) + + # Wait until the file input's value is non-empty + await page.wait_for_function("document.querySelector('#fileUpload').value.includes('f1040.pdf')", timeout=10_000) + uploaded_value = await page.evaluate("document.querySelector('#fileUpload').value") + print(f"CDP upload completed, input value: {uploaded_value}", file=sys.stderr) + + except PlaywrightTimeoutError: + print("Navigation to upload test page (CDP) timed out", file=sys.stderr) + await page.screenshot(path="screenshot-upload-cdp-error.png", full_page=True) + except Exception as e: + print(f"Error during CDP upload test: {e}", file=sys.stderr) + await page.screenshot(path="screenshot-upload-cdp-error.png", full_page=True) + + except PlaywrightTimeoutError: + print("Navigation to upload test page timed out", file=sys.stderr) + await page.screenshot(path="screenshot-upload-error.png", full_page=True) + except Exception as e: + print(f"Error during upload test: {e}", file=sys.stderr) + await page.screenshot(path="screenshot-upload-error.png", full_page=True) + await browser.close() @@ -69,17 +304,15 @@ async def run(cdp_url: str) -> None: def _resolve_cdp_url(arg: str) -> str: """Resolve the provided argument to a CDP websocket URL. - If *arg* already looks like a ws:// or wss:// URL, return it unchanged. - Otherwise, treat it as a DevTools HTTP endpoint (e.g. http://localhost:9222 - or just localhost:9222), fetch /json/version, and extract the + Treat arg as a url, fetch :9222/json/version, and extract the 'webSocketDebuggerUrl'. """ # Ensure scheme. Default to http:// if none supplied. if not arg.startswith(("http://", "https://")): - arg = f"http://{arg}" + raise ValueError(f"Expected a url, got {arg}") - version_url = urljoin(arg.rstrip("/") + "/", "json/version") + version_url = urljoin(arg.rstrip("/") + ":9222/", "json/version") try: # Chromium devtools HTTP server only accepts Host headers that are an @@ -138,27 +371,125 @@ async def _keep_alive(endpoint: str) -> None: await asyncio.sleep(1) -async def _async_main(endpoint_arg: str) -> None: - """Resolve CDP URL, start keep-alive task, and run the browser automation.""" +async def _watch_filesystem(endpoint: str) -> None: + """Watch the filesystem and print events.""" + parsed = urlparse(endpoint) + fs_api_url = f"https://{parsed.hostname}:444" + print(f"Filesystem API URL: {fs_api_url}", file=sys.stderr) + + watch_id: Optional[str] = None + + async with aiohttp.ClientSession() as session: + try: + # Ensure /tmp/downloads exists on the remote filesystem + print(f"Ensuring /tmp/downloads exists via {fs_api_url}/fs/create_directory", file=sys.stderr) + async with session.put( + f"{fs_api_url}/fs/create_directory", + json={"path": "/tmp/downloads", "recursive": True}, + ) as resp: + if resp.status != 201: + text = await resp.text() + print(f"Failed to create directory /tmp/downloads: {resp.status} - {text}", file=sys.stderr) - cdp_url = _resolve_cdp_url(endpoint_arg) + # Start watching the root directory + print(f"Starting filesystem watch on /tmp/downloads at {fs_api_url}/fs/watch", file=sys.stderr) + async with session.put( + f"{fs_api_url}/fs/watch", + json={"path": "/tmp/downloads", "recursive": True} + ) as resp: + if resp.status != 201: + text = await resp.text() + print(f"Failed to start filesystem watch: {resp.status} - {text}", file=sys.stderr) + return + + data = await resp.json() + watch_id = data.get("watch_id") + print(f"Filesystem watch started with ID: {watch_id}", file=sys.stderr) + + # Stream events + print(f"Streaming filesystem events from {fs_api_url}/fs/watch/{watch_id}/events", file=sys.stderr) + async with session.get( + f"{fs_api_url}/fs/watch/{watch_id}/events", + headers={"Accept": "text/event-stream"} + ) as resp: + if resp.status != 200: + text = await resp.text() + print(f"Failed to stream filesystem events: {resp.status} - {text}", file=sys.stderr) + return + + # Process SSE stream + async for line in resp.content: + line_str = line.decode('utf-8').strip() + if line_str.startswith("data: "): + try: + event_data = json.loads(line_str[6:]) # Skip "data: " prefix + print(f"FS Event: {event_data}", file=sys.stderr) + except json.JSONDecodeError: + print(f"Failed to parse event: {line_str}", file=sys.stderr) + + except Exception as exc: + print(f"Filesystem watch error: {exc}", file=sys.stderr) + finally: + # Clean up the watch if it was created + if watch_id: + try: + print(f"Cleaning up filesystem watch {watch_id}", file=sys.stderr) + async with session.delete(f"{fs_api_url}/fs/watch/{watch_id}") as resp: + if resp.status == 204 or resp.status == 200: + print(f"Filesystem watch {watch_id} cleaned up successfully", file=sys.stderr) + else: + text = await resp.text() + print(f"Failed to clean up watch: {resp.status} - {text}", file=sys.stderr) + except Exception as cleanup_exc: + print(f"Error during watch cleanup: {cleanup_exc}", file=sys.stderr) - # Start the keep-alive loop. - keep_alive_task = asyncio.create_task(_keep_alive(endpoint_arg)) - try: - await run(cdp_url) - finally: - # Ensure the keep-alive task is cancelled cleanly when run() completes. - keep_alive_task.cancel() - with contextlib.suppress(asyncio.CancelledError): - await keep_alive_task +async def _async_main(endpoint_arg: str | None, local: bool) -> None: + """Run browser automation either locally or via CDP connection.""" + + if local: + # Run locally without CDP connection or keep-alive + await run() + else: + # Resolve CDP URL and use keep-alive for remote connection + cdp_url = _resolve_cdp_url(endpoint_arg) + + # Start the keep-alive loop. + keep_alive_task = asyncio.create_task(_keep_alive(endpoint_arg)) + + # Start the filesystem watch task + fs_watch_task = asyncio.create_task(_watch_filesystem(endpoint_arg)) + + try: + await run(cdp_url) + finally: + # Ensure the keep-alive task is cancelled cleanly when run() completes. + keep_alive_task.cancel() + with contextlib.suppress(asyncio.CancelledError): + await keep_alive_task + + # Cancel the filesystem watch task + fs_watch_task.cancel() + with contextlib.suppress(asyncio.CancelledError): + await fs_watch_task def main() -> None: - if len(sys.argv) < 2: - print("Usage: python main.py ", file=sys.stderr) - sys.exit(1) - asyncio.run(_async_main(sys.argv[1])) + parser = argparse.ArgumentParser( + description="Browser automation script with CDP or local Chromium", + epilog="Examples:\n" + " CDP mode: python main.py localhost\n" + " or python main.py https://url-of-remote-instance.com" + " Local mode: python main.py --local", + formatter_class=argparse.RawDescriptionHelpFormatter + ) + parser.add_argument("endpoint", nargs="?", help="HTTP endpoint (e.g., localhost). Assumed to be running the devtools protocol on 9222 and the filesystem API on 444.") + parser.add_argument("--local", action="store_true", help="Launch Chromium locally with GUI instead of connecting via CDP") + args = parser.parse_args() + + if not args.local and not args.endpoint: + parser.error("Either provide an endpoint or use --local flag") + + asyncio.run(_async_main(args.endpoint, args.local)) if __name__ == "__main__": main() diff --git a/shared/ensure-common-build-run-vars.sh b/shared/ensure-common-build-run-vars.sh new file mode 100644 index 00000000..404c6da6 --- /dev/null +++ b/shared/ensure-common-build-run-vars.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +set -e -o pipefail + +IMAGE_TYPE=$1 +if [ -z "$IMAGE_TYPE" ]; then + echo "Usage: source ensure-common-build-run-vars.sh " + echo "e.g. source ensure-common-build-run-vars.sh chromium-headful" + echo "This will set the defaults for the image name and test instance name" + echo "You can override the defaults by setting the IMAGE and NAME variables" + return 1 +fi +IMAGE="${IMAGE:-onkernel/${IMAGE_TYPE}-test:latest}" +NAME="${NAME:-${IMAGE_TYPE}-test}" + +UKC_INDEX="${UKC_INDEX:-index.unikraft.io}" + +# fail if UKC_TOKEN, UKC_METRO are not set +errormsg="" +for var in UKC_TOKEN UKC_METRO; do + if [ -z "${!var}" ]; then + errormsg+="$var " + fi +done +if [ -n "$errormsg" ]; then + echo "Required variables not set: $errormsg" + exit 1 +fi