From 7b3149523e42767467041ee3639cbd83b50f7894 Mon Sep 17 00:00:00 2001 From: Ilyas Salikhov Date: Thu, 3 Sep 2026 15:28:42 +0300 Subject: [PATCH] Add Go SDK with CI and release support --- .github/workflows/ci.yml | 60 + .github/workflows/release.yml | 27 + .tool-versions | 1 + CLAUDE.md | 17 +- CONTRIBUTING.md | 4 +- LICENSE | 1 + Makefile | 44 +- README.md | 42 +- RELEASING.md | 40 +- codegen.Dockerfile | 12 +- packages/go-sdk/GO_PARITY.md | 25 + packages/go-sdk/LICENSE | 201 + packages/go-sdk/README.md | 63 + packages/go-sdk/client.go | 38 + packages/go-sdk/codeinterpreter/client.go | 254 + .../go-sdk/codeinterpreter/client_test.go | 233 + packages/go-sdk/codeinterpreter/models.go | 200 + packages/go-sdk/commands.go | 527 ++ packages/go-sdk/config.go | 193 + packages/go-sdk/config_test.go | 224 + packages/go-sdk/doc.go | 17 + packages/go-sdk/envd_test.go | 672 ++ packages/go-sdk/errors.go | 89 + packages/go-sdk/examples_test.go | 26 + packages/go-sdk/filesystem.go | 480 ++ packages/go-sdk/go.mod | 14 + packages/go-sdk/go.sum | 29 + packages/go-sdk/iam.go | 38 + packages/go-sdk/integration/sdk_test.go | 87 + .../go-sdk/internal/gen/api/client.gen.go | 6252 +++++++++++++++++ .../go-sdk/internal/gen/api/oapi-codegen.yaml | 7 + .../gen/envd/filesystem/filesystem.pb.go | 1518 ++++ .../filesystemconnect/filesystem.connect.go | 337 + .../internal/gen/envd/process/process.pb.go | 1970 ++++++ .../process/processconnect/process.connect.go | 310 + .../go-sdk/internal/gen/envdapi/client.gen.go | 1981 ++++++ .../internal/gen/envdapi/oapi-codegen.yaml | 7 + packages/go-sdk/models.go | 223 + packages/go-sdk/pty.go | 101 + packages/go-sdk/sandbox.go | 758 ++ packages/go-sdk/sandbox_test.go | 244 + packages/go-sdk/scripts/check-go-coverage.sh | 17 + packages/go-sdk/scripts/check-go-format.sh | 8 + packages/go-sdk/scripts/filter-go-envd.py | 14 + packages/go-sdk/scripts/test-go-consumer.sh | 14 + packages/go-sdk/template.go | 906 +++ packages/go-sdk/template_archive.go | 183 + packages/go-sdk/template_test.go | 281 + .../go-sdk/tests/consumer/consumer_test.go | 23 + packages/go-sdk/transport.go | 193 + packages/go-sdk/version.go | 4 + redocly.yaml | 11 + scripts/check-release-versions.mjs | 12 + scripts/set-release-version.mjs | 13 + scripts/test-published-runtime.sh | 9 + scripts/test-release-runtime.sh | 9 + spec/envd/buf-go.gen.yaml | 16 + tests/runtime/go/main.go | 45 + 58 files changed, 19091 insertions(+), 33 deletions(-) create mode 100644 packages/go-sdk/GO_PARITY.md create mode 100644 packages/go-sdk/LICENSE create mode 100644 packages/go-sdk/README.md create mode 100644 packages/go-sdk/client.go create mode 100644 packages/go-sdk/codeinterpreter/client.go create mode 100644 packages/go-sdk/codeinterpreter/client_test.go create mode 100644 packages/go-sdk/codeinterpreter/models.go create mode 100644 packages/go-sdk/commands.go create mode 100644 packages/go-sdk/config.go create mode 100644 packages/go-sdk/config_test.go create mode 100644 packages/go-sdk/doc.go create mode 100644 packages/go-sdk/envd_test.go create mode 100644 packages/go-sdk/errors.go create mode 100644 packages/go-sdk/examples_test.go create mode 100644 packages/go-sdk/filesystem.go create mode 100644 packages/go-sdk/go.mod create mode 100644 packages/go-sdk/go.sum create mode 100644 packages/go-sdk/iam.go create mode 100644 packages/go-sdk/integration/sdk_test.go create mode 100644 packages/go-sdk/internal/gen/api/client.gen.go create mode 100644 packages/go-sdk/internal/gen/api/oapi-codegen.yaml create mode 100644 packages/go-sdk/internal/gen/envd/filesystem/filesystem.pb.go create mode 100644 packages/go-sdk/internal/gen/envd/filesystem/filesystemconnect/filesystem.connect.go create mode 100644 packages/go-sdk/internal/gen/envd/process/process.pb.go create mode 100644 packages/go-sdk/internal/gen/envd/process/processconnect/process.connect.go create mode 100644 packages/go-sdk/internal/gen/envdapi/client.gen.go create mode 100644 packages/go-sdk/internal/gen/envdapi/oapi-codegen.yaml create mode 100644 packages/go-sdk/models.go create mode 100644 packages/go-sdk/pty.go create mode 100644 packages/go-sdk/sandbox.go create mode 100644 packages/go-sdk/sandbox_test.go create mode 100755 packages/go-sdk/scripts/check-go-coverage.sh create mode 100755 packages/go-sdk/scripts/check-go-format.sh create mode 100755 packages/go-sdk/scripts/filter-go-envd.py create mode 100755 packages/go-sdk/scripts/test-go-consumer.sh create mode 100644 packages/go-sdk/template.go create mode 100644 packages/go-sdk/template_archive.go create mode 100644 packages/go-sdk/template_test.go create mode 100644 packages/go-sdk/tests/consumer/consumer_test.go create mode 100644 packages/go-sdk/transport.go create mode 100644 packages/go-sdk/version.go create mode 100644 spec/envd/buf-go.gen.yaml create mode 100644 tests/runtime/go/main.go diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 86e607c31..769fae4c5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,6 +32,11 @@ jobs: - uses: actions/setup-python@v5 with: python-version: '3.12' + - uses: actions/setup-go@v6 + with: + go-version: '1.27.x' + cache: true + cache-dependency-path: packages/go-sdk/go.sum - run: pnpm install --frozen-lockfile - run: uv sync --project packages/python-sdk --frozen - run: uv sync --project packages/code-interpreter-python --frozen @@ -39,6 +44,56 @@ jobs: - run: pnpm lint - run: pnpm typecheck - run: pnpm test + - run: go mod tidy && git diff --exit-code -- go.mod go.sum + working-directory: packages/go-sdk + - run: make check-agent-instructions go-format-check go-vet go-coverage go-consumer-check + + go-test: + runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + go: ['1.24.x', '1.25.x', '1.26.x', '1.27.x'] + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v6 + with: + go-version: ${{ matrix.go }} + cache: true + cache-dependency-path: packages/go-sdk/go.sum + - run: go build ./... + working-directory: packages/go-sdk + - run: go test ./... + working-directory: packages/go-sdk + + go-race: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v6 + with: + go-version: '1.27.x' + cache: true + cache-dependency-path: packages/go-sdk/go.sum + - run: go test -race ./... + working-directory: packages/go-sdk + + go-cross-build: + runs-on: ubuntu-24.04 + strategy: + fail-fast: false + matrix: + go: ['1.24.x', '1.27.x'] + goos: [linux, darwin, windows] + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v6 + with: + go-version: ${{ matrix.go }} + cache: true + cache-dependency-path: packages/go-sdk/go.sum + - run: GOOS=${{ matrix.goos }} GOARCH=amd64 CGO_ENABLED=0 go build ./... + working-directory: packages/go-sdk deterministic-codegen: runs-on: ubuntu-24.04 @@ -51,6 +106,11 @@ jobs: with: node-version: 22 cache: pnpm + - uses: actions/setup-go@v6 + with: + go-version: '1.24.x' + cache: true + cache-dependency-path: packages/go-sdk/go.sum - run: pnpm install --frozen-lockfile - run: make generate - run: git diff --exit-code diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4399f1554..b693ee795 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -26,10 +26,23 @@ jobs: - uses: actions/setup-python@v5 with: python-version: '3.12' + - uses: actions/setup-go@v6 + with: + go-version: '1.24.13' + cache: true + cache-dependency-path: packages/go-sdk/go.sum - run: pnpm install --frozen-lockfile - run: uv sync --project packages/python-sdk --frozen - run: uv sync --project packages/code-interpreter-python --frozen - run: node scripts/check-release-versions.mjs "${GITHUB_REF_NAME}" + - name: Verify matching Go module tag + run: | + go_tag="packages/go-sdk/${GITHUB_REF_NAME}" + go_commit=$(git ls-remote origin "refs/tags/${go_tag}^{}" | cut -f1) + test "$go_commit" = "$GITHUB_SHA" + - run: go test ./... + working-directory: packages/go-sdk + - run: make go-consumer-check - run: ./scripts/build-release.sh - run: node scripts/check-public-artifacts.mjs release - run: ./scripts/test-release-artifacts.sh @@ -122,3 +135,17 @@ jobs: release/npm/*.tgz release/pypi/* release/pypi-code-interpreter/* + + go-proxy-smoke: + needs: github-release + runs-on: ubuntu-24.04 + steps: + - uses: actions/setup-go@v6 + with: + go-version: '1.24.13' + - run: | + mkdir consumer + cd consumer + go mod init example.com/agentbox-release-smoke + GOPROXY=https://proxy.golang.org go get "github.com/abox-dev/sdk/packages/go-sdk@${GITHUB_REF_NAME}" + go test github.com/abox-dev/sdk/packages/go-sdk github.com/abox-dev/sdk/packages/go-sdk/codeinterpreter diff --git a/.tool-versions b/.tool-versions index 8cb195538..162309762 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,4 +1,5 @@ deno 2.8.1 +golang 1.24.13 nodejs 22.18.0 pnpm 10.34.5 python 3.10 diff --git a/CLAUDE.md b/CLAUDE.md index aa329e173..b67c570c8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,8 +1,17 @@ -Use pnpm for JavaScript packages and uv for Python packages. +Use pnpm for JavaScript packages, uv for Python packages, and Go modules for Go packages. Use English exclusively in source code, comments, documentation, commit messages, and GitHub pull request titles and descriptions. -Keep the JavaScript and Python SDKs, including sync and async Python APIs, behaviorally aligned. -Run format checks, lint, type checks, unit tests, deterministic generation, builds, and package-install checks before committing. +Keep the JavaScript, Python sync/async, and Go SDKs behaviorally aligned, including their Code Interpreter APIs. +Use only Go syntax and runtime dependencies compatible with the `go` directive in `packages/go-sdk/go.mod`. +Run format checks, lint, type checks, unit tests, deterministic generation, builds, package-install checks, and the Go race and coverage checks before committing. Handwritten Go code must keep at least 90% statement coverage; generated packages are excluded from the threshold. The API and envd snapshots under spec/ are generated from mono/infra. Do not edit them manually. Update them with `make sync-specs MONO_DIR=/path/to/mono`, then run `make generate`. -Generated clients must depend only on checked-in snapshots and never fetch network content during generation. +Generated clients must depend only on checked-in snapshots and never fetch network content during generation. Do not edit generated Go files under `packages/go-sdk/internal/gen` manually. Public APIs, package artifacts, examples, errors, environment variables, and headers must use AgentBox naming. Upstream names are allowed only in licenses, attribution, pinned build-only codegen tooling, and wire/protobuf namespaces that are required by the runtime protocol. Default development credentials may be stored in `.env.local` or `~/.agentbox/config.json`; never print or commit them. + +When a new Go version is released: + +- Treat the `packages/go-sdk/go.mod` directive as the only source of the minimum supported Go version. Raise it only when intentionally ending support for an older release. +- Update the exact local patch version in `.tool-versions` and the Go builder in `codegen.Dockerfile`; generated code must still compile with the minimum version from `packages/go-sdk/go.mod`. +- Keep `.github/workflows/ci.yml` on a continuous matrix of every Go minor release from the `packages/go-sdk/go.mod` minimum through the current stable release. Add new minors when released; remove old minors only when raising the minimum. +- Update the Go toolchain used by `.github/workflows/release.yml` and the supported-version instructions in `RELEASING.md`. +- For a patch release, update exact tooling pins. For a minor release, add the CI matrix entry. When raising the minimum, update `packages/go-sdk/go.mod`, all pins, CI, and documentation in the same change. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4a8ff3f7c..8980f29cf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,7 @@ # Contributing -Open an issue or pull request at [abox-dev/sdk](https://github.com/abox-dev/sdk). Include tests for behavior changes and keep JavaScript and Python APIs aligned where applicable. +Open an issue or pull request at [abox-dev/sdk](https://github.com/abox-dev/sdk). Include tests for behavior changes and keep JavaScript, Python sync/async, and Go APIs aligned where applicable. + +For Go changes, run `make go-check`. Generated clients under `packages/go-sdk/internal/gen` must be regenerated with `make generate` and must not be edited manually. `packages/go-sdk/go.mod` defines the minimum supported Go version; CI also tests every newer supported minor listed in `RELEASING.md`. Use the development and generation commands documented in the root README. By contributing, you agree that your contribution is licensed under the license applicable to the package you modify. diff --git a/LICENSE b/LICENSE index ec47fef19..d10f99038 100644 --- a/LICENSE +++ b/LICENSE @@ -187,6 +187,7 @@ identification within third-party archives. Copyright 2023 FoundryLabs, Inc. + Copyright 2026 RetailDriver LLC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/Makefile b/Makefile index 1d8dde0df..62e85b57b 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,8 @@ CODEGEN_IMAGE ?= agentbox-sdk-codegen -.PHONY: generate generate-in-container codegen-image sync-specs +.PHONY: generate generate-in-container generate-go codegen-image sync-specs \ + go-format-check go-vet go-build go-test go-race go-coverage \ + go-integration go-consumer-check go-check check-agent-instructions # Generate exclusively from the checked-in snapshots under spec/. generate: codegen-image @@ -12,9 +14,49 @@ codegen-image: generate-in-container: cd packages/js-sdk && pnpm generate cd packages/python-sdk && make generate + $(MAKE) generate-go python scripts/generate-reference.py python scripts/test-reference-contract.py +generate-go: + redocly bundle go-sdk --config redocly.yaml -o spec/openapi_generated.go-sdk.yml + python scripts/filter-public-openapi.py spec/openapi_generated.go-sdk.yml + oapi-codegen --config packages/go-sdk/internal/gen/api/oapi-codegen.yaml spec/openapi_generated.go-sdk.yml + redocly bundle envd --config redocly.yaml -o spec/openapi_generated.go-envd.yml + python packages/go-sdk/scripts/filter-go-envd.py spec/openapi_generated.go-envd.yml + oapi-codegen --config packages/go-sdk/internal/gen/envdapi/oapi-codegen.yaml spec/openapi_generated.go-envd.yml + cd spec/envd && buf generate --template buf-go.gen.yaml + gofmt -w packages/go-sdk/internal/gen + +check-agent-instructions: + cmp -s AGENTS.md CLAUDE.md + +go-format-check: + cd packages/go-sdk && ./scripts/check-go-format.sh + +go-vet: + cd packages/go-sdk && go vet ./... + +go-build: + cd packages/go-sdk && go build ./... + +go-test: + cd packages/go-sdk && go test ./... + +go-race: + cd packages/go-sdk && go test -race ./... + +go-coverage: + cd packages/go-sdk && ./scripts/check-go-coverage.sh + +go-integration: + cd packages/go-sdk && go test -tags=integration ./... + +go-consumer-check: + cd packages/go-sdk && ./scripts/test-go-consumer.sh + +go-check: check-agent-instructions go-format-check go-vet go-build go-test go-race go-coverage go-consumer-check + # Maintainer-only update from a local mono checkout. sync-specs: @test -n "$(MONO_DIR)" || (echo "Usage: make sync-specs MONO_DIR=/path/to/mono" >&2; exit 2) diff --git a/README.md b/README.md index 4b6a5b0a9..45813849b 100644 --- a/README.md +++ b/README.md @@ -6,15 +6,17 @@ # AgentBox SDK -Official JavaScript, Python, and CLI clients for running isolated AgentBox sandboxes and code interpreters. - -| Package | Install | Import | -| --------------------------- | ---------------------------------------- | ---------------------------- | -| JavaScript SDK | `npm install @abox-dev/sdk` | `@abox-dev/sdk` | -| Python SDK | `pip install abox-sdk` | `agentbox` | -| JavaScript Code Interpreter | `npm install @abox-dev/code-interpreter` | `@abox-dev/code-interpreter` | -| Python Code Interpreter | `pip install abox-code-interpreter` | `agentbox_code_interpreter` | -| CLI | `npm install --global @abox-dev/cli` | `agentbox` | +Official Go, JavaScript, Python, and CLI clients for running isolated AgentBox sandboxes and code interpreters. + +| Package | Install | Import | +| --------------------------- |------------------------------------------------------------------|-------------------------------------------| +| JavaScript SDK | `npm install @abox-dev/sdk` | `@abox-dev/sdk` | +| Python SDK | `pip install abox-sdk` | `agentbox` | +| Go SDK | `go get github.com/abox-dev/sdk/packages/go-sdk` | `github.com/abox-dev/sdk/packages/go-sdk` | +| JavaScript Code Interpreter | `npm install @abox-dev/code-interpreter` | `@abox-dev/code-interpreter` | +| Python Code Interpreter | `pip install abox-code-interpreter` | `agentbox_code_interpreter` | +| Go Code Interpreter | `go get github.com/abox-dev/sdk/packages/go-sdk/codeinterpreter` | `github.com/abox-dev/sdk/packages/go-sdk/codeinterpreter` | +| CLI | `npm install --global @abox-dev/cli` | `agentbox` | ## Quick start @@ -48,9 +50,29 @@ with Sandbox.create() as sandbox: print(result.stdout) ``` +Go: + +```go +client, err := agentbox.NewClient() +if err != nil { + log.Fatal(err) +} +sandbox, err := client.Sandboxes.Create(context.Background(), nil) +if err != nil { + log.Fatal(err) +} +defer sandbox.Kill(context.Background()) + +result, err := sandbox.Commands.Run(context.Background(), "echo", &agentbox.CommandOptions{Args: []string{"Hello from AgentBox"}}) +if err != nil { + log.Fatal(err) +} +fmt.Print(string(result.Stdout)) +``` + ## Configuration -The SDKs use `AGENTBOX_API_KEY` and optionally `AGENTBOX_PROJECT_ID`, `AGENTBOX_DOMAIN`, `AGENTBOX_API_URL`, and `AGENTBOX_SANDBOX_URL`. The production defaults are `agentbox-runtime.ru`, `api.agentbox-runtime.ru`, and `sandbox.agentbox-runtime.ru`. +The SDKs use `AGENTBOX_API_KEY` and optionally `AGENTBOX_PROJECT_ID`, `AGENTBOX_DOMAIN`, `AGENTBOX_API_URL`, and `AGENTBOX_SANDBOX_URL`. The production defaults are `agentbox-runtime.ru`, `api.agentbox-runtime.ru`, and `sandbox.agentbox-runtime.ru`. Go requires Go 1.24 or newer. The CLI stores local configuration in `~/.agentbox/config.json`; environment variables take precedence. See [CLI configuration](https://docs.agentbox.ru/en/cli/configuration/). diff --git a/RELEASING.md b/RELEASING.md index 81619111f..10557031b 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -5,10 +5,12 @@ releasing the AgentBox SDK packages. The SDK repository owns package-level and end-to-end SDK tests. The mono repository owns backend HTTP/Connect regression tests and does not duplicate SDK test implementations. -All five public packages use one version: +All six public packages use one version: - npm: `@abox-dev/sdk`, `@abox-dev/code-interpreter`, `@abox-dev/cli`; - PyPI: `abox-sdk`, `abox-code-interpreter`. +- Go: `github.com/abox-dev/sdk/packages/go-sdk` (including + `codeinterpreter`). ## 1. Prepare the change @@ -38,9 +40,9 @@ uv lock --project packages/code-interpreter-python node scripts/check-release-versions.mjs vX.Y.Z ``` -`release:version` updates the five workspace manifests and both Python -`pyproject.toml` files. Regenerate both Python lock files rather than editing -them by hand. Do not release the JavaScript and Python packages at different +`release:version` updates the five workspace manifests, both Python +`pyproject.toml` files, and `packages/go-sdk/version.go`. Regenerate both Python lock files +rather than editing them by hand. Do not release any SDK packages at different versions. ## 3. Verify source and release artifacts @@ -55,6 +57,7 @@ pnpm format:check pnpm lint pnpm typecheck pnpm test +make go-check make generate git diff --exit-code ./scripts/build-release.sh @@ -77,9 +80,9 @@ published: ./scripts/test-release-runtime.sh ``` -The suite covers JavaScript, Python sync/async, Code Interpreter, CLI, -private traffic, and a temporary template build. It owns and removes its test -resources. A release must not be tagged if this suite fails. +The suite covers JavaScript, Python sync/async, Go core and Code Interpreter, +CLI, private traffic, and a temporary template build. It owns and removes its +test resources. A release must not be tagged if this suite fails. ## 5. Merge and tag @@ -91,17 +94,22 @@ commit: git switch main git pull --ff-only origin main node scripts/check-release-versions.mjs vX.Y.Z +git tag -a packages/go-sdk/vX.Y.Z -m "AgentBox Go SDK vX.Y.Z" git tag -a vX.Y.Z -m "AgentBox SDK vX.Y.Z" -git push origin vX.Y.Z +git push --atomic origin packages/go-sdk/vX.Y.Z vX.Y.Z ``` -Do not move or reuse a published tag. If a release needs a correction, publish -a new patch version. +Do not move or reuse either published tag. If a release needs a correction, +publish a new patch version. The tag workflow builds the artifacts once, verifies them, publishes npm via Trusted Publishing, publishes both PyPI projects via their GitHub environments, -and creates a GitHub Release with checksums. An existing registry file is -accepted only when its digest matches the newly built artifact. +creates a GitHub Release with checksums, and asks the public Go proxy to index +the tagged Go submodule. The `packages/go-sdk/vX.Y.Z` and `vX.Y.Z` tags must +point to the same commit. Go has no separate registry account or archive: the +immutable Git tag and Go checksum database are its published artifact. An +existing registry file is accepted only when its digest matches the newly built +artifact. ## 6. Verify the published packages @@ -112,6 +120,14 @@ packages into clean environments and repeat the KVM suite: ./scripts/test-published-runtime.sh X.Y.Z ``` +Also verify a clean Go consumer with +`GOPROXY=https://proxy.golang.org go get github.com/abox-dev/sdk/packages/go-sdk@vX.Y.Z`. + +Supported Go CI versions are 1.24.x, 1.25.x, 1.26.x, and 1.27.x. Builds and +hermetic tests run on every row; race and coverage run on 1.27.x, while +cross-builds run on 1.24.x and 1.27.x. Follow the Go-version maintenance +checklist in `AGENTS.md`/`CLAUDE.md` whenever a new patch or minor is released. + Verify the GitHub Release assets with its `SHA256SUMS`. Document public API changes in the AgentBox documentation repository. No SDK-version update is required in mono: its direct HTTP/Connect tests intentionally remain independent diff --git a/codegen.Dockerfile b/codegen.Dockerfile index 68a835f5f..669535587 100644 --- a/codegen.Dockerfile +++ b/codegen.Dockerfile @@ -1,9 +1,10 @@ -FROM golang:1.23 +FROM golang:1.24.13 AS go-tools # Install Golang deps RUN go install github.com/bufbuild/buf/cmd/buf@v1.50.1 && \ - go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28.1 && \ - go install connectrpc.com/connect/cmd/protoc-gen-connect-go@v1.18.1 + go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.12 && \ + go install connectrpc.com/connect/cmd/protoc-gen-connect-go@v1.19.1 && \ + go install github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen@v2.7.2 FROM python:3.10 @@ -12,10 +13,11 @@ FROM python:3.10 WORKDIR /workspace # Copy installed Go deps from previous build step -COPY --from=0 /go /go +COPY --from=go-tools /go /go +COPY --from=go-tools /usr/local/go /usr/local/go # Add Go binary to PATH -ENV PATH="/go/bin:${PATH}" +ENV PATH="/usr/local/go/bin:/go/bin:${PATH}" # The pinned E2B fork is a build-only upstream tool carrying the explode fix. # It is never included in AgentBox wheels or sdists. See UPSTREAM.md. diff --git a/packages/go-sdk/GO_PARITY.md b/packages/go-sdk/GO_PARITY.md new file mode 100644 index 000000000..ab621e1ff --- /dev/null +++ b/packages/go-sdk/GO_PARITY.md @@ -0,0 +1,25 @@ +# Go SDK parity matrix + +This matrix tracks the union of the JavaScript and Python SDK contracts. Python +sync/async duplicates map to one context-aware Go contract plus cancellation, +concurrency, and race coverage. + +| Contract area | JavaScript/Python source suites | Hermetic Go coverage | KVM coverage | +| --- | --- | --- | --- | +| Configuration precedence, custom headers, proxy, pooling, User-Agent, logging, request timeouts | core connection/config tests | `config_test.go` | release smoke | +| Authentication, rate limits, HTTP/Connect error mapping, cancellation | core API/envd error tests | `config_test.go`, `sandbox_test.go`, `envd_test.go` | release smoke | +| Create, connect/resume, list/filter/page, info, kill, timeout, keepalive, pause | sandbox API tests | `sandbox_test.go` | `integration/sdk_test.go` | +| Network rules, IAM payloads, metrics, structured logs | sandbox network/IAM/metrics/log tests | `sandbox_test.go` | `integration/sdk_test.go` | +| Forks, snapshots, signed upload/download URLs | sandbox fork/snapshot/signature tests | `sandbox_test.go` | core KVM lifecycle | +| Foreground/background commands, attach/list, stdin/EOF, signals, output streams, exit errors | command and command-handle tests | `envd_test.go` | `integration/sdk_test.go` | +| PTY create/attach/input/resize/kill | PTY tests | `envd_test.go` | KVM command transport | +| Text/binary/stream reads and writes, batch writes, list/stat/metadata/exists/mkdir/move/remove/watch | filesystem and watch-handle tests | `envd_test.go` | `integration/sdk_test.go` | +| Base images/templates, private registries, Dockerfile parsing, copy, packages, env/user/workdir/start/ready/cache | template builder/parser tests | `template_test.go` | `integration/sdk_test.go` | +| Build request/upload/start/poll/log/status, visibility, tags, list/info/delete | template API/build tests | `template_test.go` | `integration/sdk_test.go` | +| Code execution languages, contexts, env vars, callbacks, request/execution timeouts | Code Interpreter sandbox tests | `codeinterpreter/client_test.go` | `integration/sdk_test.go` | +| Execution/result/log/error/raw MIME models, charts, unknown fields | Code Interpreter messaging/chart tests | `codeinterpreter/client_test.go` | Code Interpreter KVM smoke | +| Consumer compatibility and release installation | package artifact tests | `scripts/test-go-consumer.sh` | `../../tests/runtime/go/main.go` | + +The CI coverage gate is 90% statement coverage across handwritten Go packages; +`internal/gen` is excluded. Every supported Go minor runs the complete hermetic +suite, and the newest minor additionally runs the race detector. diff --git a/packages/go-sdk/LICENSE b/packages/go-sdk/LICENSE new file mode 100644 index 000000000..551142cbb --- /dev/null +++ b/packages/go-sdk/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2026 RetailDriver LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/go-sdk/README.md b/packages/go-sdk/README.md new file mode 100644 index 000000000..eb2e34c3c --- /dev/null +++ b/packages/go-sdk/README.md @@ -0,0 +1,63 @@ +

AgentBox

+ +# AgentBox Go SDK + +The official Go client for AgentBox sandboxes, templates, and Code Interpreter. +Go 1.24 or newer is required. + +```bash +go get github.com/abox-dev/sdk/packages/go-sdk +``` + +```go +package main + +import ( + "context" + "log" + + agentbox "github.com/abox-dev/sdk/packages/go-sdk" +) + +func main() { + ctx := context.Background() + client, err := agentbox.NewClient() + if err != nil { + log.Fatal(err) + } + sandbox, err := client.Sandboxes.Create(ctx, nil) + if err != nil { + log.Fatal(err) + } + defer sandbox.Kill(context.Background()) + + result, err := sandbox.Commands.Run(ctx, "echo", &agentbox.CommandOptions{ + Args: []string{"Hello from AgentBox"}, + }) + if err != nil { + log.Fatal(err) + } + log.Print(string(result.Stdout)) +} +``` + +Code Interpreter is available from +`github.com/abox-dev/sdk/packages/go-sdk/codeinterpreter`. + +Documentation: [core SDK](https://docs.agentbox.ru/en/sdk/), +[sandboxes](https://docs.agentbox.ru/en/sdk/sandboxes/), +[templates](https://docs.agentbox.ru/en/sdk/templates/), and +[Code Interpreter](https://docs.agentbox.ru/en/sdk/code-interpreter/). + +`Sandbox.Kill` returns `false, nil` when the sandbox no longer exists. Command +handles can be waited on without draining their live output channels; `Wait` +always returns complete stdout and stderr collected by the SDK. Output channels +may finish draining and close after `Wait` returns. PTY callers can consume +`CommandHandle.PTY` or use `PTYOptions.OnPTY`. + +Streaming uploads have no SDK deadline by default. Set +`WriteFileOptions.RequestTimeout` to limit a complete upload. A client supplied +with `WithHTTPClient` remains authoritative, so its `http.Client.Timeout` also +applies to command, watch, download, upload, and Code Interpreter streams. Keep +that timeout at zero and use contexts or operation-specific options when streams +may be long-lived. diff --git a/packages/go-sdk/client.go b/packages/go-sdk/client.go new file mode 100644 index 000000000..1218775e0 --- /dev/null +++ b/packages/go-sdk/client.go @@ -0,0 +1,38 @@ +package agentbox + +import ( + "fmt" + "net/http" + + api "github.com/abox-dev/sdk/packages/go-sdk/internal/gen/api" +) + +// Client is an AgentBox control-plane client. +type Client struct { + config clientConfig + httpClient *http.Client + envdClient *http.Client + api *api.ClientWithResponses + + Sandboxes *SandboxService + Templates *TemplateService +} + +// NewClient creates a client. Configuration defaults to AGENTBOX_* environment +// variables and can be overridden with options. +func NewClient(options ...ClientOption) (*Client, error) { + config, err := applyOptions(options) + if err != nil { + return nil, err + } + httpClient := newHTTPClient(config) + envdClient := newEnvdHTTPClient(config, httpClient) + generated, err := api.NewClientWithResponses(config.apiURL, api.WithHTTPClient(httpClient)) + if err != nil { + return nil, fmt.Errorf("agentbox: initialize API client: %w", err) + } + client := &Client{config: config, httpClient: httpClient, envdClient: envdClient, api: generated} + client.Sandboxes = &SandboxService{client: client} + client.Templates = &TemplateService{client: client} + return client, nil +} diff --git a/packages/go-sdk/codeinterpreter/client.go b/packages/go-sdk/codeinterpreter/client.go new file mode 100644 index 000000000..a5a55077a --- /dev/null +++ b/packages/go-sdk/codeinterpreter/client.go @@ -0,0 +1,254 @@ +// Package codeinterpreter runs Python, JavaScript, and TypeScript in AgentBox. +package codeinterpreter + +import ( + "bufio" + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "strings" + "time" + + "github.com/abox-dev/sdk/packages/go-sdk" +) + +const ( + DefaultTemplate = "code-interpreter" + JupyterPort = 49999 + defaultExecutionTimeout = time.Minute +) + +// Client wraps the core client with Code Interpreter creation helpers. +type Client struct{ Core *agentbox.Client } + +func NewClient(options ...agentbox.ClientOption) (*Client, error) { + client, err := agentbox.NewClient(options...) + if err != nil { + return nil, err + } + return &Client{Core: client}, nil +} + +// Sandbox is a core sandbox with notebook-kernel APIs. +type Sandbox struct{ *agentbox.Sandbox } + +func (client *Client) Create(ctx context.Context, options *agentbox.CreateSandboxOptions) (*Sandbox, error) { + if options == nil { + options = &agentbox.CreateSandboxOptions{} + } else { + cloned := *options + options = &cloned + } + if options.Template == "" { + options.Template = DefaultTemplate + } + sandbox, err := client.Core.Sandboxes.Create(ctx, options) + if err != nil { + return nil, err + } + return &Sandbox{Sandbox: sandbox}, nil +} +func (client *Client) Connect(ctx context.Context, id string, options *agentbox.ConnectSandboxOptions) (*Sandbox, error) { + sandbox, err := client.Core.Sandboxes.Connect(ctx, id, options) + if err != nil { + return nil, err + } + return &Sandbox{Sandbox: sandbox}, nil +} + +// Language is a Code Interpreter runtime language. +type Language string + +const ( + Python Language = "python" + JavaScript Language = "javascript" + TypeScript Language = "typescript" +) + +// Context identifies a persistent kernel context. +type Context struct { + ID string `json:"id"` + Language string `json:"language"` + Cwd string `json:"cwd"` +} +type CreateContextOptions struct { + Language Language `json:"language,omitempty"` + Cwd string `json:"cwd,omitempty"` + RequestTimeout time.Duration `json:"-"` +} + +// RunCodeOptions configures code execution and streaming callbacks. +type RunCodeOptions struct { + Language Language + Context *Context + Env map[string]string + RequestTimeout time.Duration + ExecutionTimeout time.Duration + OnStdout func(OutputMessage) + OnStderr func(OutputMessage) + OnResult func(Result) + OnError func(ExecutionError) +} + +// RunCode executes source code and collects streamed NDJSON output. +func (sandbox *Sandbox) RunCode(ctx context.Context, code string, options *RunCodeOptions) (*Execution, error) { + if code == "" { + return nil, &agentbox.InvalidArgumentError{Message: "code cannot be empty"} + } + if options == nil { + options = &RunCodeOptions{} + } + if options.Context != nil && options.Language != "" { + return nil, &agentbox.InvalidArgumentError{Message: "provide context or language, not both"} + } + if options.RequestTimeout < 0 || options.ExecutionTimeout < 0 { + return nil, &agentbox.InvalidArgumentError{Message: "timeouts cannot be negative"} + } + payload := struct { + Code string `json:"code"` + ContextID string `json:"context_id,omitempty"` + Language Language `json:"language,omitempty"` + Env map[string]string `json:"env_vars,omitempty"` + }{Code: code, Language: options.Language, Env: options.Env} + if options.Context != nil { + payload.ContextID = options.Context.ID + } + body, err := json.Marshal(payload) + if err != nil { + return nil, err + } + requestCtx, cancel := context.WithCancel(ctx) + defer cancel() + requestTimeout := options.RequestTimeout + if requestTimeout == 0 { + requestTimeout = sandbox.RequestTimeout() + } + var requestTimer *time.Timer + if requestTimeout > 0 { + requestTimer = time.AfterFunc(requestTimeout, cancel) + } + response, err := sandbox.Request(requestCtx, JupyterPort, http.MethodPost, "/execute", bytes.NewReader(body), true) + if requestTimer != nil && !requestTimer.Stop() && err == nil { + response.Body.Close() + return nil, &agentbox.TimeoutError{APIError: agentbox.APIError{Message: "code execution request timed out", Cause: context.DeadlineExceeded}} + } + if err != nil { + if errors.Is(requestCtx.Err(), context.Canceled) && ctx.Err() == nil { + return nil, &agentbox.TimeoutError{APIError: agentbox.APIError{Message: "code execution request timed out", Cause: context.DeadlineExceeded}} + } + return nil, err + } + defer response.Body.Close() + if response.StatusCode < 200 || response.StatusCode >= 300 { + return nil, interpreterHTTPError(response) + } + executionTimeout := options.ExecutionTimeout + if executionTimeout == 0 { + executionTimeout = defaultExecutionTimeout + } + executionTimer := time.AfterFunc(executionTimeout, cancel) + defer executionTimer.Stop() + execution := &Execution{Logs: Logs{Stdout: []string{}, Stderr: []string{}}} + scanner := bufio.NewScanner(response.Body) + scanner.Buffer(make([]byte, 64*1024), 16*1024*1024) + for scanner.Scan() { + if err := parseOutput(scanner.Bytes(), execution, options); err != nil { + return nil, err + } + } + if err := scanner.Err(); err != nil { + if requestCtx.Err() != nil && ctx.Err() == nil { + return nil, &agentbox.TimeoutError{APIError: agentbox.APIError{Message: "code execution timed out", Cause: context.DeadlineExceeded}} + } + return nil, fmt.Errorf("codeinterpreter: read output: %w", err) + } + return execution, nil +} + +func (sandbox *Sandbox) CreateContext(ctx context.Context, options *CreateContextOptions) (*Context, error) { + if options == nil { + options = &CreateContextOptions{} + } + var result Context + if err := sandbox.contextRequest(ctx, http.MethodPost, "/contexts", options, options.RequestTimeout, &result); err != nil { + return nil, err + } + return &result, nil +} +func (sandbox *Sandbox) ListContexts(ctx context.Context) ([]Context, error) { + var result []Context + if err := sandbox.contextRequest(ctx, http.MethodGet, "/contexts", nil, 0, &result); err != nil { + return nil, err + } + return result, nil +} +func (sandbox *Sandbox) RemoveContext(ctx context.Context, contextID string) error { + if strings.TrimSpace(contextID) == "" { + return &agentbox.InvalidArgumentError{Message: "context ID cannot be empty"} + } + return sandbox.contextRequest(ctx, http.MethodDelete, "/contexts/"+url.PathEscape(contextID), nil, 0, nil) +} +func (sandbox *Sandbox) RestartContext(ctx context.Context, contextID string) error { + if strings.TrimSpace(contextID) == "" { + return &agentbox.InvalidArgumentError{Message: "context ID cannot be empty"} + } + return sandbox.contextRequest(ctx, http.MethodPost, "/contexts/"+url.PathEscape(contextID)+"/restart", nil, 0, nil) +} +func (sandbox *Sandbox) contextRequest(ctx context.Context, method, path string, body any, timeout time.Duration, output any) error { + var reader io.Reader + if body != nil { + encoded, err := json.Marshal(body) + if err != nil { + return err + } + reader = bytes.NewReader(encoded) + } + if timeout == 0 { + timeout = sandbox.RequestTimeout() + } + var requestCtx context.Context + var cancel context.CancelFunc + if timeout > 0 { + requestCtx, cancel = context.WithTimeout(ctx, timeout) + } else { + requestCtx, cancel = context.WithCancel(ctx) + } + defer cancel() + response, err := sandbox.Request(requestCtx, JupyterPort, method, path, reader, true) + if err != nil { + if errors.Is(requestCtx.Err(), context.DeadlineExceeded) { + return &agentbox.TimeoutError{APIError: agentbox.APIError{Message: "context request timed out", Cause: requestCtx.Err()}} + } + return err + } + defer response.Body.Close() + if response.StatusCode < 200 || response.StatusCode >= 300 { + return interpreterHTTPError(response) + } + if output != nil { + if err := json.NewDecoder(response.Body).Decode(output); err != nil { + return fmt.Errorf("codeinterpreter: decode response: %w", err) + } + } + return nil +} + +func interpreterHTTPError(response *http.Response) error { + data, _ := io.ReadAll(io.LimitReader(response.Body, 1<<20)) + message := strings.TrimSpace(string(data)) + if message == "" { + message = response.Status + } + apiError := agentbox.APIError{StatusCode: response.StatusCode, Message: message} + switch response.StatusCode { + case http.StatusBadGateway, http.StatusGatewayTimeout: + return &agentbox.TimeoutError{APIError: apiError} + default: + return &agentbox.SandboxError{APIError: apiError} + } +} diff --git a/packages/go-sdk/codeinterpreter/client_test.go b/packages/go-sdk/codeinterpreter/client_test.go new file mode 100644 index 000000000..a0a1034c7 --- /dev/null +++ b/packages/go-sdk/codeinterpreter/client_test.go @@ -0,0 +1,233 @@ +package codeinterpreter + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/http/httptest" + "net/url" + "strings" + "sync/atomic" + "testing" + "time" + + "github.com/abox-dev/sdk/packages/go-sdk" +) + +type rewriteTransport struct{ target *url.URL } + +func (transport rewriteTransport) RoundTrip(request *http.Request) (*http.Response, error) { + clone := request.Clone(request.Context()) + copied := *request.URL + copied.Scheme, copied.Host = transport.target.Scheme, transport.target.Host + clone.URL = &copied + return http.DefaultTransport.RoundTrip(clone) +} + +func TestCodeInterpreter(t *testing.T) { + var failContexts atomic.Bool + server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + writer.Header().Set("Content-Type", "application/json") + if failContexts.Load() && request.URL.Path == "/contexts" { + http.Error(writer, "failure", http.StatusInternalServerError) + return + } + switch { + case request.URL.Path == "/sandboxes": + writer.WriteHeader(http.StatusCreated) + fmt.Fprint(writer, `{"sandboxID":"sbx","templateID":"code-interpreter","envdVersion":"1","domain":"example.test","envdAccessToken":"envd","trafficAccessToken":"traffic"}`) + case request.URL.Path == "/sandboxes/sbx/connect": + fmt.Fprint(writer, `{"sandboxID":"sbx","templateID":"code-interpreter","envdVersion":"1","domain":"example.test"}`) + case request.URL.Path == "/execute": + var input struct { + Code string `json:"code"` + } + json.NewDecoder(request.Body).Decode(&input) + if input.Code == "request-slow" { + time.Sleep(100 * time.Millisecond) + return + } + if input.Code == "http-error" { + http.Error(writer, "failure", http.StatusInternalServerError) + return + } + if input.Code == "slow" { + writer.(http.Flusher).Flush() + time.Sleep(100 * time.Millisecond) + return + } + fmt.Fprintln(writer, `{"type":"stdout","text":"hello"}`) + fmt.Fprintln(writer, `{"type":"stderr","text":"warning"}`) + fmt.Fprintln(writer, `{"type":"result","text":"42","html":"42","is_main_result":true,"unknown":{"value":1},"chart":{"type":"future","title":"chart","elements":[],"future":true}}`) + fmt.Fprintln(writer, `{"type":"error","name":"ValueError","value":"bad","traceback":"trace"}`) + fmt.Fprintln(writer, `{"type":"number_of_executions","execution_count":3}`) + case request.URL.Path == "/contexts" && request.Method == http.MethodPost: + var input CreateContextOptions + json.NewDecoder(request.Body).Decode(&input) + if input.Cwd == "badjson" { + fmt.Fprint(writer, `{`) + return + } + if input.Language == TypeScript { + http.Error(writer, "failure", http.StatusInternalServerError) + return + } + fmt.Fprint(writer, `{"id":"ctx","language":"python","cwd":"/home/user"}`) + case request.URL.Path == "/contexts" && request.Method == http.MethodGet: + fmt.Fprint(writer, `[{"id":"ctx","language":"python","cwd":"/home/user"}]`) + case strings.HasPrefix(request.URL.Path, "/contexts/"): + writer.WriteHeader(http.StatusNoContent) + default: + http.NotFound(writer, request) + } + })) + defer server.Close() + target, _ := url.Parse(server.URL) + httpClient := &http.Client{Transport: rewriteTransport{target: target}} + client, err := NewClient(agentbox.WithAPIURL(server.URL), agentbox.WithSandboxURL(server.URL), agentbox.WithHTTPClient(httpClient)) + if err != nil { + t.Fatal(err) + } + sandbox, err := client.Create(context.Background(), nil) + if err != nil { + t.Fatal(err) + } + if sandbox.TemplateID != DefaultTemplate { + t.Fatalf("template: %s", sandbox.TemplateID) + } + stdout, stderr, results, callbackErrors := 0, 0, 0, 0 + execution, err := sandbox.RunCode(context.Background(), "print(42)", &RunCodeOptions{Language: Python, Env: map[string]string{"A": "B"}, OnStdout: func(OutputMessage) { stdout++ }, OnStderr: func(OutputMessage) { stderr++ }, OnResult: func(Result) { results++ }, OnError: func(ExecutionError) { callbackErrors++ }}) + if err != nil { + t.Fatal(err) + } + if stdout != 1 || stderr != 1 || results != 1 || callbackErrors != 1 || execution.Text() != "42" || execution.ExecutionCount != 3 || execution.Results[0].Chart.Type != ChartUnknown || len(execution.Results[0].Extra) != 1 { + t.Fatalf("execution: %#v", execution) + } + if _, err := sandbox.RunCode(context.Background(), "x", &RunCodeOptions{Language: Python, Context: &Context{ID: "ctx"}}); err == nil { + t.Fatal("expected language/context validation") + } + if _, err := sandbox.RunCode(context.Background(), "slow", &RunCodeOptions{ExecutionTimeout: 10 * time.Millisecond}); err == nil { + t.Fatal("expected execution timeout") + } else { + var timeout *agentbox.TimeoutError + if !errors.As(err, &timeout) { + t.Fatalf("timeout type: %T", err) + } + } + if _, err := sandbox.RunCode(context.Background(), "request-slow", &RunCodeOptions{RequestTimeout: 10 * time.Millisecond}); err == nil { + t.Fatal("expected request timeout") + } + if _, err := sandbox.RunCode(context.Background(), "http-error", nil); err == nil { + t.Fatal("expected HTTP error") + } + if _, err := sandbox.RunCode(context.Background(), "", nil); err == nil { + t.Fatal("expected empty-code validation") + } + if _, err := sandbox.RunCode(context.Background(), "x", &RunCodeOptions{RequestTimeout: -time.Second}); err == nil { + t.Fatal("expected timeout validation") + } + created, err := sandbox.CreateContext(context.Background(), &CreateContextOptions{Language: JavaScript, Cwd: "/tmp"}) + if err != nil || created.ID != "ctx" { + t.Fatalf("create context: %#v %v", created, err) + } + contexts, err := sandbox.ListContexts(context.Background()) + if err != nil || len(contexts) != 1 { + t.Fatalf("contexts: %#v %v", contexts, err) + } + if err := sandbox.RestartContext(context.Background(), "ctx"); err != nil { + t.Fatal(err) + } + if err := sandbox.RemoveContext(context.Background(), "ctx"); err != nil { + t.Fatal(err) + } + if err := sandbox.RemoveContext(context.Background(), ""); err == nil { + t.Fatal("expected context ID validation") + } + if err := sandbox.RestartContext(context.Background(), ""); err == nil { + t.Fatal("expected context ID validation") + } + if _, err := sandbox.CreateContext(context.Background(), &CreateContextOptions{Cwd: "badjson"}); err == nil { + t.Fatal("expected invalid context response") + } + if _, err := sandbox.CreateContext(context.Background(), &CreateContextOptions{Language: TypeScript}); err == nil { + t.Fatal("expected context HTTP error") + } + if _, err := client.Connect(context.Background(), "sbx", nil); err != nil { + t.Fatal(err) + } + noTimeoutClient, err := NewClient(agentbox.WithAPIURL(server.URL), agentbox.WithSandboxURL(server.URL), agentbox.WithHTTPClient(httpClient), agentbox.WithRequestTimeout(0)) + if err != nil { + t.Fatal(err) + } + noTimeoutSandbox, err := noTimeoutClient.Create(context.Background(), nil) + if err != nil { + t.Fatal(err) + } + if _, err := noTimeoutSandbox.ListContexts(context.Background()); err != nil { + t.Fatal(err) + } + failContexts.Store(true) + if _, err := sandbox.ListContexts(context.Background()); err == nil { + t.Fatal("expected list contexts error") + } +} + +func TestModelsAndHTTPError(t *testing.T) { + message := OutputMessage{Line: "line"} + if message.String() != "line" { + t.Fatal(message) + } + executionError := ExecutionError{Name: "Error", Value: "bad"} + if executionError.Error() != "Error: bad" { + t.Fatal(executionError) + } + var chart Chart + if err := json.Unmarshal([]byte(`{"type":"line","title":"x","elements":[],"extra":1}`), &chart); err != nil || chart.Type != ChartLine || len(chart.Extra) != 1 { + t.Fatalf("chart: %#v %v", chart, err) + } + result := resultFromRaw(map[string]json.RawMessage{"type": json.RawMessage(`"result"`), "text": json.RawMessage(`"ok"`), "is_main_result": json.RawMessage(`true`), "other": json.RawMessage(`1`)}) + if result.Text != "ok" || len(result.Formats()) != 2 { + t.Fatalf("result: %#v", result) + } + for status, target := range map[int]error{http.StatusBadGateway: &agentbox.TimeoutError{}, http.StatusGatewayTimeout: &agentbox.TimeoutError{}, http.StatusNotFound: &agentbox.SandboxError{}, http.StatusInternalServerError: &agentbox.SandboxError{}} { + response := &http.Response{StatusCode: status, Status: http.StatusText(status), Body: io.NopCloser(strings.NewReader("failure"))} + err := interpreterHTTPError(response) + if fmt.Sprintf("%T", err) != fmt.Sprintf("%T", target) { + t.Fatalf("status %d: %T", status, err) + } + } + if (Execution{}).Text() != "" { + t.Fatal("empty execution text") + } + if err := parseOutput([]byte(`not-json`), &Execution{}, &RunCodeOptions{}); err == nil { + t.Fatal("expected parse error") + } +} + +func TestClientFailures(t *testing.T) { + t.Setenv("AGENTBOX_DEBUG", "invalid") + if _, err := NewClient(); err == nil { + t.Fatal("expected client config error") + } + t.Setenv("AGENTBOX_DEBUG", "false") + server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + writer.Header().Set("Content-Type", "application/json") + writer.WriteHeader(http.StatusInternalServerError) + fmt.Fprint(writer, `{"message":"failure"}`) + })) + defer server.Close() + target, _ := url.Parse(server.URL) + client, err := NewClient(agentbox.WithAPIURL(server.URL), agentbox.WithSandboxURL(server.URL), agentbox.WithHTTPClient(&http.Client{Transport: rewriteTransport{target: target}})) + if err != nil { + t.Fatal(err) + } + if _, err := client.Create(context.Background(), nil); err == nil { + t.Fatal("expected create error") + } + if _, err := client.Connect(context.Background(), "id", nil); err == nil { + t.Fatal("expected connect error") + } +} diff --git a/packages/go-sdk/codeinterpreter/models.go b/packages/go-sdk/codeinterpreter/models.go new file mode 100644 index 000000000..7754ea054 --- /dev/null +++ b/packages/go-sdk/codeinterpreter/models.go @@ -0,0 +1,200 @@ +package codeinterpreter + +import ( + "encoding/json" + "fmt" + "slices" + "time" +) + +// OutputMessage is one stdout or stderr line. +type OutputMessage struct { + Line string + Timestamp int64 + Error bool +} + +func (message OutputMessage) String() string { return message.Line } + +// ExecutionError is a kernel error and traceback. +type ExecutionError struct { + Name string `json:"name"` + Value string `json:"value"` + Traceback string `json:"traceback"` +} + +func (e ExecutionError) Error() string { return fmt.Sprintf("%s: %s", e.Name, e.Value) } + +type Logs struct { + Stdout []string `json:"stdout"` + Stderr []string `json:"stderr"` +} +type Execution struct { + Results []Result `json:"results"` + Logs Logs `json:"logs"` + Error *ExecutionError `json:"error,omitempty"` + ExecutionCount int `json:"execution_count,omitempty"` +} + +func (execution Execution) Text() string { + for _, result := range execution.Results { + if result.IsMainResult { + return result.Text + } + } + return "" +} + +// RawData preserves every MIME representation returned by the kernel. +type RawData map[string]json.RawMessage +type Result struct { + Text, HTML, Markdown, SVG, PNG, JPEG, PDF, LaTeX, JSON, JavaScript string + Data map[string]any + Chart *Chart + Extra map[string]json.RawMessage + Raw RawData + IsMainResult bool +} + +func (result Result) Formats() []string { + formats := make([]string, 0, len(result.Raw)) + for key := range result.Raw { + formats = append(formats, key) + } + slices.Sort(formats) + return formats +} + +type ChartType string + +const ( + ChartLine ChartType = "line" + ChartScatter ChartType = "scatter" + ChartBar ChartType = "bar" + ChartPie ChartType = "pie" + ChartBoxAndWhisker ChartType = "box_and_whisker" + ChartSuper ChartType = "superchart" + ChartUnknown ChartType = "unknown" +) + +type ScaleType string + +const ( + ScaleLinear ScaleType = "linear" + ScaleDatetime ScaleType = "datetime" + ScaleCategorical ScaleType = "categorical" + ScaleLog ScaleType = "log" + ScaleSymlog ScaleType = "symlog" + ScaleLogit ScaleType = "logit" + ScaleFunction ScaleType = "function" + ScaleFunctionLog ScaleType = "functionlog" + ScaleAsinh ScaleType = "asinh" +) + +// Chart retains typed common chart properties and unknown fields in Extra. +type Chart struct { + Type ChartType `json:"type"` + Title string `json:"title"` + Elements []json.RawMessage `json:"elements"` + XLabel string `json:"x_label,omitempty"` + YLabel string `json:"y_label,omitempty"` + XUnit string `json:"x_unit,omitempty"` + YUnit string `json:"y_unit,omitempty"` + XScale ScaleType `json:"x_scale,omitempty"` + YScale ScaleType `json:"y_scale,omitempty"` + Extra map[string]json.RawMessage `json:"-"` +} + +func (chart *Chart) UnmarshalJSON(data []byte) error { + type wire Chart + var value wire + if err := json.Unmarshal(data, &value); err != nil { + return err + } + raw := map[string]json.RawMessage{} + if err := json.Unmarshal(data, &raw); err != nil { + return err + } + known := map[string]bool{"type": true, "title": true, "elements": true, "x_label": true, "y_label": true, "x_unit": true, "y_unit": true, "x_scale": true, "y_scale": true} + for key := range known { + delete(raw, key) + } + *chart = Chart(value) + chart.Extra = raw + switch chart.Type { + case ChartLine, ChartScatter, ChartBar, ChartPie, ChartBoxAndWhisker, ChartSuper: + default: + chart.Type = ChartUnknown + } + return nil +} + +func parseOutput(line []byte, execution *Execution, options *RunCodeOptions) error { + var message map[string]json.RawMessage + if err := json.Unmarshal(line, &message); err != nil { + return fmt.Errorf("codeinterpreter: invalid output: %w", err) + } + var kind string + _ = json.Unmarshal(message["type"], &kind) + switch kind { + case "stdout", "stderr": + var text string + _ = json.Unmarshal(message["text"], &text) + output := OutputMessage{Line: text, Timestamp: time.Now().UnixNano(), Error: kind == "stderr"} + if output.Error { + execution.Logs.Stderr = append(execution.Logs.Stderr, text) + if options.OnStderr != nil { + options.OnStderr(output) + } + } else { + execution.Logs.Stdout = append(execution.Logs.Stdout, text) + if options.OnStdout != nil { + options.OnStdout(output) + } + } + case "error": + var value ExecutionError + _ = json.Unmarshal(line, &value) + execution.Error = &value + if options.OnError != nil { + options.OnError(value) + } + case "number_of_executions": + _ = json.Unmarshal(message["execution_count"], &execution.ExecutionCount) + case "result": + result := resultFromRaw(message) + execution.Results = append(execution.Results, result) + if options.OnResult != nil { + options.OnResult(result) + } + } + return nil +} + +func resultFromRaw(message map[string]json.RawMessage) Result { + result := Result{Raw: RawData{}, Extra: map[string]json.RawMessage{}} + _ = json.Unmarshal(message["is_main_result"], &result.IsMainResult) + knownStrings := map[string]*string{"text": &result.Text, "html": &result.HTML, "markdown": &result.Markdown, "svg": &result.SVG, "png": &result.PNG, "jpeg": &result.JPEG, "pdf": &result.PDF, "latex": &result.LaTeX, "json": &result.JSON, "javascript": &result.JavaScript} + for key, raw := range message { + if key == "type" || key == "is_main_result" { + continue + } + result.Raw[key] = append(json.RawMessage(nil), raw...) + if target := knownStrings[key]; target != nil { + _ = json.Unmarshal(raw, target) + continue + } + switch key { + case "data": + _ = json.Unmarshal(raw, &result.Data) + case "chart": + var chart Chart + if json.Unmarshal(raw, &chart) == nil { + result.Chart = &chart + } + default: + result.Extra[key] = append(json.RawMessage(nil), raw...) + } + } + return result +} diff --git a/packages/go-sdk/commands.go b/packages/go-sdk/commands.go new file mode 100644 index 000000000..c9dce3ca6 --- /dev/null +++ b/packages/go-sdk/commands.go @@ -0,0 +1,527 @@ +package agentbox + +import ( + "bytes" + "context" + "errors" + "fmt" + "net/http" + "runtime" + "slices" + "sync" + + "connectrpc.com/connect" + process "github.com/abox-dev/sdk/packages/go-sdk/internal/gen/envd/process" + "github.com/abox-dev/sdk/packages/go-sdk/internal/gen/envd/process/processconnect" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" +) + +// CommandOptions configures a command process. +type CommandOptions struct { + Args []string + Env map[string]string + Cwd string + Tag string + Stdin bool + OnStdout func([]byte) + OnStderr func([]byte) +} + +// CommandResult contains collected process output. +type CommandResult struct { + PID uint32 + ExitCode int + Stdout []byte + Stderr []byte + Status string +} + +// CommandExitError reports a process that completed with a non-zero exit code. +type CommandExitError struct { + Result CommandResult + Message string +} + +func (e *CommandExitError) Error() string { + if e.Message != "" { + return fmt.Sprintf("agentbox: command exited with code %d: %s", e.Result.ExitCode, e.Message) + } + return fmt.Sprintf("agentbox: command exited with code %d", e.Result.ExitCode) +} + +// ProcessInfo describes a running envd process. +type ProcessInfo struct { + PID uint32 + Tag string + Command string + Args []string + Env map[string]string + Cwd string +} + +// CommandService executes and manages sandbox processes. +type CommandService struct { + sandbox *Sandbox + client processconnect.ProcessClient +} + +// CommandHandle represents a streaming process. Wait can be called without +// draining the output channels and always returns the complete collected output. +type CommandHandle struct { + service *CommandService + ready chan struct{} + mu sync.RWMutex + closeReady func() + pid uint32 + tag string + + Stdout <-chan []byte + Stderr <-chan []byte + PTY <-chan []byte + Done <-chan struct{} + + stdout *outputStream + stderr *outputStream + pty *outputStream + done chan struct{} + result CommandResult + err error +} + +func newCommandService(sandbox *Sandbox) *CommandService { + client := processconnect.NewProcessClient(sandbox.client.envdClient, sandbox.envdURL(envdPort, false), connect.WithCodec(tolerantJSONCodec{}), connect.WithAcceptCompression("gzip", nil, nil)) + return &CommandService{sandbox: sandbox, client: client} +} + +// Run executes a foreground command and collects its output. +func (service *CommandService) Run(ctx context.Context, command string, options *CommandOptions) (CommandResult, error) { + handle, err := service.Start(ctx, command, options) + if err != nil { + return CommandResult{}, err + } + stdout, stderr, pty := handle.Stdout, handle.Stderr, handle.PTY + for stdout != nil || stderr != nil || pty != nil { + select { + case <-ctx.Done(): + return CommandResult{}, ctx.Err() + case _, ok := <-stdout: + if !ok { + stdout = nil + } + case _, ok := <-stderr: + if !ok { + stderr = nil + } + case _, ok := <-pty: + if !ok { + pty = nil + } + } + } + return handle.Wait(ctx) +} + +// Start starts a process and streams output through the returned handle. +func (service *CommandService) Start(ctx context.Context, command string, options *CommandOptions) (*CommandHandle, error) { + if command == "" { + return nil, &InvalidArgumentError{Message: "command cannot be empty"} + } + if options == nil { + options = &CommandOptions{} + } + config := &process.ProcessConfig{Cmd: command, Args: options.Args, Envs: options.Env} + if options.Cwd != "" { + config.Cwd = &options.Cwd + } + stdin := options.Stdin + request := connect.NewRequest(&process.StartRequest{Process: config, Stdin: &stdin}) + if options.Tag != "" { + request.Msg.Tag = &options.Tag + } + service.addHeaders(request.Header()) + stream, err := service.client.Start(ctx, request) + if err != nil { + return nil, connectError(err) + } + handle := newCommandHandle(service, options.Tag) + go handle.receive(ctx, func() (*process.ProcessEvent, bool) { + if !stream.Receive() { + return nil, false + } + return stream.Msg().GetEvent(), true + }, stream.Err, stream.Close, outputCallbacks{stdout: options.OnStdout, stderr: options.OnStderr}) + return handle, nil +} + +// Connect attaches to an existing process by PID or tag. +func (service *CommandService) Connect(ctx context.Context, pid uint32, tag string) (*CommandHandle, error) { + selector, err := processSelector(pid, tag) + if err != nil { + return nil, err + } + request := connect.NewRequest(&process.ConnectRequest{Process: selector}) + service.addHeaders(request.Header()) + stream, err := service.client.Connect(ctx, request) + if err != nil { + return nil, connectError(err) + } + handle := newCommandHandle(service, tag) + if pid != 0 { + handle.pid = pid + handle.closeReady() + } + go handle.receive(ctx, func() (*process.ProcessEvent, bool) { + if !stream.Receive() { + return nil, false + } + return stream.Msg().GetEvent(), true + }, stream.Err, stream.Close, outputCallbacks{}) + return handle, nil +} + +// List returns currently running processes. +func (service *CommandService) List(ctx context.Context) ([]ProcessInfo, error) { + requestCtx, cancel := service.sandbox.unaryContext(ctx) + defer cancel() + request := connect.NewRequest(&process.ListRequest{}) + service.addHeaders(request.Header()) + response, err := service.client.List(requestCtx, request) + if err != nil { + return nil, connectError(err) + } + items := make([]ProcessInfo, 0, len(response.Msg.GetProcesses())) + for _, item := range response.Msg.GetProcesses() { + cfg := item.GetConfig() + info := ProcessInfo{PID: item.GetPid(), Tag: item.GetTag()} + if cfg != nil { + info.Command = cfg.GetCmd() + info.Args = cfg.GetArgs() + info.Env = cfg.GetEnvs() + info.Cwd = cfg.GetCwd() + } + items = append(items, info) + } + return items, nil +} + +// Kill sends SIGKILL to a process. +func (service *CommandService) Kill(ctx context.Context, pid uint32, tag string) error { + return service.signal(ctx, pid, tag, process.Signal_SIGNAL_SIGKILL) +} + +// Terminate sends SIGTERM to a process. +func (service *CommandService) Terminate(ctx context.Context, pid uint32, tag string) error { + return service.signal(ctx, pid, tag, process.Signal_SIGNAL_SIGTERM) +} +func (service *CommandService) signal(ctx context.Context, pid uint32, tag string, signal process.Signal) error { + selector, err := processSelector(pid, tag) + if err != nil { + return err + } + request := connect.NewRequest(&process.SendSignalRequest{Process: selector, Signal: signal}) + service.addHeaders(request.Header()) + requestCtx, cancel := service.sandbox.unaryContext(ctx) + defer cancel() + _, err = service.client.SendSignal(requestCtx, request) + return connectError(err) +} + +func (service *CommandService) addHeaders(header http.Header) { + for key, values := range service.sandbox.envdHeaders(envdPort) { + header[key] = slices.Clone(values) + } + header.Set("Keepalive-Ping-Interval", "50") +} + +func newCommandHandle(service *CommandService, tag string) *CommandHandle { + ready := make(chan struct{}) + stdout := newOutputStream() + stderr := newOutputStream() + pty := newOutputStream() + done := make(chan struct{}) + handle := &CommandHandle{ + service: service, ready: ready, closeReady: sync.OnceFunc(func() { close(ready) }), + tag: tag, + Stdout: stdout.output, Stderr: stderr.output, PTY: pty.output, Done: done, + stdout: stdout, stderr: stderr, pty: pty, done: done, + } + runtime.AddCleanup(handle, cleanupCommandOutputs, commandOutputs{stdout: stdout, stderr: stderr, pty: pty}) + return handle +} + +type outputCallbacks struct { + stdout func([]byte) + stderr func([]byte) + pty func([]byte) +} + +type commandOutputs struct{ stdout, stderr, pty *outputStream } + +func cleanupCommandOutputs(outputs commandOutputs) { + outputs.stdout.abort() + outputs.stderr.abort() + outputs.pty.abort() +} + +func (handle *CommandHandle) receive(ctx context.Context, next func() (*process.ProcessEvent, bool), streamErr, closeStream func() error, callbacks outputCallbacks) { + defer close(handle.done) + defer func() { _ = closeStream() }() + defer handle.stdout.close() + defer handle.stderr.close() + defer handle.pty.close() + for { + event, ok := next() + if !ok { + break + } + if event == nil { + continue + } + if start := event.GetStart(); start != nil { + handle.mu.Lock() + handle.pid = start.GetPid() + handle.result.PID = start.GetPid() + handle.mu.Unlock() + handle.closeReady() + continue + } + if data := event.GetData(); data != nil { + switch output := data.GetOutput().(type) { + case *process.ProcessEvent_DataEvent_Stdout: + chunk := bytes.Clone(output.Stdout) + handle.result.Stdout = append(handle.result.Stdout, chunk...) + handle.stdout.send(chunk) + if callbacks.stdout != nil { + callbacks.stdout(chunk) + } + case *process.ProcessEvent_DataEvent_Stderr: + chunk := bytes.Clone(output.Stderr) + handle.result.Stderr = append(handle.result.Stderr, chunk...) + handle.stderr.send(chunk) + if callbacks.stderr != nil { + callbacks.stderr(chunk) + } + case *process.ProcessEvent_DataEvent_Pty: + chunk := bytes.Clone(output.Pty) + handle.pty.send(chunk) + if callbacks.pty != nil { + callbacks.pty(chunk) + } + } + } + if end := event.GetEnd(); end != nil { + handle.closeReady() + handle.result.ExitCode = int(end.GetExitCode()) + handle.result.Status = end.GetStatus() + if end.GetExited() && end.GetExitCode() != 0 { + handle.err = &CommandExitError{Result: handle.result, Message: end.GetError()} + } + return + } + } + handle.closeReady() + if err := streamErr(); err != nil && !errors.Is(err, context.Canceled) { + handle.err = connectError(err) + } +} + +type outputStream struct { + output chan []byte + wake chan struct{} + aborted chan struct{} + abortOnce func() + mu sync.Mutex + queue [][]byte + closed bool +} + +func newOutputStream() *outputStream { + stream := &outputStream{output: make(chan []byte), wake: make(chan struct{}, 1), aborted: make(chan struct{})} + stream.abortOnce = sync.OnceFunc(func() { + stream.mu.Lock() + clear(stream.queue) + stream.queue = nil + stream.closed = true + stream.mu.Unlock() + close(stream.aborted) + }) + go stream.run() + return stream +} + +func (stream *outputStream) send(chunk []byte) { + stream.mu.Lock() + if !stream.closed { + stream.queue = append(stream.queue, chunk) + } + stream.mu.Unlock() + stream.notify() +} + +func (stream *outputStream) close() { + stream.mu.Lock() + stream.closed = true + stream.mu.Unlock() + stream.notify() +} + +func (stream *outputStream) abort() { stream.abortOnce() } + +func (stream *outputStream) notify() { + select { + case stream.wake <- struct{}{}: + default: + } +} + +func (stream *outputStream) run() { + defer close(stream.output) + for { + stream.mu.Lock() + if len(stream.queue) > 0 { + chunk := stream.queue[0] + stream.queue[0] = nil + stream.queue = stream.queue[1:] + stream.mu.Unlock() + select { + case stream.output <- chunk: + case <-stream.aborted: + return + } + continue + } + closed := stream.closed + stream.mu.Unlock() + if closed { + return + } + select { + case <-stream.wake: + case <-stream.aborted: + return + } + } +} + +// PID waits for and returns the process identifier. +func (handle *CommandHandle) PID(ctx context.Context) (uint32, error) { + select { + case <-ctx.Done(): + return 0, ctx.Err() + case <-handle.ready: + handle.mu.RLock() + defer handle.mu.RUnlock() + if handle.pid == 0 { + return 0, errors.New("agentbox: process did not start") + } + return handle.pid, nil + } +} + +// Wait waits for completion and returns collected output. +func (handle *CommandHandle) Wait(ctx context.Context) (CommandResult, error) { + select { + case <-ctx.Done(): + return CommandResult{}, ctx.Err() + case <-handle.done: + return handle.result, handle.err + } +} + +// Write writes bytes to process stdin. +func (handle *CommandHandle) Write(ctx context.Context, data []byte) (int, error) { + pid, err := handle.PID(ctx) + if err != nil { + return 0, err + } + request := connect.NewRequest(&process.SendInputRequest{Process: &process.ProcessSelector{Selector: &process.ProcessSelector_Pid{Pid: pid}}, Input: &process.ProcessInput{Input: &process.ProcessInput_Stdin{Stdin: data}}}) + handle.service.addHeaders(request.Header()) + requestCtx, cancel := handle.service.sandbox.unaryContext(ctx) + defer cancel() + _, err = handle.service.client.SendInput(requestCtx, request) + if err != nil { + return 0, connectError(err) + } + return len(data), nil +} + +// CloseStdin signals EOF to a non-PTY process. +func (handle *CommandHandle) CloseStdin(ctx context.Context) error { + pid, err := handle.PID(ctx) + if err != nil { + return err + } + request := connect.NewRequest(&process.CloseStdinRequest{Process: &process.ProcessSelector{Selector: &process.ProcessSelector_Pid{Pid: pid}}}) + handle.service.addHeaders(request.Header()) + requestCtx, cancel := handle.service.sandbox.unaryContext(ctx) + defer cancel() + _, err = handle.service.client.CloseStdin(requestCtx, request) + return connectError(err) +} + +// Kill sends SIGKILL to this process. +func (handle *CommandHandle) Kill(ctx context.Context) error { + pid, err := handle.PID(ctx) + if err != nil { + return err + } + return handle.service.Kill(ctx, pid, "") +} + +func processSelector(pid uint32, tag string) (*process.ProcessSelector, error) { + if pid != 0 && tag != "" { + return nil, &InvalidArgumentError{Message: "provide process PID or tag, not both"} + } + if pid != 0 { + return &process.ProcessSelector{Selector: &process.ProcessSelector_Pid{Pid: pid}}, nil + } + if tag != "" { + return &process.ProcessSelector{Selector: &process.ProcessSelector_Tag{Tag: tag}}, nil + } + return nil, &InvalidArgumentError{Message: "process PID or tag is required"} +} + +type tolerantJSONCodec struct{} + +func (tolerantJSONCodec) Name() string { return "json" } +func (tolerantJSONCodec) Marshal(value any) ([]byte, error) { + message, ok := value.(proto.Message) + if !ok { + return nil, fmt.Errorf("agentbox: expected protobuf message, got %T", value) + } + return protojson.MarshalOptions{UseProtoNames: false}.Marshal(message) +} +func (tolerantJSONCodec) Unmarshal(data []byte, value any) error { + message, ok := value.(proto.Message) + if !ok { + return fmt.Errorf("agentbox: expected protobuf message, got %T", value) + } + return protojson.UnmarshalOptions{DiscardUnknown: true}.Unmarshal(data, message) +} + +func connectError(err error) error { + if err == nil { + return nil + } + if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { + return &TimeoutError{APIError: APIError{Message: "envd request timed out", Cause: err}} + } + var value *connect.Error + if errors.As(err, &value) { + apiError := APIError{Code: value.Code().String(), Message: value.Message(), Cause: err} + switch value.Code() { + case connect.CodeUnauthenticated, connect.CodePermissionDenied: + return &AuthenticationError{APIError: apiError} + case connect.CodeResourceExhausted: + return &RateLimitError{APIError: apiError} + case connect.CodeCanceled, connect.CodeDeadlineExceeded, connect.CodeUnavailable: + return &TimeoutError{APIError: apiError} + case connect.CodeInvalidArgument: + return &InvalidArgumentError{Message: value.Message(), Cause: err} + default: + return &SandboxError{APIError: apiError} + } + } + return err +} diff --git a/packages/go-sdk/config.go b/packages/go-sdk/config.go new file mode 100644 index 000000000..3ee16aec4 --- /dev/null +++ b/packages/go-sdk/config.go @@ -0,0 +1,193 @@ +package agentbox + +import ( + "cmp" + "errors" + "log/slog" + "maps" + "net/http" + "net/url" + "os" + "strconv" + "strings" + "time" +) + +const ( + defaultDomain = "agentbox-runtime.ru" + defaultRequestTimeout = 60 * time.Second + defaultSandboxTimeout = 5 * time.Minute + envdPort = 49983 +) + +// ClientOption configures a Client. +type ClientOption func(*clientConfig) error + +type clientConfig struct { + apiKey string + domain string + apiURL string + sandboxURL string + debug bool + requestTimeout time.Duration + httpClient *http.Client + proxyURL *url.URL + headers http.Header + logger *slog.Logger +} + +func defaultClientConfig() (clientConfig, error) { + domain := cmp.Or(os.Getenv("AGENTBOX_DOMAIN"), defaultDomain) + debugMode, err := strconv.ParseBool(cmp.Or(os.Getenv("AGENTBOX_DEBUG"), "false")) + if err != nil { + return clientConfig{}, &InvalidArgumentError{Message: "AGENTBOX_DEBUG must be true or false", Cause: err} + } + apiURL := os.Getenv("AGENTBOX_API_URL") + if apiURL == "" { + if debugMode { + apiURL = "http://localhost:3000" + } else { + apiURL = "https://api." + domain + } + } + return clientConfig{ + apiKey: os.Getenv("AGENTBOX_API_KEY"), + domain: domain, + apiURL: apiURL, + sandboxURL: os.Getenv("AGENTBOX_SANDBOX_URL"), + debug: debugMode, + requestTimeout: defaultRequestTimeout, + headers: make(http.Header), + }, nil +} + +// WithAPIKey sets the AgentBox API key. By default AGENTBOX_API_KEY is used. +func WithAPIKey(apiKey string) ClientOption { + return func(config *clientConfig) error { + config.apiKey = apiKey + return nil + } +} + +// WithDomain sets the AgentBox runtime domain. +func WithDomain(domain string) ClientOption { + return func(config *clientConfig) error { + if strings.TrimSpace(domain) == "" { + return &InvalidArgumentError{Message: "domain cannot be empty"} + } + config.domain = domain + return nil + } +} + +// WithAPIURL overrides the control-plane API URL. +func WithAPIURL(value string) ClientOption { + return func(config *clientConfig) error { + if _, err := parseHTTPURL("API URL", value); err != nil { + return err + } + config.apiURL = strings.TrimRight(value, "/") + return nil + } +} + +// WithSandboxURL overrides the sandbox proxy URL. +func WithSandboxURL(value string) ClientOption { + return func(config *clientConfig) error { + if _, err := parseHTTPURL("sandbox URL", value); err != nil { + return err + } + config.sandboxURL = strings.TrimRight(value, "/") + return nil + } +} + +// WithDebug enables local envd routing. +func WithDebug(enabled bool) ClientOption { + return func(config *clientConfig) error { + config.debug = enabled + return nil + } +} + +// WithRequestTimeout sets the default unary request timeout. Zero disables it. +func WithRequestTimeout(timeout time.Duration) ClientOption { + return func(config *clientConfig) error { + if timeout < 0 { + return &InvalidArgumentError{Message: "request timeout cannot be negative"} + } + config.requestTimeout = timeout + return nil + } +} + +// WithHTTPClient supplies the HTTP client used for every request. Its Timeout +// applies to complete streaming requests as well as unary requests; leave it at +// zero and use contexts or operation options for long-lived streams. +func WithHTTPClient(client *http.Client) ClientOption { + return func(config *clientConfig) error { + if client == nil { + return &InvalidArgumentError{Message: "HTTP client cannot be nil"} + } + config.httpClient = client + return nil + } +} + +// WithProxy sets an HTTP or HTTPS proxy for SDK requests. +func WithProxy(value string) ClientOption { + return func(config *clientConfig) error { + proxyURL, err := parseHTTPURL("proxy URL", value) + if err != nil { + return err + } + config.proxyURL = proxyURL + return nil + } +} + +// WithHeaders adds headers to control-plane requests. +func WithHeaders(headers http.Header) ClientOption { + return func(config *clientConfig) error { + maps.Copy(config.headers, headers) + return nil + } +} + +// WithLogger enables structured request and lifecycle logging. +func WithLogger(logger *slog.Logger) ClientOption { + return func(config *clientConfig) error { + config.logger = logger + return nil + } +} + +func parseHTTPURL(name, value string) (*url.URL, error) { + parsed, err := url.Parse(value) + if err != nil || parsed.Host == "" || (parsed.Scheme != "http" && parsed.Scheme != "https") { + return nil, &InvalidArgumentError{Message: "invalid " + name + ": " + value, Cause: err} + } + return parsed, nil +} + +func applyOptions(options []ClientOption) (clientConfig, error) { + config, err := defaultClientConfig() + if err != nil { + return clientConfig{}, err + } + for _, option := range options { + if option == nil { + return clientConfig{}, errors.New("agentbox: nil client option") + } + if err := option(&config); err != nil { + return clientConfig{}, err + } + } + if config.apiURL == "https://api."+defaultDomain && config.domain != defaultDomain { + config.apiURL = "https://api." + config.domain + } + if config.httpClient != nil && config.proxyURL != nil { + return clientConfig{}, &InvalidArgumentError{Message: "WithHTTPClient and WithProxy cannot be combined"} + } + return config, nil +} diff --git a/packages/go-sdk/config_test.go b/packages/go-sdk/config_test.go new file mode 100644 index 000000000..fa51308cc --- /dev/null +++ b/packages/go-sdk/config_test.go @@ -0,0 +1,224 @@ +package agentbox + +import ( + "context" + "errors" + "io" + "log/slog" + "net" + "net/http" + "net/http/httptest" + "reflect" + "strings" + "testing" + "time" + + "connectrpc.com/connect" +) + +func TestClientConfigurationAndTransport(t *testing.T) { + t.Setenv("AGENTBOX_API_KEY", "environment-key") + t.Setenv("AGENTBOX_DOMAIN", "environment.test") + t.Setenv("AGENTBOX_DEBUG", "false") + var received http.Header + server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + received = request.Header.Clone() + writer.Header().Set("Content-Type", "application/json") + io.WriteString(writer, `[]`) + })) + defer server.Close() + client, err := NewClient( + WithAPIKey("option-key"), WithAPIURL(server.URL), WithDomain("option.test"), + WithSandboxURL(server.URL), WithDebug(true), WithRequestTimeout(time.Second), + WithHeaders(http.Header{"X-Custom": {"value"}}), + WithLogger(slog.New(slog.NewTextHandler(io.Discard, nil))), + ) + if err != nil { + t.Fatal(err) + } + if client.config.apiKey != "option-key" || client.config.domain != "option.test" || !client.config.debug { + t.Fatalf("unexpected config: %#v", client.config) + } + if _, err := client.Sandboxes.List(context.Background(), nil); err != nil { + t.Fatal(err) + } + if received.Get("X-API-KEY") != "option-key" || received.Get("X-Custom") != "value" || received.Get("User-Agent") != "agentbox-go-sdk/"+Version { + t.Fatalf("unexpected headers: %v", received) + } +} + +func TestTransportScopesCredentialsAndConfiguresH2C(t *testing.T) { + var sandboxHeaders http.Header + sandboxServer := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + sandboxHeaders = request.Header.Clone() + writer.WriteHeader(http.StatusNoContent) + })) + defer sandboxServer.Close() + apiServer := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + writer.Header().Set("Content-Type", "application/json") + io.WriteString(writer, `[]`) + })) + defer apiServer.Close() + client, err := NewClient(WithAPIURL(apiServer.URL), WithAPIKey("secret"), WithHeaders(http.Header{"X-Control": {"only"}})) + if err != nil { + t.Fatal(err) + } + request, _ := http.NewRequest(http.MethodGet, sandboxServer.URL+"/path?token=private", nil) + response, err := client.httpClient.Do(request) + if err != nil { + t.Fatal(err) + } + response.Body.Close() + if sandboxHeaders.Get("X-API-KEY") != "" || sandboxHeaders.Get("X-Control") != "" || sandboxHeaders.Get("User-Agent") == "" { + t.Fatalf("credentials escaped control plane: %v", sandboxHeaders) + } + + debugClient, err := NewClient(WithAPIURL(apiServer.URL), WithDebug(true)) + if err != nil { + t.Fatal(err) + } + transport := debugClient.envdClient.Transport.(*sdkTransport).base.(*http.Transport) + if transport.Protocols == nil || !transport.Protocols.UnencryptedHTTP2() || transport.Protocols.HTTP1() { + t.Fatalf("expected prior-knowledge h2c transport, got %v", transport.Protocols) + } + if transportAPIHost("::bad") != "" { + t.Fatal("invalid API URL unexpectedly has a host") + } + custom := &http.Client{Timeout: 37 * time.Second} + customClient, err := NewClient(WithAPIURL(apiServer.URL), WithHTTPClient(custom)) + if err != nil { + t.Fatal(err) + } + if customClient.httpClient.Timeout != custom.Timeout { + t.Fatalf("custom HTTP timeout = %s, want %s", customClient.httpClient.Timeout, custom.Timeout) + } +} + +func TestInvalidClientOptions(t *testing.T) { + t.Setenv("AGENTBOX_DEBUG", "invalid") + if _, err := NewClient(); err == nil { + t.Fatal("expected invalid environment error") + } + t.Setenv("AGENTBOX_DEBUG", "false") + tests := []ClientOption{WithDomain(" "), WithAPIURL("ftp://bad"), WithSandboxURL("bad"), WithProxy("bad"), WithRequestTimeout(-1), WithHTTPClient(nil), nil} + for _, option := range tests { + if _, err := NewClient(option); err == nil { + t.Fatalf("expected failure for option %#v", option) + } + } + if _, err := NewClient(WithHTTPClient(http.DefaultClient), WithProxy("http://localhost:8080")); err == nil { + t.Fatal("expected conflicting transport options") + } + t.Setenv("AGENTBOX_MAX_CONNECTIONS", "17") + t.Setenv("AGENTBOX_MAX_KEEPALIVE_CONNECTIONS", "3") + t.Setenv("AGENTBOX_KEEPALIVE_EXPIRY", "2.5") + client, err := NewClient(WithProxy("http://localhost:8080")) + if err != nil { + t.Fatal(err) + } + transport := client.httpClient.Transport.(*sdkTransport).base.(*http.Transport) + if transport.MaxIdleConns != 17 || transport.MaxIdleConnsPerHost != 3 || transport.IdleConnTimeout != 2500*time.Millisecond { + t.Fatalf("pool config: %#v", transport) + } + if roundTripper(nil) == nil { + t.Fatal("nil round tripper") + } +} + +func TestConnectErrorMappingsAndCodec(t *testing.T) { + for code, target := range map[connect.Code]error{connect.CodeUnauthenticated: &AuthenticationError{}, connect.CodePermissionDenied: &AuthenticationError{}, connect.CodeNotFound: &SandboxError{}, connect.CodeResourceExhausted: &RateLimitError{}, connect.CodeCanceled: &TimeoutError{}, connect.CodeDeadlineExceeded: &TimeoutError{}, connect.CodeUnavailable: &TimeoutError{}, connect.CodeInvalidArgument: &InvalidArgumentError{}, connect.CodeInternal: &SandboxError{}} { + mapped := connectError(connect.NewError(code, errors.New("failure"))) + if reflect.TypeOf(mapped) != reflect.TypeOf(target) { + t.Fatalf("code %s mapped to %T", code, mapped) + } + } + if connectError(nil) != nil { + t.Fatal("nil error changed") + } + plain := errors.New("plain") + if connectError(plain) != plain { + t.Fatal("plain error changed") + } + codec := tolerantJSONCodec{} + if codec.Name() != "json" { + t.Fatal(codec.Name()) + } + if _, err := codec.Marshal("bad"); err == nil { + t.Fatal("expected marshal type error") + } + if err := codec.Unmarshal(nil, "bad"); err == nil { + t.Fatal("expected unmarshal type error") + } + network := &net.DNSError{Err: "failure", Name: "example.test"} + if !isConnectionError(network) { + t.Fatal("network error not detected") + } + if _, ok := normalizeRequestError(network).(*SandboxError); !ok { + t.Fatal("network error not normalized") + } + if timeout := normalizeRequestError(context.DeadlineExceeded); !errors.Is(timeout, context.DeadlineExceeded) { + t.Fatal("deadline not preserved") + } else { + var typed *TimeoutError + if !errors.As(timeout, &typed) { + t.Fatalf("deadline mapped to %T", timeout) + } + } + ctx, cancel := withRequestTimeout(context.Background(), 0) + cancel() + if ctx.Err() == nil { + t.Fatal("zero-timeout context not cancellable") + } + client, err := NewClient(WithAPIURL("http://example.test"), WithHTTPClient(&http.Client{Transport: roundTripFunc(func(*http.Request) (*http.Response, error) { return nil, network })}), WithLogger(slog.New(slog.NewTextHandler(io.Discard, nil)))) + if err != nil { + t.Fatal(err) + } + if _, err := client.Sandboxes.List(context.Background(), nil); err == nil { + t.Fatal("expected transport error") + } + if _, err := NewClient(WithHTTPClient(&http.Client{})); err != nil { + t.Fatal(err) + } +} + +type roundTripFunc func(*http.Request) (*http.Response, error) + +func (function roundTripFunc) RoundTrip(request *http.Request) (*http.Response, error) { + return function(request) +} + +func TestErrorsAndHelpers(t *testing.T) { + cause := errors.New("cause") + values := []error{ + &SandboxError{APIError: APIError{StatusCode: 500, Message: "sandbox", Cause: cause}}, + &AuthenticationError{APIError: APIError{Message: "auth", Cause: cause}}, + &RateLimitError{APIError: APIError{Message: "rate", Cause: cause}}, + &TimeoutError{APIError: APIError{Message: "timeout", Cause: cause}}, + &FileNotFoundError{APIError: APIError{Message: "file", Cause: cause}}, + &NotEnoughSpaceError{APIError: APIError{Message: "disk", Cause: cause}}, + &SandboxNotFoundError{APIError: APIError{Message: "missing", Cause: cause}}, + &TemplateError{APIError: APIError{Message: "template", Cause: cause}}, + &BuildError{APIError: APIError{Message: "build", Cause: cause}}, + &FileUploadError{APIError: APIError{Message: "upload", Cause: cause}}, + &InvalidArgumentError{Message: "argument", Cause: cause}, + } + for _, value := range values { + if !strings.Contains(value.Error(), "agentbox:") || !errors.Is(value, cause) { + t.Fatalf("bad error %T: %v", value, value) + } + } + for status, target := range map[int]error{401: &AuthenticationError{}, 403: &AuthenticationError{}, 404: &SandboxNotFoundError{}, 413: &NotEnoughSpaceError{}, 429: &RateLimitError{}, 502: &TimeoutError{}, 504: &TimeoutError{}, 500: &SandboxError{}} { + err := decodeStatusError(status, http.StatusText(status), []byte(`{"message":"failure"}`)) + if reflect.TypeOf(err) != reflect.TypeOf(target) { + t.Fatalf("status %d mapped to %T", status, err) + } + } + if err := decodeStatusError(http.StatusBadRequest, "bad request", []byte("{\"message\":\"json-message\",\"code\":42}")); !strings.Contains(err.Error(), "json-message") { + t.Fatal(err) + } else if sandboxErr := err.(*SandboxError); sandboxErr.Code != "42" { + t.Fatalf("error code: %q", sandboxErr.Code) + } + if err := decodeStatusError(http.StatusTeapot, "teapot", nil); !strings.Contains(err.Error(), "teapot") { + t.Fatal(err) + } +} diff --git a/packages/go-sdk/doc.go b/packages/go-sdk/doc.go new file mode 100644 index 000000000..d52ff3d51 --- /dev/null +++ b/packages/go-sdk/doc.go @@ -0,0 +1,17 @@ +// Package agentbox provides the official Go client for AgentBox sandboxes. +// +// Create a client, start a sandbox, and run a command: +// +// client, err := agentbox.NewClient() +// if err != nil { +// log.Fatal(err) +// } +// sandbox, err := client.Sandboxes.Create(ctx, nil) +// if err != nil { +// log.Fatal(err) +// } +// defer sandbox.Kill(context.Background()) +// result, err := sandbox.Commands.Run(ctx, "echo", &agentbox.CommandOptions{ +// Args: []string{"Hello from AgentBox"}, +// }) +package agentbox diff --git a/packages/go-sdk/envd_test.go b/packages/go-sdk/envd_test.go new file mode 100644 index 000000000..910328e9d --- /dev/null +++ b/packages/go-sdk/envd_test.go @@ -0,0 +1,672 @@ +package agentbox + +import ( + "bytes" + "context" + "errors" + "fmt" + "io" + "net/http" + "net/http/httptest" + "strings" + "sync/atomic" + "testing" + "time" + + "connectrpc.com/connect" + api "github.com/abox-dev/sdk/packages/go-sdk/internal/gen/api" + filesystem "github.com/abox-dev/sdk/packages/go-sdk/internal/gen/envd/filesystem" + "github.com/abox-dev/sdk/packages/go-sdk/internal/gen/envd/filesystem/filesystemconnect" + process "github.com/abox-dev/sdk/packages/go-sdk/internal/gen/envd/process" + "github.com/abox-dev/sdk/packages/go-sdk/internal/gen/envd/process/processconnect" + "google.golang.org/protobuf/types/known/timestamppb" +) + +type testFilesystemServer struct { + filesystemconnect.UnimplementedFilesystemHandler +} + +type delayedReader struct { + delay time.Duration + data []byte +} + +type closeTrackingTransport struct { + base http.RoundTripper + closed atomic.Int64 +} + +func (transport *closeTrackingTransport) RoundTrip(request *http.Request) (*http.Response, error) { + response, err := transport.base.RoundTrip(request) + if err != nil { + return nil, err + } + switch request.URL.Path { + case processconnect.ProcessStartProcedure, processconnect.ProcessConnectProcedure, filesystemconnect.FilesystemWatchDirProcedure: + response.Body = &closeTrackingBody{ReadCloser: response.Body, closedCount: &transport.closed} + } + return response, nil +} + +type closeTrackingBody struct { + io.ReadCloser + closed atomic.Bool + closedCount *atomic.Int64 +} + +func (body *closeTrackingBody) Close() error { + if body.closed.CompareAndSwap(false, true) { + body.closedCount.Add(1) + } + return body.ReadCloser.Close() +} + +func (reader *delayedReader) Read(buffer []byte) (int, error) { + if reader.delay > 0 { + time.Sleep(reader.delay) + reader.delay = 0 + } + if len(reader.data) == 0 { + return 0, io.EOF + } + count := copy(buffer, reader.data) + reader.data = reader.data[count:] + return count, nil +} + +func testEntry(path string) *filesystem.EntryInfo { + return &filesystem.EntryInfo{Name: "file.txt", Path: path, Type: filesystem.FileType_FILE_TYPE_FILE, Size: 5, Mode: 0o644, Permissions: "rw-r--r--", Owner: "user", Group: "user", ModifiedTime: timestamppb.Now(), Metadata: map[string]string{"kind": "test"}} +} +func (testFilesystemServer) Stat(ctx context.Context, request *connect.Request[filesystem.StatRequest]) (*connect.Response[filesystem.StatResponse], error) { + if request.Msg.Path == "missing" { + return nil, connect.NewError(connect.CodeNotFound, fmt.Errorf("missing")) + } + if request.Msg.Path == "slow" { + select { + case <-time.After(time.Second): + case <-ctx.Done(): + return nil, ctx.Err() + } + } + return connect.NewResponse(&filesystem.StatResponse{Entry: testEntry(request.Msg.Path)}), nil +} +func (testFilesystemServer) MakeDir(_ context.Context, request *connect.Request[filesystem.MakeDirRequest]) (*connect.Response[filesystem.MakeDirResponse], error) { + entry := testEntry(request.Msg.Path) + entry.Type = filesystem.FileType_FILE_TYPE_DIRECTORY + return connect.NewResponse(&filesystem.MakeDirResponse{Entry: entry}), nil +} +func (testFilesystemServer) Move(_ context.Context, request *connect.Request[filesystem.MoveRequest]) (*connect.Response[filesystem.MoveResponse], error) { + return connect.NewResponse(&filesystem.MoveResponse{Entry: testEntry(request.Msg.Destination)}), nil +} +func (testFilesystemServer) ListDir(context.Context, *connect.Request[filesystem.ListDirRequest]) (*connect.Response[filesystem.ListDirResponse], error) { + return connect.NewResponse(&filesystem.ListDirResponse{Entries: []*filesystem.EntryInfo{testEntry("/file.txt")}}), nil +} +func (testFilesystemServer) Remove(context.Context, *connect.Request[filesystem.RemoveRequest]) (*connect.Response[filesystem.RemoveResponse], error) { + return connect.NewResponse(&filesystem.RemoveResponse{}), nil +} +func (testFilesystemServer) WatchDir(ctx context.Context, request *connect.Request[filesystem.WatchDirRequest], stream *connect.ServerStream[filesystem.WatchDirResponse]) error { + count := 1 + if request.Msg.Path == "/flood" { + count = 40 + } + for range count { + if err := stream.Send(&filesystem.WatchDirResponse{Event: &filesystem.WatchDirResponse_Filesystem{Filesystem: &filesystem.FilesystemEvent{Name: "file.txt", Type: filesystem.EventType_EVENT_TYPE_WRITE, Entry: testEntry("/file.txt")}}}); err != nil { + return err + } + } + if request.Msg.Path == "/flood" { + <-ctx.Done() + return ctx.Err() + } + return nil +} + +type testProcessServer struct { + processconnect.UnimplementedProcessHandler +} + +func (testProcessServer) List(context.Context, *connect.Request[process.ListRequest]) (*connect.Response[process.ListResponse], error) { + cwd := "/app" + tag := "tag" + return connect.NewResponse(&process.ListResponse{Processes: []*process.ProcessInfo{{Pid: 7, Tag: &tag, Config: &process.ProcessConfig{Cmd: "echo", Args: []string{"ok"}, Envs: map[string]string{"A": "B"}, Cwd: &cwd}}}}), nil +} +func (testProcessServer) Start(_ context.Context, request *connect.Request[process.StartRequest], stream *connect.ServerStream[process.StartResponse]) error { + if err := stream.Send(&process.StartResponse{Event: &process.ProcessEvent{Event: &process.ProcessEvent_Start{Start: &process.ProcessEvent_StartEvent{Pid: 7}}}}); err != nil { + return err + } + output := &process.ProcessEvent_DataEvent{Output: &process.ProcessEvent_DataEvent_Stdout{Stdout: []byte("ok\n")}} + if request.Msg.Pty != nil { + output.Output = &process.ProcessEvent_DataEvent_Pty{Pty: []byte("pty\n")} + } else if request.Msg.Process.GetCmd() == "stderr" { + output.Output = &process.ProcessEvent_DataEvent_Stderr{Stderr: []byte("bad\n")} + } + count := 1 + if request.Msg.Process.GetCmd() == "flood" { + count = 40 + } + for range count { + if err := stream.Send(&process.StartResponse{Event: &process.ProcessEvent{Event: &process.ProcessEvent_Data{Data: output}}}); err != nil { + return err + } + } + exitCode := int32(0) + message := "" + if request.Msg.Process.GetCmd() == "false" { + exitCode, message = 2, "failed" + } + return stream.Send(&process.StartResponse{Event: &process.ProcessEvent{Event: &process.ProcessEvent_End{End: &process.ProcessEvent_EndEvent{Exited: true, ExitCode: exitCode, Status: "exited", Error: &message}}}}) +} +func (testProcessServer) Connect(_ context.Context, _ *connect.Request[process.ConnectRequest], stream *connect.ServerStream[process.ConnectResponse]) error { + if err := stream.Send(&process.ConnectResponse{Event: &process.ProcessEvent{Event: &process.ProcessEvent_Data{Data: &process.ProcessEvent_DataEvent{Output: &process.ProcessEvent_DataEvent_Stderr{Stderr: []byte("connected")}}}}}); err != nil { + return err + } + return stream.Send(&process.ConnectResponse{Event: &process.ProcessEvent{Event: &process.ProcessEvent_End{End: &process.ProcessEvent_EndEvent{Exited: true}}}}) +} +func (testProcessServer) SendInput(context.Context, *connect.Request[process.SendInputRequest]) (*connect.Response[process.SendInputResponse], error) { + return connect.NewResponse(&process.SendInputResponse{}), nil +} +func (testProcessServer) SendSignal(context.Context, *connect.Request[process.SendSignalRequest]) (*connect.Response[process.SendSignalResponse], error) { + return connect.NewResponse(&process.SendSignalResponse{}), nil +} +func (testProcessServer) CloseStdin(context.Context, *connect.Request[process.CloseStdinRequest]) (*connect.Response[process.CloseStdinResponse], error) { + return connect.NewResponse(&process.CloseStdinResponse{}), nil +} +func (testProcessServer) Update(context.Context, *connect.Request[process.UpdateRequest]) (*connect.Response[process.UpdateResponse], error) { + return connect.NewResponse(&process.UpdateResponse{}), nil +} + +func newEnvdTestSandbox(t *testing.T, options ...ClientOption) (*Sandbox, func()) { + t.Helper() + mux := http.NewServeMux() + fsPath, fsHandler := filesystemconnect.NewFilesystemHandler(testFilesystemServer{}, connect.WithCodec(tolerantJSONCodec{})) + mux.Handle(fsPath, fsHandler) + processPath, processHandler := processconnect.NewProcessHandler(testProcessServer{}, connect.WithCodec(tolerantJSONCodec{})) + mux.Handle(processPath, processHandler) + mux.HandleFunc("/health", func(writer http.ResponseWriter, _ *http.Request) { + writer.WriteHeader(http.StatusNoContent) + }) + mux.HandleFunc("/headers", func(writer http.ResponseWriter, request *http.Request) { + if request.Header.Get("X-Test") != "value" || request.Header.Get("Content-Type") != "text/plain" { + http.Error(writer, "missing custom headers", http.StatusBadRequest) + return + } + writer.WriteHeader(http.StatusNoContent) + }) + mux.HandleFunc("/files", func(writer http.ResponseWriter, request *http.Request) { + if request.Method == http.MethodGet { + if request.URL.Query().Get("path") == "missing-http" { + http.NotFound(writer, request) + return + } + if request.URL.Query().Get("path") == "old-user" && request.URL.Query().Get("username") != "user" { + http.Error(writer, "missing legacy user", http.StatusBadRequest) + return + } + writer.Write([]byte("hello")) + return + } + if err := request.ParseMultipartForm(1 << 20); err != nil { + http.Error(writer, err.Error(), 400) + return + } + file, _, err := request.FormFile("file") + if err != nil { + http.Error(writer, err.Error(), 400) + return + } + data, _ := io.ReadAll(file) + file.Close() + if string(data) != "hello" { + http.Error(writer, "bad file", 400) + return + } + writer.Header().Set("Content-Type", "application/json") + fmt.Fprint(writer, `[{"name":"file.txt","type":"file","path":"/file.txt","metadata":{"kind":"test"}}]`) + }) + server := httptest.NewServer(mux) + clientOptions := []ClientOption{WithAPIURL(server.URL), WithSandboxURL(server.URL)} + clientOptions = append(clientOptions, options...) + client, err := NewClient(clientOptions...) + if err != nil { + server.Close() + t.Fatal(err) + } + sandbox := client.sandboxFromAPI(api.Sandbox{SandboxID: "sbx", TemplateID: "base", EnvdVersion: "1", EnvdAccessToken: ptr("token")}) + return sandbox, server.Close +} + +func TestFilesystem(t *testing.T) { + sandbox, closeServer := newEnvdTestSandbox(t) + defer closeServer() + ctx := context.Background() + if text, err := sandbox.Files.ReadText(ctx, "/file.txt", ""); err != nil || text != "hello" { + t.Fatalf("read: %q %v", text, err) + } + if _, err := sandbox.Files.Read(ctx, "", ""); err == nil { + t.Fatal("expected empty path validation") + } + var output strings.Builder + if count, err := sandbox.Files.ReadTo(ctx, "/file.txt", "", &output); err != nil || count != 5 { + t.Fatalf("read to: %d %v", count, err) + } + if entry, err := sandbox.Files.WriteText(ctx, "/file.txt", "hello", &WriteFileOptions{Metadata: map[string]string{"kind": "test"}}); err != nil || entry.Path != "/file.txt" { + t.Fatalf("write: %#v %v", entry, err) + } + if _, err := sandbox.Files.WriteBytes(ctx, "/file.txt", []byte("hello"), nil); err != nil { + t.Fatal(err) + } + if entries, err := sandbox.Files.WriteBatch(ctx, []WriteFile{{Path: "/file.txt", Data: strings.NewReader("hello")}}, ""); err != nil || len(entries) != 1 { + t.Fatalf("batch: %v", err) + } + if entry, err := sandbox.Files.Stat(ctx, "/file.txt"); err != nil || entry.Metadata["kind"] != "test" { + t.Fatalf("stat: %#v %v", entry, err) + } + if exists, err := sandbox.Files.Exists(ctx, "missing"); err != nil || exists { + t.Fatalf("exists: %v %v", exists, err) + } + if exists, err := sandbox.Files.Exists(ctx, "/file.txt"); err != nil || !exists { + t.Fatalf("existing file: %v %v", exists, err) + } + if entries, err := sandbox.Files.WriteBatch(ctx, []WriteFile{{Path: "", Data: nil}}, ""); err == nil || len(entries) != 0 { + t.Fatal("expected batch failure") + } + if entries, err := sandbox.Files.List(ctx, "/", 1); err != nil || len(entries) != 1 { + t.Fatalf("list: %v", err) + } + if _, err := sandbox.Files.MakeDir(ctx, "/dir"); err != nil { + t.Fatal(err) + } + if _, err := sandbox.Files.Rename(ctx, "/file.txt", "/new.txt"); err != nil { + t.Fatal(err) + } + if err := sandbox.Files.Remove(ctx, "/new.txt"); err != nil { + t.Fatal(err) + } + watcher, err := sandbox.Files.Watch(ctx, "/", &WatchOptions{IncludeEntry: true}) + if err != nil { + t.Fatal(err) + } + event := <-watcher.Events + if event.Type != "write" || event.Entry == nil { + t.Fatalf("event: %#v", event) + } + if err := watcher.Close(); err != nil { + t.Fatal(err) + } + if _, err := sandbox.Files.WriteText(ctx, "/x", "x", &WriteFileOptions{Metadata: map[string]string{"bad key": "x"}}); err == nil { + t.Fatal("expected metadata validation") + } + if _, err := sandbox.Files.ReadText(ctx, "missing-http", ""); err == nil { + t.Fatal("expected HTTP file error") + } else { + var missing *FileNotFoundError + if !errors.As(err, &missing) { + t.Fatalf("expected FileNotFoundError, got %T", err) + } + } + response, err := sandbox.Request(ctx, envdPort, http.MethodGet, "/files?path=/file.txt", nil, false) + if err != nil { + t.Fatal(err) + } + response.Body.Close() + response, err = sandbox.RequestWithOptions(ctx, envdPort, http.MethodPost, "/headers", strings.NewReader("body"), &SandboxRequestOptions{Headers: http.Header{"X-Test": {"value"}}, ContentType: "text/plain"}) + if err != nil { + t.Fatal(err) + } + response.Body.Close() + if running, err := sandbox.IsRunning(ctx); err != nil || !running { + t.Fatalf("is running: %v %v", running, err) + } +} + +func TestFileWriteStreamingTimeout(t *testing.T) { + sandbox, closeServer := newEnvdTestSandbox(t) + defer closeServer() + sandbox.client.config.requestTimeout = 10 * time.Millisecond + + entry, err := sandbox.Files.Write(t.Context(), "/file.txt", &delayedReader{delay: 30 * time.Millisecond, data: []byte("hello")}, nil) + if err != nil || entry.Path != "/file.txt" { + t.Fatalf("streaming upload inherited unary timeout: %#v %v", entry, err) + } + + _, err = sandbox.Files.Write(t.Context(), "/file.txt", &delayedReader{delay: 30 * time.Millisecond, data: []byte("hello")}, &WriteFileOptions{RequestTimeout: 10 * time.Millisecond}) + var timeout *TimeoutError + if !errors.As(err, &timeout) { + t.Fatalf("upload timeout error = %T %v", err, err) + } + if _, err := sandbox.Files.WriteText(t.Context(), "/file.txt", "hello", &WriteFileOptions{RequestTimeout: -time.Second}); err == nil { + t.Fatal("expected negative upload timeout validation") + } +} + +func TestEnvdVersionCompatibility(t *testing.T) { + sandbox, closeServer := newEnvdTestSandbox(t) + defer closeServer() + sandbox.EnvdVersion = "0.3.9" + if _, err := sandbox.Files.ReadText(t.Context(), "old-user", ""); err != nil { + t.Fatal(err) + } + if _, err := sandbox.Files.WriteText(t.Context(), "/file.txt", "hello", &WriteFileOptions{Metadata: map[string]string{"kind": "test"}}); err == nil { + t.Fatal("expected metadata version gate") + } + if _, err := sandbox.Files.Watch(t.Context(), "/", &WatchOptions{IncludeEntry: true}); err == nil { + t.Fatal("expected watch entry version gate") + } else { + var sandboxError *SandboxError + if !errors.As(err, &sandboxError) { + t.Fatalf("watch version error = %T, want SandboxError", err) + } + } + sandbox.EnvdVersion = "0.1.3" + if _, err := sandbox.Files.Watch(t.Context(), "/", &WatchOptions{Recursive: true}); err == nil { + t.Fatal("expected recursive watch version gate") + } + sandbox.EnvdVersion = "0.6.3" + if _, err := sandbox.Files.Watch(t.Context(), "/", &WatchOptions{AllowNetworkMounts: true}); err == nil { + t.Fatal("expected network mount watch version gate") + } + if envdAtLeast("0.6.1", 0, 6, 2) || !envdAtLeast("v0.6.2-beta", 0, 6, 2) || !envdAtLeast("invalid", 9, 0, 0) || !envdAtLeast("1.2.3.4", 9, 0, 0) { + t.Fatal("unexpected envd version comparison") + } + response, err := sandbox.RequestWithOptions(t.Context(), envdPort, http.MethodGet, "/health", nil, nil) + if err != nil { + t.Fatal(err) + } + response.Body.Close() +} + +func TestUnaryEnvdRequestTimeout(t *testing.T) { + sandbox, closeServer := newEnvdTestSandbox(t) + defer closeServer() + sandbox.client.config.requestTimeout = 10 * time.Millisecond + _, err := sandbox.Files.Stat(t.Context(), "slow") + var timeout *TimeoutError + if !errors.As(err, &timeout) { + t.Fatalf("expected TimeoutError, got %T: %v", err, err) + } +} + +func TestCommandsAndPTY(t *testing.T) { + sandbox, closeServer := newEnvdTestSandbox(t) + defer closeServer() + ctx := context.Background() + stdoutCalled := false + result, err := sandbox.Commands.Run(ctx, "echo", &CommandOptions{Args: []string{"ok"}, OnStdout: func([]byte) { stdoutCalled = true }}) + if err != nil || string(result.Stdout) != "ok\n" || !stdoutCalled { + t.Fatalf("run: %#v %v", result, err) + } + if _, err := sandbox.Commands.Run(ctx, "echo", nil); err != nil { + t.Fatal(err) + } + stderrCalled := false + if result, err := sandbox.Commands.Run(ctx, "stderr", &CommandOptions{OnStderr: func([]byte) { stderrCalled = true }}); err != nil || string(result.Stderr) != "bad\n" || !stderrCalled { + t.Fatalf("stderr: %#v %v", result, err) + } + if _, err := sandbox.Commands.Run(ctx, "false", nil); err == nil { + t.Fatal("expected command exit error") + } + processes, err := sandbox.Commands.List(ctx) + if err != nil || len(processes) != 1 || processes[0].Cwd != "/app" { + t.Fatalf("list: %#v %v", processes, err) + } + handle, err := sandbox.Commands.Connect(ctx, 7, "") + if err != nil { + t.Fatal(err) + } + defaultPTY, err := sandbox.PTY.Create(ctx, "sh", nil) + if err != nil { + t.Fatal(err) + } + for range defaultPTY.PTY { + } + if result, err := defaultPTY.Wait(ctx); err != nil || len(result.Stdout) != 0 { + t.Fatal(err) + } + if _, err := handle.Write(ctx, []byte("input")); err != nil { + t.Fatal(err) + } + if err := handle.CloseStdin(ctx); err != nil { + t.Fatal(err) + } + if err := handle.Kill(ctx); err != nil { + t.Fatal(err) + } + if err := sandbox.Commands.Terminate(ctx, 7, ""); err != nil { + t.Fatal(err) + } + for range handle.Stderr { + } + if _, err := handle.Wait(ctx); err != nil { + t.Fatal(err) + } + var ptyOutput bytes.Buffer + pty, err := sandbox.PTY.Create(ctx, "sh", &PTYOptions{Cols: 100, Rows: 40, OnPTY: func(chunk []byte) { ptyOutput.Write(chunk) }}) + if err != nil { + t.Fatal(err) + } + if err := sandbox.PTY.Input(ctx, pty, []byte("x")); err != nil { + t.Fatal(err) + } + if err := sandbox.PTY.Resize(ctx, pty, 80, 24); err != nil { + t.Fatal(err) + } + if err := sandbox.PTY.Kill(ctx, pty); err != nil { + t.Fatal(err) + } + for range pty.PTY { + } + if _, err := pty.Wait(ctx); err != nil { + t.Fatal(err) + } + if ptyOutput.String() != "pty\n" { + t.Fatalf("PTY callback output = %q", ptyOutput.String()) + } + if _, err := processSelector(1, "tag"); err == nil { + t.Fatal("expected selector error") + } + attached, err := sandbox.PTY.Connect(ctx, 7, "") + if err != nil { + t.Fatal(err) + } + for range attached.Stderr { + } + if _, err := attached.Wait(ctx); err != nil { + t.Fatal(err) + } + withoutStart, err := sandbox.Commands.Connect(ctx, 0, "tag") + if err != nil { + t.Fatal(err) + } + if _, err := withoutStart.PID(ctx); err == nil { + t.Fatal("expected PID error for stream ending without a start event") + } +} + +func TestCommandWaitDoesNotRequireDrainingOutput(t *testing.T) { + sandbox, closeServer := newEnvdTestSandbox(t) + defer closeServer() + handle, err := sandbox.Commands.Start(t.Context(), "flood", nil) + if err != nil { + t.Fatal(err) + } + waitCtx, cancel := context.WithTimeout(t.Context(), time.Second) + defer cancel() + result, err := handle.Wait(waitCtx) + if err != nil { + t.Fatal(err) + } + if expected := bytes.Repeat([]byte("ok\n"), 40); !bytes.Equal(result.Stdout, expected) { + t.Fatalf("stdout length = %d, want %d", len(result.Stdout), len(expected)) + } +} + +func TestCommandWaitPreservesOutputChannelTail(t *testing.T) { + sandbox, closeServer := newEnvdTestSandbox(t) + defer closeServer() + handle, err := sandbox.Commands.Start(t.Context(), "flood", nil) + if err != nil { + t.Fatal(err) + } + read := make(chan []byte, 1) + go func() { + var output bytes.Buffer + for chunk := range handle.Stdout { + output.Write(chunk) + time.Sleep(2 * time.Millisecond) + } + read <- output.Bytes() + }() + + result, err := handle.Wait(t.Context()) + if err != nil { + t.Fatal(err) + } + expected := bytes.Repeat([]byte("ok\n"), 40) + if !bytes.Equal(result.Stdout, expected) { + t.Fatalf("result stdout length = %d, want %d", len(result.Stdout), len(expected)) + } + select { + case output := <-read: + if !bytes.Equal(output, expected) { + t.Fatalf("channel stdout length = %d, want %d", len(output), len(expected)) + } + case <-time.After(time.Second): + t.Fatal("stdout channel did not finish draining") + } +} + +func TestStreamingResponsesAreClosed(t *testing.T) { + transport := &closeTrackingTransport{base: http.DefaultTransport.(*http.Transport).Clone()} + sandbox, closeServer := newEnvdTestSandbox(t, WithHTTPClient(&http.Client{Transport: transport})) + defer closeServer() + + const commandRuns = 30 + for range commandRuns { + if _, err := sandbox.Commands.Run(t.Context(), "echo", nil); err != nil { + t.Fatal(err) + } + } + + connected, err := sandbox.Commands.Connect(t.Context(), 7, "") + if err != nil { + t.Fatal(err) + } + for range connected.Stderr { + } + if _, err := connected.Wait(t.Context()); err != nil { + t.Fatal(err) + } + + terminal, err := sandbox.PTY.Create(t.Context(), "sh", nil) + if err != nil { + t.Fatal(err) + } + for range terminal.PTY { + } + if _, err := terminal.Wait(t.Context()); err != nil { + t.Fatal(err) + } + + watcher, err := sandbox.Files.Watch(t.Context(), "/", nil) + if err != nil { + t.Fatal(err) + } + if event := <-watcher.Events; event.Type != "write" { + t.Fatalf("watch event = %#v", event) + } + if err := watcher.Close(); err != nil { + t.Fatal(err) + } + + if got, want := transport.closed.Load(), int64(commandRuns+3); got != want { + t.Fatalf("closed streaming response bodies = %d, want %d", got, want) + } +} + +func TestWatchCloseDoesNotRequireDrainingEvents(t *testing.T) { + sandbox, closeServer := newEnvdTestSandbox(t) + defer closeServer() + watcher, err := sandbox.Files.Watch(t.Context(), "/flood", nil) + if err != nil { + t.Fatal(err) + } + deadline := time.Now().Add(time.Second) + for len(watcher.Events) < cap(watcher.Events) && time.Now().Before(deadline) { + time.Sleep(time.Millisecond) + } + closed := make(chan error, 1) + go func() { closed <- watcher.Close() }() + select { + case err := <-closed: + if err != nil { + t.Fatal(err) + } + case <-time.After(time.Second): + t.Fatal("watcher Close blocked with an unread full event channel") + } +} + +func ptr[T any](value T) *T { return &value } + +func TestCommandHelpersAndFileMappings(t *testing.T) { + if (&CommandExitError{Result: CommandResult{ExitCode: 2}, Message: "bad"}).Error() == "" || (&CommandExitError{Result: CommandResult{ExitCode: 1}}).Error() == "" { + t.Fatal("empty exit error") + } + if _, err := processSelector(0, ""); err == nil { + t.Fatal("expected selector validation") + } + if selector, err := processSelector(0, "tag"); err != nil || selector.GetTag() != "tag" { + t.Fatalf("tag selector: %v", err) + } + for value, expected := range map[filesystem.EventType]string{filesystem.EventType_EVENT_TYPE_CREATE: "create", filesystem.EventType_EVENT_TYPE_WRITE: "write", filesystem.EventType_EVENT_TYPE_REMOVE: "remove", filesystem.EventType_EVENT_TYPE_RENAME: "rename", filesystem.EventType_EVENT_TYPE_CHMOD: "chmod", filesystem.EventType_EVENT_TYPE_UNSPECIFIED: "unknown"} { + if mapEventType(value) != expected { + t.Fatalf("event %v", value) + } + } + if mapEntry(nil) != nil { + t.Fatal("nil entry changed") + } + for value, expected := range map[filesystem.FileType]FileType{filesystem.FileType_FILE_TYPE_FILE: FileTypeFile, filesystem.FileType_FILE_TYPE_DIRECTORY: FileTypeDirectory, filesystem.FileType_FILE_TYPE_SYMLINK: FileTypeSymlink} { + entry := testEntry("/x") + entry.Type = value + if mapEntry(entry).Type != expected { + t.Fatalf("type %v", value) + } + } + entryWithoutTime := testEntry("/x") + entryWithoutTime.ModifiedTime = nil + if !mapEntry(entryWithoutTime).ModifiedAt.IsZero() { + t.Fatal("unexpected modification time") + } + header := make(http.Header) + if err := addMetadataHeaders(header, map[string]string{"ok": "bad\n"}); err == nil { + t.Fatal("expected bad metadata value") + } + sandbox, closeServer := newEnvdTestSandbox(t) + defer closeServer() + ctx := context.Background() + if _, err := sandbox.Commands.Start(ctx, "", nil); err == nil { + t.Fatal("expected empty command") + } + if _, err := sandbox.PTY.Create(ctx, "", nil); err == nil { + t.Fatal("expected empty PTY command") + } + handle := newCommandHandle(sandbox.Commands, "") + canceled, cancel := context.WithCancel(ctx) + cancel() + if _, err := handle.PID(canceled); err == nil { + t.Fatal("expected PID cancellation") + } + if _, err := handle.Wait(canceled); err == nil { + t.Fatal("expected wait cancellation") + } + if err := sandbox.PTY.Resize(ctx, handle, 0, 1); err == nil { + t.Fatal("expected invalid PTY size") + } + if _, err := sandbox.Files.Write(ctx, "", nil, nil); err == nil { + t.Fatal("expected write validation") + } + if _, err := sandbox.Request(ctx, envdPort, "bad\nmethod", "/", nil, false); err == nil { + t.Fatal("expected request validation") + } +} diff --git a/packages/go-sdk/errors.go b/packages/go-sdk/errors.go new file mode 100644 index 000000000..0cd865c9e --- /dev/null +++ b/packages/go-sdk/errors.go @@ -0,0 +1,89 @@ +package agentbox + +import "fmt" + +// APIError describes a non-successful HTTP or Connect response. +type APIError struct { + StatusCode int + Code string + Message string + Cause error +} + +func (e *APIError) Error() string { + if e.StatusCode != 0 { + return fmt.Sprintf("agentbox: %d: %s", e.StatusCode, e.Message) + } + return "agentbox: " + e.Message +} + +func (e *APIError) Unwrap() error { return e.Cause } + +// SandboxError is the base error for sandbox operations. +type SandboxError struct{ APIError } + +func (e *SandboxError) Error() string { return e.APIError.Error() } +func (e *SandboxError) Unwrap() error { return e.APIError.Unwrap() } + +// AuthenticationError reports missing or invalid credentials. +type AuthenticationError struct{ APIError } + +func (e *AuthenticationError) Error() string { return e.APIError.Error() } +func (e *AuthenticationError) Unwrap() error { return e.APIError.Unwrap() } + +// RateLimitError reports an exhausted API quota. +type RateLimitError struct{ APIError } + +func (e *RateLimitError) Error() string { return e.APIError.Error() } +func (e *RateLimitError) Unwrap() error { return e.APIError.Unwrap() } + +// TimeoutError reports a request, execution, or sandbox timeout. +type TimeoutError struct{ APIError } + +func (e *TimeoutError) Error() string { return e.APIError.Error() } +func (e *TimeoutError) Unwrap() error { return e.APIError.Unwrap() } + +// InvalidArgumentError reports invalid SDK input. +type InvalidArgumentError struct { + Message string + Cause error +} + +func (e *InvalidArgumentError) Error() string { return "agentbox: " + e.Message } +func (e *InvalidArgumentError) Unwrap() error { return e.Cause } + +// FileNotFoundError reports a missing sandbox file. +type FileNotFoundError struct{ APIError } + +func (e *FileNotFoundError) Error() string { return e.APIError.Error() } +func (e *FileNotFoundError) Unwrap() error { return e.APIError.Unwrap() } + +// NotEnoughSpaceError reports exhausted sandbox storage. +type NotEnoughSpaceError struct{ APIError } + +func (e *NotEnoughSpaceError) Error() string { return e.APIError.Error() } +func (e *NotEnoughSpaceError) Unwrap() error { return e.APIError.Unwrap() } + +// SandboxNotFoundError reports a missing or expired sandbox. +type SandboxNotFoundError struct{ APIError } + +func (e *SandboxNotFoundError) Error() string { return e.APIError.Error() } +func (e *SandboxNotFoundError) Unwrap() error { return e.APIError.Unwrap() } + +// TemplateError reports an invalid or incompatible template. +type TemplateError struct{ APIError } + +func (e *TemplateError) Error() string { return e.APIError.Error() } +func (e *TemplateError) Unwrap() error { return e.APIError.Unwrap() } + +// BuildError reports a failed template build. +type BuildError struct{ APIError } + +func (e *BuildError) Error() string { return e.APIError.Error() } +func (e *BuildError) Unwrap() error { return e.APIError.Unwrap() } + +// FileUploadError reports a failed template file upload. +type FileUploadError struct{ APIError } + +func (e *FileUploadError) Error() string { return e.APIError.Error() } +func (e *FileUploadError) Unwrap() error { return e.APIError.Unwrap() } diff --git a/packages/go-sdk/examples_test.go b/packages/go-sdk/examples_test.go new file mode 100644 index 000000000..1468b63f9 --- /dev/null +++ b/packages/go-sdk/examples_test.go @@ -0,0 +1,26 @@ +package agentbox_test + +import ( + "context" + "log" + + "github.com/abox-dev/sdk/packages/go-sdk" +) + +func ExampleClient() { + client, err := agentbox.NewClient() + if err != nil { + log.Fatal(err) + } + sandbox, err := client.Sandboxes.Create(context.Background(), nil) + if err != nil { + log.Fatal(err) + } + defer sandbox.Kill(context.Background()) + _, _ = sandbox.Commands.Run(context.Background(), "echo", &agentbox.CommandOptions{Args: []string{"hello"}}) +} + +func ExampleTemplateBuilder() { + template := agentbox.NewTemplate(".").FromPython("3.13").Copy("requirements.txt", "/app/", nil).PipInstall().Workdir("/app") + _, _ = template.JSON() +} diff --git a/packages/go-sdk/filesystem.go b/packages/go-sdk/filesystem.go new file mode 100644 index 000000000..b615b17c1 --- /dev/null +++ b/packages/go-sdk/filesystem.go @@ -0,0 +1,480 @@ +package agentbox + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "maps" + "mime/multipart" + "net/http" + "net/url" + pathpkg "path" + "regexp" + "slices" + "strconv" + "strings" + "sync" + "time" + + "connectrpc.com/connect" + filesystem "github.com/abox-dev/sdk/packages/go-sdk/internal/gen/envd/filesystem" + "github.com/abox-dev/sdk/packages/go-sdk/internal/gen/envd/filesystem/filesystemconnect" +) + +// FileType identifies a filesystem entry kind. +type FileType string + +const ( + FileTypeFile FileType = "file" + FileTypeDirectory FileType = "dir" + FileTypeSymlink FileType = "symlink" +) + +// EntryInfo describes a sandbox filesystem entry. +type EntryInfo struct { + Name string + Type FileType + Path string + Size int64 + Mode uint32 + Permissions string + Owner string + Group string + ModifiedAt time.Time + SymlinkTarget string + Metadata map[string]string +} + +// WriteFile describes one batch upload. +type WriteFile struct { + Path string + Data io.Reader + Metadata map[string]string +} + +// WriteFileOptions configures file ownership, metadata, and upload timeout. +type WriteFileOptions struct { + User string + Metadata map[string]string + // RequestTimeout limits the complete streaming upload. Zero leaves the + // upload bounded only by ctx and a custom HTTP client timeout. + RequestTimeout time.Duration +} + +// WatchOptions configures recursive and enriched filesystem events. +type WatchOptions struct{ Recursive, IncludeEntry, AllowNetworkMounts bool } + +// FileEvent describes a filesystem change. +type FileEvent struct { + Name string + Type string + Entry *EntryInfo +} + +// WatchHandle owns a directory watch stream. +type WatchHandle struct { + Events <-chan FileEvent + done <-chan struct{} + cancel context.CancelFunc + mu sync.Mutex + err error +} + +// Close stops the watcher. +func (handle *WatchHandle) Close() error { + handle.cancel() + <-handle.done + handle.mu.Lock() + defer handle.mu.Unlock() + return handle.err +} + +// FileService reads and mutates sandbox files. +type FileService struct { + sandbox *Sandbox + client filesystemconnect.FilesystemClient +} + +func newFileService(sandbox *Sandbox) *FileService { + return &FileService{sandbox: sandbox, client: filesystemconnect.NewFilesystemClient(sandbox.client.envdClient, sandbox.envdURL(envdPort, false), connect.WithCodec(tolerantJSONCodec{}), connect.WithAcceptCompression("gzip", nil, nil))} +} + +// Read opens a streaming file response. The caller must close it. +func (service *FileService) Read(ctx context.Context, path, user string) (io.ReadCloser, error) { + if path == "" { + return nil, &InvalidArgumentError{Message: "file path cannot be empty"} + } + user = service.sandbox.resolveUser(user) + endpoint, _ := url.Parse(service.sandbox.envdURL(envdPort, false) + "/files") + query := endpoint.Query() + query.Set("path", path) + if user != "" { + query.Set("username", user) + } + endpoint.RawQuery = query.Encode() + request, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil) + if err != nil { + return nil, err + } + request.Header = service.sandbox.envdHeaders(envdPort) + response, err := service.sandbox.client.httpClient.Do(request) + if err != nil { + return nil, normalizeRequestError(err) + } + if response.StatusCode < 200 || response.StatusCode >= 300 { + defer response.Body.Close() + return nil, decodeFileHTTPError(response) + } + return response.Body, nil +} + +// ReadBytes reads a complete file. +func (service *FileService) ReadBytes(ctx context.Context, path, user string) ([]byte, error) { + reader, err := service.Read(ctx, path, user) + if err != nil { + return nil, err + } + defer reader.Close() + return io.ReadAll(reader) +} + +// ReadText reads a UTF-8 file as a string. +func (service *FileService) ReadText(ctx context.Context, path, user string) (string, error) { + data, err := service.ReadBytes(ctx, path, user) + return string(data), err +} + +// ReadTo streams a file into writer. +func (service *FileService) ReadTo(ctx context.Context, path, user string, writer io.Writer) (int64, error) { + reader, err := service.Read(ctx, path, user) + if err != nil { + return 0, err + } + defer reader.Close() + return io.Copy(writer, reader) +} + +// Write uploads a file from reader. +func (service *FileService) Write(ctx context.Context, path string, reader io.Reader, options *WriteFileOptions) (*EntryInfo, error) { + if path == "" || reader == nil { + return nil, &InvalidArgumentError{Message: "file path and reader are required"} + } + if options == nil { + options = &WriteFileOptions{} + } + if options.RequestTimeout < 0 { + return nil, &InvalidArgumentError{Message: "file upload timeout cannot be negative"} + } + if len(options.Metadata) > 0 && !envdAtLeast(service.sandbox.EnvdVersion, 0, 6, 2) { + return nil, &TemplateError{APIError: APIError{Message: "file metadata requires envd 0.6.2 or later"}} + } + user := service.sandbox.resolveUser(options.User) + endpoint, _ := url.Parse(service.sandbox.envdURL(envdPort, false) + "/files") + query := endpoint.Query() + query.Set("path", path) + if user != "" { + query.Set("username", user) + } + endpoint.RawQuery = query.Encode() + headers := service.sandbox.envdHeaders(envdPort) + if err := addMetadataHeaders(headers, options.Metadata); err != nil { + return nil, err + } + requestCtx, cancel := withRequestTimeout(ctx, options.RequestTimeout) + defer cancel() + pipeReader, pipeWriter := io.Pipe() + multipartWriter := multipart.NewWriter(pipeWriter) + request, err := http.NewRequestWithContext(requestCtx, http.MethodPost, endpoint.String(), pipeReader) + if err != nil { + pipeReader.Close() + pipeWriter.Close() + return nil, err + } + go writeMultipartFile(pipeWriter, multipartWriter, pathpkg.Base(path), reader) + request.Header = headers + request.Header.Set("Content-Type", multipartWriter.FormDataContentType()) + response, err := service.sandbox.client.httpClient.Do(request) + if err != nil { + pipeReader.CloseWithError(err) + return nil, normalizeRequestError(err) + } + defer response.Body.Close() + if response.StatusCode < 200 || response.StatusCode >= 300 { + return nil, decodeFileHTTPError(response) + } + var entries []struct { + Name string `json:"name"` + Type string `json:"type"` + Path string `json:"path"` + Metadata map[string]string `json:"metadata"` + } + if err := json.NewDecoder(response.Body).Decode(&entries); err != nil { + return nil, fmt.Errorf("agentbox: decode upload response: %w", err) + } + if len(entries) == 0 { + return nil, &FileUploadError{APIError: APIError{Message: "upload response did not contain a file"}} + } + return &EntryInfo{Name: entries[0].Name, Type: FileType(entries[0].Type), Path: entries[0].Path, Metadata: entries[0].Metadata}, nil +} + +// WriteText uploads a string. +func (service *FileService) WriteText(ctx context.Context, path, text string, options *WriteFileOptions) (*EntryInfo, error) { + return service.Write(ctx, path, strings.NewReader(text), options) +} + +// WriteBytes uploads bytes. +func (service *FileService) WriteBytes(ctx context.Context, path string, data []byte, options *WriteFileOptions) (*EntryInfo, error) { + return service.Write(ctx, path, bytes.NewReader(data), options) +} + +// WriteBatch writes files in order and stops at the first failure. +func (service *FileService) WriteBatch(ctx context.Context, files []WriteFile, user string) ([]EntryInfo, error) { + result := make([]EntryInfo, 0, len(files)) + for _, file := range files { + entry, err := service.Write(ctx, file.Path, file.Data, &WriteFileOptions{User: user, Metadata: file.Metadata}) + if err != nil { + return result, err + } + result = append(result, *entry) + } + return result, nil +} + +// Stat returns information about a path. +func (service *FileService) Stat(ctx context.Context, path string) (*EntryInfo, error) { + requestCtx, cancel := service.sandbox.unaryContext(ctx) + defer cancel() + request := connect.NewRequest(&filesystem.StatRequest{Path: path}) + service.addHeaders(request.Header()) + response, err := service.client.Stat(requestCtx, request) + if err != nil { + return nil, fileConnectError(err) + } + return mapEntry(response.Msg.GetEntry()), nil +} + +// Exists reports whether path exists. +func (service *FileService) Exists(ctx context.Context, path string) (bool, error) { + _, err := service.Stat(ctx, path) + var notFound *FileNotFoundError + if errors.As(err, ¬Found) { + return false, nil + } + return err == nil, err +} + +// List lists path recursively up to depth. +func (service *FileService) List(ctx context.Context, path string, depth uint32) ([]EntryInfo, error) { + requestCtx, cancel := service.sandbox.unaryContext(ctx) + defer cancel() + request := connect.NewRequest(&filesystem.ListDirRequest{Path: path, Depth: depth}) + service.addHeaders(request.Header()) + response, err := service.client.ListDir(requestCtx, request) + if err != nil { + return nil, fileConnectError(err) + } + result := make([]EntryInfo, 0, len(response.Msg.GetEntries())) + for _, entry := range response.Msg.GetEntries() { + result = append(result, *mapEntry(entry)) + } + return result, nil +} + +// MakeDir creates a directory. +func (service *FileService) MakeDir(ctx context.Context, path string) (*EntryInfo, error) { + requestCtx, cancel := service.sandbox.unaryContext(ctx) + defer cancel() + request := connect.NewRequest(&filesystem.MakeDirRequest{Path: path}) + service.addHeaders(request.Header()) + response, err := service.client.MakeDir(requestCtx, request) + if err != nil { + return nil, fileConnectError(err) + } + return mapEntry(response.Msg.GetEntry()), nil +} + +// Rename moves a filesystem entry. +func (service *FileService) Rename(ctx context.Context, source, destination string) (*EntryInfo, error) { + requestCtx, cancel := service.sandbox.unaryContext(ctx) + defer cancel() + request := connect.NewRequest(&filesystem.MoveRequest{Source: source, Destination: destination}) + service.addHeaders(request.Header()) + response, err := service.client.Move(requestCtx, request) + if err != nil { + return nil, fileConnectError(err) + } + return mapEntry(response.Msg.GetEntry()), nil +} + +// Remove recursively removes a filesystem entry. +func (service *FileService) Remove(ctx context.Context, path string) error { + requestCtx, cancel := service.sandbox.unaryContext(ctx) + defer cancel() + request := connect.NewRequest(&filesystem.RemoveRequest{Path: path}) + service.addHeaders(request.Header()) + _, err := service.client.Remove(requestCtx, request) + return fileConnectError(err) +} + +func writeMultipartFile(pipe *io.PipeWriter, writer *multipart.Writer, name string, reader io.Reader) { + part, err := writer.CreateFormFile("file", name) + if err == nil { + _, err = io.Copy(part, reader) + } + if err == nil { + err = writer.Close() + } + if err != nil { + _ = pipe.CloseWithError(err) + return + } + _ = pipe.Close() +} + +// Watch watches a directory until context cancellation or Close. +func (service *FileService) Watch(ctx context.Context, path string, options *WatchOptions) (*WatchHandle, error) { + if options == nil { + options = &WatchOptions{} + } + if options.Recursive && !envdAtLeast(service.sandbox.EnvdVersion, 0, 1, 4) { + return nil, &SandboxError{APIError: APIError{Message: "recursive watch requires envd 0.1.4 or later"}} + } + if options.IncludeEntry && !envdAtLeast(service.sandbox.EnvdVersion, 0, 6, 3) { + return nil, &SandboxError{APIError: APIError{Message: "watch entry details require envd 0.6.3 or later"}} + } + if options.AllowNetworkMounts && !envdAtLeast(service.sandbox.EnvdVersion, 0, 6, 4) { + return nil, &SandboxError{APIError: APIError{Message: "watching network mounts requires envd 0.6.4 or later"}} + } + watchCtx, cancel := context.WithCancel(ctx) + request := connect.NewRequest(&filesystem.WatchDirRequest{Path: path, Recursive: options.Recursive, IncludeEntry: options.IncludeEntry, AllowNetworkMounts: options.AllowNetworkMounts}) + service.addHeaders(request.Header()) + stream, err := service.client.WatchDir(watchCtx, request) + if err != nil { + cancel() + return nil, fileConnectError(err) + } + events := make(chan FileEvent, 16) + done := make(chan struct{}) + handle := &WatchHandle{Events: events, done: done, cancel: cancel} + go func() { + defer close(done) + defer close(events) + defer func() { _ = stream.Close() }() + for stream.Receive() { + event := stream.Msg().GetFilesystem() + if event != nil { + select { + case events <- FileEvent{Name: event.GetName(), Type: mapEventType(event.GetType()), Entry: mapEntry(event.GetEntry())}: + case <-watchCtx.Done(): + return + } + } + } + if err := stream.Err(); err != nil && !errors.Is(err, context.Canceled) { + handle.mu.Lock() + handle.err = fileConnectError(err) + handle.mu.Unlock() + } + }() + return handle, nil +} + +// SignedReadURL creates a directly usable download URL. +func (service *FileService) SignedReadURL(path, user string, expiration time.Time) (string, error) { + return service.signedURL(path, user, "read", expiration) +} + +// SignedWriteURL creates a directly usable upload URL. +func (service *FileService) SignedWriteURL(path, user string, expiration time.Time) (string, error) { + return service.signedURL(path, user, "write", expiration) +} +func (service *FileService) signedURL(path, user, operation string, expiration time.Time) (string, error) { + user = service.sandbox.resolveUser(user) + signature, unix, err := fileSignature(path, operation, user, service.sandbox.envdAccessToken, expiration) + if err != nil { + return "", err + } + endpoint, _ := url.Parse(service.sandbox.envdURL(envdPort, true) + "/files") + query := endpoint.Query() + query.Set("path", path) + query.Set("signature", signature) + if user != "" { + query.Set("username", user) + } + if unix != 0 { + query.Set("signature_expiration", strconv.FormatInt(unix, 10)) + } + endpoint.RawQuery = query.Encode() + return endpoint.String(), nil +} + +func (service *FileService) addHeaders(header http.Header) { + for key, values := range service.sandbox.envdHeaders(envdPort) { + header[key] = slices.Clone(values) + } + header.Set("Keepalive-Ping-Interval", "50") +} +func mapEntry(entry *filesystem.EntryInfo) *EntryInfo { + if entry == nil { + return nil + } + result := &EntryInfo{Name: entry.GetName(), Path: entry.GetPath(), Size: entry.GetSize(), Mode: entry.GetMode(), Permissions: entry.GetPermissions(), Owner: entry.GetOwner(), Group: entry.GetGroup(), SymlinkTarget: entry.GetSymlinkTarget(), Metadata: maps.Clone(entry.GetMetadata())} + if entry.GetModifiedTime() != nil { + result.ModifiedAt = entry.GetModifiedTime().AsTime() + } + switch entry.GetType() { + case filesystem.FileType_FILE_TYPE_FILE: + result.Type = FileTypeFile + case filesystem.FileType_FILE_TYPE_DIRECTORY: + result.Type = FileTypeDirectory + case filesystem.FileType_FILE_TYPE_SYMLINK: + result.Type = FileTypeSymlink + } + return result +} +func mapEventType(value filesystem.EventType) string { + switch value { + case filesystem.EventType_EVENT_TYPE_CREATE: + return "create" + case filesystem.EventType_EVENT_TYPE_WRITE: + return "write" + case filesystem.EventType_EVENT_TYPE_REMOVE: + return "remove" + case filesystem.EventType_EVENT_TYPE_RENAME: + return "rename" + case filesystem.EventType_EVENT_TYPE_CHMOD: + return "chmod" + default: + return "unknown" + } +} +func fileConnectError(err error) error { + if err == nil { + return nil + } + var value *connect.Error + if errors.As(err, &value) && value.Code() == connect.CodeNotFound { + return &FileNotFoundError{APIError: APIError{Code: value.Code().String(), Message: value.Message(), Cause: err}} + } + return connectError(err) +} + +var metadataKey = regexp.MustCompile(`^[A-Za-z0-9!#$%&'*+\-.^_` + "`" + `|~]+$`) + +func addMetadataHeaders(header http.Header, metadata map[string]string) error { + for key, value := range metadata { + if !metadataKey.MatchString(key) { + return &InvalidArgumentError{Message: "invalid file metadata key: " + key} + } + for _, char := range []byte(value) { + if char < 0x20 || char > 0x7e { + return &InvalidArgumentError{Message: "file metadata values must be printable ASCII"} + } + } + header.Set("X-Metadata-"+key, value) + } + return nil +} diff --git a/packages/go-sdk/go.mod b/packages/go-sdk/go.mod new file mode 100644 index 000000000..0552ae7d6 --- /dev/null +++ b/packages/go-sdk/go.mod @@ -0,0 +1,14 @@ +module github.com/abox-dev/sdk/packages/go-sdk + +go 1.24.0 + +require ( + connectrpc.com/connect v1.19.1 + github.com/oapi-codegen/runtime v1.7.0 + google.golang.org/protobuf v1.36.12 +) + +require ( + github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect + github.com/google/uuid v1.6.0 // indirect +) diff --git a/packages/go-sdk/go.sum b/packages/go-sdk/go.sum new file mode 100644 index 000000000..a1c3ca35e --- /dev/null +++ b/packages/go-sdk/go.sum @@ -0,0 +1,29 @@ +connectrpc.com/connect v1.19.1 h1:R5M57z05+90EfEvCY1b7hBxDVOUl45PrtXtAV2fOC14= +connectrpc.com/connect v1.19.1/go.mod h1:tN20fjdGlewnSFeZxLKb0xwIZ6ozc3OQs2hTXy4du9w= +github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk= +github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ= +github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk= +github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w= +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/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE= +github.com/oapi-codegen/nullable v1.1.0 h1:eAh8JVc5430VtYVnq00Hrbpag9PFRGWLjxR1/3KntMs= +github.com/oapi-codegen/nullable v1.1.0/go.mod h1:KUZ3vUzkmEKY90ksAmit2+5juDIhIZhfDl+0PwOQlFY= +github.com/oapi-codegen/runtime v1.7.0 h1:t7358VYPvNbWJ9gdAkIK/smVeHpBf6yp8VTsaZsb/7k= +github.com/oapi-codegen/runtime v1.7.0/go.mod h1:GwV7hC2hviaMzj+ITfHVRESK5J2W/GefVwIND/bMGvU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc= +google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/packages/go-sdk/iam.go b/packages/go-sdk/iam.go new file mode 100644 index 000000000..bf08348a8 --- /dev/null +++ b/packages/go-sdk/iam.go @@ -0,0 +1,38 @@ +package agentbox + +import ( + "fmt" + "strings" + "unicode" +) + +// ValidateIAMTokenName verifies that name can be embedded in the workload-token +// placeholder grammar understood by the AgentBox egress proxy. +func ValidateIAMTokenName(name string) error { + if name == "" || strings.ContainsAny(name, "{}") || strings.IndexFunc(name, unicode.IsControl) >= 0 { + return &InvalidArgumentError{Message: fmt.Sprintf("IAM token name %q cannot be empty or contain braces or control characters", name)} + } + return nil +} + +// IAMTokenPlaceholder returns the value the egress proxy replaces with a +// freshly minted workload token. +func IAMTokenPlaceholder(name string) (string, error) { + if err := ValidateIAMTokenName(name); err != nil { + return "", err + } + return "${agentbox.identity.tokens." + name + "}", nil +} + +// IAMTokenPlaceholders returns placeholders for the supplied registered names. +func IAMTokenPlaceholders(names ...string) (map[string]string, error) { + result := make(map[string]string, len(names)) + for _, name := range names { + value, err := IAMTokenPlaceholder(name) + if err != nil { + return nil, err + } + result[name] = value + } + return result, nil +} diff --git a/packages/go-sdk/integration/sdk_test.go b/packages/go-sdk/integration/sdk_test.go new file mode 100644 index 000000000..3428a92ad --- /dev/null +++ b/packages/go-sdk/integration/sdk_test.go @@ -0,0 +1,87 @@ +//go:build integration + +package integration_test + +import ( + "context" + "fmt" + "strings" + "testing" + "time" + + "github.com/abox-dev/sdk/packages/go-sdk" + "github.com/abox-dev/sdk/packages/go-sdk/codeinterpreter" +) + +func TestCoreKVM(t *testing.T) { + client, err := agentbox.NewClient() + if err != nil { + t.Fatal(err) + } + ctx, cancel := context.WithTimeout(t.Context(), 10*time.Minute) + defer cancel() + sandbox, err := client.Sandboxes.Create(ctx, &agentbox.CreateSandboxOptions{Timeout: 5 * time.Minute, Metadata: map[string]string{"sdk": "go-integration"}}) + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { _, _ = sandbox.Kill(context.Background()) }) + result, err := sandbox.Commands.Run(ctx, "sh", &agentbox.CommandOptions{Args: []string{"-lc", "printf go-sdk"}}) + if err != nil { + t.Fatal(err) + } + if string(result.Stdout) != "go-sdk" { + t.Fatalf("stdout: %q", result.Stdout) + } + if _, err := sandbox.Files.WriteText(ctx, "/tmp/go-sdk.txt", "content", nil); err != nil { + t.Fatal(err) + } + if text, err := sandbox.Files.ReadText(ctx, "/tmp/go-sdk.txt", ""); err != nil || text != "content" { + t.Fatalf("file: %q %v", text, err) + } + if info, err := sandbox.Info(ctx); err != nil || info.SandboxID != sandbox.ID { + t.Fatalf("info: %#v %v", info, err) + } +} + +func TestCodeInterpreterKVM(t *testing.T) { + client, err := codeinterpreter.NewClient() + if err != nil { + t.Fatal(err) + } + ctx, cancel := context.WithTimeout(t.Context(), 10*time.Minute) + defer cancel() + sandbox, err := client.Create(ctx, &agentbox.CreateSandboxOptions{Timeout: 5 * time.Minute}) + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { _, _ = sandbox.Kill(context.Background()) }) + execution, err := sandbox.RunCode(ctx, "6 * 7", nil) + if err != nil { + t.Fatal(err) + } + if !strings.Contains(execution.Text(), "42") { + t.Fatalf("result: %#v", execution) + } + codeContext, err := sandbox.CreateContext(ctx, &codeinterpreter.CreateContextOptions{Language: codeinterpreter.Python}) + if err != nil { + t.Fatal(err) + } + if err := sandbox.RemoveContext(ctx, codeContext.ID); err != nil { + t.Fatal(err) + } +} + +func TestTemplateKVM(t *testing.T) { + client, err := agentbox.NewClient() + if err != nil { + t.Fatal(err) + } + ctx, cancel := context.WithTimeout(t.Context(), 20*time.Minute) + defer cancel() + name := fmt.Sprintf("go-sdk-integration-%d", time.Now().Unix()) + reference, err := client.Templates.Build(ctx, agentbox.NewTemplate("").FromAlpine("").Run("printf go-sdk >/tmp/go-sdk"), name, &agentbox.TemplateBuildOptions{CPUCount: 2, MemoryMB: 1024}) + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { _ = client.Templates.Delete(context.Background(), reference.TemplateID) }) +} diff --git a/packages/go-sdk/internal/gen/api/client.gen.go b/packages/go-sdk/internal/gen/api/client.gen.go new file mode 100644 index 000000000..236b3fab5 --- /dev/null +++ b/packages/go-sdk/internal/gen/api/client.gen.go @@ -0,0 +1,6252 @@ +// Package api provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.7.2 DO NOT EDIT. +package api + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "strings" + "time" + + "github.com/oapi-codegen/runtime" + openapi_types "github.com/oapi-codegen/runtime/types" +) + +// Defines values for AWSRegistryType. +const ( + Aws AWSRegistryType = "aws" +) + +// Valid indicates whether the value is a known member of the AWSRegistryType enum. +func (e AWSRegistryType) Valid() bool { + switch e { + case Aws: + return true + default: + return false + } +} + +// Defines values for GCPRegistryType. +const ( + Gcp GCPRegistryType = "gcp" +) + +// Valid indicates whether the value is a known member of the GCPRegistryType enum. +func (e GCPRegistryType) Valid() bool { + switch e { + case Gcp: + return true + default: + return false + } +} + +// Defines values for GeneralRegistryType. +const ( + Registry GeneralRegistryType = "registry" +) + +// Valid indicates whether the value is a known member of the GeneralRegistryType enum. +func (e GeneralRegistryType) Valid() bool { + switch e { + case Registry: + return true + default: + return false + } +} + +// Defines values for LogLevel. +const ( + LogLevelDebug LogLevel = "debug" + LogLevelError LogLevel = "error" + LogLevelInfo LogLevel = "info" + LogLevelWarn LogLevel = "warn" +) + +// Valid indicates whether the value is a known member of the LogLevel enum. +func (e LogLevel) Valid() bool { + switch e { + case LogLevelDebug: + return true + case LogLevelError: + return true + case LogLevelInfo: + return true + case LogLevelWarn: + return true + default: + return false + } +} + +// Defines values for LogsDirection. +const ( + LogsDirectionBackward LogsDirection = "backward" + LogsDirectionForward LogsDirection = "forward" +) + +// Valid indicates whether the value is a known member of the LogsDirection enum. +func (e LogsDirection) Valid() bool { + switch e { + case LogsDirectionBackward: + return true + case LogsDirectionForward: + return true + default: + return false + } +} + +// Defines values for LogsSource. +const ( + LogsSourcePersistent LogsSource = "persistent" + LogsSourceTemporary LogsSource = "temporary" +) + +// Valid indicates whether the value is a known member of the LogsSource enum. +func (e LogsSource) Valid() bool { + switch e { + case LogsSourcePersistent: + return true + case LogsSourceTemporary: + return true + default: + return false + } +} + +// Defines values for SandboxOnTimeout. +const ( + Kill SandboxOnTimeout = "kill" + Pause SandboxOnTimeout = "pause" +) + +// Valid indicates whether the value is a known member of the SandboxOnTimeout enum. +func (e SandboxOnTimeout) Valid() bool { + switch e { + case Kill: + return true + case Pause: + return true + default: + return false + } +} + +// Defines values for SandboxState. +const ( + Paused SandboxState = "paused" + Running SandboxState = "running" +) + +// Valid indicates whether the value is a known member of the SandboxState enum. +func (e SandboxState) Valid() bool { + switch e { + case Paused: + return true + case Running: + return true + default: + return false + } +} + +// Defines values for TemplateBuildStatus. +const ( + TemplateBuildStatusBuilding TemplateBuildStatus = "building" + TemplateBuildStatusError TemplateBuildStatus = "error" + TemplateBuildStatusReady TemplateBuildStatus = "ready" + TemplateBuildStatusWaiting TemplateBuildStatus = "waiting" +) + +// Valid indicates whether the value is a known member of the TemplateBuildStatus enum. +func (e TemplateBuildStatus) Valid() bool { + switch e { + case TemplateBuildStatusBuilding: + return true + case TemplateBuildStatusError: + return true + case TemplateBuildStatusReady: + return true + case TemplateBuildStatusWaiting: + return true + default: + return false + } +} + +// AWSRegistry defines model for AWSRegistry. +type AWSRegistry struct { + // AwsAccessKeyID AWS Access Key ID for ECR authentication + AwsAccessKeyID string `json:"awsAccessKeyId"` + + // AwsRegion AWS Region where the ECR registry is located + AwsRegion string `json:"awsRegion"` + + // AwsSecretAccessKey AWS Secret Access Key for ECR authentication + AwsSecretAccessKey string `json:"awsSecretAccessKey"` + + // Type Type of registry authentication + Type AWSRegistryType `json:"type"` +} + +// AWSRegistryType Type of registry authentication +type AWSRegistryType string + +// AssignTemplateTagsRequest defines model for AssignTemplateTagsRequest. +type AssignTemplateTagsRequest struct { + // Tags Tags to assign to the template + Tags []string `json:"tags"` + + // Target Target template in "name:tag" format + Target string `json:"target"` +} + +// AssignedTemplateTags defines model for AssignedTemplateTags. +type AssignedTemplateTags struct { + // BuildID Identifier of the build associated with these tags + BuildID openapi_types.UUID `json:"buildID"` + + // Tags Assigned tags of the template + Tags []string `json:"tags"` +} + +// BuildLogEntry defines model for BuildLogEntry. +type BuildLogEntry struct { + // ID Stable identifier used to reconcile overlapping live log pages + ID *string `json:"id,omitempty"` + + // Level State of the sandbox + Level LogLevel `json:"level"` + + // Message Log message content + Message string `json:"message"` + + // Step Step in the build process related to the log entry + Step *string `json:"step,omitempty"` + + // Timestamp Timestamp of the log entry + Timestamp time.Time `json:"timestamp"` +} + +// BuildStatusReason defines model for BuildStatusReason. +type BuildStatusReason struct { + // LogEntries Log entries related to the status reason + LogEntries *[]BuildLogEntry `json:"logEntries,omitempty"` + + // Message Message with the status reason, currently reporting only for error status + Message string `json:"message"` + + // Step Step that failed + Step *string `json:"step,omitempty"` +} + +// CPUCount CPU cores for the sandbox +type CPUCount = int32 + +// ConnectSandbox defines model for ConnectSandbox. +type ConnectSandbox struct { + // Timeout Timeout in seconds from the current time after which the sandbox should expire + Timeout int32 `json:"timeout"` +} + +// DeleteTemplateTagsRequest defines model for DeleteTemplateTagsRequest. +type DeleteTemplateTagsRequest struct { + // Name Name of the template + Name string `json:"name"` + + // Tags Tags to delete + Tags []string `json:"tags"` +} + +// DiskSizeMB Disk size for the sandbox in MiB +type DiskSizeMB = int32 + +// EnvVars defines model for EnvVars. +type EnvVars map[string]string + +// EnvdVersion Version of the envd running in the sandbox +type EnvdVersion = string + +// Error defines model for Error. +type Error struct { + // Code Error code + Code int32 `json:"code"` + + // Message Error + Message string `json:"message"` +} + +// FromImageRegistry defines model for FromImageRegistry. +type FromImageRegistry struct { + union json.RawMessage +} + +// GCPRegistry defines model for GCPRegistry. +type GCPRegistry struct { + // ServiceAccountJSON Service Account JSON for GCP authentication + ServiceAccountJSON string `json:"serviceAccountJson"` + + // Type Type of registry authentication + Type GCPRegistryType `json:"type"` +} + +// GCPRegistryType Type of registry authentication +type GCPRegistryType string + +// GeneralRegistry defines model for GeneralRegistry. +type GeneralRegistry struct { + // Password Password to use for the registry + Password string `json:"password"` + + // Type Type of registry authentication + Type GeneralRegistryType `json:"type"` + + // Username Username to use for the registry + Username string `json:"username"` +} + +// GeneralRegistryType Type of registry authentication +type GeneralRegistryType string + +// ListedSandbox defines model for ListedSandbox. +type ListedSandbox struct { + // Alias Alias of the template + Alias *string `json:"alias,omitempty"` + + // CPUCount CPU cores for the sandbox + CPUCount CPUCount `json:"cpuCount"` + + // DiskSizeMB Disk size for the sandbox in MiB + DiskSizeMB DiskSizeMB `json:"diskSizeMB"` + + // EndAt Time when the sandbox will expire + EndAt time.Time `json:"endAt"` + + // EnvdVersion Version of the envd running in the sandbox + EnvdVersion EnvdVersion `json:"envdVersion"` + + // MemoryMB Memory for the sandbox in MiB + MemoryMB MemoryMB `json:"memoryMB"` + Metadata *SandboxMetadata `json:"metadata,omitempty"` + + // SandboxID Identifier of the sandbox + SandboxID string `json:"sandboxID"` + + // StartedAt Time when the sandbox was started + StartedAt time.Time `json:"startedAt"` + + // State State of the sandbox + State SandboxState `json:"state"` + + // TemplateID Identifier of the template from which is the sandbox created + TemplateID string `json:"templateID"` +} + +// LogLevel State of the sandbox +type LogLevel string + +// LogsDirection Direction of the logs that should be returned +type LogsDirection string + +// LogsSource Source of the logs that should be returned +type LogsSource string + +// MemoryMB Memory for the sandbox in MiB +type MemoryMB = int32 + +// NewSandbox defines model for NewSandbox. +type NewSandbox struct { + // AllowInternetAccess Allow sandbox to access the internet. When set to false, it behaves the same as specifying denyOut to 0.0.0.0/0 in the network config. + AllowInternetAccess *bool `json:"allow_internet_access,omitempty"` + + // AutoPause Automatically pauses the sandbox after the timeout + AutoPause *bool `json:"autoPause,omitempty"` + + // AutoPauseMemory Controls the snapshot kind taken when the sandbox auto-pauses on timeout (only relevant when autoPause is true). When false, the auto-pause drops the in-memory state and persists only the filesystem (a filesystem-only snapshot); resuming it cold-boots (reboots) the sandbox from disk. Such a snapshot cannot be auto-resumed by traffic and must be resumed explicitly, so it cannot be combined with autoResume. Defaults to true (full memory snapshot). + AutoPauseMemory *bool `json:"autoPauseMemory,omitempty"` + + // AutoResume Auto-resume configuration for paused sandboxes. + AutoResume *SandboxAutoResumeConfig `json:"autoResume,omitempty"` + EnvVars *EnvVars `json:"envVars,omitempty"` + + // Iam Sandbox workload identity configuration. A non-empty, valid tokens map enables workload identity for the sandbox. + Iam *SandboxIam `json:"iam,omitempty"` + Metadata *SandboxMetadata `json:"metadata,omitempty"` + Network *SandboxNetworkConfig `json:"network,omitempty"` + + // Secure Secure all system communication with sandbox + Secure *bool `json:"secure,omitempty"` + + // TemplateID Identifier of the required template + TemplateID string `json:"templateID"` + + // Timeout Time to live for the sandbox in seconds. + Timeout *int32 `json:"timeout,omitempty"` +} + +// Sandbox defines model for Sandbox. +type Sandbox struct { + // Alias Alias of the template + Alias *string `json:"alias,omitempty"` + + // Domain Base domain where the sandbox traffic is accessible + Domain *string `json:"domain,omitempty"` + + // EnvdAccessToken Access token used for envd communication + EnvdAccessToken *string `json:"envdAccessToken,omitempty"` + + // EnvdVersion Version of the envd running in the sandbox + EnvdVersion EnvdVersion `json:"envdVersion"` + + // SandboxID Identifier of the sandbox + SandboxID string `json:"sandboxID"` + + // TemplateID Identifier of the template from which is the sandbox created + TemplateID string `json:"templateID"` + + // TrafficAccessToken Token required for accessing sandbox via proxy. + TrafficAccessToken *string `json:"trafficAccessToken,omitempty"` +} + +// SandboxAutoResumeConfig Auto-resume configuration for paused sandboxes. +type SandboxAutoResumeConfig struct { + // Enabled Auto-resume enabled flag for paused sandboxes. Default false. + Enabled SandboxAutoResumeEnabled `json:"enabled"` +} + +// SandboxAutoResumeEnabled Auto-resume enabled flag for paused sandboxes. Default false. +type SandboxAutoResumeEnabled = bool + +// SandboxDetail defines model for SandboxDetail. +type SandboxDetail struct { + // Alias Alias of the template + Alias *string `json:"alias,omitempty"` + + // AllowInternetAccess Whether internet access was explicitly enabled or disabled for the sandbox. Null means it was not explicitly set. + AllowInternetAccess *bool `json:"allowInternetAccess,omitempty"` + + // CPUCount CPU cores for the sandbox + CPUCount CPUCount `json:"cpuCount"` + + // DiskSizeMB Disk size for the sandbox in MiB + DiskSizeMB DiskSizeMB `json:"diskSizeMB"` + + // Domain Base domain where the sandbox traffic is accessible + Domain *string `json:"domain,omitempty"` + + // EndAt Time when the sandbox will expire + EndAt time.Time `json:"endAt"` + + // EnvdAccessToken Access token used for envd communication + EnvdAccessToken *string `json:"envdAccessToken,omitempty"` + + // EnvdVersion Version of the envd running in the sandbox + EnvdVersion EnvdVersion `json:"envdVersion"` + + // Lifecycle Sandbox lifecycle policy returned by sandbox info. + Lifecycle *SandboxLifecycle `json:"lifecycle,omitempty"` + + // MemoryMB Memory for the sandbox in MiB + MemoryMB MemoryMB `json:"memoryMB"` + Metadata *SandboxMetadata `json:"metadata,omitempty"` + Network *SandboxNetworkConfig `json:"network,omitempty"` + + // SandboxID Identifier of the sandbox + SandboxID string `json:"sandboxID"` + + // StartedAt Time when the sandbox was started + StartedAt time.Time `json:"startedAt"` + + // State State of the sandbox + State SandboxState `json:"state"` + + // TemplateID Identifier of the template from which is the sandbox created + TemplateID string `json:"templateID"` +} + +// SandboxForkRequest defines model for SandboxForkRequest. +type SandboxForkRequest struct { + // Count Number of forked sandboxes to create. All forks boot from the same snapshot, so the snapshot is captured once regardless of count. Each fork succeeds or fails independently; the outcome of each is reported in its entry of the response list. + Count *int32 `json:"count,omitempty"` + + // Timeout Time to live for the new forked sandboxes in seconds. + Timeout *int32 `json:"timeout,omitempty"` +} + +// SandboxForkResult Result of one requested fork. Exactly one of sandbox or error is set: sandbox when the fork started successfully, error when it failed to start. +type SandboxForkResult struct { + Error *Error `json:"error,omitempty"` + Sandbox *Sandbox `json:"sandbox,omitempty"` +} + +// SandboxIam Sandbox workload identity configuration. A non-empty, valid tokens map enables workload identity for the sandbox. +type SandboxIam struct { + // Tokens Named workload-token definitions, keyed by a caller-chosen token name. + Tokens *SandboxIamTokens `json:"tokens,omitempty"` +} + +// SandboxIamToken defines model for SandboxIamToken. +type SandboxIamToken struct { + // Audience Audience of the workload token, stored exactly as provided. + Audience string `json:"audience"` + + // TokenType Workload token type. + TokenType string `json:"tokenType"` +} + +// SandboxIamTokens Named workload-token definitions, keyed by a caller-chosen token name. +type SandboxIamTokens map[string]SandboxIamToken + +// SandboxLifecycle Sandbox lifecycle policy returned by sandbox info. +type SandboxLifecycle struct { + // AutoResume Whether the sandbox can auto-resume. + AutoResume bool `json:"autoResume"` + + // OnTimeout Action taken when the sandbox times out. + OnTimeout SandboxOnTimeout `json:"onTimeout"` +} + +// SandboxLogEntry defines model for SandboxLogEntry. +type SandboxLogEntry struct { + Fields map[string]string `json:"fields"` + + // ID Stable identifier used to reconcile overlapping live log pages + ID *string `json:"id,omitempty"` + + // Level State of the sandbox + Level LogLevel `json:"level"` + + // Message Log message content + Message string `json:"message"` + + // Timestamp Timestamp of the log entry + Timestamp time.Time `json:"timestamp"` +} + +// SandboxLogsV2Response defines model for SandboxLogsV2Response. +type SandboxLogsV2Response struct { + // Logs Sandbox logs structured + Logs []SandboxLogEntry `json:"logs"` + + // NextCursor Opaque continuation cursor for the next page + NextCursor *string `json:"nextCursor,omitempty"` +} + +// SandboxMetadata defines model for SandboxMetadata. +type SandboxMetadata map[string]string + +// SandboxMetric Metric entry with timestamp and line +type SandboxMetric struct { + // CPUCount Number of CPU cores + CPUCount int32 `json:"cpuCount"` + + // CPUUsedPct CPU usage percentage + CPUUsedPct float32 `json:"cpuUsedPct"` + + // DiskTotal Total disk space in bytes + DiskTotal int64 `json:"diskTotal"` + + // DiskUsed Disk used in bytes + DiskUsed int64 `json:"diskUsed"` + + // MemCache Cached memory (page cache) in bytes + MemCache int64 `json:"memCache"` + + // MemTotal Total memory in bytes + MemTotal int64 `json:"memTotal"` + + // MemUsed Memory used in bytes + MemUsed int64 `json:"memUsed"` + + // TimestampUnix Timestamp of the metric entry in Unix time (seconds since epoch) + TimestampUnix int64 `json:"timestampUnix"` +} + +// SandboxNetworkConfig defines model for SandboxNetworkConfig. +type SandboxNetworkConfig struct { + // AllowOut List of allowed destinations for egress traffic. Each entry can be a CIDR block (e.g. "8.8.8.8/32"), a bare IP address (e.g. "8.8.8.8"), or a domain name (e.g. "example.com", "*.example.com"). Allowed entries always take precedence over denied entries. + AllowOut *[]string `json:"allowOut,omitempty"` + + // AllowPublicTraffic Specify if the sandbox URLs should be accessible only with authentication. + AllowPublicTraffic *bool `json:"allowPublicTraffic,omitempty"` + + // DenyOut List of denied CIDR blocks or IP addresses for egress traffic. Domain names are not supported for deny rules. + DenyOut *[]string `json:"denyOut,omitempty"` + + // MaskRequestHost Specify host mask which will be used for all sandbox requests + MaskRequestHost *string `json:"maskRequestHost,omitempty"` + + // Rules Per-domain transform rules applied to matching egress HTTP/HTTPS requests. Keys are domains (e.g. "api.example.com", "example.com"). A domain listed here is not automatically allowed - use allowOut to permit the traffic. + Rules *map[string][]SandboxNetworkRule `json:"rules,omitempty"` +} + +// SandboxNetworkRule Transform rule applied to egress requests matching a domain pattern. +type SandboxNetworkRule struct { + // Transform Transformations applied to matching egress requests before forwarding. + Transform *SandboxNetworkTransform `json:"transform,omitempty"` +} + +// SandboxNetworkTransform Transformations applied to matching egress requests before forwarding. +type SandboxNetworkTransform struct { + // Headers HTTP headers to inject or override in matching requests. An existing header with the same name is replaced. Values are plain strings; secret resolution happens client-side before sending to the API. + Headers *map[string]string `json:"headers,omitempty"` +} + +// SandboxNetworkUpdateConfig Network configuration update for a running sandbox. Replaces the current egress rules with the provided configuration. Omitting a field clears it. +type SandboxNetworkUpdateConfig struct { + // AllowOut List of allowed destinations for egress traffic. Each entry can be a CIDR block (e.g. "8.8.8.8/32"), a bare IP address (e.g. "8.8.8.8"), or a domain name (e.g. "example.com", "*.example.com"). Allowed entries always take precedence over denied entries. + AllowOut *[]string `json:"allowOut,omitempty"` + + // AllowInternetAccess Allow sandbox to access the internet. When set to false, it behaves the same as specifying denyOut to 0.0.0.0/0 in the network config. + AllowInternetAccess *bool `json:"allow_internet_access,omitempty"` + + // DenyOut List of denied CIDR blocks or IP addresses for egress traffic. Domain names are not supported for deny rules. + DenyOut *[]string `json:"denyOut,omitempty"` + + // Rules Per-domain transform rules. Replaces all existing rules when provided. + Rules *map[string][]SandboxNetworkRule `json:"rules,omitempty"` +} + +// SandboxOnTimeout Action taken when the sandbox times out. +type SandboxOnTimeout string + +// SandboxPauseRequest defines model for SandboxPauseRequest. +type SandboxPauseRequest struct { + // Memory Whether to capture a full memory snapshot. When false, only the filesystem is persisted and resuming the sandbox cold-boots (reboots) it from disk, losing in-memory state, running processes, and open connections. Resume it with an explicit request (connect or resume); auto-resume, which can be triggered by arbitrary traffic, refuses such a sandbox. Defaults to true. + Memory *bool `json:"memory,omitempty"` +} + +// SandboxRefreshRequest defines model for SandboxRefreshRequest. +type SandboxRefreshRequest struct { + // Duration Duration for which the sandbox should be kept alive in seconds + Duration *int `json:"duration,omitempty"` +} + +// SandboxSnapshotRequest defines model for SandboxSnapshotRequest. +type SandboxSnapshotRequest struct { + // Name Optional name for the snapshot template. If a snapshot template with this name already exists, a new build will be assigned to the existing template instead of creating a new one. + Name *string `json:"name,omitempty"` +} + +// SandboxState State of the sandbox +type SandboxState string + +// SandboxTimeoutRequest defines model for SandboxTimeoutRequest. +type SandboxTimeoutRequest struct { + // Timeout Timeout in seconds from the current time after which the sandbox should expire + Timeout int32 `json:"timeout"` +} + +// SandboxesWithMetrics defines model for SandboxesWithMetrics. +type SandboxesWithMetrics struct { + Sandboxes map[string]SandboxMetric `json:"sandboxes"` +} + +// SnapshotInfo defines model for SnapshotInfo. +type SnapshotInfo struct { + // Names Full names of the snapshot template including team namespace and tag (e.g. team-slug/my-snapshot:v2) + Names []string `json:"names"` + + // SnapshotID Identifier of the snapshot template including the tag. Uses namespace/alias when a name was provided (e.g. team-slug/my-snapshot:default), otherwise falls back to the raw template ID (e.g. abc123:default). + SnapshotID string `json:"snapshotID"` +} + +// TeamUser defines model for TeamUser. +type TeamUser struct { + // ID Identifier of the user + ID openapi_types.UUID `json:"id"` +} + +// Template defines model for Template. +type Template struct { + // BuildCount Number of times the template was built + BuildCount int32 `json:"buildCount"` + + // BuildID Identifier of the last successful build for given template + BuildID string `json:"buildID"` + + // BuildStatus Status of the template build + BuildStatus TemplateBuildStatus `json:"buildStatus"` + + // CPUCount CPU cores for the sandbox + CPUCount CPUCount `json:"cpuCount"` + + // CreatedAt Time when the template was created + CreatedAt time.Time `json:"createdAt"` + CreatedBy *TeamUser `json:"createdBy"` + + // DiskSizeMB Disk size for the sandbox in MiB + DiskSizeMB DiskSizeMB `json:"diskSizeMB"` + + // EnvdVersion Version of the envd running in the sandbox + EnvdVersion EnvdVersion `json:"envdVersion"` + + // LastSpawnedAt Time when the template was last used + LastSpawnedAt *time.Time `json:"lastSpawnedAt"` + + // MemoryMB Memory for the sandbox in MiB + MemoryMB MemoryMB `json:"memoryMB"` + + // Names Names of the template (namespace/alias format when namespaced) + Names []string `json:"names"` + + // Public Whether the template is public or only accessible by the team + Public bool `json:"public"` + + // SpawnCount Number of times the template was used + SpawnCount int64 `json:"spawnCount"` + + // TemplateID Identifier of the template + TemplateID string `json:"templateID"` + + // UpdatedAt Time when the template was last updated + UpdatedAt time.Time `json:"updatedAt"` +} + +// TemplateAliasResponse defines model for TemplateAliasResponse. +type TemplateAliasResponse struct { + // Public Whether the template is public or only accessible by the team + Public bool `json:"public"` + + // TemplateID Identifier of the template + TemplateID string `json:"templateID"` +} + +// TemplateBuild defines model for TemplateBuild. +type TemplateBuild struct { + // BuildID Identifier of the build + BuildID openapi_types.UUID `json:"buildID"` + + // CPUCount CPU cores for the sandbox + CPUCount CPUCount `json:"cpuCount"` + + // CreatedAt Time when the build was created + CreatedAt time.Time `json:"createdAt"` + + // DiskSizeMB Disk size for the sandbox in MiB + DiskSizeMB *DiskSizeMB `json:"diskSizeMB,omitempty"` + + // EnvdVersion Version of the envd running in the sandbox + EnvdVersion *EnvdVersion `json:"envdVersion,omitempty"` + + // FinishedAt Time when the build was finished + FinishedAt *time.Time `json:"finishedAt,omitempty"` + + // MemoryMB Memory for the sandbox in MiB + MemoryMB MemoryMB `json:"memoryMB"` + + // Status Status of the template build + Status TemplateBuildStatus `json:"status"` + + // UpdatedAt Time when the build was last updated + UpdatedAt time.Time `json:"updatedAt"` +} + +// TemplateBuildFileUpload defines model for TemplateBuildFileUpload. +type TemplateBuildFileUpload struct { + // Present Whether the file is already present in the cache + Present bool `json:"present"` + + // URL Url where the file should be uploaded to + URL *string `json:"url,omitempty"` +} + +// TemplateBuildInfo defines model for TemplateBuildInfo. +type TemplateBuildInfo struct { + // BuildID Identifier of the build + BuildID string `json:"buildID"` + + // LogEntries Build logs structured + LogEntries []BuildLogEntry `json:"logEntries"` + + // Logs Build logs + Logs []string `json:"logs"` + Reason *BuildStatusReason `json:"reason,omitempty"` + + // Status Status of the template build + Status TemplateBuildStatus `json:"status"` + + // TemplateID Identifier of the template + TemplateID string `json:"templateID"` +} + +// TemplateBuildLogsResponse defines model for TemplateBuildLogsResponse. +type TemplateBuildLogsResponse struct { + // Logs Build logs structured + Logs []BuildLogEntry `json:"logs"` + + // NextCursor Opaque continuation cursor for the next page + NextCursor *string `json:"nextCursor,omitempty"` + + // Source Source of the logs that should be returned + Source *LogsSource `json:"source,omitempty"` +} + +// TemplateBuildRequestV3 defines model for TemplateBuildRequestV3. +type TemplateBuildRequestV3 struct { + // CPUCount CPU cores for the sandbox + CPUCount *CPUCount `json:"cpuCount,omitempty"` + + // MemoryMB Memory for the sandbox in MiB + MemoryMB *MemoryMB `json:"memoryMB,omitempty"` + + // Name Name of the template. Can include a tag with colon separator (e.g. "my-template" or "my-template:v1"). If tag is included, it will be treated as if the tag was provided in the tags array. + Name *string `json:"name,omitempty"` + + // Tags Tags to assign to the template build + Tags *[]string `json:"tags,omitempty"` +} + +// TemplateBuildStartV2 defines model for TemplateBuildStartV2. +type TemplateBuildStartV2 struct { + // Force Whether the whole build should be forced to run regardless of the cache + Force *bool `json:"force,omitempty"` + + // FromImage Image to use as a base for the template build + FromImage *string `json:"fromImage,omitempty"` + FromImageRegistry *FromImageRegistry `json:"fromImageRegistry,omitempty"` + + // FromTemplate Template to use as a base for the template build + FromTemplate *string `json:"fromTemplate,omitempty"` + + // ReadyCmd Ready check command to execute in the template after the build + ReadyCmd *string `json:"readyCmd,omitempty"` + + // StartCmd Start command to execute in the template after the build + StartCmd *string `json:"startCmd,omitempty"` + + // Steps List of steps to execute in the template build + Steps *[]TemplateStep `json:"steps,omitempty"` +} + +// TemplateBuildStatus Status of the template build +type TemplateBuildStatus string + +// TemplateRequestResponseV3 defines model for TemplateRequestResponseV3. +type TemplateRequestResponseV3 struct { + // BuildID Identifier of the last successful build for given template + BuildID string `json:"buildID"` + + // Names Names of the template + Names []string `json:"names"` + + // Public Whether the template is public or only accessible by the team + Public bool `json:"public"` + + // Tags Tags assigned to the template build + Tags []string `json:"tags"` + + // TemplateID Identifier of the template + TemplateID string `json:"templateID"` +} + +// TemplateStep Step in the template build process +type TemplateStep struct { + // Args Arguments for the step + Args *[]string `json:"args,omitempty"` + + // FilesHash Hash of the files used in the step + FilesHash *string `json:"filesHash,omitempty"` + + // Force Whether the step should be forced to run regardless of the cache + Force *bool `json:"force,omitempty"` + + // Type Type of the step + Type string `json:"type"` +} + +// TemplateTag defines model for TemplateTag. +type TemplateTag struct { + // BuildID Identifier of the build associated with this tag + BuildID openapi_types.UUID `json:"buildID"` + + // CreatedAt Time when the tag was assigned + CreatedAt time.Time `json:"createdAt"` + + // Tag The tag name + Tag string `json:"tag"` +} + +// TemplateUpdateRequest defines model for TemplateUpdateRequest. +type TemplateUpdateRequest struct { + // Public Whether the template is public or only accessible by the team + Public *bool `json:"public,omitempty"` +} + +// TemplateUpdateResponse defines model for TemplateUpdateResponse. +type TemplateUpdateResponse struct { + // Names Names of the template (namespace/alias format when namespaced) + Names []string `json:"names"` +} + +// TemplateWithBuilds defines model for TemplateWithBuilds. +type TemplateWithBuilds struct { + // Builds List of builds for the template + Builds []TemplateBuild `json:"builds"` + + // CreatedAt Time when the template was created + CreatedAt time.Time `json:"createdAt"` + + // LastSpawnedAt Time when the template was last used + LastSpawnedAt *time.Time `json:"lastSpawnedAt"` + + // Names Names of the template (namespace/alias format when namespaced) + Names []string `json:"names"` + + // Public Whether the template is public or only accessible by the team + Public bool `json:"public"` + + // SpawnCount Number of times the template was used + SpawnCount int64 `json:"spawnCount"` + + // TemplateID Identifier of the template + TemplateID string `json:"templateID"` + + // UpdatedAt Time when the template was last updated + UpdatedAt time.Time `json:"updatedAt"` +} + +// BuildID defines model for buildID. +type BuildID = string + +// PaginationLimit defines model for paginationLimit. +type PaginationLimit = int32 + +// PaginationNextToken defines model for paginationNextToken. +type PaginationNextToken = string + +// SandboxID defines model for sandboxID. +type SandboxID = string + +// TemplateID defines model for templateID. +type TemplateID = string + +// N400 defines model for 400. +type N400 = Error + +// N401 defines model for 401. +type N401 = Error + +// N403 defines model for 403. +type N403 = Error + +// N404 defines model for 404. +type N404 = Error + +// N409 defines model for 409. +type N409 = Error + +// N500 defines model for 500. +type N500 = Error + +// GetSandboxesMetricsParams defines parameters for GetSandboxesMetrics. +type GetSandboxesMetricsParams struct { + // SandboxIds Comma-separated list of sandbox IDs to get metrics for + SandboxIds []string `form:"sandbox_ids" json:"sandbox_ids"` +} + +// GetSandboxesSandboxIDMetricsParams defines parameters for GetSandboxesSandboxIDMetrics. +type GetSandboxesSandboxIDMetricsParams struct { + // Start Unix timestamp for the start of the interval, in seconds, for which the metrics + Start *int64 `form:"start,omitempty" json:"start,omitempty"` + End *int64 `form:"end,omitempty" json:"end,omitempty"` +} + +// GetSnapshotsParams defines parameters for GetSnapshots. +type GetSnapshotsParams struct { + SandboxID *string `form:"sandboxID,omitempty" json:"sandboxID,omitempty"` + + // Name Filter snapshots by name or ID, optionally tag-qualified (e.g. "my-snapshot", "my-team/my-snapshot" or "my-snapshot:v1"). + Name *string `form:"name,omitempty" json:"name,omitempty"` + + // Limit Maximum number of items to return per page + Limit *PaginationLimit `form:"limit,omitempty" json:"limit,omitempty"` + + // NextToken Cursor to start the list from + NextToken *PaginationNextToken `form:"nextToken,omitempty" json:"nextToken,omitempty"` +} + +// GetTemplatesTemplateIDParams defines parameters for GetTemplatesTemplateID. +type GetTemplatesTemplateIDParams struct { + // NextToken Cursor to start the list from + NextToken *PaginationNextToken `form:"nextToken,omitempty" json:"nextToken,omitempty"` + + // Limit Maximum number of items to return per page + Limit *PaginationLimit `form:"limit,omitempty" json:"limit,omitempty"` +} + +// GetTemplatesTemplateIDBuildsBuildIDLogsParams defines parameters for GetTemplatesTemplateIDBuildsBuildIDLogs. +type GetTemplatesTemplateIDBuildsBuildIDLogsParams struct { + // PageCursor Opaque continuation cursor returned as nextCursor by the previous page + PageCursor *string `form:"pageCursor,omitempty" json:"pageCursor,omitempty"` + + // Cursor Starting timestamp of the logs that should be returned in milliseconds + Cursor *int64 `form:"cursor,omitempty" json:"cursor,omitempty"` + + // Limit Maximum number of logs that should be returned + Limit *int32 `form:"limit,omitempty" json:"limit,omitempty"` + Direction *LogsDirection `form:"direction,omitempty" json:"direction,omitempty"` + Level *LogLevel `form:"level,omitempty" json:"level,omitempty"` + + // Source Source of the logs that should be returned from + Source *LogsSource `form:"source,omitempty" json:"source,omitempty"` +} + +// GetTemplatesTemplateIDBuildsBuildIDStatusParams defines parameters for GetTemplatesTemplateIDBuildsBuildIDStatus. +type GetTemplatesTemplateIDBuildsBuildIDStatusParams struct { + // LogsOffset Index of the starting build log that should be returned with the template + LogsOffset *int32 `form:"logsOffset,omitempty" json:"logsOffset,omitempty"` + + // Limit Maximum number of logs that should be returned + Limit *int32 `form:"limit,omitempty" json:"limit,omitempty"` + Level *LogLevel `form:"level,omitempty" json:"level,omitempty"` +} + +// GetV2SandboxesParams defines parameters for GetV2Sandboxes. +type GetV2SandboxesParams struct { + // Metadata Metadata query used to filter the sandboxes (e.g. "user=abc&app=prod"). Each key and values must be URL encoded. + Metadata *string `form:"metadata,omitempty" json:"metadata,omitempty"` + + // State Filter sandboxes by one or more states + State *[]SandboxState `form:"state,omitempty" json:"state,omitempty"` + + // NextToken Cursor to start the list from + NextToken *PaginationNextToken `form:"nextToken,omitempty" json:"nextToken,omitempty"` + + // Limit Maximum number of items to return per page + Limit *PaginationLimit `form:"limit,omitempty" json:"limit,omitempty"` +} + +// GetV2SandboxesSandboxIDLogsParams defines parameters for GetV2SandboxesSandboxIDLogs. +type GetV2SandboxesSandboxIDLogsParams struct { + // PageCursor Opaque continuation cursor returned as nextCursor by the previous page + PageCursor *string `form:"pageCursor,omitempty" json:"pageCursor,omitempty"` + + // Cursor Starting timestamp of the logs that should be returned in milliseconds + Cursor *int64 `form:"cursor,omitempty" json:"cursor,omitempty"` + + // Limit Maximum number of logs that should be returned + Limit *int32 `form:"limit,omitempty" json:"limit,omitempty"` + + // Direction Direction of the logs that should be returned + Direction *LogsDirection `form:"direction,omitempty" json:"direction,omitempty"` + + // Level Minimum log level to return. Logs below this level are excluded + Level *LogLevel `form:"level,omitempty" json:"level,omitempty"` + + // Search Case-sensitive substring match on log message content + Search *string `form:"search,omitempty" json:"search,omitempty"` +} + +// GetV2TemplatesParams defines parameters for GetV2Templates. +type GetV2TemplatesParams struct { + TeamID *string `form:"teamID,omitempty" json:"teamID,omitempty"` + + // NextToken Cursor to start the list from + NextToken *PaginationNextToken `form:"nextToken,omitempty" json:"nextToken,omitempty"` + + // Limit Maximum number of items to return per page + Limit *PaginationLimit `form:"limit,omitempty" json:"limit,omitempty"` +} + +// PostSandboxesJSONRequestBody defines body for PostSandboxes for application/json ContentType. +type PostSandboxesJSONRequestBody = NewSandbox + +// PostSandboxesSandboxIDConnectJSONRequestBody defines body for PostSandboxesSandboxIDConnect for application/json ContentType. +type PostSandboxesSandboxIDConnectJSONRequestBody = ConnectSandbox + +// PostSandboxesSandboxIDForkJSONRequestBody defines body for PostSandboxesSandboxIDFork for application/json ContentType. +type PostSandboxesSandboxIDForkJSONRequestBody = SandboxForkRequest + +// PutSandboxesSandboxIDNetworkJSONRequestBody defines body for PutSandboxesSandboxIDNetwork for application/json ContentType. +type PutSandboxesSandboxIDNetworkJSONRequestBody = SandboxNetworkUpdateConfig + +// PostSandboxesSandboxIDPauseJSONRequestBody defines body for PostSandboxesSandboxIDPause for application/json ContentType. +type PostSandboxesSandboxIDPauseJSONRequestBody = SandboxPauseRequest + +// PostSandboxesSandboxIDRefreshesJSONRequestBody defines body for PostSandboxesSandboxIDRefreshes for application/json ContentType. +type PostSandboxesSandboxIDRefreshesJSONRequestBody = SandboxRefreshRequest + +// PostSandboxesSandboxIDSnapshotsJSONRequestBody defines body for PostSandboxesSandboxIDSnapshots for application/json ContentType. +type PostSandboxesSandboxIDSnapshotsJSONRequestBody = SandboxSnapshotRequest + +// PostSandboxesSandboxIDTimeoutJSONRequestBody defines body for PostSandboxesSandboxIDTimeout for application/json ContentType. +type PostSandboxesSandboxIDTimeoutJSONRequestBody = SandboxTimeoutRequest + +// DeleteTemplatesTagsJSONRequestBody defines body for DeleteTemplatesTags for application/json ContentType. +type DeleteTemplatesTagsJSONRequestBody = DeleteTemplateTagsRequest + +// PostTemplatesTagsJSONRequestBody defines body for PostTemplatesTags for application/json ContentType. +type PostTemplatesTagsJSONRequestBody = AssignTemplateTagsRequest + +// PatchV2TemplatesTemplateIDJSONRequestBody defines body for PatchV2TemplatesTemplateID for application/json ContentType. +type PatchV2TemplatesTemplateIDJSONRequestBody = TemplateUpdateRequest + +// PostV2TemplatesTemplateIDBuildsBuildIDJSONRequestBody defines body for PostV2TemplatesTemplateIDBuildsBuildID for application/json ContentType. +type PostV2TemplatesTemplateIDBuildsBuildIDJSONRequestBody = TemplateBuildStartV2 + +// PostV3TemplatesJSONRequestBody defines body for PostV3Templates for application/json ContentType. +type PostV3TemplatesJSONRequestBody = TemplateBuildRequestV3 + +// AsAWSRegistry returns the union data inside the FromImageRegistry as a AWSRegistry +func (t FromImageRegistry) AsAWSRegistry() (AWSRegistry, error) { + var body AWSRegistry + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromAWSRegistry overwrites any union data inside the FromImageRegistry as the provided AWSRegistry +func (t *FromImageRegistry) FromAWSRegistry(v AWSRegistry) error { + v.Type = "aws" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeAWSRegistry performs a merge with any union data inside the FromImageRegistry, using the provided AWSRegistry +func (t *FromImageRegistry) MergeAWSRegistry(v AWSRegistry) error { + v.Type = "aws" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsGCPRegistry returns the union data inside the FromImageRegistry as a GCPRegistry +func (t FromImageRegistry) AsGCPRegistry() (GCPRegistry, error) { + var body GCPRegistry + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromGCPRegistry overwrites any union data inside the FromImageRegistry as the provided GCPRegistry +func (t *FromImageRegistry) FromGCPRegistry(v GCPRegistry) error { + v.Type = "gcp" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeGCPRegistry performs a merge with any union data inside the FromImageRegistry, using the provided GCPRegistry +func (t *FromImageRegistry) MergeGCPRegistry(v GCPRegistry) error { + v.Type = "gcp" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +// AsGeneralRegistry returns the union data inside the FromImageRegistry as a GeneralRegistry +func (t FromImageRegistry) AsGeneralRegistry() (GeneralRegistry, error) { + var body GeneralRegistry + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromGeneralRegistry overwrites any union data inside the FromImageRegistry as the provided GeneralRegistry +func (t *FromImageRegistry) FromGeneralRegistry(v GeneralRegistry) error { + v.Type = "registry" + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeGeneralRegistry performs a merge with any union data inside the FromImageRegistry, using the provided GeneralRegistry +func (t *FromImageRegistry) MergeGeneralRegistry(v GeneralRegistry) error { + v.Type = "registry" + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + +func (t FromImageRegistry) Discriminator() (string, error) { + var discriminator struct { + Discriminator string `json:"type"` + } + err := json.Unmarshal(t.union, &discriminator) + return discriminator.Discriminator, err +} + +func (t FromImageRegistry) ValueByDiscriminator() (interface{}, error) { + discriminator, err := t.Discriminator() + if err != nil { + return nil, err + } + switch discriminator { + case "aws": + return t.AsAWSRegistry() + case "gcp": + return t.AsGCPRegistry() + case "registry": + return t.AsGeneralRegistry() + default: + return nil, errors.New("unknown discriminator value: " + discriminator) + } +} + +func (t FromImageRegistry) MarshalJSON() ([]byte, error) { + b, err := t.union.MarshalJSON() + return b, err +} + +func (t *FromImageRegistry) UnmarshalJSON(b []byte) error { + err := t.union.UnmarshalJSON(b) + return err +} + +// RequestEditorFn is the function signature for the RequestEditor callback function +type RequestEditorFn func(ctx context.Context, req *http.Request) error + +// Doer performs HTTP requests. +// +// The standard http.Client implements this interface. +type HttpRequestDoer interface { + Do(req *http.Request) (*http.Response, error) +} + +// Client which conforms to the OpenAPI3 specification for this service. +type Client struct { + // The endpoint of the server conforming to this interface, with scheme, + // https://api.deepmap.com for example. This can contain a path relative + // to the server, such as https://api.deepmap.com/dev-test, and all the + // paths in the swagger spec will be appended to the server. + Server string + + // Doer for performing requests, typically a *http.Client with any + // customized settings, such as certificate chains. + Client HttpRequestDoer + + // A list of callbacks for modifying requests which are generated before sending over + // the network. + RequestEditors []RequestEditorFn +} + +// ClientOption allows setting custom parameters during construction +type ClientOption func(*Client) error + +// Creates a new Client, with reasonable defaults +func NewClient(server string, opts ...ClientOption) (*Client, error) { + // create a client with sane default values + client := Client{ + Server: server, + } + // mutate client and add all optional params + for _, o := range opts { + if err := o(&client); err != nil { + return nil, err + } + } + // ensure the server URL always has a trailing slash + if !strings.HasSuffix(client.Server, "/") { + client.Server += "/" + } + // create httpClient, if not already present + if client.Client == nil { + client.Client = &http.Client{} + } + return &client, nil +} + +// WithHTTPClient allows overriding the default Doer, which is +// automatically created using http.Client. This is useful for tests. +func WithHTTPClient(doer HttpRequestDoer) ClientOption { + return func(c *Client) error { + c.Client = doer + return nil + } +} + +// WithRequestEditorFn allows setting up a callback function, which will be +// called right before sending the request. This can be used to mutate the request. +func WithRequestEditorFn(fn RequestEditorFn) ClientOption { + return func(c *Client) error { + c.RequestEditors = append(c.RequestEditors, fn) + return nil + } +} + +// The interface specification for the client above. +type ClientInterface interface { + // PostSandboxesWithBody request with any body + PostSandboxesWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + PostSandboxes(ctx context.Context, body PostSandboxesJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetSandboxesMetrics request + GetSandboxesMetrics(ctx context.Context, params *GetSandboxesMetricsParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // DeleteSandboxesSandboxID request + DeleteSandboxesSandboxID(ctx context.Context, sandboxID SandboxID, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetSandboxesSandboxID request + GetSandboxesSandboxID(ctx context.Context, sandboxID SandboxID, reqEditors ...RequestEditorFn) (*http.Response, error) + + // PostSandboxesSandboxIDConnectWithBody request with any body + PostSandboxesSandboxIDConnectWithBody(ctx context.Context, sandboxID SandboxID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + PostSandboxesSandboxIDConnect(ctx context.Context, sandboxID SandboxID, body PostSandboxesSandboxIDConnectJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // PostSandboxesSandboxIDForkWithBody request with any body + PostSandboxesSandboxIDForkWithBody(ctx context.Context, sandboxID SandboxID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + PostSandboxesSandboxIDFork(ctx context.Context, sandboxID SandboxID, body PostSandboxesSandboxIDForkJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetSandboxesSandboxIDMetrics request + GetSandboxesSandboxIDMetrics(ctx context.Context, sandboxID SandboxID, params *GetSandboxesSandboxIDMetricsParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // PutSandboxesSandboxIDNetworkWithBody request with any body + PutSandboxesSandboxIDNetworkWithBody(ctx context.Context, sandboxID SandboxID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + PutSandboxesSandboxIDNetwork(ctx context.Context, sandboxID SandboxID, body PutSandboxesSandboxIDNetworkJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // PostSandboxesSandboxIDPauseWithBody request with any body + PostSandboxesSandboxIDPauseWithBody(ctx context.Context, sandboxID SandboxID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + PostSandboxesSandboxIDPause(ctx context.Context, sandboxID SandboxID, body PostSandboxesSandboxIDPauseJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // PostSandboxesSandboxIDRefreshesWithBody request with any body + PostSandboxesSandboxIDRefreshesWithBody(ctx context.Context, sandboxID SandboxID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + PostSandboxesSandboxIDRefreshes(ctx context.Context, sandboxID SandboxID, body PostSandboxesSandboxIDRefreshesJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // PostSandboxesSandboxIDSnapshotsWithBody request with any body + PostSandboxesSandboxIDSnapshotsWithBody(ctx context.Context, sandboxID SandboxID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + PostSandboxesSandboxIDSnapshots(ctx context.Context, sandboxID SandboxID, body PostSandboxesSandboxIDSnapshotsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // PostSandboxesSandboxIDTimeoutWithBody request with any body + PostSandboxesSandboxIDTimeoutWithBody(ctx context.Context, sandboxID SandboxID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + PostSandboxesSandboxIDTimeout(ctx context.Context, sandboxID SandboxID, body PostSandboxesSandboxIDTimeoutJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetSnapshots request + GetSnapshots(ctx context.Context, params *GetSnapshotsParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetTemplatesAliasesAlias request + GetTemplatesAliasesAlias(ctx context.Context, alias string, reqEditors ...RequestEditorFn) (*http.Response, error) + + // DeleteTemplatesTagsWithBody request with any body + DeleteTemplatesTagsWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + DeleteTemplatesTags(ctx context.Context, body DeleteTemplatesTagsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // PostTemplatesTagsWithBody request with any body + PostTemplatesTagsWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + PostTemplatesTags(ctx context.Context, body PostTemplatesTagsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // DeleteTemplatesTemplateID request + DeleteTemplatesTemplateID(ctx context.Context, templateID TemplateID, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetTemplatesTemplateID request + GetTemplatesTemplateID(ctx context.Context, templateID TemplateID, params *GetTemplatesTemplateIDParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetTemplatesTemplateIDBuildsBuildIDLogs request + GetTemplatesTemplateIDBuildsBuildIDLogs(ctx context.Context, templateID TemplateID, buildID BuildID, params *GetTemplatesTemplateIDBuildsBuildIDLogsParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetTemplatesTemplateIDBuildsBuildIDStatus request + GetTemplatesTemplateIDBuildsBuildIDStatus(ctx context.Context, templateID TemplateID, buildID BuildID, params *GetTemplatesTemplateIDBuildsBuildIDStatusParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetTemplatesTemplateIDFilesHash request + GetTemplatesTemplateIDFilesHash(ctx context.Context, templateID TemplateID, hash string, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetTemplatesTemplateIDTags request + GetTemplatesTemplateIDTags(ctx context.Context, templateID TemplateID, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetV2Sandboxes request + GetV2Sandboxes(ctx context.Context, params *GetV2SandboxesParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetV2SandboxesSandboxIDLogs request + GetV2SandboxesSandboxIDLogs(ctx context.Context, sandboxID SandboxID, params *GetV2SandboxesSandboxIDLogsParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetV2Templates request + GetV2Templates(ctx context.Context, params *GetV2TemplatesParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // PatchV2TemplatesTemplateIDWithBody request with any body + PatchV2TemplatesTemplateIDWithBody(ctx context.Context, templateID TemplateID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + PatchV2TemplatesTemplateID(ctx context.Context, templateID TemplateID, body PatchV2TemplatesTemplateIDJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // PostV2TemplatesTemplateIDBuildsBuildIDWithBody request with any body + PostV2TemplatesTemplateIDBuildsBuildIDWithBody(ctx context.Context, templateID TemplateID, buildID BuildID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + PostV2TemplatesTemplateIDBuildsBuildID(ctx context.Context, templateID TemplateID, buildID BuildID, body PostV2TemplatesTemplateIDBuildsBuildIDJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // PostV3TemplatesWithBody request with any body + PostV3TemplatesWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + PostV3Templates(ctx context.Context, body PostV3TemplatesJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) +} + +func (c *Client) PostSandboxesWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostSandboxesRequestWithBody(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) PostSandboxes(ctx context.Context, body PostSandboxesJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostSandboxesRequest(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) GetSandboxesMetrics(ctx context.Context, params *GetSandboxesMetricsParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetSandboxesMetricsRequest(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) DeleteSandboxesSandboxID(ctx context.Context, sandboxID SandboxID, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteSandboxesSandboxIDRequest(c.Server, sandboxID) + 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) GetSandboxesSandboxID(ctx context.Context, sandboxID SandboxID, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetSandboxesSandboxIDRequest(c.Server, sandboxID) + 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) PostSandboxesSandboxIDConnectWithBody(ctx context.Context, sandboxID SandboxID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostSandboxesSandboxIDConnectRequestWithBody(c.Server, sandboxID, 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) PostSandboxesSandboxIDConnect(ctx context.Context, sandboxID SandboxID, body PostSandboxesSandboxIDConnectJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostSandboxesSandboxIDConnectRequest(c.Server, sandboxID, 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) PostSandboxesSandboxIDForkWithBody(ctx context.Context, sandboxID SandboxID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostSandboxesSandboxIDForkRequestWithBody(c.Server, sandboxID, 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) PostSandboxesSandboxIDFork(ctx context.Context, sandboxID SandboxID, body PostSandboxesSandboxIDForkJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostSandboxesSandboxIDForkRequest(c.Server, sandboxID, 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) GetSandboxesSandboxIDMetrics(ctx context.Context, sandboxID SandboxID, params *GetSandboxesSandboxIDMetricsParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetSandboxesSandboxIDMetricsRequest(c.Server, sandboxID, 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) PutSandboxesSandboxIDNetworkWithBody(ctx context.Context, sandboxID SandboxID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPutSandboxesSandboxIDNetworkRequestWithBody(c.Server, sandboxID, 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) PutSandboxesSandboxIDNetwork(ctx context.Context, sandboxID SandboxID, body PutSandboxesSandboxIDNetworkJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPutSandboxesSandboxIDNetworkRequest(c.Server, sandboxID, 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) PostSandboxesSandboxIDPauseWithBody(ctx context.Context, sandboxID SandboxID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostSandboxesSandboxIDPauseRequestWithBody(c.Server, sandboxID, 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) PostSandboxesSandboxIDPause(ctx context.Context, sandboxID SandboxID, body PostSandboxesSandboxIDPauseJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostSandboxesSandboxIDPauseRequest(c.Server, sandboxID, 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) PostSandboxesSandboxIDRefreshesWithBody(ctx context.Context, sandboxID SandboxID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostSandboxesSandboxIDRefreshesRequestWithBody(c.Server, sandboxID, 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) PostSandboxesSandboxIDRefreshes(ctx context.Context, sandboxID SandboxID, body PostSandboxesSandboxIDRefreshesJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostSandboxesSandboxIDRefreshesRequest(c.Server, sandboxID, 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) PostSandboxesSandboxIDSnapshotsWithBody(ctx context.Context, sandboxID SandboxID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostSandboxesSandboxIDSnapshotsRequestWithBody(c.Server, sandboxID, 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) PostSandboxesSandboxIDSnapshots(ctx context.Context, sandboxID SandboxID, body PostSandboxesSandboxIDSnapshotsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostSandboxesSandboxIDSnapshotsRequest(c.Server, sandboxID, 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) PostSandboxesSandboxIDTimeoutWithBody(ctx context.Context, sandboxID SandboxID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostSandboxesSandboxIDTimeoutRequestWithBody(c.Server, sandboxID, 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) PostSandboxesSandboxIDTimeout(ctx context.Context, sandboxID SandboxID, body PostSandboxesSandboxIDTimeoutJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostSandboxesSandboxIDTimeoutRequest(c.Server, sandboxID, 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) GetSnapshots(ctx context.Context, params *GetSnapshotsParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetSnapshotsRequest(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) GetTemplatesAliasesAlias(ctx context.Context, alias string, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetTemplatesAliasesAliasRequest(c.Server, alias) + 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) DeleteTemplatesTagsWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteTemplatesTagsRequestWithBody(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) DeleteTemplatesTags(ctx context.Context, body DeleteTemplatesTagsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteTemplatesTagsRequest(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) PostTemplatesTagsWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostTemplatesTagsRequestWithBody(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) PostTemplatesTags(ctx context.Context, body PostTemplatesTagsJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostTemplatesTagsRequest(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) DeleteTemplatesTemplateID(ctx context.Context, templateID TemplateID, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteTemplatesTemplateIDRequest(c.Server, templateID) + 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) GetTemplatesTemplateID(ctx context.Context, templateID TemplateID, params *GetTemplatesTemplateIDParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetTemplatesTemplateIDRequest(c.Server, templateID, 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) GetTemplatesTemplateIDBuildsBuildIDLogs(ctx context.Context, templateID TemplateID, buildID BuildID, params *GetTemplatesTemplateIDBuildsBuildIDLogsParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetTemplatesTemplateIDBuildsBuildIDLogsRequest(c.Server, templateID, buildID, 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) GetTemplatesTemplateIDBuildsBuildIDStatus(ctx context.Context, templateID TemplateID, buildID BuildID, params *GetTemplatesTemplateIDBuildsBuildIDStatusParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetTemplatesTemplateIDBuildsBuildIDStatusRequest(c.Server, templateID, buildID, 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) GetTemplatesTemplateIDFilesHash(ctx context.Context, templateID TemplateID, hash string, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetTemplatesTemplateIDFilesHashRequest(c.Server, templateID, hash) + 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) GetTemplatesTemplateIDTags(ctx context.Context, templateID TemplateID, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetTemplatesTemplateIDTagsRequest(c.Server, templateID) + 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) GetV2Sandboxes(ctx context.Context, params *GetV2SandboxesParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetV2SandboxesRequest(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) GetV2SandboxesSandboxIDLogs(ctx context.Context, sandboxID SandboxID, params *GetV2SandboxesSandboxIDLogsParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetV2SandboxesSandboxIDLogsRequest(c.Server, sandboxID, 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) GetV2Templates(ctx context.Context, params *GetV2TemplatesParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetV2TemplatesRequest(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) PatchV2TemplatesTemplateIDWithBody(ctx context.Context, templateID TemplateID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPatchV2TemplatesTemplateIDRequestWithBody(c.Server, templateID, 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) PatchV2TemplatesTemplateID(ctx context.Context, templateID TemplateID, body PatchV2TemplatesTemplateIDJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPatchV2TemplatesTemplateIDRequest(c.Server, templateID, 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) PostV2TemplatesTemplateIDBuildsBuildIDWithBody(ctx context.Context, templateID TemplateID, buildID BuildID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV2TemplatesTemplateIDBuildsBuildIDRequestWithBody(c.Server, templateID, buildID, 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) PostV2TemplatesTemplateIDBuildsBuildID(ctx context.Context, templateID TemplateID, buildID BuildID, body PostV2TemplatesTemplateIDBuildsBuildIDJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV2TemplatesTemplateIDBuildsBuildIDRequest(c.Server, templateID, buildID, 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) PostV3TemplatesWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV3TemplatesRequestWithBody(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) PostV3Templates(ctx context.Context, body PostV3TemplatesJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostV3TemplatesRequest(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) +} + +// NewPostSandboxesRequest calls the generic PostSandboxes builder with application/json body +func NewPostSandboxesRequest(server string, body PostSandboxesJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewPostSandboxesRequestWithBody(server, "application/json", bodyReader) +} + +// NewPostSandboxesRequestWithBody generates requests for PostSandboxes with any type of body +func NewPostSandboxesRequestWithBody(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("/sandboxes") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGetSandboxesMetricsRequest generates requests for GetSandboxesMetrics +func NewGetSandboxesMetricsRequest(server string, params *GetSandboxesMetricsParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/sandboxes/metrics") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.SandboxIds != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", false, "sandbox_ids", params.SandboxIds, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewDeleteSandboxesSandboxIDRequest generates requests for DeleteSandboxesSandboxID +func NewDeleteSandboxesSandboxIDRequest(server string, sandboxID SandboxID) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "sandboxID", sandboxID, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/sandboxes/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetSandboxesSandboxIDRequest generates requests for GetSandboxesSandboxID +func NewGetSandboxesSandboxIDRequest(server string, sandboxID SandboxID) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "sandboxID", sandboxID, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/sandboxes/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewPostSandboxesSandboxIDConnectRequest calls the generic PostSandboxesSandboxIDConnect builder with application/json body +func NewPostSandboxesSandboxIDConnectRequest(server string, sandboxID SandboxID, body PostSandboxesSandboxIDConnectJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewPostSandboxesSandboxIDConnectRequestWithBody(server, sandboxID, "application/json", bodyReader) +} + +// NewPostSandboxesSandboxIDConnectRequestWithBody generates requests for PostSandboxesSandboxIDConnect with any type of body +func NewPostSandboxesSandboxIDConnectRequestWithBody(server string, sandboxID SandboxID, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "sandboxID", sandboxID, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/sandboxes/%s/connect", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewPostSandboxesSandboxIDForkRequest calls the generic PostSandboxesSandboxIDFork builder with application/json body +func NewPostSandboxesSandboxIDForkRequest(server string, sandboxID SandboxID, body PostSandboxesSandboxIDForkJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewPostSandboxesSandboxIDForkRequestWithBody(server, sandboxID, "application/json", bodyReader) +} + +// NewPostSandboxesSandboxIDForkRequestWithBody generates requests for PostSandboxesSandboxIDFork with any type of body +func NewPostSandboxesSandboxIDForkRequestWithBody(server string, sandboxID SandboxID, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "sandboxID", sandboxID, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/sandboxes/%s/fork", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGetSandboxesSandboxIDMetricsRequest generates requests for GetSandboxesSandboxIDMetrics +func NewGetSandboxesSandboxIDMetricsRequest(server string, sandboxID SandboxID, params *GetSandboxesSandboxIDMetricsParams) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "sandboxID", sandboxID, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/sandboxes/%s/metrics", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.Start != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "start", *params.Start, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: "int64"}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.End != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "end", *params.End, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: "int64"}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewPutSandboxesSandboxIDNetworkRequest calls the generic PutSandboxesSandboxIDNetwork builder with application/json body +func NewPutSandboxesSandboxIDNetworkRequest(server string, sandboxID SandboxID, body PutSandboxesSandboxIDNetworkJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewPutSandboxesSandboxIDNetworkRequestWithBody(server, sandboxID, "application/json", bodyReader) +} + +// NewPutSandboxesSandboxIDNetworkRequestWithBody generates requests for PutSandboxesSandboxIDNetwork with any type of body +func NewPutSandboxesSandboxIDNetworkRequestWithBody(server string, sandboxID SandboxID, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "sandboxID", sandboxID, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/sandboxes/%s/network", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPut, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewPostSandboxesSandboxIDPauseRequest calls the generic PostSandboxesSandboxIDPause builder with application/json body +func NewPostSandboxesSandboxIDPauseRequest(server string, sandboxID SandboxID, body PostSandboxesSandboxIDPauseJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewPostSandboxesSandboxIDPauseRequestWithBody(server, sandboxID, "application/json", bodyReader) +} + +// NewPostSandboxesSandboxIDPauseRequestWithBody generates requests for PostSandboxesSandboxIDPause with any type of body +func NewPostSandboxesSandboxIDPauseRequestWithBody(server string, sandboxID SandboxID, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "sandboxID", sandboxID, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/sandboxes/%s/pause", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewPostSandboxesSandboxIDRefreshesRequest calls the generic PostSandboxesSandboxIDRefreshes builder with application/json body +func NewPostSandboxesSandboxIDRefreshesRequest(server string, sandboxID SandboxID, body PostSandboxesSandboxIDRefreshesJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewPostSandboxesSandboxIDRefreshesRequestWithBody(server, sandboxID, "application/json", bodyReader) +} + +// NewPostSandboxesSandboxIDRefreshesRequestWithBody generates requests for PostSandboxesSandboxIDRefreshes with any type of body +func NewPostSandboxesSandboxIDRefreshesRequestWithBody(server string, sandboxID SandboxID, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "sandboxID", sandboxID, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/sandboxes/%s/refreshes", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewPostSandboxesSandboxIDSnapshotsRequest calls the generic PostSandboxesSandboxIDSnapshots builder with application/json body +func NewPostSandboxesSandboxIDSnapshotsRequest(server string, sandboxID SandboxID, body PostSandboxesSandboxIDSnapshotsJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewPostSandboxesSandboxIDSnapshotsRequestWithBody(server, sandboxID, "application/json", bodyReader) +} + +// NewPostSandboxesSandboxIDSnapshotsRequestWithBody generates requests for PostSandboxesSandboxIDSnapshots with any type of body +func NewPostSandboxesSandboxIDSnapshotsRequestWithBody(server string, sandboxID SandboxID, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "sandboxID", sandboxID, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/sandboxes/%s/snapshots", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewPostSandboxesSandboxIDTimeoutRequest calls the generic PostSandboxesSandboxIDTimeout builder with application/json body +func NewPostSandboxesSandboxIDTimeoutRequest(server string, sandboxID SandboxID, body PostSandboxesSandboxIDTimeoutJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewPostSandboxesSandboxIDTimeoutRequestWithBody(server, sandboxID, "application/json", bodyReader) +} + +// NewPostSandboxesSandboxIDTimeoutRequestWithBody generates requests for PostSandboxesSandboxIDTimeout with any type of body +func NewPostSandboxesSandboxIDTimeoutRequestWithBody(server string, sandboxID SandboxID, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "sandboxID", sandboxID, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/sandboxes/%s/timeout", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGetSnapshotsRequest generates requests for GetSnapshots +func NewGetSnapshotsRequest(server string, params *GetSnapshotsParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/snapshots") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.SandboxID != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "sandboxID", *params.SandboxID, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Name != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "name", *params.Name, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Limit != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "limit", *params.Limit, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: "int32"}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.NextToken != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "nextToken", *params.NextToken, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetTemplatesAliasesAliasRequest generates requests for GetTemplatesAliasesAlias +func NewGetTemplatesAliasesAliasRequest(server string, alias string) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "alias", alias, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/templates/aliases/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewDeleteTemplatesTagsRequest calls the generic DeleteTemplatesTags builder with application/json body +func NewDeleteTemplatesTagsRequest(server string, body DeleteTemplatesTagsJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewDeleteTemplatesTagsRequestWithBody(server, "application/json", bodyReader) +} + +// NewDeleteTemplatesTagsRequestWithBody generates requests for DeleteTemplatesTags with any type of body +func NewDeleteTemplatesTagsRequestWithBody(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("/templates/tags") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewPostTemplatesTagsRequest calls the generic PostTemplatesTags builder with application/json body +func NewPostTemplatesTagsRequest(server string, body PostTemplatesTagsJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewPostTemplatesTagsRequestWithBody(server, "application/json", bodyReader) +} + +// NewPostTemplatesTagsRequestWithBody generates requests for PostTemplatesTags with any type of body +func NewPostTemplatesTagsRequestWithBody(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("/templates/tags") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewDeleteTemplatesTemplateIDRequest generates requests for DeleteTemplatesTemplateID +func NewDeleteTemplatesTemplateIDRequest(server string, templateID TemplateID) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "templateID", templateID, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/templates/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetTemplatesTemplateIDRequest generates requests for GetTemplatesTemplateID +func NewGetTemplatesTemplateIDRequest(server string, templateID TemplateID, params *GetTemplatesTemplateIDParams) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "templateID", templateID, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/templates/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.NextToken != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "nextToken", *params.NextToken, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Limit != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "limit", *params.Limit, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: "int32"}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetTemplatesTemplateIDBuildsBuildIDLogsRequest generates requests for GetTemplatesTemplateIDBuildsBuildIDLogs +func NewGetTemplatesTemplateIDBuildsBuildIDLogsRequest(server string, templateID TemplateID, buildID BuildID, params *GetTemplatesTemplateIDBuildsBuildIDLogsParams) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "templateID", templateID, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + var pathParam1 string + + pathParam1, err = runtime.StyleParamWithOptions("simple", false, "buildID", buildID, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/templates/%s/builds/%s/logs", pathParam0, pathParam1) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.PageCursor != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "pageCursor", *params.PageCursor, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Cursor != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cursor", *params.Cursor, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: "int64"}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Limit != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "limit", *params.Limit, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: "int32"}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Direction != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "direction", *params.Direction, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Level != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "level", *params.Level, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Source != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "source", *params.Source, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetTemplatesTemplateIDBuildsBuildIDStatusRequest generates requests for GetTemplatesTemplateIDBuildsBuildIDStatus +func NewGetTemplatesTemplateIDBuildsBuildIDStatusRequest(server string, templateID TemplateID, buildID BuildID, params *GetTemplatesTemplateIDBuildsBuildIDStatusParams) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "templateID", templateID, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + var pathParam1 string + + pathParam1, err = runtime.StyleParamWithOptions("simple", false, "buildID", buildID, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/templates/%s/builds/%s/status", pathParam0, pathParam1) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.LogsOffset != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "logsOffset", *params.LogsOffset, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: "int32"}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Limit != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "limit", *params.Limit, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: "int32"}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Level != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "level", *params.Level, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetTemplatesTemplateIDFilesHashRequest generates requests for GetTemplatesTemplateIDFilesHash +func NewGetTemplatesTemplateIDFilesHashRequest(server string, templateID TemplateID, hash string) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "templateID", templateID, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + var pathParam1 string + + pathParam1, err = runtime.StyleParamWithOptions("simple", false, "hash", hash, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/templates/%s/files/%s", pathParam0, pathParam1) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetTemplatesTemplateIDTagsRequest generates requests for GetTemplatesTemplateIDTags +func NewGetTemplatesTemplateIDTagsRequest(server string, templateID TemplateID) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "templateID", templateID, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/templates/%s/tags", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetV2SandboxesRequest generates requests for GetV2Sandboxes +func NewGetV2SandboxesRequest(server string, params *GetV2SandboxesParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/v2/sandboxes") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.Metadata != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "metadata", *params.Metadata, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.State != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", false, "state", *params.State, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "array", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.NextToken != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "nextToken", *params.NextToken, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Limit != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "limit", *params.Limit, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: "int32"}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetV2SandboxesSandboxIDLogsRequest generates requests for GetV2SandboxesSandboxIDLogs +func NewGetV2SandboxesSandboxIDLogsRequest(server string, sandboxID SandboxID, params *GetV2SandboxesSandboxIDLogsParams) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "sandboxID", sandboxID, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/v2/sandboxes/%s/logs", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.PageCursor != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "pageCursor", *params.PageCursor, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Cursor != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "cursor", *params.Cursor, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: "int64"}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Limit != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "limit", *params.Limit, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: "int32"}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Direction != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "direction", *params.Direction, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Level != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "level", *params.Level, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Search != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "search", *params.Search, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetV2TemplatesRequest generates requests for GetV2Templates +func NewGetV2TemplatesRequest(server string, params *GetV2TemplatesParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/v2/templates") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.TeamID != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "teamID", *params.TeamID, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.NextToken != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "nextToken", *params.NextToken, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Limit != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "limit", *params.Limit, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: "int32"}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewPatchV2TemplatesTemplateIDRequest calls the generic PatchV2TemplatesTemplateID builder with application/json body +func NewPatchV2TemplatesTemplateIDRequest(server string, templateID TemplateID, body PatchV2TemplatesTemplateIDJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewPatchV2TemplatesTemplateIDRequestWithBody(server, templateID, "application/json", bodyReader) +} + +// NewPatchV2TemplatesTemplateIDRequestWithBody generates requests for PatchV2TemplatesTemplateID with any type of body +func NewPatchV2TemplatesTemplateIDRequestWithBody(server string, templateID TemplateID, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "templateID", templateID, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/v2/templates/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPatch, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewPostV2TemplatesTemplateIDBuildsBuildIDRequest calls the generic PostV2TemplatesTemplateIDBuildsBuildID builder with application/json body +func NewPostV2TemplatesTemplateIDBuildsBuildIDRequest(server string, templateID TemplateID, buildID BuildID, body PostV2TemplatesTemplateIDBuildsBuildIDJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewPostV2TemplatesTemplateIDBuildsBuildIDRequestWithBody(server, templateID, buildID, "application/json", bodyReader) +} + +// NewPostV2TemplatesTemplateIDBuildsBuildIDRequestWithBody generates requests for PostV2TemplatesTemplateIDBuildsBuildID with any type of body +func NewPostV2TemplatesTemplateIDBuildsBuildIDRequestWithBody(server string, templateID TemplateID, buildID BuildID, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "templateID", templateID, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + var pathParam1 string + + pathParam1, err = runtime.StyleParamWithOptions("simple", false, "buildID", buildID, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/v2/templates/%s/builds/%s", pathParam0, pathParam1) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewPostV3TemplatesRequest calls the generic PostV3Templates builder with application/json body +func NewPostV3TemplatesRequest(server string, body PostV3TemplatesJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewPostV3TemplatesRequestWithBody(server, "application/json", bodyReader) +} + +// NewPostV3TemplatesRequestWithBody generates requests for PostV3Templates with any type of body +func NewPostV3TemplatesRequestWithBody(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("/v3/templates") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, 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 +} + +// ClientWithResponses builds on ClientInterface to offer response payloads +type ClientWithResponses struct { + ClientInterface +} + +// 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 +} + +// 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 + } +} + +// ClientWithResponsesInterface is the interface specification for the client with responses above. +type ClientWithResponsesInterface interface { + // PostSandboxesWithBodyWithResponse request with any body + PostSandboxesWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostSandboxesResponse, error) + + PostSandboxesWithResponse(ctx context.Context, body PostSandboxesJSONRequestBody, reqEditors ...RequestEditorFn) (*PostSandboxesResponse, error) + + // GetSandboxesMetricsWithResponse request + GetSandboxesMetricsWithResponse(ctx context.Context, params *GetSandboxesMetricsParams, reqEditors ...RequestEditorFn) (*GetSandboxesMetricsResponse, error) + + // DeleteSandboxesSandboxIDWithResponse request + DeleteSandboxesSandboxIDWithResponse(ctx context.Context, sandboxID SandboxID, reqEditors ...RequestEditorFn) (*DeleteSandboxesSandboxIDResponse, error) + + // GetSandboxesSandboxIDWithResponse request + GetSandboxesSandboxIDWithResponse(ctx context.Context, sandboxID SandboxID, reqEditors ...RequestEditorFn) (*GetSandboxesSandboxIDResponse, error) + + // PostSandboxesSandboxIDConnectWithBodyWithResponse request with any body + PostSandboxesSandboxIDConnectWithBodyWithResponse(ctx context.Context, sandboxID SandboxID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostSandboxesSandboxIDConnectResponse, error) + + PostSandboxesSandboxIDConnectWithResponse(ctx context.Context, sandboxID SandboxID, body PostSandboxesSandboxIDConnectJSONRequestBody, reqEditors ...RequestEditorFn) (*PostSandboxesSandboxIDConnectResponse, error) + + // PostSandboxesSandboxIDForkWithBodyWithResponse request with any body + PostSandboxesSandboxIDForkWithBodyWithResponse(ctx context.Context, sandboxID SandboxID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostSandboxesSandboxIDForkResponse, error) + + PostSandboxesSandboxIDForkWithResponse(ctx context.Context, sandboxID SandboxID, body PostSandboxesSandboxIDForkJSONRequestBody, reqEditors ...RequestEditorFn) (*PostSandboxesSandboxIDForkResponse, error) + + // GetSandboxesSandboxIDMetricsWithResponse request + GetSandboxesSandboxIDMetricsWithResponse(ctx context.Context, sandboxID SandboxID, params *GetSandboxesSandboxIDMetricsParams, reqEditors ...RequestEditorFn) (*GetSandboxesSandboxIDMetricsResponse, error) + + // PutSandboxesSandboxIDNetworkWithBodyWithResponse request with any body + PutSandboxesSandboxIDNetworkWithBodyWithResponse(ctx context.Context, sandboxID SandboxID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PutSandboxesSandboxIDNetworkResponse, error) + + PutSandboxesSandboxIDNetworkWithResponse(ctx context.Context, sandboxID SandboxID, body PutSandboxesSandboxIDNetworkJSONRequestBody, reqEditors ...RequestEditorFn) (*PutSandboxesSandboxIDNetworkResponse, error) + + // PostSandboxesSandboxIDPauseWithBodyWithResponse request with any body + PostSandboxesSandboxIDPauseWithBodyWithResponse(ctx context.Context, sandboxID SandboxID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostSandboxesSandboxIDPauseResponse, error) + + PostSandboxesSandboxIDPauseWithResponse(ctx context.Context, sandboxID SandboxID, body PostSandboxesSandboxIDPauseJSONRequestBody, reqEditors ...RequestEditorFn) (*PostSandboxesSandboxIDPauseResponse, error) + + // PostSandboxesSandboxIDRefreshesWithBodyWithResponse request with any body + PostSandboxesSandboxIDRefreshesWithBodyWithResponse(ctx context.Context, sandboxID SandboxID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostSandboxesSandboxIDRefreshesResponse, error) + + PostSandboxesSandboxIDRefreshesWithResponse(ctx context.Context, sandboxID SandboxID, body PostSandboxesSandboxIDRefreshesJSONRequestBody, reqEditors ...RequestEditorFn) (*PostSandboxesSandboxIDRefreshesResponse, error) + + // PostSandboxesSandboxIDSnapshotsWithBodyWithResponse request with any body + PostSandboxesSandboxIDSnapshotsWithBodyWithResponse(ctx context.Context, sandboxID SandboxID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostSandboxesSandboxIDSnapshotsResponse, error) + + PostSandboxesSandboxIDSnapshotsWithResponse(ctx context.Context, sandboxID SandboxID, body PostSandboxesSandboxIDSnapshotsJSONRequestBody, reqEditors ...RequestEditorFn) (*PostSandboxesSandboxIDSnapshotsResponse, error) + + // PostSandboxesSandboxIDTimeoutWithBodyWithResponse request with any body + PostSandboxesSandboxIDTimeoutWithBodyWithResponse(ctx context.Context, sandboxID SandboxID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostSandboxesSandboxIDTimeoutResponse, error) + + PostSandboxesSandboxIDTimeoutWithResponse(ctx context.Context, sandboxID SandboxID, body PostSandboxesSandboxIDTimeoutJSONRequestBody, reqEditors ...RequestEditorFn) (*PostSandboxesSandboxIDTimeoutResponse, error) + + // GetSnapshotsWithResponse request + GetSnapshotsWithResponse(ctx context.Context, params *GetSnapshotsParams, reqEditors ...RequestEditorFn) (*GetSnapshotsResponse, error) + + // GetTemplatesAliasesAliasWithResponse request + GetTemplatesAliasesAliasWithResponse(ctx context.Context, alias string, reqEditors ...RequestEditorFn) (*GetTemplatesAliasesAliasResponse, error) + + // DeleteTemplatesTagsWithBodyWithResponse request with any body + DeleteTemplatesTagsWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*DeleteTemplatesTagsResponse, error) + + DeleteTemplatesTagsWithResponse(ctx context.Context, body DeleteTemplatesTagsJSONRequestBody, reqEditors ...RequestEditorFn) (*DeleteTemplatesTagsResponse, error) + + // PostTemplatesTagsWithBodyWithResponse request with any body + PostTemplatesTagsWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostTemplatesTagsResponse, error) + + PostTemplatesTagsWithResponse(ctx context.Context, body PostTemplatesTagsJSONRequestBody, reqEditors ...RequestEditorFn) (*PostTemplatesTagsResponse, error) + + // DeleteTemplatesTemplateIDWithResponse request + DeleteTemplatesTemplateIDWithResponse(ctx context.Context, templateID TemplateID, reqEditors ...RequestEditorFn) (*DeleteTemplatesTemplateIDResponse, error) + + // GetTemplatesTemplateIDWithResponse request + GetTemplatesTemplateIDWithResponse(ctx context.Context, templateID TemplateID, params *GetTemplatesTemplateIDParams, reqEditors ...RequestEditorFn) (*GetTemplatesTemplateIDResponse, error) + + // GetTemplatesTemplateIDBuildsBuildIDLogsWithResponse request + GetTemplatesTemplateIDBuildsBuildIDLogsWithResponse(ctx context.Context, templateID TemplateID, buildID BuildID, params *GetTemplatesTemplateIDBuildsBuildIDLogsParams, reqEditors ...RequestEditorFn) (*GetTemplatesTemplateIDBuildsBuildIDLogsResponse, error) + + // GetTemplatesTemplateIDBuildsBuildIDStatusWithResponse request + GetTemplatesTemplateIDBuildsBuildIDStatusWithResponse(ctx context.Context, templateID TemplateID, buildID BuildID, params *GetTemplatesTemplateIDBuildsBuildIDStatusParams, reqEditors ...RequestEditorFn) (*GetTemplatesTemplateIDBuildsBuildIDStatusResponse, error) + + // GetTemplatesTemplateIDFilesHashWithResponse request + GetTemplatesTemplateIDFilesHashWithResponse(ctx context.Context, templateID TemplateID, hash string, reqEditors ...RequestEditorFn) (*GetTemplatesTemplateIDFilesHashResponse, error) + + // GetTemplatesTemplateIDTagsWithResponse request + GetTemplatesTemplateIDTagsWithResponse(ctx context.Context, templateID TemplateID, reqEditors ...RequestEditorFn) (*GetTemplatesTemplateIDTagsResponse, error) + + // GetV2SandboxesWithResponse request + GetV2SandboxesWithResponse(ctx context.Context, params *GetV2SandboxesParams, reqEditors ...RequestEditorFn) (*GetV2SandboxesResponse, error) + + // GetV2SandboxesSandboxIDLogsWithResponse request + GetV2SandboxesSandboxIDLogsWithResponse(ctx context.Context, sandboxID SandboxID, params *GetV2SandboxesSandboxIDLogsParams, reqEditors ...RequestEditorFn) (*GetV2SandboxesSandboxIDLogsResponse, error) + + // GetV2TemplatesWithResponse request + GetV2TemplatesWithResponse(ctx context.Context, params *GetV2TemplatesParams, reqEditors ...RequestEditorFn) (*GetV2TemplatesResponse, error) + + // PatchV2TemplatesTemplateIDWithBodyWithResponse request with any body + PatchV2TemplatesTemplateIDWithBodyWithResponse(ctx context.Context, templateID TemplateID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchV2TemplatesTemplateIDResponse, error) + + PatchV2TemplatesTemplateIDWithResponse(ctx context.Context, templateID TemplateID, body PatchV2TemplatesTemplateIDJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchV2TemplatesTemplateIDResponse, error) + + // PostV2TemplatesTemplateIDBuildsBuildIDWithBodyWithResponse request with any body + PostV2TemplatesTemplateIDBuildsBuildIDWithBodyWithResponse(ctx context.Context, templateID TemplateID, buildID BuildID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV2TemplatesTemplateIDBuildsBuildIDResponse, error) + + PostV2TemplatesTemplateIDBuildsBuildIDWithResponse(ctx context.Context, templateID TemplateID, buildID BuildID, body PostV2TemplatesTemplateIDBuildsBuildIDJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV2TemplatesTemplateIDBuildsBuildIDResponse, error) + + // PostV3TemplatesWithBodyWithResponse request with any body + PostV3TemplatesWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV3TemplatesResponse, error) + + PostV3TemplatesWithResponse(ctx context.Context, body PostV3TemplatesJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV3TemplatesResponse, error) +} + +type PostSandboxesResponse struct { + Body []byte + HTTPResponse *http.Response + JSON201 *Sandbox + JSON400 *N400 + JSON401 *N401 + JSON500 *N500 +} + +// Status returns HTTPResponse.Status +func (r PostSandboxesResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PostSandboxesResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r PostSandboxesResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetSandboxesMetricsResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *SandboxesWithMetrics + JSON400 *N400 + JSON401 *N401 + JSON500 *N500 +} + +// Status returns HTTPResponse.Status +func (r GetSandboxesMetricsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetSandboxesMetricsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetSandboxesMetricsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type DeleteSandboxesSandboxIDResponse struct { + Body []byte + HTTPResponse *http.Response + JSON401 *N401 + JSON404 *N404 + JSON500 *N500 +} + +// Status returns HTTPResponse.Status +func (r DeleteSandboxesSandboxIDResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r DeleteSandboxesSandboxIDResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DeleteSandboxesSandboxIDResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetSandboxesSandboxIDResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *SandboxDetail + JSON401 *N401 + JSON404 *N404 + JSON500 *N500 +} + +// Status returns HTTPResponse.Status +func (r GetSandboxesSandboxIDResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetSandboxesSandboxIDResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetSandboxesSandboxIDResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type PostSandboxesSandboxIDConnectResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *Sandbox + JSON201 *Sandbox + JSON400 *N400 + JSON401 *N401 + JSON404 *N404 + JSON500 *N500 +} + +// Status returns HTTPResponse.Status +func (r PostSandboxesSandboxIDConnectResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PostSandboxesSandboxIDConnectResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r PostSandboxesSandboxIDConnectResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type PostSandboxesSandboxIDForkResponse struct { + Body []byte + HTTPResponse *http.Response + JSON201 *[]SandboxForkResult + JSON401 *N401 + JSON404 *N404 + JSON409 *N409 + JSON500 *N500 +} + +// Status returns HTTPResponse.Status +func (r PostSandboxesSandboxIDForkResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PostSandboxesSandboxIDForkResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r PostSandboxesSandboxIDForkResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetSandboxesSandboxIDMetricsResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *[]SandboxMetric + JSON400 *N400 + JSON401 *N401 + JSON404 *N404 + JSON500 *N500 +} + +// Status returns HTTPResponse.Status +func (r GetSandboxesSandboxIDMetricsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetSandboxesSandboxIDMetricsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetSandboxesSandboxIDMetricsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type PutSandboxesSandboxIDNetworkResponse struct { + Body []byte + HTTPResponse *http.Response + JSON401 *N401 + JSON404 *N404 + JSON409 *N409 + JSON500 *N500 +} + +// Status returns HTTPResponse.Status +func (r PutSandboxesSandboxIDNetworkResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PutSandboxesSandboxIDNetworkResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r PutSandboxesSandboxIDNetworkResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type PostSandboxesSandboxIDPauseResponse struct { + Body []byte + HTTPResponse *http.Response + JSON401 *N401 + JSON404 *N404 + JSON409 *N409 + JSON500 *N500 +} + +// Status returns HTTPResponse.Status +func (r PostSandboxesSandboxIDPauseResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PostSandboxesSandboxIDPauseResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r PostSandboxesSandboxIDPauseResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type PostSandboxesSandboxIDRefreshesResponse struct { + Body []byte + HTTPResponse *http.Response + JSON401 *N401 + JSON404 *N404 +} + +// Status returns HTTPResponse.Status +func (r PostSandboxesSandboxIDRefreshesResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PostSandboxesSandboxIDRefreshesResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r PostSandboxesSandboxIDRefreshesResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type PostSandboxesSandboxIDSnapshotsResponse struct { + Body []byte + HTTPResponse *http.Response + JSON201 *SnapshotInfo + JSON400 *N400 + JSON401 *N401 + JSON404 *N404 + JSON500 *N500 +} + +// Status returns HTTPResponse.Status +func (r PostSandboxesSandboxIDSnapshotsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PostSandboxesSandboxIDSnapshotsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r PostSandboxesSandboxIDSnapshotsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type PostSandboxesSandboxIDTimeoutResponse struct { + Body []byte + HTTPResponse *http.Response + JSON401 *N401 + JSON404 *N404 + JSON500 *N500 +} + +// Status returns HTTPResponse.Status +func (r PostSandboxesSandboxIDTimeoutResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PostSandboxesSandboxIDTimeoutResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r PostSandboxesSandboxIDTimeoutResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetSnapshotsResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *[]SnapshotInfo + JSON401 *N401 + JSON500 *N500 +} + +// Status returns HTTPResponse.Status +func (r GetSnapshotsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetSnapshotsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetSnapshotsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetTemplatesAliasesAliasResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *TemplateAliasResponse + JSON400 *N400 + JSON403 *N403 + JSON404 *N404 + JSON500 *N500 +} + +// Status returns HTTPResponse.Status +func (r GetTemplatesAliasesAliasResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetTemplatesAliasesAliasResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetTemplatesAliasesAliasResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type DeleteTemplatesTagsResponse struct { + Body []byte + HTTPResponse *http.Response + JSON400 *N400 + JSON401 *N401 + JSON404 *N404 + JSON500 *N500 +} + +// Status returns HTTPResponse.Status +func (r DeleteTemplatesTagsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r DeleteTemplatesTagsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DeleteTemplatesTagsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type PostTemplatesTagsResponse struct { + Body []byte + HTTPResponse *http.Response + JSON201 *AssignedTemplateTags + JSON400 *N400 + JSON401 *N401 + JSON404 *N404 + JSON500 *N500 +} + +// Status returns HTTPResponse.Status +func (r PostTemplatesTagsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PostTemplatesTagsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r PostTemplatesTagsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type DeleteTemplatesTemplateIDResponse struct { + Body []byte + HTTPResponse *http.Response + JSON401 *N401 + JSON500 *N500 +} + +// Status returns HTTPResponse.Status +func (r DeleteTemplatesTemplateIDResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r DeleteTemplatesTemplateIDResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DeleteTemplatesTemplateIDResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetTemplatesTemplateIDResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *TemplateWithBuilds + JSON401 *N401 + JSON500 *N500 +} + +// Status returns HTTPResponse.Status +func (r GetTemplatesTemplateIDResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetTemplatesTemplateIDResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetTemplatesTemplateIDResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetTemplatesTemplateIDBuildsBuildIDLogsResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *TemplateBuildLogsResponse + JSON401 *N401 + JSON404 *N404 + JSON500 *N500 +} + +// Status returns HTTPResponse.Status +func (r GetTemplatesTemplateIDBuildsBuildIDLogsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetTemplatesTemplateIDBuildsBuildIDLogsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetTemplatesTemplateIDBuildsBuildIDLogsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetTemplatesTemplateIDBuildsBuildIDStatusResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *TemplateBuildInfo + JSON401 *N401 + JSON404 *N404 + JSON500 *N500 +} + +// Status returns HTTPResponse.Status +func (r GetTemplatesTemplateIDBuildsBuildIDStatusResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetTemplatesTemplateIDBuildsBuildIDStatusResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetTemplatesTemplateIDBuildsBuildIDStatusResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetTemplatesTemplateIDFilesHashResponse struct { + Body []byte + HTTPResponse *http.Response + JSON201 *TemplateBuildFileUpload + JSON400 *N400 + JSON401 *N401 + JSON404 *N404 + JSON500 *N500 +} + +// Status returns HTTPResponse.Status +func (r GetTemplatesTemplateIDFilesHashResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetTemplatesTemplateIDFilesHashResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetTemplatesTemplateIDFilesHashResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetTemplatesTemplateIDTagsResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *[]TemplateTag + JSON401 *N401 + JSON403 *N403 + JSON404 *N404 + JSON500 *N500 +} + +// Status returns HTTPResponse.Status +func (r GetTemplatesTemplateIDTagsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetTemplatesTemplateIDTagsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetTemplatesTemplateIDTagsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetV2SandboxesResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *[]ListedSandbox + JSON400 *N400 + JSON401 *N401 + JSON500 *N500 +} + +// Status returns HTTPResponse.Status +func (r GetV2SandboxesResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetV2SandboxesResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetV2SandboxesResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetV2SandboxesSandboxIDLogsResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *SandboxLogsV2Response + JSON401 *N401 + JSON404 *N404 + JSON500 *N500 +} + +// Status returns HTTPResponse.Status +func (r GetV2SandboxesSandboxIDLogsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetV2SandboxesSandboxIDLogsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetV2SandboxesSandboxIDLogsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetV2TemplatesResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *[]Template + JSON400 *N400 + JSON401 *N401 + JSON403 *N403 + JSON500 *N500 +} + +// Status returns HTTPResponse.Status +func (r GetV2TemplatesResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetV2TemplatesResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetV2TemplatesResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type PatchV2TemplatesTemplateIDResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *TemplateUpdateResponse + JSON400 *N400 + JSON401 *N401 + JSON500 *N500 +} + +// Status returns HTTPResponse.Status +func (r PatchV2TemplatesTemplateIDResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PatchV2TemplatesTemplateIDResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r PatchV2TemplatesTemplateIDResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type PostV2TemplatesTemplateIDBuildsBuildIDResponse struct { + Body []byte + HTTPResponse *http.Response + JSON401 *N401 + JSON500 *N500 +} + +// Status returns HTTPResponse.Status +func (r PostV2TemplatesTemplateIDBuildsBuildIDResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PostV2TemplatesTemplateIDBuildsBuildIDResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r PostV2TemplatesTemplateIDBuildsBuildIDResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type PostV3TemplatesResponse struct { + Body []byte + HTTPResponse *http.Response + JSON202 *TemplateRequestResponseV3 + JSON400 *N400 + JSON401 *N401 + JSON403 *N403 + JSON500 *N500 +} + +// Status returns HTTPResponse.Status +func (r PostV3TemplatesResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PostV3TemplatesResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r PostV3TemplatesResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +// PostSandboxesWithBodyWithResponse request with arbitrary body returning *PostSandboxesResponse +func (c *ClientWithResponses) PostSandboxesWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostSandboxesResponse, error) { + rsp, err := c.PostSandboxesWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostSandboxesResponse(rsp) +} + +func (c *ClientWithResponses) PostSandboxesWithResponse(ctx context.Context, body PostSandboxesJSONRequestBody, reqEditors ...RequestEditorFn) (*PostSandboxesResponse, error) { + rsp, err := c.PostSandboxes(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostSandboxesResponse(rsp) +} + +// GetSandboxesMetricsWithResponse request returning *GetSandboxesMetricsResponse +func (c *ClientWithResponses) GetSandboxesMetricsWithResponse(ctx context.Context, params *GetSandboxesMetricsParams, reqEditors ...RequestEditorFn) (*GetSandboxesMetricsResponse, error) { + rsp, err := c.GetSandboxesMetrics(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetSandboxesMetricsResponse(rsp) +} + +// DeleteSandboxesSandboxIDWithResponse request returning *DeleteSandboxesSandboxIDResponse +func (c *ClientWithResponses) DeleteSandboxesSandboxIDWithResponse(ctx context.Context, sandboxID SandboxID, reqEditors ...RequestEditorFn) (*DeleteSandboxesSandboxIDResponse, error) { + rsp, err := c.DeleteSandboxesSandboxID(ctx, sandboxID, reqEditors...) + if err != nil { + return nil, err + } + return ParseDeleteSandboxesSandboxIDResponse(rsp) +} + +// GetSandboxesSandboxIDWithResponse request returning *GetSandboxesSandboxIDResponse +func (c *ClientWithResponses) GetSandboxesSandboxIDWithResponse(ctx context.Context, sandboxID SandboxID, reqEditors ...RequestEditorFn) (*GetSandboxesSandboxIDResponse, error) { + rsp, err := c.GetSandboxesSandboxID(ctx, sandboxID, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetSandboxesSandboxIDResponse(rsp) +} + +// PostSandboxesSandboxIDConnectWithBodyWithResponse request with arbitrary body returning *PostSandboxesSandboxIDConnectResponse +func (c *ClientWithResponses) PostSandboxesSandboxIDConnectWithBodyWithResponse(ctx context.Context, sandboxID SandboxID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostSandboxesSandboxIDConnectResponse, error) { + rsp, err := c.PostSandboxesSandboxIDConnectWithBody(ctx, sandboxID, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostSandboxesSandboxIDConnectResponse(rsp) +} + +func (c *ClientWithResponses) PostSandboxesSandboxIDConnectWithResponse(ctx context.Context, sandboxID SandboxID, body PostSandboxesSandboxIDConnectJSONRequestBody, reqEditors ...RequestEditorFn) (*PostSandboxesSandboxIDConnectResponse, error) { + rsp, err := c.PostSandboxesSandboxIDConnect(ctx, sandboxID, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostSandboxesSandboxIDConnectResponse(rsp) +} + +// PostSandboxesSandboxIDForkWithBodyWithResponse request with arbitrary body returning *PostSandboxesSandboxIDForkResponse +func (c *ClientWithResponses) PostSandboxesSandboxIDForkWithBodyWithResponse(ctx context.Context, sandboxID SandboxID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostSandboxesSandboxIDForkResponse, error) { + rsp, err := c.PostSandboxesSandboxIDForkWithBody(ctx, sandboxID, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostSandboxesSandboxIDForkResponse(rsp) +} + +func (c *ClientWithResponses) PostSandboxesSandboxIDForkWithResponse(ctx context.Context, sandboxID SandboxID, body PostSandboxesSandboxIDForkJSONRequestBody, reqEditors ...RequestEditorFn) (*PostSandboxesSandboxIDForkResponse, error) { + rsp, err := c.PostSandboxesSandboxIDFork(ctx, sandboxID, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostSandboxesSandboxIDForkResponse(rsp) +} + +// GetSandboxesSandboxIDMetricsWithResponse request returning *GetSandboxesSandboxIDMetricsResponse +func (c *ClientWithResponses) GetSandboxesSandboxIDMetricsWithResponse(ctx context.Context, sandboxID SandboxID, params *GetSandboxesSandboxIDMetricsParams, reqEditors ...RequestEditorFn) (*GetSandboxesSandboxIDMetricsResponse, error) { + rsp, err := c.GetSandboxesSandboxIDMetrics(ctx, sandboxID, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetSandboxesSandboxIDMetricsResponse(rsp) +} + +// PutSandboxesSandboxIDNetworkWithBodyWithResponse request with arbitrary body returning *PutSandboxesSandboxIDNetworkResponse +func (c *ClientWithResponses) PutSandboxesSandboxIDNetworkWithBodyWithResponse(ctx context.Context, sandboxID SandboxID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PutSandboxesSandboxIDNetworkResponse, error) { + rsp, err := c.PutSandboxesSandboxIDNetworkWithBody(ctx, sandboxID, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePutSandboxesSandboxIDNetworkResponse(rsp) +} + +func (c *ClientWithResponses) PutSandboxesSandboxIDNetworkWithResponse(ctx context.Context, sandboxID SandboxID, body PutSandboxesSandboxIDNetworkJSONRequestBody, reqEditors ...RequestEditorFn) (*PutSandboxesSandboxIDNetworkResponse, error) { + rsp, err := c.PutSandboxesSandboxIDNetwork(ctx, sandboxID, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePutSandboxesSandboxIDNetworkResponse(rsp) +} + +// PostSandboxesSandboxIDPauseWithBodyWithResponse request with arbitrary body returning *PostSandboxesSandboxIDPauseResponse +func (c *ClientWithResponses) PostSandboxesSandboxIDPauseWithBodyWithResponse(ctx context.Context, sandboxID SandboxID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostSandboxesSandboxIDPauseResponse, error) { + rsp, err := c.PostSandboxesSandboxIDPauseWithBody(ctx, sandboxID, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostSandboxesSandboxIDPauseResponse(rsp) +} + +func (c *ClientWithResponses) PostSandboxesSandboxIDPauseWithResponse(ctx context.Context, sandboxID SandboxID, body PostSandboxesSandboxIDPauseJSONRequestBody, reqEditors ...RequestEditorFn) (*PostSandboxesSandboxIDPauseResponse, error) { + rsp, err := c.PostSandboxesSandboxIDPause(ctx, sandboxID, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostSandboxesSandboxIDPauseResponse(rsp) +} + +// PostSandboxesSandboxIDRefreshesWithBodyWithResponse request with arbitrary body returning *PostSandboxesSandboxIDRefreshesResponse +func (c *ClientWithResponses) PostSandboxesSandboxIDRefreshesWithBodyWithResponse(ctx context.Context, sandboxID SandboxID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostSandboxesSandboxIDRefreshesResponse, error) { + rsp, err := c.PostSandboxesSandboxIDRefreshesWithBody(ctx, sandboxID, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostSandboxesSandboxIDRefreshesResponse(rsp) +} + +func (c *ClientWithResponses) PostSandboxesSandboxIDRefreshesWithResponse(ctx context.Context, sandboxID SandboxID, body PostSandboxesSandboxIDRefreshesJSONRequestBody, reqEditors ...RequestEditorFn) (*PostSandboxesSandboxIDRefreshesResponse, error) { + rsp, err := c.PostSandboxesSandboxIDRefreshes(ctx, sandboxID, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostSandboxesSandboxIDRefreshesResponse(rsp) +} + +// PostSandboxesSandboxIDSnapshotsWithBodyWithResponse request with arbitrary body returning *PostSandboxesSandboxIDSnapshotsResponse +func (c *ClientWithResponses) PostSandboxesSandboxIDSnapshotsWithBodyWithResponse(ctx context.Context, sandboxID SandboxID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostSandboxesSandboxIDSnapshotsResponse, error) { + rsp, err := c.PostSandboxesSandboxIDSnapshotsWithBody(ctx, sandboxID, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostSandboxesSandboxIDSnapshotsResponse(rsp) +} + +func (c *ClientWithResponses) PostSandboxesSandboxIDSnapshotsWithResponse(ctx context.Context, sandboxID SandboxID, body PostSandboxesSandboxIDSnapshotsJSONRequestBody, reqEditors ...RequestEditorFn) (*PostSandboxesSandboxIDSnapshotsResponse, error) { + rsp, err := c.PostSandboxesSandboxIDSnapshots(ctx, sandboxID, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostSandboxesSandboxIDSnapshotsResponse(rsp) +} + +// PostSandboxesSandboxIDTimeoutWithBodyWithResponse request with arbitrary body returning *PostSandboxesSandboxIDTimeoutResponse +func (c *ClientWithResponses) PostSandboxesSandboxIDTimeoutWithBodyWithResponse(ctx context.Context, sandboxID SandboxID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostSandboxesSandboxIDTimeoutResponse, error) { + rsp, err := c.PostSandboxesSandboxIDTimeoutWithBody(ctx, sandboxID, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostSandboxesSandboxIDTimeoutResponse(rsp) +} + +func (c *ClientWithResponses) PostSandboxesSandboxIDTimeoutWithResponse(ctx context.Context, sandboxID SandboxID, body PostSandboxesSandboxIDTimeoutJSONRequestBody, reqEditors ...RequestEditorFn) (*PostSandboxesSandboxIDTimeoutResponse, error) { + rsp, err := c.PostSandboxesSandboxIDTimeout(ctx, sandboxID, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostSandboxesSandboxIDTimeoutResponse(rsp) +} + +// GetSnapshotsWithResponse request returning *GetSnapshotsResponse +func (c *ClientWithResponses) GetSnapshotsWithResponse(ctx context.Context, params *GetSnapshotsParams, reqEditors ...RequestEditorFn) (*GetSnapshotsResponse, error) { + rsp, err := c.GetSnapshots(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetSnapshotsResponse(rsp) +} + +// GetTemplatesAliasesAliasWithResponse request returning *GetTemplatesAliasesAliasResponse +func (c *ClientWithResponses) GetTemplatesAliasesAliasWithResponse(ctx context.Context, alias string, reqEditors ...RequestEditorFn) (*GetTemplatesAliasesAliasResponse, error) { + rsp, err := c.GetTemplatesAliasesAlias(ctx, alias, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetTemplatesAliasesAliasResponse(rsp) +} + +// DeleteTemplatesTagsWithBodyWithResponse request with arbitrary body returning *DeleteTemplatesTagsResponse +func (c *ClientWithResponses) DeleteTemplatesTagsWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*DeleteTemplatesTagsResponse, error) { + rsp, err := c.DeleteTemplatesTagsWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseDeleteTemplatesTagsResponse(rsp) +} + +func (c *ClientWithResponses) DeleteTemplatesTagsWithResponse(ctx context.Context, body DeleteTemplatesTagsJSONRequestBody, reqEditors ...RequestEditorFn) (*DeleteTemplatesTagsResponse, error) { + rsp, err := c.DeleteTemplatesTags(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseDeleteTemplatesTagsResponse(rsp) +} + +// PostTemplatesTagsWithBodyWithResponse request with arbitrary body returning *PostTemplatesTagsResponse +func (c *ClientWithResponses) PostTemplatesTagsWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostTemplatesTagsResponse, error) { + rsp, err := c.PostTemplatesTagsWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostTemplatesTagsResponse(rsp) +} + +func (c *ClientWithResponses) PostTemplatesTagsWithResponse(ctx context.Context, body PostTemplatesTagsJSONRequestBody, reqEditors ...RequestEditorFn) (*PostTemplatesTagsResponse, error) { + rsp, err := c.PostTemplatesTags(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostTemplatesTagsResponse(rsp) +} + +// DeleteTemplatesTemplateIDWithResponse request returning *DeleteTemplatesTemplateIDResponse +func (c *ClientWithResponses) DeleteTemplatesTemplateIDWithResponse(ctx context.Context, templateID TemplateID, reqEditors ...RequestEditorFn) (*DeleteTemplatesTemplateIDResponse, error) { + rsp, err := c.DeleteTemplatesTemplateID(ctx, templateID, reqEditors...) + if err != nil { + return nil, err + } + return ParseDeleteTemplatesTemplateIDResponse(rsp) +} + +// GetTemplatesTemplateIDWithResponse request returning *GetTemplatesTemplateIDResponse +func (c *ClientWithResponses) GetTemplatesTemplateIDWithResponse(ctx context.Context, templateID TemplateID, params *GetTemplatesTemplateIDParams, reqEditors ...RequestEditorFn) (*GetTemplatesTemplateIDResponse, error) { + rsp, err := c.GetTemplatesTemplateID(ctx, templateID, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetTemplatesTemplateIDResponse(rsp) +} + +// GetTemplatesTemplateIDBuildsBuildIDLogsWithResponse request returning *GetTemplatesTemplateIDBuildsBuildIDLogsResponse +func (c *ClientWithResponses) GetTemplatesTemplateIDBuildsBuildIDLogsWithResponse(ctx context.Context, templateID TemplateID, buildID BuildID, params *GetTemplatesTemplateIDBuildsBuildIDLogsParams, reqEditors ...RequestEditorFn) (*GetTemplatesTemplateIDBuildsBuildIDLogsResponse, error) { + rsp, err := c.GetTemplatesTemplateIDBuildsBuildIDLogs(ctx, templateID, buildID, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetTemplatesTemplateIDBuildsBuildIDLogsResponse(rsp) +} + +// GetTemplatesTemplateIDBuildsBuildIDStatusWithResponse request returning *GetTemplatesTemplateIDBuildsBuildIDStatusResponse +func (c *ClientWithResponses) GetTemplatesTemplateIDBuildsBuildIDStatusWithResponse(ctx context.Context, templateID TemplateID, buildID BuildID, params *GetTemplatesTemplateIDBuildsBuildIDStatusParams, reqEditors ...RequestEditorFn) (*GetTemplatesTemplateIDBuildsBuildIDStatusResponse, error) { + rsp, err := c.GetTemplatesTemplateIDBuildsBuildIDStatus(ctx, templateID, buildID, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetTemplatesTemplateIDBuildsBuildIDStatusResponse(rsp) +} + +// GetTemplatesTemplateIDFilesHashWithResponse request returning *GetTemplatesTemplateIDFilesHashResponse +func (c *ClientWithResponses) GetTemplatesTemplateIDFilesHashWithResponse(ctx context.Context, templateID TemplateID, hash string, reqEditors ...RequestEditorFn) (*GetTemplatesTemplateIDFilesHashResponse, error) { + rsp, err := c.GetTemplatesTemplateIDFilesHash(ctx, templateID, hash, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetTemplatesTemplateIDFilesHashResponse(rsp) +} + +// GetTemplatesTemplateIDTagsWithResponse request returning *GetTemplatesTemplateIDTagsResponse +func (c *ClientWithResponses) GetTemplatesTemplateIDTagsWithResponse(ctx context.Context, templateID TemplateID, reqEditors ...RequestEditorFn) (*GetTemplatesTemplateIDTagsResponse, error) { + rsp, err := c.GetTemplatesTemplateIDTags(ctx, templateID, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetTemplatesTemplateIDTagsResponse(rsp) +} + +// GetV2SandboxesWithResponse request returning *GetV2SandboxesResponse +func (c *ClientWithResponses) GetV2SandboxesWithResponse(ctx context.Context, params *GetV2SandboxesParams, reqEditors ...RequestEditorFn) (*GetV2SandboxesResponse, error) { + rsp, err := c.GetV2Sandboxes(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetV2SandboxesResponse(rsp) +} + +// GetV2SandboxesSandboxIDLogsWithResponse request returning *GetV2SandboxesSandboxIDLogsResponse +func (c *ClientWithResponses) GetV2SandboxesSandboxIDLogsWithResponse(ctx context.Context, sandboxID SandboxID, params *GetV2SandboxesSandboxIDLogsParams, reqEditors ...RequestEditorFn) (*GetV2SandboxesSandboxIDLogsResponse, error) { + rsp, err := c.GetV2SandboxesSandboxIDLogs(ctx, sandboxID, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetV2SandboxesSandboxIDLogsResponse(rsp) +} + +// GetV2TemplatesWithResponse request returning *GetV2TemplatesResponse +func (c *ClientWithResponses) GetV2TemplatesWithResponse(ctx context.Context, params *GetV2TemplatesParams, reqEditors ...RequestEditorFn) (*GetV2TemplatesResponse, error) { + rsp, err := c.GetV2Templates(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetV2TemplatesResponse(rsp) +} + +// PatchV2TemplatesTemplateIDWithBodyWithResponse request with arbitrary body returning *PatchV2TemplatesTemplateIDResponse +func (c *ClientWithResponses) PatchV2TemplatesTemplateIDWithBodyWithResponse(ctx context.Context, templateID TemplateID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PatchV2TemplatesTemplateIDResponse, error) { + rsp, err := c.PatchV2TemplatesTemplateIDWithBody(ctx, templateID, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePatchV2TemplatesTemplateIDResponse(rsp) +} + +func (c *ClientWithResponses) PatchV2TemplatesTemplateIDWithResponse(ctx context.Context, templateID TemplateID, body PatchV2TemplatesTemplateIDJSONRequestBody, reqEditors ...RequestEditorFn) (*PatchV2TemplatesTemplateIDResponse, error) { + rsp, err := c.PatchV2TemplatesTemplateID(ctx, templateID, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePatchV2TemplatesTemplateIDResponse(rsp) +} + +// PostV2TemplatesTemplateIDBuildsBuildIDWithBodyWithResponse request with arbitrary body returning *PostV2TemplatesTemplateIDBuildsBuildIDResponse +func (c *ClientWithResponses) PostV2TemplatesTemplateIDBuildsBuildIDWithBodyWithResponse(ctx context.Context, templateID TemplateID, buildID BuildID, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV2TemplatesTemplateIDBuildsBuildIDResponse, error) { + rsp, err := c.PostV2TemplatesTemplateIDBuildsBuildIDWithBody(ctx, templateID, buildID, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostV2TemplatesTemplateIDBuildsBuildIDResponse(rsp) +} + +func (c *ClientWithResponses) PostV2TemplatesTemplateIDBuildsBuildIDWithResponse(ctx context.Context, templateID TemplateID, buildID BuildID, body PostV2TemplatesTemplateIDBuildsBuildIDJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV2TemplatesTemplateIDBuildsBuildIDResponse, error) { + rsp, err := c.PostV2TemplatesTemplateIDBuildsBuildID(ctx, templateID, buildID, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostV2TemplatesTemplateIDBuildsBuildIDResponse(rsp) +} + +// PostV3TemplatesWithBodyWithResponse request with arbitrary body returning *PostV3TemplatesResponse +func (c *ClientWithResponses) PostV3TemplatesWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostV3TemplatesResponse, error) { + rsp, err := c.PostV3TemplatesWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostV3TemplatesResponse(rsp) +} + +func (c *ClientWithResponses) PostV3TemplatesWithResponse(ctx context.Context, body PostV3TemplatesJSONRequestBody, reqEditors ...RequestEditorFn) (*PostV3TemplatesResponse, error) { + rsp, err := c.PostV3Templates(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostV3TemplatesResponse(rsp) +} + +// ParsePostSandboxesResponse parses an HTTP response from a PostSandboxesWithResponse call +func ParsePostSandboxesResponse(rsp *http.Response) (*PostSandboxesResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PostSandboxesResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: + var dest Sandbox + 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 N400 + 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 == 401: + var dest N401 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest N500 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseGetSandboxesMetricsResponse parses an HTTP response from a GetSandboxesMetricsWithResponse call +func ParseGetSandboxesMetricsResponse(rsp *http.Response) (*GetSandboxesMetricsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetSandboxesMetricsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest SandboxesWithMetrics + 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 N400 + 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 == 401: + var dest N401 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest N500 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseDeleteSandboxesSandboxIDResponse parses an HTTP response from a DeleteSandboxesSandboxIDWithResponse call +func ParseDeleteSandboxesSandboxIDResponse(rsp *http.Response) (*DeleteSandboxesSandboxIDResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DeleteSandboxesSandboxIDResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest N401 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest N404 + 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 N500 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseGetSandboxesSandboxIDResponse parses an HTTP response from a GetSandboxesSandboxIDWithResponse call +func ParseGetSandboxesSandboxIDResponse(rsp *http.Response) (*GetSandboxesSandboxIDResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetSandboxesSandboxIDResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest SandboxDetail + 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 == 401: + var dest N401 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest N404 + 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 N500 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParsePostSandboxesSandboxIDConnectResponse parses an HTTP response from a PostSandboxesSandboxIDConnectWithResponse call +func ParsePostSandboxesSandboxIDConnectResponse(rsp *http.Response) (*PostSandboxesSandboxIDConnectResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PostSandboxesSandboxIDConnectResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest Sandbox + 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 == 201: + var dest Sandbox + 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 N400 + 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 == 401: + var dest N401 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest N404 + 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 N500 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParsePostSandboxesSandboxIDForkResponse parses an HTTP response from a PostSandboxesSandboxIDForkWithResponse call +func ParsePostSandboxesSandboxIDForkResponse(rsp *http.Response) (*PostSandboxesSandboxIDForkResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PostSandboxesSandboxIDForkResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: + var dest []SandboxForkResult + 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 == 401: + var dest N401 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest N404 + 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 == 409: + var dest N409 + 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 N500 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseGetSandboxesSandboxIDMetricsResponse parses an HTTP response from a GetSandboxesSandboxIDMetricsWithResponse call +func ParseGetSandboxesSandboxIDMetricsResponse(rsp *http.Response) (*GetSandboxesSandboxIDMetricsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetSandboxesSandboxIDMetricsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest []SandboxMetric + 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 N400 + 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 == 401: + var dest N401 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest N404 + 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 N500 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParsePutSandboxesSandboxIDNetworkResponse parses an HTTP response from a PutSandboxesSandboxIDNetworkWithResponse call +func ParsePutSandboxesSandboxIDNetworkResponse(rsp *http.Response) (*PutSandboxesSandboxIDNetworkResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PutSandboxesSandboxIDNetworkResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest N401 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest N404 + 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 == 409: + var dest N409 + 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 N500 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParsePostSandboxesSandboxIDPauseResponse parses an HTTP response from a PostSandboxesSandboxIDPauseWithResponse call +func ParsePostSandboxesSandboxIDPauseResponse(rsp *http.Response) (*PostSandboxesSandboxIDPauseResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PostSandboxesSandboxIDPauseResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest N401 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest N404 + 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 == 409: + var dest N409 + 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 N500 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParsePostSandboxesSandboxIDRefreshesResponse parses an HTTP response from a PostSandboxesSandboxIDRefreshesWithResponse call +func ParsePostSandboxesSandboxIDRefreshesResponse(rsp *http.Response) (*PostSandboxesSandboxIDRefreshesResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PostSandboxesSandboxIDRefreshesResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest N401 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest N404 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON404 = &dest + + } + + return response, nil +} + +// ParsePostSandboxesSandboxIDSnapshotsResponse parses an HTTP response from a PostSandboxesSandboxIDSnapshotsWithResponse call +func ParsePostSandboxesSandboxIDSnapshotsResponse(rsp *http.Response) (*PostSandboxesSandboxIDSnapshotsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PostSandboxesSandboxIDSnapshotsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: + var dest SnapshotInfo + 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 N400 + 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 == 401: + var dest N401 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest N404 + 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 N500 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParsePostSandboxesSandboxIDTimeoutResponse parses an HTTP response from a PostSandboxesSandboxIDTimeoutWithResponse call +func ParsePostSandboxesSandboxIDTimeoutResponse(rsp *http.Response) (*PostSandboxesSandboxIDTimeoutResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PostSandboxesSandboxIDTimeoutResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest N401 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest N404 + 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 N500 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseGetSnapshotsResponse parses an HTTP response from a GetSnapshotsWithResponse call +func ParseGetSnapshotsResponse(rsp *http.Response) (*GetSnapshotsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetSnapshotsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest []SnapshotInfo + 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 == 401: + var dest N401 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest N500 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseGetTemplatesAliasesAliasResponse parses an HTTP response from a GetTemplatesAliasesAliasWithResponse call +func ParseGetTemplatesAliasesAliasResponse(rsp *http.Response) (*GetTemplatesAliasesAliasResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetTemplatesAliasesAliasResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TemplateAliasResponse + 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 N400 + 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 == 403: + var dest N403 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest N404 + 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 N500 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseDeleteTemplatesTagsResponse parses an HTTP response from a DeleteTemplatesTagsWithResponse call +func ParseDeleteTemplatesTagsResponse(rsp *http.Response) (*DeleteTemplatesTagsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DeleteTemplatesTagsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest N400 + 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 == 401: + var dest N401 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest N404 + 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 N500 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParsePostTemplatesTagsResponse parses an HTTP response from a PostTemplatesTagsWithResponse call +func ParsePostTemplatesTagsResponse(rsp *http.Response) (*PostTemplatesTagsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PostTemplatesTagsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: + var dest AssignedTemplateTags + 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 N400 + 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 == 401: + var dest N401 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest N404 + 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 N500 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseDeleteTemplatesTemplateIDResponse parses an HTTP response from a DeleteTemplatesTemplateIDWithResponse call +func ParseDeleteTemplatesTemplateIDResponse(rsp *http.Response) (*DeleteTemplatesTemplateIDResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DeleteTemplatesTemplateIDResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest N401 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest N500 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseGetTemplatesTemplateIDResponse parses an HTTP response from a GetTemplatesTemplateIDWithResponse call +func ParseGetTemplatesTemplateIDResponse(rsp *http.Response) (*GetTemplatesTemplateIDResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetTemplatesTemplateIDResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TemplateWithBuilds + 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 == 401: + var dest N401 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest N500 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseGetTemplatesTemplateIDBuildsBuildIDLogsResponse parses an HTTP response from a GetTemplatesTemplateIDBuildsBuildIDLogsWithResponse call +func ParseGetTemplatesTemplateIDBuildsBuildIDLogsResponse(rsp *http.Response) (*GetTemplatesTemplateIDBuildsBuildIDLogsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetTemplatesTemplateIDBuildsBuildIDLogsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TemplateBuildLogsResponse + 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 == 401: + var dest N401 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest N404 + 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 N500 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseGetTemplatesTemplateIDBuildsBuildIDStatusResponse parses an HTTP response from a GetTemplatesTemplateIDBuildsBuildIDStatusWithResponse call +func ParseGetTemplatesTemplateIDBuildsBuildIDStatusResponse(rsp *http.Response) (*GetTemplatesTemplateIDBuildsBuildIDStatusResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetTemplatesTemplateIDBuildsBuildIDStatusResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TemplateBuildInfo + 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 == 401: + var dest N401 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest N404 + 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 N500 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseGetTemplatesTemplateIDFilesHashResponse parses an HTTP response from a GetTemplatesTemplateIDFilesHashWithResponse call +func ParseGetTemplatesTemplateIDFilesHashResponse(rsp *http.Response) (*GetTemplatesTemplateIDFilesHashResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetTemplatesTemplateIDFilesHashResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 201: + var dest TemplateBuildFileUpload + 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 N400 + 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 == 401: + var dest N401 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest N404 + 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 N500 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseGetTemplatesTemplateIDTagsResponse parses an HTTP response from a GetTemplatesTemplateIDTagsWithResponse call +func ParseGetTemplatesTemplateIDTagsResponse(rsp *http.Response) (*GetTemplatesTemplateIDTagsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetTemplatesTemplateIDTagsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest []TemplateTag + 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 == 401: + var dest N401 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest N403 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest N404 + 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 N500 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseGetV2SandboxesResponse parses an HTTP response from a GetV2SandboxesWithResponse call +func ParseGetV2SandboxesResponse(rsp *http.Response) (*GetV2SandboxesResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetV2SandboxesResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest []ListedSandbox + 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 N400 + 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 == 401: + var dest N401 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest N500 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseGetV2SandboxesSandboxIDLogsResponse parses an HTTP response from a GetV2SandboxesSandboxIDLogsWithResponse call +func ParseGetV2SandboxesSandboxIDLogsResponse(rsp *http.Response) (*GetV2SandboxesSandboxIDLogsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetV2SandboxesSandboxIDLogsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest SandboxLogsV2Response + 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 == 401: + var dest N401 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest N404 + 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 N500 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseGetV2TemplatesResponse parses an HTTP response from a GetV2TemplatesWithResponse call +func ParseGetV2TemplatesResponse(rsp *http.Response) (*GetV2TemplatesResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetV2TemplatesResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest []Template + 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 N400 + 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 == 401: + var dest N401 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest N403 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest N500 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParsePatchV2TemplatesTemplateIDResponse parses an HTTP response from a PatchV2TemplatesTemplateIDWithResponse call +func ParsePatchV2TemplatesTemplateIDResponse(rsp *http.Response) (*PatchV2TemplatesTemplateIDResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PatchV2TemplatesTemplateIDResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest TemplateUpdateResponse + 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 N400 + 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 == 401: + var dest N401 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest N500 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParsePostV2TemplatesTemplateIDBuildsBuildIDResponse parses an HTTP response from a PostV2TemplatesTemplateIDBuildsBuildIDWithResponse call +func ParsePostV2TemplatesTemplateIDBuildsBuildIDResponse(rsp *http.Response) (*PostV2TemplatesTemplateIDBuildsBuildIDResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PostV2TemplatesTemplateIDBuildsBuildIDResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 401: + var dest N401 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest N500 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParsePostV3TemplatesResponse parses an HTTP response from a PostV3TemplatesWithResponse call +func ParsePostV3TemplatesResponse(rsp *http.Response) (*PostV3TemplatesResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PostV3TemplatesResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 202: + var dest TemplateRequestResponseV3 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON202 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest N400 + 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 == 401: + var dest N401 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403: + var dest N403 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON403 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest N500 + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} diff --git a/packages/go-sdk/internal/gen/api/oapi-codegen.yaml b/packages/go-sdk/internal/gen/api/oapi-codegen.yaml new file mode 100644 index 000000000..978ed43b3 --- /dev/null +++ b/packages/go-sdk/internal/gen/api/oapi-codegen.yaml @@ -0,0 +1,7 @@ +package: api +generate: + models: true + client: true +output: packages/go-sdk/internal/gen/api/client.gen.go +output-options: + name-normalizer: ToCamelCaseWithInitialisms diff --git a/packages/go-sdk/internal/gen/envd/filesystem/filesystem.pb.go b/packages/go-sdk/internal/gen/envd/filesystem/filesystem.pb.go new file mode 100644 index 000000000..ceaba9c42 --- /dev/null +++ b/packages/go-sdk/internal/gen/envd/filesystem/filesystem.pb.go @@ -0,0 +1,1518 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.12 +// protoc (unknown) +// source: filesystem/filesystem.proto + +package filesystem + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type FileType int32 + +const ( + FileType_FILE_TYPE_UNSPECIFIED FileType = 0 + FileType_FILE_TYPE_FILE FileType = 1 + FileType_FILE_TYPE_DIRECTORY FileType = 2 + FileType_FILE_TYPE_SYMLINK FileType = 3 +) + +// Enum value maps for FileType. +var ( + FileType_name = map[int32]string{ + 0: "FILE_TYPE_UNSPECIFIED", + 1: "FILE_TYPE_FILE", + 2: "FILE_TYPE_DIRECTORY", + 3: "FILE_TYPE_SYMLINK", + } + FileType_value = map[string]int32{ + "FILE_TYPE_UNSPECIFIED": 0, + "FILE_TYPE_FILE": 1, + "FILE_TYPE_DIRECTORY": 2, + "FILE_TYPE_SYMLINK": 3, + } +) + +func (x FileType) Enum() *FileType { + p := new(FileType) + *p = x + return p +} + +func (x FileType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (FileType) Descriptor() protoreflect.EnumDescriptor { + return file_filesystem_filesystem_proto_enumTypes[0].Descriptor() +} + +func (FileType) Type() protoreflect.EnumType { + return &file_filesystem_filesystem_proto_enumTypes[0] +} + +func (x FileType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use FileType.Descriptor instead. +func (FileType) EnumDescriptor() ([]byte, []int) { + return file_filesystem_filesystem_proto_rawDescGZIP(), []int{0} +} + +type EventType int32 + +const ( + EventType_EVENT_TYPE_UNSPECIFIED EventType = 0 + EventType_EVENT_TYPE_CREATE EventType = 1 + EventType_EVENT_TYPE_WRITE EventType = 2 + EventType_EVENT_TYPE_REMOVE EventType = 3 + EventType_EVENT_TYPE_RENAME EventType = 4 + EventType_EVENT_TYPE_CHMOD EventType = 5 +) + +// Enum value maps for EventType. +var ( + EventType_name = map[int32]string{ + 0: "EVENT_TYPE_UNSPECIFIED", + 1: "EVENT_TYPE_CREATE", + 2: "EVENT_TYPE_WRITE", + 3: "EVENT_TYPE_REMOVE", + 4: "EVENT_TYPE_RENAME", + 5: "EVENT_TYPE_CHMOD", + } + EventType_value = map[string]int32{ + "EVENT_TYPE_UNSPECIFIED": 0, + "EVENT_TYPE_CREATE": 1, + "EVENT_TYPE_WRITE": 2, + "EVENT_TYPE_REMOVE": 3, + "EVENT_TYPE_RENAME": 4, + "EVENT_TYPE_CHMOD": 5, + } +) + +func (x EventType) Enum() *EventType { + p := new(EventType) + *p = x + return p +} + +func (x EventType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (EventType) Descriptor() protoreflect.EnumDescriptor { + return file_filesystem_filesystem_proto_enumTypes[1].Descriptor() +} + +func (EventType) Type() protoreflect.EnumType { + return &file_filesystem_filesystem_proto_enumTypes[1] +} + +func (x EventType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use EventType.Descriptor instead. +func (EventType) EnumDescriptor() ([]byte, []int) { + return file_filesystem_filesystem_proto_rawDescGZIP(), []int{1} +} + +type MoveRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Source string `protobuf:"bytes,1,opt,name=source,proto3" json:"source,omitempty"` + Destination string `protobuf:"bytes,2,opt,name=destination,proto3" json:"destination,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *MoveRequest) Reset() { + *x = MoveRequest{} + mi := &file_filesystem_filesystem_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *MoveRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MoveRequest) ProtoMessage() {} + +func (x *MoveRequest) ProtoReflect() protoreflect.Message { + mi := &file_filesystem_filesystem_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MoveRequest.ProtoReflect.Descriptor instead. +func (*MoveRequest) Descriptor() ([]byte, []int) { + return file_filesystem_filesystem_proto_rawDescGZIP(), []int{0} +} + +func (x *MoveRequest) GetSource() string { + if x != nil { + return x.Source + } + return "" +} + +func (x *MoveRequest) GetDestination() string { + if x != nil { + return x.Destination + } + return "" +} + +type MoveResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Entry *EntryInfo `protobuf:"bytes,1,opt,name=entry,proto3" json:"entry,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *MoveResponse) Reset() { + *x = MoveResponse{} + mi := &file_filesystem_filesystem_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *MoveResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MoveResponse) ProtoMessage() {} + +func (x *MoveResponse) ProtoReflect() protoreflect.Message { + mi := &file_filesystem_filesystem_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MoveResponse.ProtoReflect.Descriptor instead. +func (*MoveResponse) Descriptor() ([]byte, []int) { + return file_filesystem_filesystem_proto_rawDescGZIP(), []int{1} +} + +func (x *MoveResponse) GetEntry() *EntryInfo { + if x != nil { + return x.Entry + } + return nil +} + +type MakeDirRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *MakeDirRequest) Reset() { + *x = MakeDirRequest{} + mi := &file_filesystem_filesystem_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *MakeDirRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MakeDirRequest) ProtoMessage() {} + +func (x *MakeDirRequest) ProtoReflect() protoreflect.Message { + mi := &file_filesystem_filesystem_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MakeDirRequest.ProtoReflect.Descriptor instead. +func (*MakeDirRequest) Descriptor() ([]byte, []int) { + return file_filesystem_filesystem_proto_rawDescGZIP(), []int{2} +} + +func (x *MakeDirRequest) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +type MakeDirResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Entry *EntryInfo `protobuf:"bytes,1,opt,name=entry,proto3" json:"entry,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *MakeDirResponse) Reset() { + *x = MakeDirResponse{} + mi := &file_filesystem_filesystem_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *MakeDirResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MakeDirResponse) ProtoMessage() {} + +func (x *MakeDirResponse) ProtoReflect() protoreflect.Message { + mi := &file_filesystem_filesystem_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use MakeDirResponse.ProtoReflect.Descriptor instead. +func (*MakeDirResponse) Descriptor() ([]byte, []int) { + return file_filesystem_filesystem_proto_rawDescGZIP(), []int{3} +} + +func (x *MakeDirResponse) GetEntry() *EntryInfo { + if x != nil { + return x.Entry + } + return nil +} + +type RemoveRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RemoveRequest) Reset() { + *x = RemoveRequest{} + mi := &file_filesystem_filesystem_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RemoveRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RemoveRequest) ProtoMessage() {} + +func (x *RemoveRequest) ProtoReflect() protoreflect.Message { + mi := &file_filesystem_filesystem_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RemoveRequest.ProtoReflect.Descriptor instead. +func (*RemoveRequest) Descriptor() ([]byte, []int) { + return file_filesystem_filesystem_proto_rawDescGZIP(), []int{4} +} + +func (x *RemoveRequest) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +type RemoveResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RemoveResponse) Reset() { + *x = RemoveResponse{} + mi := &file_filesystem_filesystem_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RemoveResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RemoveResponse) ProtoMessage() {} + +func (x *RemoveResponse) ProtoReflect() protoreflect.Message { + mi := &file_filesystem_filesystem_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RemoveResponse.ProtoReflect.Descriptor instead. +func (*RemoveResponse) Descriptor() ([]byte, []int) { + return file_filesystem_filesystem_proto_rawDescGZIP(), []int{5} +} + +type StatRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StatRequest) Reset() { + *x = StatRequest{} + mi := &file_filesystem_filesystem_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StatRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StatRequest) ProtoMessage() {} + +func (x *StatRequest) ProtoReflect() protoreflect.Message { + mi := &file_filesystem_filesystem_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StatRequest.ProtoReflect.Descriptor instead. +func (*StatRequest) Descriptor() ([]byte, []int) { + return file_filesystem_filesystem_proto_rawDescGZIP(), []int{6} +} + +func (x *StatRequest) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +type StatResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Entry *EntryInfo `protobuf:"bytes,1,opt,name=entry,proto3" json:"entry,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StatResponse) Reset() { + *x = StatResponse{} + mi := &file_filesystem_filesystem_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StatResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StatResponse) ProtoMessage() {} + +func (x *StatResponse) ProtoReflect() protoreflect.Message { + mi := &file_filesystem_filesystem_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StatResponse.ProtoReflect.Descriptor instead. +func (*StatResponse) Descriptor() ([]byte, []int) { + return file_filesystem_filesystem_proto_rawDescGZIP(), []int{7} +} + +func (x *StatResponse) GetEntry() *EntryInfo { + if x != nil { + return x.Entry + } + return nil +} + +type EntryInfo struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Type FileType `protobuf:"varint,2,opt,name=type,proto3,enum=filesystem.FileType" json:"type,omitempty"` + Path string `protobuf:"bytes,3,opt,name=path,proto3" json:"path,omitempty"` + Size int64 `protobuf:"varint,4,opt,name=size,proto3" json:"size,omitempty"` + Mode uint32 `protobuf:"varint,5,opt,name=mode,proto3" json:"mode,omitempty"` + Permissions string `protobuf:"bytes,6,opt,name=permissions,proto3" json:"permissions,omitempty"` + Owner string `protobuf:"bytes,7,opt,name=owner,proto3" json:"owner,omitempty"` + Group string `protobuf:"bytes,8,opt,name=group,proto3" json:"group,omitempty"` + ModifiedTime *timestamppb.Timestamp `protobuf:"bytes,9,opt,name=modified_time,json=modifiedTime,proto3" json:"modified_time,omitempty"` + // If the entry is a symlink, this field contains the target of the symlink. + SymlinkTarget *string `protobuf:"bytes,10,opt,name=symlink_target,json=symlinkTarget,proto3,oneof" json:"symlink_target,omitempty"` + // User-defined metadata stored as extended attributes (xattrs) on the file. + // Keys live under the `user.agentbox.` xattr namespace; the prefix is stripped here. + // Plain `user.*` xattrs written by other tooling are not reflected. + Metadata map[string]string `protobuf:"bytes,11,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *EntryInfo) Reset() { + *x = EntryInfo{} + mi := &file_filesystem_filesystem_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *EntryInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EntryInfo) ProtoMessage() {} + +func (x *EntryInfo) ProtoReflect() protoreflect.Message { + mi := &file_filesystem_filesystem_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use EntryInfo.ProtoReflect.Descriptor instead. +func (*EntryInfo) Descriptor() ([]byte, []int) { + return file_filesystem_filesystem_proto_rawDescGZIP(), []int{8} +} + +func (x *EntryInfo) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *EntryInfo) GetType() FileType { + if x != nil { + return x.Type + } + return FileType_FILE_TYPE_UNSPECIFIED +} + +func (x *EntryInfo) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *EntryInfo) GetSize() int64 { + if x != nil { + return x.Size + } + return 0 +} + +func (x *EntryInfo) GetMode() uint32 { + if x != nil { + return x.Mode + } + return 0 +} + +func (x *EntryInfo) GetPermissions() string { + if x != nil { + return x.Permissions + } + return "" +} + +func (x *EntryInfo) GetOwner() string { + if x != nil { + return x.Owner + } + return "" +} + +func (x *EntryInfo) GetGroup() string { + if x != nil { + return x.Group + } + return "" +} + +func (x *EntryInfo) GetModifiedTime() *timestamppb.Timestamp { + if x != nil { + return x.ModifiedTime + } + return nil +} + +func (x *EntryInfo) GetSymlinkTarget() string { + if x != nil && x.SymlinkTarget != nil { + return *x.SymlinkTarget + } + return "" +} + +func (x *EntryInfo) GetMetadata() map[string]string { + if x != nil { + return x.Metadata + } + return nil +} + +type ListDirRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + Depth uint32 `protobuf:"varint,2,opt,name=depth,proto3" json:"depth,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListDirRequest) Reset() { + *x = ListDirRequest{} + mi := &file_filesystem_filesystem_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListDirRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListDirRequest) ProtoMessage() {} + +func (x *ListDirRequest) ProtoReflect() protoreflect.Message { + mi := &file_filesystem_filesystem_proto_msgTypes[9] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListDirRequest.ProtoReflect.Descriptor instead. +func (*ListDirRequest) Descriptor() ([]byte, []int) { + return file_filesystem_filesystem_proto_rawDescGZIP(), []int{9} +} + +func (x *ListDirRequest) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *ListDirRequest) GetDepth() uint32 { + if x != nil { + return x.Depth + } + return 0 +} + +type ListDirResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Entries []*EntryInfo `protobuf:"bytes,1,rep,name=entries,proto3" json:"entries,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListDirResponse) Reset() { + *x = ListDirResponse{} + mi := &file_filesystem_filesystem_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListDirResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListDirResponse) ProtoMessage() {} + +func (x *ListDirResponse) ProtoReflect() protoreflect.Message { + mi := &file_filesystem_filesystem_proto_msgTypes[10] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListDirResponse.ProtoReflect.Descriptor instead. +func (*ListDirResponse) Descriptor() ([]byte, []int) { + return file_filesystem_filesystem_proto_rawDescGZIP(), []int{10} +} + +func (x *ListDirResponse) GetEntries() []*EntryInfo { + if x != nil { + return x.Entries + } + return nil +} + +type WatchDirRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + Recursive bool `protobuf:"varint,2,opt,name=recursive,proto3" json:"recursive,omitempty"` + // If true, each FilesystemEvent includes the EntryInfo of the affected entry, when available. + IncludeEntry bool `protobuf:"varint,3,opt,name=include_entry,json=includeEntry,proto3" json:"include_entry,omitempty"` + // If true, allows watching paths on network filesystem mounts (NFS, CIFS, SMB, FUSE). + // Events on network mounts may be unreliable or not delivered at all. + AllowNetworkMounts bool `protobuf:"varint,4,opt,name=allow_network_mounts,json=allowNetworkMounts,proto3" json:"allow_network_mounts,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchDirRequest) Reset() { + *x = WatchDirRequest{} + mi := &file_filesystem_filesystem_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchDirRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchDirRequest) ProtoMessage() {} + +func (x *WatchDirRequest) ProtoReflect() protoreflect.Message { + mi := &file_filesystem_filesystem_proto_msgTypes[11] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchDirRequest.ProtoReflect.Descriptor instead. +func (*WatchDirRequest) Descriptor() ([]byte, []int) { + return file_filesystem_filesystem_proto_rawDescGZIP(), []int{11} +} + +func (x *WatchDirRequest) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *WatchDirRequest) GetRecursive() bool { + if x != nil { + return x.Recursive + } + return false +} + +func (x *WatchDirRequest) GetIncludeEntry() bool { + if x != nil { + return x.IncludeEntry + } + return false +} + +func (x *WatchDirRequest) GetAllowNetworkMounts() bool { + if x != nil { + return x.AllowNetworkMounts + } + return false +} + +type FilesystemEvent struct { + state protoimpl.MessageState `protogen:"open.v1"` + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Type EventType `protobuf:"varint,2,opt,name=type,proto3,enum=filesystem.EventType" json:"type,omitempty"` + // Info of the entry that triggered the event. Only populated when include_entry + // was requested and the entry could be stat-ed (e.g. not set for remove/rename-away + // events, where the entry no longer exists at this path). + Entry *EntryInfo `protobuf:"bytes,3,opt,name=entry,proto3,oneof" json:"entry,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *FilesystemEvent) Reset() { + *x = FilesystemEvent{} + mi := &file_filesystem_filesystem_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *FilesystemEvent) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FilesystemEvent) ProtoMessage() {} + +func (x *FilesystemEvent) ProtoReflect() protoreflect.Message { + mi := &file_filesystem_filesystem_proto_msgTypes[12] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FilesystemEvent.ProtoReflect.Descriptor instead. +func (*FilesystemEvent) Descriptor() ([]byte, []int) { + return file_filesystem_filesystem_proto_rawDescGZIP(), []int{12} +} + +func (x *FilesystemEvent) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *FilesystemEvent) GetType() EventType { + if x != nil { + return x.Type + } + return EventType_EVENT_TYPE_UNSPECIFIED +} + +func (x *FilesystemEvent) GetEntry() *EntryInfo { + if x != nil { + return x.Entry + } + return nil +} + +type WatchDirResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Types that are valid to be assigned to Event: + // + // *WatchDirResponse_Start + // *WatchDirResponse_Filesystem + // *WatchDirResponse_Keepalive + Event isWatchDirResponse_Event `protobuf_oneof:"event"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchDirResponse) Reset() { + *x = WatchDirResponse{} + mi := &file_filesystem_filesystem_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchDirResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchDirResponse) ProtoMessage() {} + +func (x *WatchDirResponse) ProtoReflect() protoreflect.Message { + mi := &file_filesystem_filesystem_proto_msgTypes[13] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchDirResponse.ProtoReflect.Descriptor instead. +func (*WatchDirResponse) Descriptor() ([]byte, []int) { + return file_filesystem_filesystem_proto_rawDescGZIP(), []int{13} +} + +func (x *WatchDirResponse) GetEvent() isWatchDirResponse_Event { + if x != nil { + return x.Event + } + return nil +} + +func (x *WatchDirResponse) GetStart() *WatchDirResponse_StartEvent { + if x != nil { + if x, ok := x.Event.(*WatchDirResponse_Start); ok { + return x.Start + } + } + return nil +} + +func (x *WatchDirResponse) GetFilesystem() *FilesystemEvent { + if x != nil { + if x, ok := x.Event.(*WatchDirResponse_Filesystem); ok { + return x.Filesystem + } + } + return nil +} + +func (x *WatchDirResponse) GetKeepalive() *WatchDirResponse_KeepAlive { + if x != nil { + if x, ok := x.Event.(*WatchDirResponse_Keepalive); ok { + return x.Keepalive + } + } + return nil +} + +type isWatchDirResponse_Event interface { + isWatchDirResponse_Event() +} + +type WatchDirResponse_Start struct { + Start *WatchDirResponse_StartEvent `protobuf:"bytes,1,opt,name=start,proto3,oneof"` +} + +type WatchDirResponse_Filesystem struct { + Filesystem *FilesystemEvent `protobuf:"bytes,2,opt,name=filesystem,proto3,oneof"` +} + +type WatchDirResponse_Keepalive struct { + Keepalive *WatchDirResponse_KeepAlive `protobuf:"bytes,3,opt,name=keepalive,proto3,oneof"` +} + +func (*WatchDirResponse_Start) isWatchDirResponse_Event() {} + +func (*WatchDirResponse_Filesystem) isWatchDirResponse_Event() {} + +func (*WatchDirResponse_Keepalive) isWatchDirResponse_Event() {} + +type CreateWatcherRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"` + Recursive bool `protobuf:"varint,2,opt,name=recursive,proto3" json:"recursive,omitempty"` + // If true, each FilesystemEvent includes the EntryInfo of the affected entry, when available. + IncludeEntry bool `protobuf:"varint,3,opt,name=include_entry,json=includeEntry,proto3" json:"include_entry,omitempty"` + // If true, allows watching paths on network filesystem mounts (NFS, CIFS, SMB, FUSE). + // Events on network mounts may be unreliable or not delivered at all. + AllowNetworkMounts bool `protobuf:"varint,4,opt,name=allow_network_mounts,json=allowNetworkMounts,proto3" json:"allow_network_mounts,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateWatcherRequest) Reset() { + *x = CreateWatcherRequest{} + mi := &file_filesystem_filesystem_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateWatcherRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateWatcherRequest) ProtoMessage() {} + +func (x *CreateWatcherRequest) ProtoReflect() protoreflect.Message { + mi := &file_filesystem_filesystem_proto_msgTypes[14] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateWatcherRequest.ProtoReflect.Descriptor instead. +func (*CreateWatcherRequest) Descriptor() ([]byte, []int) { + return file_filesystem_filesystem_proto_rawDescGZIP(), []int{14} +} + +func (x *CreateWatcherRequest) GetPath() string { + if x != nil { + return x.Path + } + return "" +} + +func (x *CreateWatcherRequest) GetRecursive() bool { + if x != nil { + return x.Recursive + } + return false +} + +func (x *CreateWatcherRequest) GetIncludeEntry() bool { + if x != nil { + return x.IncludeEntry + } + return false +} + +func (x *CreateWatcherRequest) GetAllowNetworkMounts() bool { + if x != nil { + return x.AllowNetworkMounts + } + return false +} + +type CreateWatcherResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + WatcherId string `protobuf:"bytes,1,opt,name=watcher_id,json=watcherId,proto3" json:"watcher_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateWatcherResponse) Reset() { + *x = CreateWatcherResponse{} + mi := &file_filesystem_filesystem_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateWatcherResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateWatcherResponse) ProtoMessage() {} + +func (x *CreateWatcherResponse) ProtoReflect() protoreflect.Message { + mi := &file_filesystem_filesystem_proto_msgTypes[15] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateWatcherResponse.ProtoReflect.Descriptor instead. +func (*CreateWatcherResponse) Descriptor() ([]byte, []int) { + return file_filesystem_filesystem_proto_rawDescGZIP(), []int{15} +} + +func (x *CreateWatcherResponse) GetWatcherId() string { + if x != nil { + return x.WatcherId + } + return "" +} + +type GetWatcherEventsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + WatcherId string `protobuf:"bytes,1,opt,name=watcher_id,json=watcherId,proto3" json:"watcher_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetWatcherEventsRequest) Reset() { + *x = GetWatcherEventsRequest{} + mi := &file_filesystem_filesystem_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetWatcherEventsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetWatcherEventsRequest) ProtoMessage() {} + +func (x *GetWatcherEventsRequest) ProtoReflect() protoreflect.Message { + mi := &file_filesystem_filesystem_proto_msgTypes[16] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetWatcherEventsRequest.ProtoReflect.Descriptor instead. +func (*GetWatcherEventsRequest) Descriptor() ([]byte, []int) { + return file_filesystem_filesystem_proto_rawDescGZIP(), []int{16} +} + +func (x *GetWatcherEventsRequest) GetWatcherId() string { + if x != nil { + return x.WatcherId + } + return "" +} + +type GetWatcherEventsResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Events []*FilesystemEvent `protobuf:"bytes,1,rep,name=events,proto3" json:"events,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetWatcherEventsResponse) Reset() { + *x = GetWatcherEventsResponse{} + mi := &file_filesystem_filesystem_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetWatcherEventsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetWatcherEventsResponse) ProtoMessage() {} + +func (x *GetWatcherEventsResponse) ProtoReflect() protoreflect.Message { + mi := &file_filesystem_filesystem_proto_msgTypes[17] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetWatcherEventsResponse.ProtoReflect.Descriptor instead. +func (*GetWatcherEventsResponse) Descriptor() ([]byte, []int) { + return file_filesystem_filesystem_proto_rawDescGZIP(), []int{17} +} + +func (x *GetWatcherEventsResponse) GetEvents() []*FilesystemEvent { + if x != nil { + return x.Events + } + return nil +} + +type RemoveWatcherRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + WatcherId string `protobuf:"bytes,1,opt,name=watcher_id,json=watcherId,proto3" json:"watcher_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RemoveWatcherRequest) Reset() { + *x = RemoveWatcherRequest{} + mi := &file_filesystem_filesystem_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RemoveWatcherRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RemoveWatcherRequest) ProtoMessage() {} + +func (x *RemoveWatcherRequest) ProtoReflect() protoreflect.Message { + mi := &file_filesystem_filesystem_proto_msgTypes[18] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RemoveWatcherRequest.ProtoReflect.Descriptor instead. +func (*RemoveWatcherRequest) Descriptor() ([]byte, []int) { + return file_filesystem_filesystem_proto_rawDescGZIP(), []int{18} +} + +func (x *RemoveWatcherRequest) GetWatcherId() string { + if x != nil { + return x.WatcherId + } + return "" +} + +type RemoveWatcherResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *RemoveWatcherResponse) Reset() { + *x = RemoveWatcherResponse{} + mi := &file_filesystem_filesystem_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RemoveWatcherResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RemoveWatcherResponse) ProtoMessage() {} + +func (x *RemoveWatcherResponse) ProtoReflect() protoreflect.Message { + mi := &file_filesystem_filesystem_proto_msgTypes[19] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RemoveWatcherResponse.ProtoReflect.Descriptor instead. +func (*RemoveWatcherResponse) Descriptor() ([]byte, []int) { + return file_filesystem_filesystem_proto_rawDescGZIP(), []int{19} +} + +type WatchDirResponse_StartEvent struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchDirResponse_StartEvent) Reset() { + *x = WatchDirResponse_StartEvent{} + mi := &file_filesystem_filesystem_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchDirResponse_StartEvent) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchDirResponse_StartEvent) ProtoMessage() {} + +func (x *WatchDirResponse_StartEvent) ProtoReflect() protoreflect.Message { + mi := &file_filesystem_filesystem_proto_msgTypes[21] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchDirResponse_StartEvent.ProtoReflect.Descriptor instead. +func (*WatchDirResponse_StartEvent) Descriptor() ([]byte, []int) { + return file_filesystem_filesystem_proto_rawDescGZIP(), []int{13, 0} +} + +type WatchDirResponse_KeepAlive struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *WatchDirResponse_KeepAlive) Reset() { + *x = WatchDirResponse_KeepAlive{} + mi := &file_filesystem_filesystem_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *WatchDirResponse_KeepAlive) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WatchDirResponse_KeepAlive) ProtoMessage() {} + +func (x *WatchDirResponse_KeepAlive) ProtoReflect() protoreflect.Message { + mi := &file_filesystem_filesystem_proto_msgTypes[22] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WatchDirResponse_KeepAlive.ProtoReflect.Descriptor instead. +func (*WatchDirResponse_KeepAlive) Descriptor() ([]byte, []int) { + return file_filesystem_filesystem_proto_rawDescGZIP(), []int{13, 1} +} + +var File_filesystem_filesystem_proto protoreflect.FileDescriptor + +const file_filesystem_filesystem_proto_rawDesc = "" + + "\n" + + "\x1bfilesystem/filesystem.proto\x12\n" + + "filesystem\x1a\x1fgoogle/protobuf/timestamp.proto\"G\n" + + "\vMoveRequest\x12\x16\n" + + "\x06source\x18\x01 \x01(\tR\x06source\x12 \n" + + "\vdestination\x18\x02 \x01(\tR\vdestination\";\n" + + "\fMoveResponse\x12+\n" + + "\x05entry\x18\x01 \x01(\v2\x15.filesystem.EntryInfoR\x05entry\"$\n" + + "\x0eMakeDirRequest\x12\x12\n" + + "\x04path\x18\x01 \x01(\tR\x04path\">\n" + + "\x0fMakeDirResponse\x12+\n" + + "\x05entry\x18\x01 \x01(\v2\x15.filesystem.EntryInfoR\x05entry\"#\n" + + "\rRemoveRequest\x12\x12\n" + + "\x04path\x18\x01 \x01(\tR\x04path\"\x10\n" + + "\x0eRemoveResponse\"!\n" + + "\vStatRequest\x12\x12\n" + + "\x04path\x18\x01 \x01(\tR\x04path\";\n" + + "\fStatResponse\x12+\n" + + "\x05entry\x18\x01 \x01(\v2\x15.filesystem.EntryInfoR\x05entry\"\xd1\x03\n" + + "\tEntryInfo\x12\x12\n" + + "\x04name\x18\x01 \x01(\tR\x04name\x12(\n" + + "\x04type\x18\x02 \x01(\x0e2\x14.filesystem.FileTypeR\x04type\x12\x12\n" + + "\x04path\x18\x03 \x01(\tR\x04path\x12\x12\n" + + "\x04size\x18\x04 \x01(\x03R\x04size\x12\x12\n" + + "\x04mode\x18\x05 \x01(\rR\x04mode\x12 \n" + + "\vpermissions\x18\x06 \x01(\tR\vpermissions\x12\x14\n" + + "\x05owner\x18\a \x01(\tR\x05owner\x12\x14\n" + + "\x05group\x18\b \x01(\tR\x05group\x12?\n" + + "\rmodified_time\x18\t \x01(\v2\x1a.google.protobuf.TimestampR\fmodifiedTime\x12*\n" + + "\x0esymlink_target\x18\n" + + " \x01(\tH\x00R\rsymlinkTarget\x88\x01\x01\x12?\n" + + "\bmetadata\x18\v \x03(\v2#.filesystem.EntryInfo.MetadataEntryR\bmetadata\x1a;\n" + + "\rMetadataEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01B\x11\n" + + "\x0f_symlink_target\":\n" + + "\x0eListDirRequest\x12\x12\n" + + "\x04path\x18\x01 \x01(\tR\x04path\x12\x14\n" + + "\x05depth\x18\x02 \x01(\rR\x05depth\"B\n" + + "\x0fListDirResponse\x12/\n" + + "\aentries\x18\x01 \x03(\v2\x15.filesystem.EntryInfoR\aentries\"\x9a\x01\n" + + "\x0fWatchDirRequest\x12\x12\n" + + "\x04path\x18\x01 \x01(\tR\x04path\x12\x1c\n" + + "\trecursive\x18\x02 \x01(\bR\trecursive\x12#\n" + + "\rinclude_entry\x18\x03 \x01(\bR\fincludeEntry\x120\n" + + "\x14allow_network_mounts\x18\x04 \x01(\bR\x12allowNetworkMounts\"\x8c\x01\n" + + "\x0fFilesystemEvent\x12\x12\n" + + "\x04name\x18\x01 \x01(\tR\x04name\x12)\n" + + "\x04type\x18\x02 \x01(\x0e2\x15.filesystem.EventTypeR\x04type\x120\n" + + "\x05entry\x18\x03 \x01(\v2\x15.filesystem.EntryInfoH\x00R\x05entry\x88\x01\x01B\b\n" + + "\x06_entry\"\xfe\x01\n" + + "\x10WatchDirResponse\x12?\n" + + "\x05start\x18\x01 \x01(\v2'.filesystem.WatchDirResponse.StartEventH\x00R\x05start\x12=\n" + + "\n" + + "filesystem\x18\x02 \x01(\v2\x1b.filesystem.FilesystemEventH\x00R\n" + + "filesystem\x12F\n" + + "\tkeepalive\x18\x03 \x01(\v2&.filesystem.WatchDirResponse.KeepAliveH\x00R\tkeepalive\x1a\f\n" + + "\n" + + "StartEvent\x1a\v\n" + + "\tKeepAliveB\a\n" + + "\x05event\"\x9f\x01\n" + + "\x14CreateWatcherRequest\x12\x12\n" + + "\x04path\x18\x01 \x01(\tR\x04path\x12\x1c\n" + + "\trecursive\x18\x02 \x01(\bR\trecursive\x12#\n" + + "\rinclude_entry\x18\x03 \x01(\bR\fincludeEntry\x120\n" + + "\x14allow_network_mounts\x18\x04 \x01(\bR\x12allowNetworkMounts\"6\n" + + "\x15CreateWatcherResponse\x12\x1d\n" + + "\n" + + "watcher_id\x18\x01 \x01(\tR\twatcherId\"8\n" + + "\x17GetWatcherEventsRequest\x12\x1d\n" + + "\n" + + "watcher_id\x18\x01 \x01(\tR\twatcherId\"O\n" + + "\x18GetWatcherEventsResponse\x123\n" + + "\x06events\x18\x01 \x03(\v2\x1b.filesystem.FilesystemEventR\x06events\"5\n" + + "\x14RemoveWatcherRequest\x12\x1d\n" + + "\n" + + "watcher_id\x18\x01 \x01(\tR\twatcherId\"\x17\n" + + "\x15RemoveWatcherResponse*i\n" + + "\bFileType\x12\x19\n" + + "\x15FILE_TYPE_UNSPECIFIED\x10\x00\x12\x12\n" + + "\x0eFILE_TYPE_FILE\x10\x01\x12\x17\n" + + "\x13FILE_TYPE_DIRECTORY\x10\x02\x12\x15\n" + + "\x11FILE_TYPE_SYMLINK\x10\x03*\x98\x01\n" + + "\tEventType\x12\x1a\n" + + "\x16EVENT_TYPE_UNSPECIFIED\x10\x00\x12\x15\n" + + "\x11EVENT_TYPE_CREATE\x10\x01\x12\x14\n" + + "\x10EVENT_TYPE_WRITE\x10\x02\x12\x15\n" + + "\x11EVENT_TYPE_REMOVE\x10\x03\x12\x15\n" + + "\x11EVENT_TYPE_RENAME\x10\x04\x12\x14\n" + + "\x10EVENT_TYPE_CHMOD\x10\x052\x9f\x05\n" + + "\n" + + "Filesystem\x129\n" + + "\x04Stat\x12\x17.filesystem.StatRequest\x1a\x18.filesystem.StatResponse\x12B\n" + + "\aMakeDir\x12\x1a.filesystem.MakeDirRequest\x1a\x1b.filesystem.MakeDirResponse\x129\n" + + "\x04Move\x12\x17.filesystem.MoveRequest\x1a\x18.filesystem.MoveResponse\x12B\n" + + "\aListDir\x12\x1a.filesystem.ListDirRequest\x1a\x1b.filesystem.ListDirResponse\x12?\n" + + "\x06Remove\x12\x19.filesystem.RemoveRequest\x1a\x1a.filesystem.RemoveResponse\x12G\n" + + "\bWatchDir\x12\x1b.filesystem.WatchDirRequest\x1a\x1c.filesystem.WatchDirResponse0\x01\x12T\n" + + "\rCreateWatcher\x12 .filesystem.CreateWatcherRequest\x1a!.filesystem.CreateWatcherResponse\x12]\n" + + "\x10GetWatcherEvents\x12#.filesystem.GetWatcherEventsRequest\x1a$.filesystem.GetWatcherEventsResponse\x12T\n" + + "\rRemoveWatcher\x12 .filesystem.RemoveWatcherRequest\x1a!.filesystem.RemoveWatcherResponseB\xaf\x01\n" + + "\x0ecom.filesystemB\x0fFilesystemProtoP\x01ZDgithub.com/abox-dev/sdk/packages/go-sdk/internal/gen/envd/filesystem\xa2\x02\x03FXX\xaa\x02\n" + + "Filesystem\xca\x02\n" + + "Filesystem\xe2\x02\x16Filesystem\\GPBMetadata\xea\x02\n" + + "Filesystemb\x06proto3" + +var ( + file_filesystem_filesystem_proto_rawDescOnce sync.Once + file_filesystem_filesystem_proto_rawDescData []byte +) + +func file_filesystem_filesystem_proto_rawDescGZIP() []byte { + file_filesystem_filesystem_proto_rawDescOnce.Do(func() { + file_filesystem_filesystem_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_filesystem_filesystem_proto_rawDesc), len(file_filesystem_filesystem_proto_rawDesc))) + }) + return file_filesystem_filesystem_proto_rawDescData +} + +var file_filesystem_filesystem_proto_enumTypes = make([]protoimpl.EnumInfo, 2) +var file_filesystem_filesystem_proto_msgTypes = make([]protoimpl.MessageInfo, 23) +var file_filesystem_filesystem_proto_goTypes = []any{ + (FileType)(0), // 0: filesystem.FileType + (EventType)(0), // 1: filesystem.EventType + (*MoveRequest)(nil), // 2: filesystem.MoveRequest + (*MoveResponse)(nil), // 3: filesystem.MoveResponse + (*MakeDirRequest)(nil), // 4: filesystem.MakeDirRequest + (*MakeDirResponse)(nil), // 5: filesystem.MakeDirResponse + (*RemoveRequest)(nil), // 6: filesystem.RemoveRequest + (*RemoveResponse)(nil), // 7: filesystem.RemoveResponse + (*StatRequest)(nil), // 8: filesystem.StatRequest + (*StatResponse)(nil), // 9: filesystem.StatResponse + (*EntryInfo)(nil), // 10: filesystem.EntryInfo + (*ListDirRequest)(nil), // 11: filesystem.ListDirRequest + (*ListDirResponse)(nil), // 12: filesystem.ListDirResponse + (*WatchDirRequest)(nil), // 13: filesystem.WatchDirRequest + (*FilesystemEvent)(nil), // 14: filesystem.FilesystemEvent + (*WatchDirResponse)(nil), // 15: filesystem.WatchDirResponse + (*CreateWatcherRequest)(nil), // 16: filesystem.CreateWatcherRequest + (*CreateWatcherResponse)(nil), // 17: filesystem.CreateWatcherResponse + (*GetWatcherEventsRequest)(nil), // 18: filesystem.GetWatcherEventsRequest + (*GetWatcherEventsResponse)(nil), // 19: filesystem.GetWatcherEventsResponse + (*RemoveWatcherRequest)(nil), // 20: filesystem.RemoveWatcherRequest + (*RemoveWatcherResponse)(nil), // 21: filesystem.RemoveWatcherResponse + nil, // 22: filesystem.EntryInfo.MetadataEntry + (*WatchDirResponse_StartEvent)(nil), // 23: filesystem.WatchDirResponse.StartEvent + (*WatchDirResponse_KeepAlive)(nil), // 24: filesystem.WatchDirResponse.KeepAlive + (*timestamppb.Timestamp)(nil), // 25: google.protobuf.Timestamp +} +var file_filesystem_filesystem_proto_depIdxs = []int32{ + 10, // 0: filesystem.MoveResponse.entry:type_name -> filesystem.EntryInfo + 10, // 1: filesystem.MakeDirResponse.entry:type_name -> filesystem.EntryInfo + 10, // 2: filesystem.StatResponse.entry:type_name -> filesystem.EntryInfo + 0, // 3: filesystem.EntryInfo.type:type_name -> filesystem.FileType + 25, // 4: filesystem.EntryInfo.modified_time:type_name -> google.protobuf.Timestamp + 22, // 5: filesystem.EntryInfo.metadata:type_name -> filesystem.EntryInfo.MetadataEntry + 10, // 6: filesystem.ListDirResponse.entries:type_name -> filesystem.EntryInfo + 1, // 7: filesystem.FilesystemEvent.type:type_name -> filesystem.EventType + 10, // 8: filesystem.FilesystemEvent.entry:type_name -> filesystem.EntryInfo + 23, // 9: filesystem.WatchDirResponse.start:type_name -> filesystem.WatchDirResponse.StartEvent + 14, // 10: filesystem.WatchDirResponse.filesystem:type_name -> filesystem.FilesystemEvent + 24, // 11: filesystem.WatchDirResponse.keepalive:type_name -> filesystem.WatchDirResponse.KeepAlive + 14, // 12: filesystem.GetWatcherEventsResponse.events:type_name -> filesystem.FilesystemEvent + 8, // 13: filesystem.Filesystem.Stat:input_type -> filesystem.StatRequest + 4, // 14: filesystem.Filesystem.MakeDir:input_type -> filesystem.MakeDirRequest + 2, // 15: filesystem.Filesystem.Move:input_type -> filesystem.MoveRequest + 11, // 16: filesystem.Filesystem.ListDir:input_type -> filesystem.ListDirRequest + 6, // 17: filesystem.Filesystem.Remove:input_type -> filesystem.RemoveRequest + 13, // 18: filesystem.Filesystem.WatchDir:input_type -> filesystem.WatchDirRequest + 16, // 19: filesystem.Filesystem.CreateWatcher:input_type -> filesystem.CreateWatcherRequest + 18, // 20: filesystem.Filesystem.GetWatcherEvents:input_type -> filesystem.GetWatcherEventsRequest + 20, // 21: filesystem.Filesystem.RemoveWatcher:input_type -> filesystem.RemoveWatcherRequest + 9, // 22: filesystem.Filesystem.Stat:output_type -> filesystem.StatResponse + 5, // 23: filesystem.Filesystem.MakeDir:output_type -> filesystem.MakeDirResponse + 3, // 24: filesystem.Filesystem.Move:output_type -> filesystem.MoveResponse + 12, // 25: filesystem.Filesystem.ListDir:output_type -> filesystem.ListDirResponse + 7, // 26: filesystem.Filesystem.Remove:output_type -> filesystem.RemoveResponse + 15, // 27: filesystem.Filesystem.WatchDir:output_type -> filesystem.WatchDirResponse + 17, // 28: filesystem.Filesystem.CreateWatcher:output_type -> filesystem.CreateWatcherResponse + 19, // 29: filesystem.Filesystem.GetWatcherEvents:output_type -> filesystem.GetWatcherEventsResponse + 21, // 30: filesystem.Filesystem.RemoveWatcher:output_type -> filesystem.RemoveWatcherResponse + 22, // [22:31] is the sub-list for method output_type + 13, // [13:22] is the sub-list for method input_type + 13, // [13:13] is the sub-list for extension type_name + 13, // [13:13] is the sub-list for extension extendee + 0, // [0:13] is the sub-list for field type_name +} + +func init() { file_filesystem_filesystem_proto_init() } +func file_filesystem_filesystem_proto_init() { + if File_filesystem_filesystem_proto != nil { + return + } + file_filesystem_filesystem_proto_msgTypes[8].OneofWrappers = []any{} + file_filesystem_filesystem_proto_msgTypes[12].OneofWrappers = []any{} + file_filesystem_filesystem_proto_msgTypes[13].OneofWrappers = []any{ + (*WatchDirResponse_Start)(nil), + (*WatchDirResponse_Filesystem)(nil), + (*WatchDirResponse_Keepalive)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_filesystem_filesystem_proto_rawDesc), len(file_filesystem_filesystem_proto_rawDesc)), + NumEnums: 2, + NumMessages: 23, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_filesystem_filesystem_proto_goTypes, + DependencyIndexes: file_filesystem_filesystem_proto_depIdxs, + EnumInfos: file_filesystem_filesystem_proto_enumTypes, + MessageInfos: file_filesystem_filesystem_proto_msgTypes, + }.Build() + File_filesystem_filesystem_proto = out.File + file_filesystem_filesystem_proto_goTypes = nil + file_filesystem_filesystem_proto_depIdxs = nil +} diff --git a/packages/go-sdk/internal/gen/envd/filesystem/filesystemconnect/filesystem.connect.go b/packages/go-sdk/internal/gen/envd/filesystem/filesystemconnect/filesystem.connect.go new file mode 100644 index 000000000..63bfd020c --- /dev/null +++ b/packages/go-sdk/internal/gen/envd/filesystem/filesystemconnect/filesystem.connect.go @@ -0,0 +1,337 @@ +// Code generated by protoc-gen-connect-go. DO NOT EDIT. +// +// Source: filesystem/filesystem.proto + +package filesystemconnect + +import ( + connect "connectrpc.com/connect" + context "context" + errors "errors" + filesystem "github.com/abox-dev/sdk/packages/go-sdk/internal/gen/envd/filesystem" + http "net/http" + strings "strings" +) + +// This is a compile-time assertion to ensure that this generated file and the connect package are +// compatible. If you get a compiler error that this constant is not defined, this code was +// generated with a version of connect newer than the one compiled into your binary. You can fix the +// problem by either regenerating this code with an older version of connect or updating the connect +// version compiled into your binary. +const _ = connect.IsAtLeastVersion1_13_0 + +const ( + // FilesystemName is the fully-qualified name of the Filesystem service. + FilesystemName = "filesystem.Filesystem" +) + +// These constants are the fully-qualified names of the RPCs defined in this package. They're +// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route. +// +// Note that these are different from the fully-qualified method names used by +// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to +// reflection-formatted method names, remove the leading slash and convert the remaining slash to a +// period. +const ( + // FilesystemStatProcedure is the fully-qualified name of the Filesystem's Stat RPC. + FilesystemStatProcedure = "/filesystem.Filesystem/Stat" + // FilesystemMakeDirProcedure is the fully-qualified name of the Filesystem's MakeDir RPC. + FilesystemMakeDirProcedure = "/filesystem.Filesystem/MakeDir" + // FilesystemMoveProcedure is the fully-qualified name of the Filesystem's Move RPC. + FilesystemMoveProcedure = "/filesystem.Filesystem/Move" + // FilesystemListDirProcedure is the fully-qualified name of the Filesystem's ListDir RPC. + FilesystemListDirProcedure = "/filesystem.Filesystem/ListDir" + // FilesystemRemoveProcedure is the fully-qualified name of the Filesystem's Remove RPC. + FilesystemRemoveProcedure = "/filesystem.Filesystem/Remove" + // FilesystemWatchDirProcedure is the fully-qualified name of the Filesystem's WatchDir RPC. + FilesystemWatchDirProcedure = "/filesystem.Filesystem/WatchDir" + // FilesystemCreateWatcherProcedure is the fully-qualified name of the Filesystem's CreateWatcher + // RPC. + FilesystemCreateWatcherProcedure = "/filesystem.Filesystem/CreateWatcher" + // FilesystemGetWatcherEventsProcedure is the fully-qualified name of the Filesystem's + // GetWatcherEvents RPC. + FilesystemGetWatcherEventsProcedure = "/filesystem.Filesystem/GetWatcherEvents" + // FilesystemRemoveWatcherProcedure is the fully-qualified name of the Filesystem's RemoveWatcher + // RPC. + FilesystemRemoveWatcherProcedure = "/filesystem.Filesystem/RemoveWatcher" +) + +// FilesystemClient is a client for the filesystem.Filesystem service. +type FilesystemClient interface { + Stat(context.Context, *connect.Request[filesystem.StatRequest]) (*connect.Response[filesystem.StatResponse], error) + MakeDir(context.Context, *connect.Request[filesystem.MakeDirRequest]) (*connect.Response[filesystem.MakeDirResponse], error) + Move(context.Context, *connect.Request[filesystem.MoveRequest]) (*connect.Response[filesystem.MoveResponse], error) + ListDir(context.Context, *connect.Request[filesystem.ListDirRequest]) (*connect.Response[filesystem.ListDirResponse], error) + Remove(context.Context, *connect.Request[filesystem.RemoveRequest]) (*connect.Response[filesystem.RemoveResponse], error) + WatchDir(context.Context, *connect.Request[filesystem.WatchDirRequest]) (*connect.ServerStreamForClient[filesystem.WatchDirResponse], error) + // Non-streaming versions of WatchDir + CreateWatcher(context.Context, *connect.Request[filesystem.CreateWatcherRequest]) (*connect.Response[filesystem.CreateWatcherResponse], error) + GetWatcherEvents(context.Context, *connect.Request[filesystem.GetWatcherEventsRequest]) (*connect.Response[filesystem.GetWatcherEventsResponse], error) + RemoveWatcher(context.Context, *connect.Request[filesystem.RemoveWatcherRequest]) (*connect.Response[filesystem.RemoveWatcherResponse], error) +} + +// NewFilesystemClient constructs a client for the filesystem.Filesystem service. By default, it +// uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, and sends +// uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() or +// connect.WithGRPCWeb() options. +// +// The URL supplied here should be the base URL for the Connect or gRPC server (for example, +// http://api.acme.com or https://acme.com/grpc). +func NewFilesystemClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) FilesystemClient { + baseURL = strings.TrimRight(baseURL, "/") + filesystemMethods := filesystem.File_filesystem_filesystem_proto.Services().ByName("Filesystem").Methods() + return &filesystemClient{ + stat: connect.NewClient[filesystem.StatRequest, filesystem.StatResponse]( + httpClient, + baseURL+FilesystemStatProcedure, + connect.WithSchema(filesystemMethods.ByName("Stat")), + connect.WithClientOptions(opts...), + ), + makeDir: connect.NewClient[filesystem.MakeDirRequest, filesystem.MakeDirResponse]( + httpClient, + baseURL+FilesystemMakeDirProcedure, + connect.WithSchema(filesystemMethods.ByName("MakeDir")), + connect.WithClientOptions(opts...), + ), + move: connect.NewClient[filesystem.MoveRequest, filesystem.MoveResponse]( + httpClient, + baseURL+FilesystemMoveProcedure, + connect.WithSchema(filesystemMethods.ByName("Move")), + connect.WithClientOptions(opts...), + ), + listDir: connect.NewClient[filesystem.ListDirRequest, filesystem.ListDirResponse]( + httpClient, + baseURL+FilesystemListDirProcedure, + connect.WithSchema(filesystemMethods.ByName("ListDir")), + connect.WithClientOptions(opts...), + ), + remove: connect.NewClient[filesystem.RemoveRequest, filesystem.RemoveResponse]( + httpClient, + baseURL+FilesystemRemoveProcedure, + connect.WithSchema(filesystemMethods.ByName("Remove")), + connect.WithClientOptions(opts...), + ), + watchDir: connect.NewClient[filesystem.WatchDirRequest, filesystem.WatchDirResponse]( + httpClient, + baseURL+FilesystemWatchDirProcedure, + connect.WithSchema(filesystemMethods.ByName("WatchDir")), + connect.WithClientOptions(opts...), + ), + createWatcher: connect.NewClient[filesystem.CreateWatcherRequest, filesystem.CreateWatcherResponse]( + httpClient, + baseURL+FilesystemCreateWatcherProcedure, + connect.WithSchema(filesystemMethods.ByName("CreateWatcher")), + connect.WithClientOptions(opts...), + ), + getWatcherEvents: connect.NewClient[filesystem.GetWatcherEventsRequest, filesystem.GetWatcherEventsResponse]( + httpClient, + baseURL+FilesystemGetWatcherEventsProcedure, + connect.WithSchema(filesystemMethods.ByName("GetWatcherEvents")), + connect.WithClientOptions(opts...), + ), + removeWatcher: connect.NewClient[filesystem.RemoveWatcherRequest, filesystem.RemoveWatcherResponse]( + httpClient, + baseURL+FilesystemRemoveWatcherProcedure, + connect.WithSchema(filesystemMethods.ByName("RemoveWatcher")), + connect.WithClientOptions(opts...), + ), + } +} + +// filesystemClient implements FilesystemClient. +type filesystemClient struct { + stat *connect.Client[filesystem.StatRequest, filesystem.StatResponse] + makeDir *connect.Client[filesystem.MakeDirRequest, filesystem.MakeDirResponse] + move *connect.Client[filesystem.MoveRequest, filesystem.MoveResponse] + listDir *connect.Client[filesystem.ListDirRequest, filesystem.ListDirResponse] + remove *connect.Client[filesystem.RemoveRequest, filesystem.RemoveResponse] + watchDir *connect.Client[filesystem.WatchDirRequest, filesystem.WatchDirResponse] + createWatcher *connect.Client[filesystem.CreateWatcherRequest, filesystem.CreateWatcherResponse] + getWatcherEvents *connect.Client[filesystem.GetWatcherEventsRequest, filesystem.GetWatcherEventsResponse] + removeWatcher *connect.Client[filesystem.RemoveWatcherRequest, filesystem.RemoveWatcherResponse] +} + +// Stat calls filesystem.Filesystem.Stat. +func (c *filesystemClient) Stat(ctx context.Context, req *connect.Request[filesystem.StatRequest]) (*connect.Response[filesystem.StatResponse], error) { + return c.stat.CallUnary(ctx, req) +} + +// MakeDir calls filesystem.Filesystem.MakeDir. +func (c *filesystemClient) MakeDir(ctx context.Context, req *connect.Request[filesystem.MakeDirRequest]) (*connect.Response[filesystem.MakeDirResponse], error) { + return c.makeDir.CallUnary(ctx, req) +} + +// Move calls filesystem.Filesystem.Move. +func (c *filesystemClient) Move(ctx context.Context, req *connect.Request[filesystem.MoveRequest]) (*connect.Response[filesystem.MoveResponse], error) { + return c.move.CallUnary(ctx, req) +} + +// ListDir calls filesystem.Filesystem.ListDir. +func (c *filesystemClient) ListDir(ctx context.Context, req *connect.Request[filesystem.ListDirRequest]) (*connect.Response[filesystem.ListDirResponse], error) { + return c.listDir.CallUnary(ctx, req) +} + +// Remove calls filesystem.Filesystem.Remove. +func (c *filesystemClient) Remove(ctx context.Context, req *connect.Request[filesystem.RemoveRequest]) (*connect.Response[filesystem.RemoveResponse], error) { + return c.remove.CallUnary(ctx, req) +} + +// WatchDir calls filesystem.Filesystem.WatchDir. +func (c *filesystemClient) WatchDir(ctx context.Context, req *connect.Request[filesystem.WatchDirRequest]) (*connect.ServerStreamForClient[filesystem.WatchDirResponse], error) { + return c.watchDir.CallServerStream(ctx, req) +} + +// CreateWatcher calls filesystem.Filesystem.CreateWatcher. +func (c *filesystemClient) CreateWatcher(ctx context.Context, req *connect.Request[filesystem.CreateWatcherRequest]) (*connect.Response[filesystem.CreateWatcherResponse], error) { + return c.createWatcher.CallUnary(ctx, req) +} + +// GetWatcherEvents calls filesystem.Filesystem.GetWatcherEvents. +func (c *filesystemClient) GetWatcherEvents(ctx context.Context, req *connect.Request[filesystem.GetWatcherEventsRequest]) (*connect.Response[filesystem.GetWatcherEventsResponse], error) { + return c.getWatcherEvents.CallUnary(ctx, req) +} + +// RemoveWatcher calls filesystem.Filesystem.RemoveWatcher. +func (c *filesystemClient) RemoveWatcher(ctx context.Context, req *connect.Request[filesystem.RemoveWatcherRequest]) (*connect.Response[filesystem.RemoveWatcherResponse], error) { + return c.removeWatcher.CallUnary(ctx, req) +} + +// FilesystemHandler is an implementation of the filesystem.Filesystem service. +type FilesystemHandler interface { + Stat(context.Context, *connect.Request[filesystem.StatRequest]) (*connect.Response[filesystem.StatResponse], error) + MakeDir(context.Context, *connect.Request[filesystem.MakeDirRequest]) (*connect.Response[filesystem.MakeDirResponse], error) + Move(context.Context, *connect.Request[filesystem.MoveRequest]) (*connect.Response[filesystem.MoveResponse], error) + ListDir(context.Context, *connect.Request[filesystem.ListDirRequest]) (*connect.Response[filesystem.ListDirResponse], error) + Remove(context.Context, *connect.Request[filesystem.RemoveRequest]) (*connect.Response[filesystem.RemoveResponse], error) + WatchDir(context.Context, *connect.Request[filesystem.WatchDirRequest], *connect.ServerStream[filesystem.WatchDirResponse]) error + // Non-streaming versions of WatchDir + CreateWatcher(context.Context, *connect.Request[filesystem.CreateWatcherRequest]) (*connect.Response[filesystem.CreateWatcherResponse], error) + GetWatcherEvents(context.Context, *connect.Request[filesystem.GetWatcherEventsRequest]) (*connect.Response[filesystem.GetWatcherEventsResponse], error) + RemoveWatcher(context.Context, *connect.Request[filesystem.RemoveWatcherRequest]) (*connect.Response[filesystem.RemoveWatcherResponse], error) +} + +// NewFilesystemHandler builds an HTTP handler from the service implementation. It returns the path +// on which to mount the handler and the handler itself. +// +// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf +// and JSON codecs. They also support gzip compression. +func NewFilesystemHandler(svc FilesystemHandler, opts ...connect.HandlerOption) (string, http.Handler) { + filesystemMethods := filesystem.File_filesystem_filesystem_proto.Services().ByName("Filesystem").Methods() + filesystemStatHandler := connect.NewUnaryHandler( + FilesystemStatProcedure, + svc.Stat, + connect.WithSchema(filesystemMethods.ByName("Stat")), + connect.WithHandlerOptions(opts...), + ) + filesystemMakeDirHandler := connect.NewUnaryHandler( + FilesystemMakeDirProcedure, + svc.MakeDir, + connect.WithSchema(filesystemMethods.ByName("MakeDir")), + connect.WithHandlerOptions(opts...), + ) + filesystemMoveHandler := connect.NewUnaryHandler( + FilesystemMoveProcedure, + svc.Move, + connect.WithSchema(filesystemMethods.ByName("Move")), + connect.WithHandlerOptions(opts...), + ) + filesystemListDirHandler := connect.NewUnaryHandler( + FilesystemListDirProcedure, + svc.ListDir, + connect.WithSchema(filesystemMethods.ByName("ListDir")), + connect.WithHandlerOptions(opts...), + ) + filesystemRemoveHandler := connect.NewUnaryHandler( + FilesystemRemoveProcedure, + svc.Remove, + connect.WithSchema(filesystemMethods.ByName("Remove")), + connect.WithHandlerOptions(opts...), + ) + filesystemWatchDirHandler := connect.NewServerStreamHandler( + FilesystemWatchDirProcedure, + svc.WatchDir, + connect.WithSchema(filesystemMethods.ByName("WatchDir")), + connect.WithHandlerOptions(opts...), + ) + filesystemCreateWatcherHandler := connect.NewUnaryHandler( + FilesystemCreateWatcherProcedure, + svc.CreateWatcher, + connect.WithSchema(filesystemMethods.ByName("CreateWatcher")), + connect.WithHandlerOptions(opts...), + ) + filesystemGetWatcherEventsHandler := connect.NewUnaryHandler( + FilesystemGetWatcherEventsProcedure, + svc.GetWatcherEvents, + connect.WithSchema(filesystemMethods.ByName("GetWatcherEvents")), + connect.WithHandlerOptions(opts...), + ) + filesystemRemoveWatcherHandler := connect.NewUnaryHandler( + FilesystemRemoveWatcherProcedure, + svc.RemoveWatcher, + connect.WithSchema(filesystemMethods.ByName("RemoveWatcher")), + connect.WithHandlerOptions(opts...), + ) + return "/filesystem.Filesystem/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case FilesystemStatProcedure: + filesystemStatHandler.ServeHTTP(w, r) + case FilesystemMakeDirProcedure: + filesystemMakeDirHandler.ServeHTTP(w, r) + case FilesystemMoveProcedure: + filesystemMoveHandler.ServeHTTP(w, r) + case FilesystemListDirProcedure: + filesystemListDirHandler.ServeHTTP(w, r) + case FilesystemRemoveProcedure: + filesystemRemoveHandler.ServeHTTP(w, r) + case FilesystemWatchDirProcedure: + filesystemWatchDirHandler.ServeHTTP(w, r) + case FilesystemCreateWatcherProcedure: + filesystemCreateWatcherHandler.ServeHTTP(w, r) + case FilesystemGetWatcherEventsProcedure: + filesystemGetWatcherEventsHandler.ServeHTTP(w, r) + case FilesystemRemoveWatcherProcedure: + filesystemRemoveWatcherHandler.ServeHTTP(w, r) + default: + http.NotFound(w, r) + } + }) +} + +// UnimplementedFilesystemHandler returns CodeUnimplemented from all methods. +type UnimplementedFilesystemHandler struct{} + +func (UnimplementedFilesystemHandler) Stat(context.Context, *connect.Request[filesystem.StatRequest]) (*connect.Response[filesystem.StatResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("filesystem.Filesystem.Stat is not implemented")) +} + +func (UnimplementedFilesystemHandler) MakeDir(context.Context, *connect.Request[filesystem.MakeDirRequest]) (*connect.Response[filesystem.MakeDirResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("filesystem.Filesystem.MakeDir is not implemented")) +} + +func (UnimplementedFilesystemHandler) Move(context.Context, *connect.Request[filesystem.MoveRequest]) (*connect.Response[filesystem.MoveResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("filesystem.Filesystem.Move is not implemented")) +} + +func (UnimplementedFilesystemHandler) ListDir(context.Context, *connect.Request[filesystem.ListDirRequest]) (*connect.Response[filesystem.ListDirResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("filesystem.Filesystem.ListDir is not implemented")) +} + +func (UnimplementedFilesystemHandler) Remove(context.Context, *connect.Request[filesystem.RemoveRequest]) (*connect.Response[filesystem.RemoveResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("filesystem.Filesystem.Remove is not implemented")) +} + +func (UnimplementedFilesystemHandler) WatchDir(context.Context, *connect.Request[filesystem.WatchDirRequest], *connect.ServerStream[filesystem.WatchDirResponse]) error { + return connect.NewError(connect.CodeUnimplemented, errors.New("filesystem.Filesystem.WatchDir is not implemented")) +} + +func (UnimplementedFilesystemHandler) CreateWatcher(context.Context, *connect.Request[filesystem.CreateWatcherRequest]) (*connect.Response[filesystem.CreateWatcherResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("filesystem.Filesystem.CreateWatcher is not implemented")) +} + +func (UnimplementedFilesystemHandler) GetWatcherEvents(context.Context, *connect.Request[filesystem.GetWatcherEventsRequest]) (*connect.Response[filesystem.GetWatcherEventsResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("filesystem.Filesystem.GetWatcherEvents is not implemented")) +} + +func (UnimplementedFilesystemHandler) RemoveWatcher(context.Context, *connect.Request[filesystem.RemoveWatcherRequest]) (*connect.Response[filesystem.RemoveWatcherResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("filesystem.Filesystem.RemoveWatcher is not implemented")) +} diff --git a/packages/go-sdk/internal/gen/envd/process/process.pb.go b/packages/go-sdk/internal/gen/envd/process/process.pb.go new file mode 100644 index 000000000..584083469 --- /dev/null +++ b/packages/go-sdk/internal/gen/envd/process/process.pb.go @@ -0,0 +1,1970 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.12 +// protoc (unknown) +// source: process/process.proto + +package process + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Signal int32 + +const ( + Signal_SIGNAL_UNSPECIFIED Signal = 0 + Signal_SIGNAL_SIGTERM Signal = 15 + Signal_SIGNAL_SIGKILL Signal = 9 +) + +// Enum value maps for Signal. +var ( + Signal_name = map[int32]string{ + 0: "SIGNAL_UNSPECIFIED", + 15: "SIGNAL_SIGTERM", + 9: "SIGNAL_SIGKILL", + } + Signal_value = map[string]int32{ + "SIGNAL_UNSPECIFIED": 0, + "SIGNAL_SIGTERM": 15, + "SIGNAL_SIGKILL": 9, + } +) + +func (x Signal) Enum() *Signal { + p := new(Signal) + *p = x + return p +} + +func (x Signal) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (Signal) Descriptor() protoreflect.EnumDescriptor { + return file_process_process_proto_enumTypes[0].Descriptor() +} + +func (Signal) Type() protoreflect.EnumType { + return &file_process_process_proto_enumTypes[0] +} + +func (x Signal) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use Signal.Descriptor instead. +func (Signal) EnumDescriptor() ([]byte, []int) { + return file_process_process_proto_rawDescGZIP(), []int{0} +} + +type PTY struct { + state protoimpl.MessageState `protogen:"open.v1"` + Size *PTY_Size `protobuf:"bytes,1,opt,name=size,proto3" json:"size,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *PTY) Reset() { + *x = PTY{} + mi := &file_process_process_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *PTY) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PTY) ProtoMessage() {} + +func (x *PTY) ProtoReflect() protoreflect.Message { + mi := &file_process_process_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PTY.ProtoReflect.Descriptor instead. +func (*PTY) Descriptor() ([]byte, []int) { + return file_process_process_proto_rawDescGZIP(), []int{0} +} + +func (x *PTY) GetSize() *PTY_Size { + if x != nil { + return x.Size + } + return nil +} + +type ProcessConfig struct { + state protoimpl.MessageState `protogen:"open.v1"` + Cmd string `protobuf:"bytes,1,opt,name=cmd,proto3" json:"cmd,omitempty"` + Args []string `protobuf:"bytes,2,rep,name=args,proto3" json:"args,omitempty"` + Envs map[string]string `protobuf:"bytes,3,rep,name=envs,proto3" json:"envs,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + Cwd *string `protobuf:"bytes,4,opt,name=cwd,proto3,oneof" json:"cwd,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProcessConfig) Reset() { + *x = ProcessConfig{} + mi := &file_process_process_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProcessConfig) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProcessConfig) ProtoMessage() {} + +func (x *ProcessConfig) ProtoReflect() protoreflect.Message { + mi := &file_process_process_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProcessConfig.ProtoReflect.Descriptor instead. +func (*ProcessConfig) Descriptor() ([]byte, []int) { + return file_process_process_proto_rawDescGZIP(), []int{1} +} + +func (x *ProcessConfig) GetCmd() string { + if x != nil { + return x.Cmd + } + return "" +} + +func (x *ProcessConfig) GetArgs() []string { + if x != nil { + return x.Args + } + return nil +} + +func (x *ProcessConfig) GetEnvs() map[string]string { + if x != nil { + return x.Envs + } + return nil +} + +func (x *ProcessConfig) GetCwd() string { + if x != nil && x.Cwd != nil { + return *x.Cwd + } + return "" +} + +type ListRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListRequest) Reset() { + *x = ListRequest{} + mi := &file_process_process_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListRequest) ProtoMessage() {} + +func (x *ListRequest) ProtoReflect() protoreflect.Message { + mi := &file_process_process_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListRequest.ProtoReflect.Descriptor instead. +func (*ListRequest) Descriptor() ([]byte, []int) { + return file_process_process_proto_rawDescGZIP(), []int{2} +} + +type ProcessInfo struct { + state protoimpl.MessageState `protogen:"open.v1"` + Config *ProcessConfig `protobuf:"bytes,1,opt,name=config,proto3" json:"config,omitempty"` + Pid uint32 `protobuf:"varint,2,opt,name=pid,proto3" json:"pid,omitempty"` + Tag *string `protobuf:"bytes,3,opt,name=tag,proto3,oneof" json:"tag,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProcessInfo) Reset() { + *x = ProcessInfo{} + mi := &file_process_process_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProcessInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProcessInfo) ProtoMessage() {} + +func (x *ProcessInfo) ProtoReflect() protoreflect.Message { + mi := &file_process_process_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProcessInfo.ProtoReflect.Descriptor instead. +func (*ProcessInfo) Descriptor() ([]byte, []int) { + return file_process_process_proto_rawDescGZIP(), []int{3} +} + +func (x *ProcessInfo) GetConfig() *ProcessConfig { + if x != nil { + return x.Config + } + return nil +} + +func (x *ProcessInfo) GetPid() uint32 { + if x != nil { + return x.Pid + } + return 0 +} + +func (x *ProcessInfo) GetTag() string { + if x != nil && x.Tag != nil { + return *x.Tag + } + return "" +} + +type ListResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Processes []*ProcessInfo `protobuf:"bytes,1,rep,name=processes,proto3" json:"processes,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListResponse) Reset() { + *x = ListResponse{} + mi := &file_process_process_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListResponse) ProtoMessage() {} + +func (x *ListResponse) ProtoReflect() protoreflect.Message { + mi := &file_process_process_proto_msgTypes[4] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ListResponse.ProtoReflect.Descriptor instead. +func (*ListResponse) Descriptor() ([]byte, []int) { + return file_process_process_proto_rawDescGZIP(), []int{4} +} + +func (x *ListResponse) GetProcesses() []*ProcessInfo { + if x != nil { + return x.Processes + } + return nil +} + +type StartRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Process *ProcessConfig `protobuf:"bytes,1,opt,name=process,proto3" json:"process,omitempty"` + Pty *PTY `protobuf:"bytes,2,opt,name=pty,proto3,oneof" json:"pty,omitempty"` + Tag *string `protobuf:"bytes,3,opt,name=tag,proto3,oneof" json:"tag,omitempty"` + // This is optional for backwards compatibility. + // We default to true. New SDK versions will set this to false by default. + Stdin *bool `protobuf:"varint,4,opt,name=stdin,proto3,oneof" json:"stdin,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StartRequest) Reset() { + *x = StartRequest{} + mi := &file_process_process_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StartRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StartRequest) ProtoMessage() {} + +func (x *StartRequest) ProtoReflect() protoreflect.Message { + mi := &file_process_process_proto_msgTypes[5] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StartRequest.ProtoReflect.Descriptor instead. +func (*StartRequest) Descriptor() ([]byte, []int) { + return file_process_process_proto_rawDescGZIP(), []int{5} +} + +func (x *StartRequest) GetProcess() *ProcessConfig { + if x != nil { + return x.Process + } + return nil +} + +func (x *StartRequest) GetPty() *PTY { + if x != nil { + return x.Pty + } + return nil +} + +func (x *StartRequest) GetTag() string { + if x != nil && x.Tag != nil { + return *x.Tag + } + return "" +} + +func (x *StartRequest) GetStdin() bool { + if x != nil && x.Stdin != nil { + return *x.Stdin + } + return false +} + +type UpdateRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Process *ProcessSelector `protobuf:"bytes,1,opt,name=process,proto3" json:"process,omitempty"` + Pty *PTY `protobuf:"bytes,2,opt,name=pty,proto3,oneof" json:"pty,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateRequest) Reset() { + *x = UpdateRequest{} + mi := &file_process_process_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateRequest) ProtoMessage() {} + +func (x *UpdateRequest) ProtoReflect() protoreflect.Message { + mi := &file_process_process_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateRequest.ProtoReflect.Descriptor instead. +func (*UpdateRequest) Descriptor() ([]byte, []int) { + return file_process_process_proto_rawDescGZIP(), []int{6} +} + +func (x *UpdateRequest) GetProcess() *ProcessSelector { + if x != nil { + return x.Process + } + return nil +} + +func (x *UpdateRequest) GetPty() *PTY { + if x != nil { + return x.Pty + } + return nil +} + +type UpdateResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateResponse) Reset() { + *x = UpdateResponse{} + mi := &file_process_process_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateResponse) ProtoMessage() {} + +func (x *UpdateResponse) ProtoReflect() protoreflect.Message { + mi := &file_process_process_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpdateResponse.ProtoReflect.Descriptor instead. +func (*UpdateResponse) Descriptor() ([]byte, []int) { + return file_process_process_proto_rawDescGZIP(), []int{7} +} + +type ProcessEvent struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Types that are valid to be assigned to Event: + // + // *ProcessEvent_Start + // *ProcessEvent_Data + // *ProcessEvent_End + // *ProcessEvent_Keepalive + Event isProcessEvent_Event `protobuf_oneof:"event"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProcessEvent) Reset() { + *x = ProcessEvent{} + mi := &file_process_process_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProcessEvent) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProcessEvent) ProtoMessage() {} + +func (x *ProcessEvent) ProtoReflect() protoreflect.Message { + mi := &file_process_process_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProcessEvent.ProtoReflect.Descriptor instead. +func (*ProcessEvent) Descriptor() ([]byte, []int) { + return file_process_process_proto_rawDescGZIP(), []int{8} +} + +func (x *ProcessEvent) GetEvent() isProcessEvent_Event { + if x != nil { + return x.Event + } + return nil +} + +func (x *ProcessEvent) GetStart() *ProcessEvent_StartEvent { + if x != nil { + if x, ok := x.Event.(*ProcessEvent_Start); ok { + return x.Start + } + } + return nil +} + +func (x *ProcessEvent) GetData() *ProcessEvent_DataEvent { + if x != nil { + if x, ok := x.Event.(*ProcessEvent_Data); ok { + return x.Data + } + } + return nil +} + +func (x *ProcessEvent) GetEnd() *ProcessEvent_EndEvent { + if x != nil { + if x, ok := x.Event.(*ProcessEvent_End); ok { + return x.End + } + } + return nil +} + +func (x *ProcessEvent) GetKeepalive() *ProcessEvent_KeepAlive { + if x != nil { + if x, ok := x.Event.(*ProcessEvent_Keepalive); ok { + return x.Keepalive + } + } + return nil +} + +type isProcessEvent_Event interface { + isProcessEvent_Event() +} + +type ProcessEvent_Start struct { + Start *ProcessEvent_StartEvent `protobuf:"bytes,1,opt,name=start,proto3,oneof"` +} + +type ProcessEvent_Data struct { + Data *ProcessEvent_DataEvent `protobuf:"bytes,2,opt,name=data,proto3,oneof"` +} + +type ProcessEvent_End struct { + End *ProcessEvent_EndEvent `protobuf:"bytes,3,opt,name=end,proto3,oneof"` +} + +type ProcessEvent_Keepalive struct { + Keepalive *ProcessEvent_KeepAlive `protobuf:"bytes,4,opt,name=keepalive,proto3,oneof"` +} + +func (*ProcessEvent_Start) isProcessEvent_Event() {} + +func (*ProcessEvent_Data) isProcessEvent_Event() {} + +func (*ProcessEvent_End) isProcessEvent_Event() {} + +func (*ProcessEvent_Keepalive) isProcessEvent_Event() {} + +type StartResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Event *ProcessEvent `protobuf:"bytes,1,opt,name=event,proto3" json:"event,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StartResponse) Reset() { + *x = StartResponse{} + mi := &file_process_process_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StartResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StartResponse) ProtoMessage() {} + +func (x *StartResponse) ProtoReflect() protoreflect.Message { + mi := &file_process_process_proto_msgTypes[9] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StartResponse.ProtoReflect.Descriptor instead. +func (*StartResponse) Descriptor() ([]byte, []int) { + return file_process_process_proto_rawDescGZIP(), []int{9} +} + +func (x *StartResponse) GetEvent() *ProcessEvent { + if x != nil { + return x.Event + } + return nil +} + +type ConnectResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Event *ProcessEvent `protobuf:"bytes,1,opt,name=event,proto3" json:"event,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ConnectResponse) Reset() { + *x = ConnectResponse{} + mi := &file_process_process_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ConnectResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConnectResponse) ProtoMessage() {} + +func (x *ConnectResponse) ProtoReflect() protoreflect.Message { + mi := &file_process_process_proto_msgTypes[10] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConnectResponse.ProtoReflect.Descriptor instead. +func (*ConnectResponse) Descriptor() ([]byte, []int) { + return file_process_process_proto_rawDescGZIP(), []int{10} +} + +func (x *ConnectResponse) GetEvent() *ProcessEvent { + if x != nil { + return x.Event + } + return nil +} + +type SendInputRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Process *ProcessSelector `protobuf:"bytes,1,opt,name=process,proto3" json:"process,omitempty"` + Input *ProcessInput `protobuf:"bytes,2,opt,name=input,proto3" json:"input,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SendInputRequest) Reset() { + *x = SendInputRequest{} + mi := &file_process_process_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SendInputRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SendInputRequest) ProtoMessage() {} + +func (x *SendInputRequest) ProtoReflect() protoreflect.Message { + mi := &file_process_process_proto_msgTypes[11] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SendInputRequest.ProtoReflect.Descriptor instead. +func (*SendInputRequest) Descriptor() ([]byte, []int) { + return file_process_process_proto_rawDescGZIP(), []int{11} +} + +func (x *SendInputRequest) GetProcess() *ProcessSelector { + if x != nil { + return x.Process + } + return nil +} + +func (x *SendInputRequest) GetInput() *ProcessInput { + if x != nil { + return x.Input + } + return nil +} + +type SendInputResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SendInputResponse) Reset() { + *x = SendInputResponse{} + mi := &file_process_process_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SendInputResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SendInputResponse) ProtoMessage() {} + +func (x *SendInputResponse) ProtoReflect() protoreflect.Message { + mi := &file_process_process_proto_msgTypes[12] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SendInputResponse.ProtoReflect.Descriptor instead. +func (*SendInputResponse) Descriptor() ([]byte, []int) { + return file_process_process_proto_rawDescGZIP(), []int{12} +} + +type ProcessInput struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Types that are valid to be assigned to Input: + // + // *ProcessInput_Stdin + // *ProcessInput_Pty + Input isProcessInput_Input `protobuf_oneof:"input"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProcessInput) Reset() { + *x = ProcessInput{} + mi := &file_process_process_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProcessInput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProcessInput) ProtoMessage() {} + +func (x *ProcessInput) ProtoReflect() protoreflect.Message { + mi := &file_process_process_proto_msgTypes[13] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProcessInput.ProtoReflect.Descriptor instead. +func (*ProcessInput) Descriptor() ([]byte, []int) { + return file_process_process_proto_rawDescGZIP(), []int{13} +} + +func (x *ProcessInput) GetInput() isProcessInput_Input { + if x != nil { + return x.Input + } + return nil +} + +func (x *ProcessInput) GetStdin() []byte { + if x != nil { + if x, ok := x.Input.(*ProcessInput_Stdin); ok { + return x.Stdin + } + } + return nil +} + +func (x *ProcessInput) GetPty() []byte { + if x != nil { + if x, ok := x.Input.(*ProcessInput_Pty); ok { + return x.Pty + } + } + return nil +} + +type isProcessInput_Input interface { + isProcessInput_Input() +} + +type ProcessInput_Stdin struct { + Stdin []byte `protobuf:"bytes,1,opt,name=stdin,proto3,oneof"` +} + +type ProcessInput_Pty struct { + Pty []byte `protobuf:"bytes,2,opt,name=pty,proto3,oneof"` +} + +func (*ProcessInput_Stdin) isProcessInput_Input() {} + +func (*ProcessInput_Pty) isProcessInput_Input() {} + +type StreamInputRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Types that are valid to be assigned to Event: + // + // *StreamInputRequest_Start + // *StreamInputRequest_Data + // *StreamInputRequest_Keepalive + Event isStreamInputRequest_Event `protobuf_oneof:"event"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StreamInputRequest) Reset() { + *x = StreamInputRequest{} + mi := &file_process_process_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StreamInputRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StreamInputRequest) ProtoMessage() {} + +func (x *StreamInputRequest) ProtoReflect() protoreflect.Message { + mi := &file_process_process_proto_msgTypes[14] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StreamInputRequest.ProtoReflect.Descriptor instead. +func (*StreamInputRequest) Descriptor() ([]byte, []int) { + return file_process_process_proto_rawDescGZIP(), []int{14} +} + +func (x *StreamInputRequest) GetEvent() isStreamInputRequest_Event { + if x != nil { + return x.Event + } + return nil +} + +func (x *StreamInputRequest) GetStart() *StreamInputRequest_StartEvent { + if x != nil { + if x, ok := x.Event.(*StreamInputRequest_Start); ok { + return x.Start + } + } + return nil +} + +func (x *StreamInputRequest) GetData() *StreamInputRequest_DataEvent { + if x != nil { + if x, ok := x.Event.(*StreamInputRequest_Data); ok { + return x.Data + } + } + return nil +} + +func (x *StreamInputRequest) GetKeepalive() *StreamInputRequest_KeepAlive { + if x != nil { + if x, ok := x.Event.(*StreamInputRequest_Keepalive); ok { + return x.Keepalive + } + } + return nil +} + +type isStreamInputRequest_Event interface { + isStreamInputRequest_Event() +} + +type StreamInputRequest_Start struct { + Start *StreamInputRequest_StartEvent `protobuf:"bytes,1,opt,name=start,proto3,oneof"` +} + +type StreamInputRequest_Data struct { + Data *StreamInputRequest_DataEvent `protobuf:"bytes,2,opt,name=data,proto3,oneof"` +} + +type StreamInputRequest_Keepalive struct { + Keepalive *StreamInputRequest_KeepAlive `protobuf:"bytes,3,opt,name=keepalive,proto3,oneof"` +} + +func (*StreamInputRequest_Start) isStreamInputRequest_Event() {} + +func (*StreamInputRequest_Data) isStreamInputRequest_Event() {} + +func (*StreamInputRequest_Keepalive) isStreamInputRequest_Event() {} + +type StreamInputResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StreamInputResponse) Reset() { + *x = StreamInputResponse{} + mi := &file_process_process_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StreamInputResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StreamInputResponse) ProtoMessage() {} + +func (x *StreamInputResponse) ProtoReflect() protoreflect.Message { + mi := &file_process_process_proto_msgTypes[15] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StreamInputResponse.ProtoReflect.Descriptor instead. +func (*StreamInputResponse) Descriptor() ([]byte, []int) { + return file_process_process_proto_rawDescGZIP(), []int{15} +} + +type SendSignalRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Process *ProcessSelector `protobuf:"bytes,1,opt,name=process,proto3" json:"process,omitempty"` + Signal Signal `protobuf:"varint,2,opt,name=signal,proto3,enum=process.Signal" json:"signal,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SendSignalRequest) Reset() { + *x = SendSignalRequest{} + mi := &file_process_process_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SendSignalRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SendSignalRequest) ProtoMessage() {} + +func (x *SendSignalRequest) ProtoReflect() protoreflect.Message { + mi := &file_process_process_proto_msgTypes[16] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SendSignalRequest.ProtoReflect.Descriptor instead. +func (*SendSignalRequest) Descriptor() ([]byte, []int) { + return file_process_process_proto_rawDescGZIP(), []int{16} +} + +func (x *SendSignalRequest) GetProcess() *ProcessSelector { + if x != nil { + return x.Process + } + return nil +} + +func (x *SendSignalRequest) GetSignal() Signal { + if x != nil { + return x.Signal + } + return Signal_SIGNAL_UNSPECIFIED +} + +type SendSignalResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SendSignalResponse) Reset() { + *x = SendSignalResponse{} + mi := &file_process_process_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SendSignalResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SendSignalResponse) ProtoMessage() {} + +func (x *SendSignalResponse) ProtoReflect() protoreflect.Message { + mi := &file_process_process_proto_msgTypes[17] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use SendSignalResponse.ProtoReflect.Descriptor instead. +func (*SendSignalResponse) Descriptor() ([]byte, []int) { + return file_process_process_proto_rawDescGZIP(), []int{17} +} + +type CloseStdinRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Process *ProcessSelector `protobuf:"bytes,1,opt,name=process,proto3" json:"process,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CloseStdinRequest) Reset() { + *x = CloseStdinRequest{} + mi := &file_process_process_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CloseStdinRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CloseStdinRequest) ProtoMessage() {} + +func (x *CloseStdinRequest) ProtoReflect() protoreflect.Message { + mi := &file_process_process_proto_msgTypes[18] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CloseStdinRequest.ProtoReflect.Descriptor instead. +func (*CloseStdinRequest) Descriptor() ([]byte, []int) { + return file_process_process_proto_rawDescGZIP(), []int{18} +} + +func (x *CloseStdinRequest) GetProcess() *ProcessSelector { + if x != nil { + return x.Process + } + return nil +} + +type CloseStdinResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CloseStdinResponse) Reset() { + *x = CloseStdinResponse{} + mi := &file_process_process_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CloseStdinResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CloseStdinResponse) ProtoMessage() {} + +func (x *CloseStdinResponse) ProtoReflect() protoreflect.Message { + mi := &file_process_process_proto_msgTypes[19] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CloseStdinResponse.ProtoReflect.Descriptor instead. +func (*CloseStdinResponse) Descriptor() ([]byte, []int) { + return file_process_process_proto_rawDescGZIP(), []int{19} +} + +type ConnectRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Process *ProcessSelector `protobuf:"bytes,1,opt,name=process,proto3" json:"process,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ConnectRequest) Reset() { + *x = ConnectRequest{} + mi := &file_process_process_proto_msgTypes[20] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ConnectRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConnectRequest) ProtoMessage() {} + +func (x *ConnectRequest) ProtoReflect() protoreflect.Message { + mi := &file_process_process_proto_msgTypes[20] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ConnectRequest.ProtoReflect.Descriptor instead. +func (*ConnectRequest) Descriptor() ([]byte, []int) { + return file_process_process_proto_rawDescGZIP(), []int{20} +} + +func (x *ConnectRequest) GetProcess() *ProcessSelector { + if x != nil { + return x.Process + } + return nil +} + +type ProcessSelector struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Types that are valid to be assigned to Selector: + // + // *ProcessSelector_Pid + // *ProcessSelector_Tag + Selector isProcessSelector_Selector `protobuf_oneof:"selector"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProcessSelector) Reset() { + *x = ProcessSelector{} + mi := &file_process_process_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProcessSelector) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProcessSelector) ProtoMessage() {} + +func (x *ProcessSelector) ProtoReflect() protoreflect.Message { + mi := &file_process_process_proto_msgTypes[21] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProcessSelector.ProtoReflect.Descriptor instead. +func (*ProcessSelector) Descriptor() ([]byte, []int) { + return file_process_process_proto_rawDescGZIP(), []int{21} +} + +func (x *ProcessSelector) GetSelector() isProcessSelector_Selector { + if x != nil { + return x.Selector + } + return nil +} + +func (x *ProcessSelector) GetPid() uint32 { + if x != nil { + if x, ok := x.Selector.(*ProcessSelector_Pid); ok { + return x.Pid + } + } + return 0 +} + +func (x *ProcessSelector) GetTag() string { + if x != nil { + if x, ok := x.Selector.(*ProcessSelector_Tag); ok { + return x.Tag + } + } + return "" +} + +type isProcessSelector_Selector interface { + isProcessSelector_Selector() +} + +type ProcessSelector_Pid struct { + Pid uint32 `protobuf:"varint,1,opt,name=pid,proto3,oneof"` +} + +type ProcessSelector_Tag struct { + Tag string `protobuf:"bytes,2,opt,name=tag,proto3,oneof"` +} + +func (*ProcessSelector_Pid) isProcessSelector_Selector() {} + +func (*ProcessSelector_Tag) isProcessSelector_Selector() {} + +type PTY_Size struct { + state protoimpl.MessageState `protogen:"open.v1"` + Cols uint32 `protobuf:"varint,1,opt,name=cols,proto3" json:"cols,omitempty"` + Rows uint32 `protobuf:"varint,2,opt,name=rows,proto3" json:"rows,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *PTY_Size) Reset() { + *x = PTY_Size{} + mi := &file_process_process_proto_msgTypes[22] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *PTY_Size) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PTY_Size) ProtoMessage() {} + +func (x *PTY_Size) ProtoReflect() protoreflect.Message { + mi := &file_process_process_proto_msgTypes[22] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PTY_Size.ProtoReflect.Descriptor instead. +func (*PTY_Size) Descriptor() ([]byte, []int) { + return file_process_process_proto_rawDescGZIP(), []int{0, 0} +} + +func (x *PTY_Size) GetCols() uint32 { + if x != nil { + return x.Cols + } + return 0 +} + +func (x *PTY_Size) GetRows() uint32 { + if x != nil { + return x.Rows + } + return 0 +} + +type ProcessEvent_StartEvent struct { + state protoimpl.MessageState `protogen:"open.v1"` + Pid uint32 `protobuf:"varint,1,opt,name=pid,proto3" json:"pid,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProcessEvent_StartEvent) Reset() { + *x = ProcessEvent_StartEvent{} + mi := &file_process_process_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProcessEvent_StartEvent) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProcessEvent_StartEvent) ProtoMessage() {} + +func (x *ProcessEvent_StartEvent) ProtoReflect() protoreflect.Message { + mi := &file_process_process_proto_msgTypes[24] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProcessEvent_StartEvent.ProtoReflect.Descriptor instead. +func (*ProcessEvent_StartEvent) Descriptor() ([]byte, []int) { + return file_process_process_proto_rawDescGZIP(), []int{8, 0} +} + +func (x *ProcessEvent_StartEvent) GetPid() uint32 { + if x != nil { + return x.Pid + } + return 0 +} + +type ProcessEvent_DataEvent struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Types that are valid to be assigned to Output: + // + // *ProcessEvent_DataEvent_Stdout + // *ProcessEvent_DataEvent_Stderr + // *ProcessEvent_DataEvent_Pty + Output isProcessEvent_DataEvent_Output `protobuf_oneof:"output"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProcessEvent_DataEvent) Reset() { + *x = ProcessEvent_DataEvent{} + mi := &file_process_process_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProcessEvent_DataEvent) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProcessEvent_DataEvent) ProtoMessage() {} + +func (x *ProcessEvent_DataEvent) ProtoReflect() protoreflect.Message { + mi := &file_process_process_proto_msgTypes[25] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProcessEvent_DataEvent.ProtoReflect.Descriptor instead. +func (*ProcessEvent_DataEvent) Descriptor() ([]byte, []int) { + return file_process_process_proto_rawDescGZIP(), []int{8, 1} +} + +func (x *ProcessEvent_DataEvent) GetOutput() isProcessEvent_DataEvent_Output { + if x != nil { + return x.Output + } + return nil +} + +func (x *ProcessEvent_DataEvent) GetStdout() []byte { + if x != nil { + if x, ok := x.Output.(*ProcessEvent_DataEvent_Stdout); ok { + return x.Stdout + } + } + return nil +} + +func (x *ProcessEvent_DataEvent) GetStderr() []byte { + if x != nil { + if x, ok := x.Output.(*ProcessEvent_DataEvent_Stderr); ok { + return x.Stderr + } + } + return nil +} + +func (x *ProcessEvent_DataEvent) GetPty() []byte { + if x != nil { + if x, ok := x.Output.(*ProcessEvent_DataEvent_Pty); ok { + return x.Pty + } + } + return nil +} + +type isProcessEvent_DataEvent_Output interface { + isProcessEvent_DataEvent_Output() +} + +type ProcessEvent_DataEvent_Stdout struct { + Stdout []byte `protobuf:"bytes,1,opt,name=stdout,proto3,oneof"` +} + +type ProcessEvent_DataEvent_Stderr struct { + Stderr []byte `protobuf:"bytes,2,opt,name=stderr,proto3,oneof"` +} + +type ProcessEvent_DataEvent_Pty struct { + Pty []byte `protobuf:"bytes,3,opt,name=pty,proto3,oneof"` +} + +func (*ProcessEvent_DataEvent_Stdout) isProcessEvent_DataEvent_Output() {} + +func (*ProcessEvent_DataEvent_Stderr) isProcessEvent_DataEvent_Output() {} + +func (*ProcessEvent_DataEvent_Pty) isProcessEvent_DataEvent_Output() {} + +type ProcessEvent_EndEvent struct { + state protoimpl.MessageState `protogen:"open.v1"` + ExitCode int32 `protobuf:"zigzag32,1,opt,name=exit_code,json=exitCode,proto3" json:"exit_code,omitempty"` + Exited bool `protobuf:"varint,2,opt,name=exited,proto3" json:"exited,omitempty"` + Status string `protobuf:"bytes,3,opt,name=status,proto3" json:"status,omitempty"` + Error *string `protobuf:"bytes,4,opt,name=error,proto3,oneof" json:"error,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProcessEvent_EndEvent) Reset() { + *x = ProcessEvent_EndEvent{} + mi := &file_process_process_proto_msgTypes[26] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProcessEvent_EndEvent) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProcessEvent_EndEvent) ProtoMessage() {} + +func (x *ProcessEvent_EndEvent) ProtoReflect() protoreflect.Message { + mi := &file_process_process_proto_msgTypes[26] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProcessEvent_EndEvent.ProtoReflect.Descriptor instead. +func (*ProcessEvent_EndEvent) Descriptor() ([]byte, []int) { + return file_process_process_proto_rawDescGZIP(), []int{8, 2} +} + +func (x *ProcessEvent_EndEvent) GetExitCode() int32 { + if x != nil { + return x.ExitCode + } + return 0 +} + +func (x *ProcessEvent_EndEvent) GetExited() bool { + if x != nil { + return x.Exited + } + return false +} + +func (x *ProcessEvent_EndEvent) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *ProcessEvent_EndEvent) GetError() string { + if x != nil && x.Error != nil { + return *x.Error + } + return "" +} + +type ProcessEvent_KeepAlive struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ProcessEvent_KeepAlive) Reset() { + *x = ProcessEvent_KeepAlive{} + mi := &file_process_process_proto_msgTypes[27] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ProcessEvent_KeepAlive) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProcessEvent_KeepAlive) ProtoMessage() {} + +func (x *ProcessEvent_KeepAlive) ProtoReflect() protoreflect.Message { + mi := &file_process_process_proto_msgTypes[27] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProcessEvent_KeepAlive.ProtoReflect.Descriptor instead. +func (*ProcessEvent_KeepAlive) Descriptor() ([]byte, []int) { + return file_process_process_proto_rawDescGZIP(), []int{8, 3} +} + +type StreamInputRequest_StartEvent struct { + state protoimpl.MessageState `protogen:"open.v1"` + Process *ProcessSelector `protobuf:"bytes,1,opt,name=process,proto3" json:"process,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StreamInputRequest_StartEvent) Reset() { + *x = StreamInputRequest_StartEvent{} + mi := &file_process_process_proto_msgTypes[28] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StreamInputRequest_StartEvent) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StreamInputRequest_StartEvent) ProtoMessage() {} + +func (x *StreamInputRequest_StartEvent) ProtoReflect() protoreflect.Message { + mi := &file_process_process_proto_msgTypes[28] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StreamInputRequest_StartEvent.ProtoReflect.Descriptor instead. +func (*StreamInputRequest_StartEvent) Descriptor() ([]byte, []int) { + return file_process_process_proto_rawDescGZIP(), []int{14, 0} +} + +func (x *StreamInputRequest_StartEvent) GetProcess() *ProcessSelector { + if x != nil { + return x.Process + } + return nil +} + +type StreamInputRequest_DataEvent struct { + state protoimpl.MessageState `protogen:"open.v1"` + Input *ProcessInput `protobuf:"bytes,2,opt,name=input,proto3" json:"input,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StreamInputRequest_DataEvent) Reset() { + *x = StreamInputRequest_DataEvent{} + mi := &file_process_process_proto_msgTypes[29] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StreamInputRequest_DataEvent) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StreamInputRequest_DataEvent) ProtoMessage() {} + +func (x *StreamInputRequest_DataEvent) ProtoReflect() protoreflect.Message { + mi := &file_process_process_proto_msgTypes[29] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StreamInputRequest_DataEvent.ProtoReflect.Descriptor instead. +func (*StreamInputRequest_DataEvent) Descriptor() ([]byte, []int) { + return file_process_process_proto_rawDescGZIP(), []int{14, 1} +} + +func (x *StreamInputRequest_DataEvent) GetInput() *ProcessInput { + if x != nil { + return x.Input + } + return nil +} + +type StreamInputRequest_KeepAlive struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *StreamInputRequest_KeepAlive) Reset() { + *x = StreamInputRequest_KeepAlive{} + mi := &file_process_process_proto_msgTypes[30] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *StreamInputRequest_KeepAlive) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StreamInputRequest_KeepAlive) ProtoMessage() {} + +func (x *StreamInputRequest_KeepAlive) ProtoReflect() protoreflect.Message { + mi := &file_process_process_proto_msgTypes[30] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StreamInputRequest_KeepAlive.ProtoReflect.Descriptor instead. +func (*StreamInputRequest_KeepAlive) Descriptor() ([]byte, []int) { + return file_process_process_proto_rawDescGZIP(), []int{14, 2} +} + +var File_process_process_proto protoreflect.FileDescriptor + +const file_process_process_proto_rawDesc = "" + + "\n" + + "\x15process/process.proto\x12\aprocess\"\\\n" + + "\x03PTY\x12%\n" + + "\x04size\x18\x01 \x01(\v2\x11.process.PTY.SizeR\x04size\x1a.\n" + + "\x04Size\x12\x12\n" + + "\x04cols\x18\x01 \x01(\rR\x04cols\x12\x12\n" + + "\x04rows\x18\x02 \x01(\rR\x04rows\"\xc3\x01\n" + + "\rProcessConfig\x12\x10\n" + + "\x03cmd\x18\x01 \x01(\tR\x03cmd\x12\x12\n" + + "\x04args\x18\x02 \x03(\tR\x04args\x124\n" + + "\x04envs\x18\x03 \x03(\v2 .process.ProcessConfig.EnvsEntryR\x04envs\x12\x15\n" + + "\x03cwd\x18\x04 \x01(\tH\x00R\x03cwd\x88\x01\x01\x1a7\n" + + "\tEnvsEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01B\x06\n" + + "\x04_cwd\"\r\n" + + "\vListRequest\"n\n" + + "\vProcessInfo\x12.\n" + + "\x06config\x18\x01 \x01(\v2\x16.process.ProcessConfigR\x06config\x12\x10\n" + + "\x03pid\x18\x02 \x01(\rR\x03pid\x12\x15\n" + + "\x03tag\x18\x03 \x01(\tH\x00R\x03tag\x88\x01\x01B\x06\n" + + "\x04_tag\"B\n" + + "\fListResponse\x122\n" + + "\tprocesses\x18\x01 \x03(\v2\x14.process.ProcessInfoR\tprocesses\"\xb1\x01\n" + + "\fStartRequest\x120\n" + + "\aprocess\x18\x01 \x01(\v2\x16.process.ProcessConfigR\aprocess\x12#\n" + + "\x03pty\x18\x02 \x01(\v2\f.process.PTYH\x00R\x03pty\x88\x01\x01\x12\x15\n" + + "\x03tag\x18\x03 \x01(\tH\x01R\x03tag\x88\x01\x01\x12\x19\n" + + "\x05stdin\x18\x04 \x01(\bH\x02R\x05stdin\x88\x01\x01B\x06\n" + + "\x04_ptyB\x06\n" + + "\x04_tagB\b\n" + + "\x06_stdin\"p\n" + + "\rUpdateRequest\x122\n" + + "\aprocess\x18\x01 \x01(\v2\x18.process.ProcessSelectorR\aprocess\x12#\n" + + "\x03pty\x18\x02 \x01(\v2\f.process.PTYH\x00R\x03pty\x88\x01\x01B\x06\n" + + "\x04_pty\"\x10\n" + + "\x0eUpdateResponse\"\x87\x04\n" + + "\fProcessEvent\x128\n" + + "\x05start\x18\x01 \x01(\v2 .process.ProcessEvent.StartEventH\x00R\x05start\x125\n" + + "\x04data\x18\x02 \x01(\v2\x1f.process.ProcessEvent.DataEventH\x00R\x04data\x122\n" + + "\x03end\x18\x03 \x01(\v2\x1e.process.ProcessEvent.EndEventH\x00R\x03end\x12?\n" + + "\tkeepalive\x18\x04 \x01(\v2\x1f.process.ProcessEvent.KeepAliveH\x00R\tkeepalive\x1a\x1e\n" + + "\n" + + "StartEvent\x12\x10\n" + + "\x03pid\x18\x01 \x01(\rR\x03pid\x1a]\n" + + "\tDataEvent\x12\x18\n" + + "\x06stdout\x18\x01 \x01(\fH\x00R\x06stdout\x12\x18\n" + + "\x06stderr\x18\x02 \x01(\fH\x00R\x06stderr\x12\x12\n" + + "\x03pty\x18\x03 \x01(\fH\x00R\x03ptyB\b\n" + + "\x06output\x1a|\n" + + "\bEndEvent\x12\x1b\n" + + "\texit_code\x18\x01 \x01(\x11R\bexitCode\x12\x16\n" + + "\x06exited\x18\x02 \x01(\bR\x06exited\x12\x16\n" + + "\x06status\x18\x03 \x01(\tR\x06status\x12\x19\n" + + "\x05error\x18\x04 \x01(\tH\x00R\x05error\x88\x01\x01B\b\n" + + "\x06_error\x1a\v\n" + + "\tKeepAliveB\a\n" + + "\x05event\"<\n" + + "\rStartResponse\x12+\n" + + "\x05event\x18\x01 \x01(\v2\x15.process.ProcessEventR\x05event\">\n" + + "\x0fConnectResponse\x12+\n" + + "\x05event\x18\x01 \x01(\v2\x15.process.ProcessEventR\x05event\"s\n" + + "\x10SendInputRequest\x122\n" + + "\aprocess\x18\x01 \x01(\v2\x18.process.ProcessSelectorR\aprocess\x12+\n" + + "\x05input\x18\x02 \x01(\v2\x15.process.ProcessInputR\x05input\"\x13\n" + + "\x11SendInputResponse\"C\n" + + "\fProcessInput\x12\x16\n" + + "\x05stdin\x18\x01 \x01(\fH\x00R\x05stdin\x12\x12\n" + + "\x03pty\x18\x02 \x01(\fH\x00R\x03ptyB\a\n" + + "\x05input\"\xea\x02\n" + + "\x12StreamInputRequest\x12>\n" + + "\x05start\x18\x01 \x01(\v2&.process.StreamInputRequest.StartEventH\x00R\x05start\x12;\n" + + "\x04data\x18\x02 \x01(\v2%.process.StreamInputRequest.DataEventH\x00R\x04data\x12E\n" + + "\tkeepalive\x18\x03 \x01(\v2%.process.StreamInputRequest.KeepAliveH\x00R\tkeepalive\x1a@\n" + + "\n" + + "StartEvent\x122\n" + + "\aprocess\x18\x01 \x01(\v2\x18.process.ProcessSelectorR\aprocess\x1a8\n" + + "\tDataEvent\x12+\n" + + "\x05input\x18\x02 \x01(\v2\x15.process.ProcessInputR\x05input\x1a\v\n" + + "\tKeepAliveB\a\n" + + "\x05event\"\x15\n" + + "\x13StreamInputResponse\"p\n" + + "\x11SendSignalRequest\x122\n" + + "\aprocess\x18\x01 \x01(\v2\x18.process.ProcessSelectorR\aprocess\x12'\n" + + "\x06signal\x18\x02 \x01(\x0e2\x0f.process.SignalR\x06signal\"\x14\n" + + "\x12SendSignalResponse\"G\n" + + "\x11CloseStdinRequest\x122\n" + + "\aprocess\x18\x01 \x01(\v2\x18.process.ProcessSelectorR\aprocess\"\x14\n" + + "\x12CloseStdinResponse\"D\n" + + "\x0eConnectRequest\x122\n" + + "\aprocess\x18\x01 \x01(\v2\x18.process.ProcessSelectorR\aprocess\"E\n" + + "\x0fProcessSelector\x12\x12\n" + + "\x03pid\x18\x01 \x01(\rH\x00R\x03pid\x12\x12\n" + + "\x03tag\x18\x02 \x01(\tH\x00R\x03tagB\n" + + "\n" + + "\bselector*H\n" + + "\x06Signal\x12\x16\n" + + "\x12SIGNAL_UNSPECIFIED\x10\x00\x12\x12\n" + + "\x0eSIGNAL_SIGTERM\x10\x0f\x12\x12\n" + + "\x0eSIGNAL_SIGKILL\x10\t2\x91\x04\n" + + "\aProcess\x123\n" + + "\x04List\x12\x14.process.ListRequest\x1a\x15.process.ListResponse\x12>\n" + + "\aConnect\x12\x17.process.ConnectRequest\x1a\x18.process.ConnectResponse0\x01\x128\n" + + "\x05Start\x12\x15.process.StartRequest\x1a\x16.process.StartResponse0\x01\x129\n" + + "\x06Update\x12\x16.process.UpdateRequest\x1a\x17.process.UpdateResponse\x12J\n" + + "\vStreamInput\x12\x1b.process.StreamInputRequest\x1a\x1c.process.StreamInputResponse(\x01\x12B\n" + + "\tSendInput\x12\x19.process.SendInputRequest\x1a\x1a.process.SendInputResponse\x12E\n" + + "\n" + + "SendSignal\x12\x1a.process.SendSignalRequest\x1a\x1b.process.SendSignalResponse\x12E\n" + + "\n" + + "CloseStdin\x12\x1a.process.CloseStdinRequest\x1a\x1b.process.CloseStdinResponseB\x9a\x01\n" + + "\vcom.processB\fProcessProtoP\x01ZAgithub.com/abox-dev/sdk/packages/go-sdk/internal/gen/envd/process\xa2\x02\x03PXX\xaa\x02\aProcess\xca\x02\aProcess\xe2\x02\x13Process\\GPBMetadata\xea\x02\aProcessb\x06proto3" + +var ( + file_process_process_proto_rawDescOnce sync.Once + file_process_process_proto_rawDescData []byte +) + +func file_process_process_proto_rawDescGZIP() []byte { + file_process_process_proto_rawDescOnce.Do(func() { + file_process_process_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_process_process_proto_rawDesc), len(file_process_process_proto_rawDesc))) + }) + return file_process_process_proto_rawDescData +} + +var file_process_process_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_process_process_proto_msgTypes = make([]protoimpl.MessageInfo, 31) +var file_process_process_proto_goTypes = []any{ + (Signal)(0), // 0: process.Signal + (*PTY)(nil), // 1: process.PTY + (*ProcessConfig)(nil), // 2: process.ProcessConfig + (*ListRequest)(nil), // 3: process.ListRequest + (*ProcessInfo)(nil), // 4: process.ProcessInfo + (*ListResponse)(nil), // 5: process.ListResponse + (*StartRequest)(nil), // 6: process.StartRequest + (*UpdateRequest)(nil), // 7: process.UpdateRequest + (*UpdateResponse)(nil), // 8: process.UpdateResponse + (*ProcessEvent)(nil), // 9: process.ProcessEvent + (*StartResponse)(nil), // 10: process.StartResponse + (*ConnectResponse)(nil), // 11: process.ConnectResponse + (*SendInputRequest)(nil), // 12: process.SendInputRequest + (*SendInputResponse)(nil), // 13: process.SendInputResponse + (*ProcessInput)(nil), // 14: process.ProcessInput + (*StreamInputRequest)(nil), // 15: process.StreamInputRequest + (*StreamInputResponse)(nil), // 16: process.StreamInputResponse + (*SendSignalRequest)(nil), // 17: process.SendSignalRequest + (*SendSignalResponse)(nil), // 18: process.SendSignalResponse + (*CloseStdinRequest)(nil), // 19: process.CloseStdinRequest + (*CloseStdinResponse)(nil), // 20: process.CloseStdinResponse + (*ConnectRequest)(nil), // 21: process.ConnectRequest + (*ProcessSelector)(nil), // 22: process.ProcessSelector + (*PTY_Size)(nil), // 23: process.PTY.Size + nil, // 24: process.ProcessConfig.EnvsEntry + (*ProcessEvent_StartEvent)(nil), // 25: process.ProcessEvent.StartEvent + (*ProcessEvent_DataEvent)(nil), // 26: process.ProcessEvent.DataEvent + (*ProcessEvent_EndEvent)(nil), // 27: process.ProcessEvent.EndEvent + (*ProcessEvent_KeepAlive)(nil), // 28: process.ProcessEvent.KeepAlive + (*StreamInputRequest_StartEvent)(nil), // 29: process.StreamInputRequest.StartEvent + (*StreamInputRequest_DataEvent)(nil), // 30: process.StreamInputRequest.DataEvent + (*StreamInputRequest_KeepAlive)(nil), // 31: process.StreamInputRequest.KeepAlive +} +var file_process_process_proto_depIdxs = []int32{ + 23, // 0: process.PTY.size:type_name -> process.PTY.Size + 24, // 1: process.ProcessConfig.envs:type_name -> process.ProcessConfig.EnvsEntry + 2, // 2: process.ProcessInfo.config:type_name -> process.ProcessConfig + 4, // 3: process.ListResponse.processes:type_name -> process.ProcessInfo + 2, // 4: process.StartRequest.process:type_name -> process.ProcessConfig + 1, // 5: process.StartRequest.pty:type_name -> process.PTY + 22, // 6: process.UpdateRequest.process:type_name -> process.ProcessSelector + 1, // 7: process.UpdateRequest.pty:type_name -> process.PTY + 25, // 8: process.ProcessEvent.start:type_name -> process.ProcessEvent.StartEvent + 26, // 9: process.ProcessEvent.data:type_name -> process.ProcessEvent.DataEvent + 27, // 10: process.ProcessEvent.end:type_name -> process.ProcessEvent.EndEvent + 28, // 11: process.ProcessEvent.keepalive:type_name -> process.ProcessEvent.KeepAlive + 9, // 12: process.StartResponse.event:type_name -> process.ProcessEvent + 9, // 13: process.ConnectResponse.event:type_name -> process.ProcessEvent + 22, // 14: process.SendInputRequest.process:type_name -> process.ProcessSelector + 14, // 15: process.SendInputRequest.input:type_name -> process.ProcessInput + 29, // 16: process.StreamInputRequest.start:type_name -> process.StreamInputRequest.StartEvent + 30, // 17: process.StreamInputRequest.data:type_name -> process.StreamInputRequest.DataEvent + 31, // 18: process.StreamInputRequest.keepalive:type_name -> process.StreamInputRequest.KeepAlive + 22, // 19: process.SendSignalRequest.process:type_name -> process.ProcessSelector + 0, // 20: process.SendSignalRequest.signal:type_name -> process.Signal + 22, // 21: process.CloseStdinRequest.process:type_name -> process.ProcessSelector + 22, // 22: process.ConnectRequest.process:type_name -> process.ProcessSelector + 22, // 23: process.StreamInputRequest.StartEvent.process:type_name -> process.ProcessSelector + 14, // 24: process.StreamInputRequest.DataEvent.input:type_name -> process.ProcessInput + 3, // 25: process.Process.List:input_type -> process.ListRequest + 21, // 26: process.Process.Connect:input_type -> process.ConnectRequest + 6, // 27: process.Process.Start:input_type -> process.StartRequest + 7, // 28: process.Process.Update:input_type -> process.UpdateRequest + 15, // 29: process.Process.StreamInput:input_type -> process.StreamInputRequest + 12, // 30: process.Process.SendInput:input_type -> process.SendInputRequest + 17, // 31: process.Process.SendSignal:input_type -> process.SendSignalRequest + 19, // 32: process.Process.CloseStdin:input_type -> process.CloseStdinRequest + 5, // 33: process.Process.List:output_type -> process.ListResponse + 11, // 34: process.Process.Connect:output_type -> process.ConnectResponse + 10, // 35: process.Process.Start:output_type -> process.StartResponse + 8, // 36: process.Process.Update:output_type -> process.UpdateResponse + 16, // 37: process.Process.StreamInput:output_type -> process.StreamInputResponse + 13, // 38: process.Process.SendInput:output_type -> process.SendInputResponse + 18, // 39: process.Process.SendSignal:output_type -> process.SendSignalResponse + 20, // 40: process.Process.CloseStdin:output_type -> process.CloseStdinResponse + 33, // [33:41] is the sub-list for method output_type + 25, // [25:33] is the sub-list for method input_type + 25, // [25:25] is the sub-list for extension type_name + 25, // [25:25] is the sub-list for extension extendee + 0, // [0:25] is the sub-list for field type_name +} + +func init() { file_process_process_proto_init() } +func file_process_process_proto_init() { + if File_process_process_proto != nil { + return + } + file_process_process_proto_msgTypes[1].OneofWrappers = []any{} + file_process_process_proto_msgTypes[3].OneofWrappers = []any{} + file_process_process_proto_msgTypes[5].OneofWrappers = []any{} + file_process_process_proto_msgTypes[6].OneofWrappers = []any{} + file_process_process_proto_msgTypes[8].OneofWrappers = []any{ + (*ProcessEvent_Start)(nil), + (*ProcessEvent_Data)(nil), + (*ProcessEvent_End)(nil), + (*ProcessEvent_Keepalive)(nil), + } + file_process_process_proto_msgTypes[13].OneofWrappers = []any{ + (*ProcessInput_Stdin)(nil), + (*ProcessInput_Pty)(nil), + } + file_process_process_proto_msgTypes[14].OneofWrappers = []any{ + (*StreamInputRequest_Start)(nil), + (*StreamInputRequest_Data)(nil), + (*StreamInputRequest_Keepalive)(nil), + } + file_process_process_proto_msgTypes[21].OneofWrappers = []any{ + (*ProcessSelector_Pid)(nil), + (*ProcessSelector_Tag)(nil), + } + file_process_process_proto_msgTypes[25].OneofWrappers = []any{ + (*ProcessEvent_DataEvent_Stdout)(nil), + (*ProcessEvent_DataEvent_Stderr)(nil), + (*ProcessEvent_DataEvent_Pty)(nil), + } + file_process_process_proto_msgTypes[26].OneofWrappers = []any{} + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_process_process_proto_rawDesc), len(file_process_process_proto_rawDesc)), + NumEnums: 1, + NumMessages: 31, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_process_process_proto_goTypes, + DependencyIndexes: file_process_process_proto_depIdxs, + EnumInfos: file_process_process_proto_enumTypes, + MessageInfos: file_process_process_proto_msgTypes, + }.Build() + File_process_process_proto = out.File + file_process_process_proto_goTypes = nil + file_process_process_proto_depIdxs = nil +} diff --git a/packages/go-sdk/internal/gen/envd/process/processconnect/process.connect.go b/packages/go-sdk/internal/gen/envd/process/processconnect/process.connect.go new file mode 100644 index 000000000..27aad8c87 --- /dev/null +++ b/packages/go-sdk/internal/gen/envd/process/processconnect/process.connect.go @@ -0,0 +1,310 @@ +// Code generated by protoc-gen-connect-go. DO NOT EDIT. +// +// Source: process/process.proto + +package processconnect + +import ( + connect "connectrpc.com/connect" + context "context" + errors "errors" + process "github.com/abox-dev/sdk/packages/go-sdk/internal/gen/envd/process" + http "net/http" + strings "strings" +) + +// This is a compile-time assertion to ensure that this generated file and the connect package are +// compatible. If you get a compiler error that this constant is not defined, this code was +// generated with a version of connect newer than the one compiled into your binary. You can fix the +// problem by either regenerating this code with an older version of connect or updating the connect +// version compiled into your binary. +const _ = connect.IsAtLeastVersion1_13_0 + +const ( + // ProcessName is the fully-qualified name of the Process service. + ProcessName = "process.Process" +) + +// These constants are the fully-qualified names of the RPCs defined in this package. They're +// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route. +// +// Note that these are different from the fully-qualified method names used by +// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to +// reflection-formatted method names, remove the leading slash and convert the remaining slash to a +// period. +const ( + // ProcessListProcedure is the fully-qualified name of the Process's List RPC. + ProcessListProcedure = "/process.Process/List" + // ProcessConnectProcedure is the fully-qualified name of the Process's Connect RPC. + ProcessConnectProcedure = "/process.Process/Connect" + // ProcessStartProcedure is the fully-qualified name of the Process's Start RPC. + ProcessStartProcedure = "/process.Process/Start" + // ProcessUpdateProcedure is the fully-qualified name of the Process's Update RPC. + ProcessUpdateProcedure = "/process.Process/Update" + // ProcessStreamInputProcedure is the fully-qualified name of the Process's StreamInput RPC. + ProcessStreamInputProcedure = "/process.Process/StreamInput" + // ProcessSendInputProcedure is the fully-qualified name of the Process's SendInput RPC. + ProcessSendInputProcedure = "/process.Process/SendInput" + // ProcessSendSignalProcedure is the fully-qualified name of the Process's SendSignal RPC. + ProcessSendSignalProcedure = "/process.Process/SendSignal" + // ProcessCloseStdinProcedure is the fully-qualified name of the Process's CloseStdin RPC. + ProcessCloseStdinProcedure = "/process.Process/CloseStdin" +) + +// ProcessClient is a client for the process.Process service. +type ProcessClient interface { + List(context.Context, *connect.Request[process.ListRequest]) (*connect.Response[process.ListResponse], error) + Connect(context.Context, *connect.Request[process.ConnectRequest]) (*connect.ServerStreamForClient[process.ConnectResponse], error) + Start(context.Context, *connect.Request[process.StartRequest]) (*connect.ServerStreamForClient[process.StartResponse], error) + Update(context.Context, *connect.Request[process.UpdateRequest]) (*connect.Response[process.UpdateResponse], error) + // Client input stream ensures ordering of messages + StreamInput(context.Context) *connect.ClientStreamForClient[process.StreamInputRequest, process.StreamInputResponse] + SendInput(context.Context, *connect.Request[process.SendInputRequest]) (*connect.Response[process.SendInputResponse], error) + SendSignal(context.Context, *connect.Request[process.SendSignalRequest]) (*connect.Response[process.SendSignalResponse], error) + // Close stdin to signal EOF to the process. + // Only works for non-PTY processes. For PTY, send Ctrl+D (0x04) instead. + CloseStdin(context.Context, *connect.Request[process.CloseStdinRequest]) (*connect.Response[process.CloseStdinResponse], error) +} + +// NewProcessClient constructs a client for the process.Process service. By default, it uses the +// Connect protocol with the binary Protobuf Codec, asks for gzipped responses, and sends +// uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() or +// connect.WithGRPCWeb() options. +// +// The URL supplied here should be the base URL for the Connect or gRPC server (for example, +// http://api.acme.com or https://acme.com/grpc). +func NewProcessClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) ProcessClient { + baseURL = strings.TrimRight(baseURL, "/") + processMethods := process.File_process_process_proto.Services().ByName("Process").Methods() + return &processClient{ + list: connect.NewClient[process.ListRequest, process.ListResponse]( + httpClient, + baseURL+ProcessListProcedure, + connect.WithSchema(processMethods.ByName("List")), + connect.WithClientOptions(opts...), + ), + connect: connect.NewClient[process.ConnectRequest, process.ConnectResponse]( + httpClient, + baseURL+ProcessConnectProcedure, + connect.WithSchema(processMethods.ByName("Connect")), + connect.WithClientOptions(opts...), + ), + start: connect.NewClient[process.StartRequest, process.StartResponse]( + httpClient, + baseURL+ProcessStartProcedure, + connect.WithSchema(processMethods.ByName("Start")), + connect.WithClientOptions(opts...), + ), + update: connect.NewClient[process.UpdateRequest, process.UpdateResponse]( + httpClient, + baseURL+ProcessUpdateProcedure, + connect.WithSchema(processMethods.ByName("Update")), + connect.WithClientOptions(opts...), + ), + streamInput: connect.NewClient[process.StreamInputRequest, process.StreamInputResponse]( + httpClient, + baseURL+ProcessStreamInputProcedure, + connect.WithSchema(processMethods.ByName("StreamInput")), + connect.WithClientOptions(opts...), + ), + sendInput: connect.NewClient[process.SendInputRequest, process.SendInputResponse]( + httpClient, + baseURL+ProcessSendInputProcedure, + connect.WithSchema(processMethods.ByName("SendInput")), + connect.WithClientOptions(opts...), + ), + sendSignal: connect.NewClient[process.SendSignalRequest, process.SendSignalResponse]( + httpClient, + baseURL+ProcessSendSignalProcedure, + connect.WithSchema(processMethods.ByName("SendSignal")), + connect.WithClientOptions(opts...), + ), + closeStdin: connect.NewClient[process.CloseStdinRequest, process.CloseStdinResponse]( + httpClient, + baseURL+ProcessCloseStdinProcedure, + connect.WithSchema(processMethods.ByName("CloseStdin")), + connect.WithClientOptions(opts...), + ), + } +} + +// processClient implements ProcessClient. +type processClient struct { + list *connect.Client[process.ListRequest, process.ListResponse] + connect *connect.Client[process.ConnectRequest, process.ConnectResponse] + start *connect.Client[process.StartRequest, process.StartResponse] + update *connect.Client[process.UpdateRequest, process.UpdateResponse] + streamInput *connect.Client[process.StreamInputRequest, process.StreamInputResponse] + sendInput *connect.Client[process.SendInputRequest, process.SendInputResponse] + sendSignal *connect.Client[process.SendSignalRequest, process.SendSignalResponse] + closeStdin *connect.Client[process.CloseStdinRequest, process.CloseStdinResponse] +} + +// List calls process.Process.List. +func (c *processClient) List(ctx context.Context, req *connect.Request[process.ListRequest]) (*connect.Response[process.ListResponse], error) { + return c.list.CallUnary(ctx, req) +} + +// Connect calls process.Process.Connect. +func (c *processClient) Connect(ctx context.Context, req *connect.Request[process.ConnectRequest]) (*connect.ServerStreamForClient[process.ConnectResponse], error) { + return c.connect.CallServerStream(ctx, req) +} + +// Start calls process.Process.Start. +func (c *processClient) Start(ctx context.Context, req *connect.Request[process.StartRequest]) (*connect.ServerStreamForClient[process.StartResponse], error) { + return c.start.CallServerStream(ctx, req) +} + +// Update calls process.Process.Update. +func (c *processClient) Update(ctx context.Context, req *connect.Request[process.UpdateRequest]) (*connect.Response[process.UpdateResponse], error) { + return c.update.CallUnary(ctx, req) +} + +// StreamInput calls process.Process.StreamInput. +func (c *processClient) StreamInput(ctx context.Context) *connect.ClientStreamForClient[process.StreamInputRequest, process.StreamInputResponse] { + return c.streamInput.CallClientStream(ctx) +} + +// SendInput calls process.Process.SendInput. +func (c *processClient) SendInput(ctx context.Context, req *connect.Request[process.SendInputRequest]) (*connect.Response[process.SendInputResponse], error) { + return c.sendInput.CallUnary(ctx, req) +} + +// SendSignal calls process.Process.SendSignal. +func (c *processClient) SendSignal(ctx context.Context, req *connect.Request[process.SendSignalRequest]) (*connect.Response[process.SendSignalResponse], error) { + return c.sendSignal.CallUnary(ctx, req) +} + +// CloseStdin calls process.Process.CloseStdin. +func (c *processClient) CloseStdin(ctx context.Context, req *connect.Request[process.CloseStdinRequest]) (*connect.Response[process.CloseStdinResponse], error) { + return c.closeStdin.CallUnary(ctx, req) +} + +// ProcessHandler is an implementation of the process.Process service. +type ProcessHandler interface { + List(context.Context, *connect.Request[process.ListRequest]) (*connect.Response[process.ListResponse], error) + Connect(context.Context, *connect.Request[process.ConnectRequest], *connect.ServerStream[process.ConnectResponse]) error + Start(context.Context, *connect.Request[process.StartRequest], *connect.ServerStream[process.StartResponse]) error + Update(context.Context, *connect.Request[process.UpdateRequest]) (*connect.Response[process.UpdateResponse], error) + // Client input stream ensures ordering of messages + StreamInput(context.Context, *connect.ClientStream[process.StreamInputRequest]) (*connect.Response[process.StreamInputResponse], error) + SendInput(context.Context, *connect.Request[process.SendInputRequest]) (*connect.Response[process.SendInputResponse], error) + SendSignal(context.Context, *connect.Request[process.SendSignalRequest]) (*connect.Response[process.SendSignalResponse], error) + // Close stdin to signal EOF to the process. + // Only works for non-PTY processes. For PTY, send Ctrl+D (0x04) instead. + CloseStdin(context.Context, *connect.Request[process.CloseStdinRequest]) (*connect.Response[process.CloseStdinResponse], error) +} + +// NewProcessHandler builds an HTTP handler from the service implementation. It returns the path on +// which to mount the handler and the handler itself. +// +// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf +// and JSON codecs. They also support gzip compression. +func NewProcessHandler(svc ProcessHandler, opts ...connect.HandlerOption) (string, http.Handler) { + processMethods := process.File_process_process_proto.Services().ByName("Process").Methods() + processListHandler := connect.NewUnaryHandler( + ProcessListProcedure, + svc.List, + connect.WithSchema(processMethods.ByName("List")), + connect.WithHandlerOptions(opts...), + ) + processConnectHandler := connect.NewServerStreamHandler( + ProcessConnectProcedure, + svc.Connect, + connect.WithSchema(processMethods.ByName("Connect")), + connect.WithHandlerOptions(opts...), + ) + processStartHandler := connect.NewServerStreamHandler( + ProcessStartProcedure, + svc.Start, + connect.WithSchema(processMethods.ByName("Start")), + connect.WithHandlerOptions(opts...), + ) + processUpdateHandler := connect.NewUnaryHandler( + ProcessUpdateProcedure, + svc.Update, + connect.WithSchema(processMethods.ByName("Update")), + connect.WithHandlerOptions(opts...), + ) + processStreamInputHandler := connect.NewClientStreamHandler( + ProcessStreamInputProcedure, + svc.StreamInput, + connect.WithSchema(processMethods.ByName("StreamInput")), + connect.WithHandlerOptions(opts...), + ) + processSendInputHandler := connect.NewUnaryHandler( + ProcessSendInputProcedure, + svc.SendInput, + connect.WithSchema(processMethods.ByName("SendInput")), + connect.WithHandlerOptions(opts...), + ) + processSendSignalHandler := connect.NewUnaryHandler( + ProcessSendSignalProcedure, + svc.SendSignal, + connect.WithSchema(processMethods.ByName("SendSignal")), + connect.WithHandlerOptions(opts...), + ) + processCloseStdinHandler := connect.NewUnaryHandler( + ProcessCloseStdinProcedure, + svc.CloseStdin, + connect.WithSchema(processMethods.ByName("CloseStdin")), + connect.WithHandlerOptions(opts...), + ) + return "/process.Process/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case ProcessListProcedure: + processListHandler.ServeHTTP(w, r) + case ProcessConnectProcedure: + processConnectHandler.ServeHTTP(w, r) + case ProcessStartProcedure: + processStartHandler.ServeHTTP(w, r) + case ProcessUpdateProcedure: + processUpdateHandler.ServeHTTP(w, r) + case ProcessStreamInputProcedure: + processStreamInputHandler.ServeHTTP(w, r) + case ProcessSendInputProcedure: + processSendInputHandler.ServeHTTP(w, r) + case ProcessSendSignalProcedure: + processSendSignalHandler.ServeHTTP(w, r) + case ProcessCloseStdinProcedure: + processCloseStdinHandler.ServeHTTP(w, r) + default: + http.NotFound(w, r) + } + }) +} + +// UnimplementedProcessHandler returns CodeUnimplemented from all methods. +type UnimplementedProcessHandler struct{} + +func (UnimplementedProcessHandler) List(context.Context, *connect.Request[process.ListRequest]) (*connect.Response[process.ListResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("process.Process.List is not implemented")) +} + +func (UnimplementedProcessHandler) Connect(context.Context, *connect.Request[process.ConnectRequest], *connect.ServerStream[process.ConnectResponse]) error { + return connect.NewError(connect.CodeUnimplemented, errors.New("process.Process.Connect is not implemented")) +} + +func (UnimplementedProcessHandler) Start(context.Context, *connect.Request[process.StartRequest], *connect.ServerStream[process.StartResponse]) error { + return connect.NewError(connect.CodeUnimplemented, errors.New("process.Process.Start is not implemented")) +} + +func (UnimplementedProcessHandler) Update(context.Context, *connect.Request[process.UpdateRequest]) (*connect.Response[process.UpdateResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("process.Process.Update is not implemented")) +} + +func (UnimplementedProcessHandler) StreamInput(context.Context, *connect.ClientStream[process.StreamInputRequest]) (*connect.Response[process.StreamInputResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("process.Process.StreamInput is not implemented")) +} + +func (UnimplementedProcessHandler) SendInput(context.Context, *connect.Request[process.SendInputRequest]) (*connect.Response[process.SendInputResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("process.Process.SendInput is not implemented")) +} + +func (UnimplementedProcessHandler) SendSignal(context.Context, *connect.Request[process.SendSignalRequest]) (*connect.Response[process.SendSignalResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("process.Process.SendSignal is not implemented")) +} + +func (UnimplementedProcessHandler) CloseStdin(context.Context, *connect.Request[process.CloseStdinRequest]) (*connect.Response[process.CloseStdinResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("process.Process.CloseStdin is not implemented")) +} diff --git a/packages/go-sdk/internal/gen/envdapi/client.gen.go b/packages/go-sdk/internal/gen/envdapi/client.gen.go new file mode 100644 index 000000000..06c1d6a3c --- /dev/null +++ b/packages/go-sdk/internal/gen/envdapi/client.gen.go @@ -0,0 +1,1981 @@ +// Package envdapi provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.7.2 DO NOT EDIT. +package envdapi + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "strings" + "time" + + "github.com/oapi-codegen/runtime" + openapi_types "github.com/oapi-codegen/runtime/types" +) + +const ( + AccessTokenAuthScopes accessTokenAuthContextKey = "AccessTokenAuth.Scopes" +) + +// Defines values for EntryInfoType. +const ( + File EntryInfoType = "file" +) + +// Valid indicates whether the value is a known member of the EntryInfoType enum. +func (e EntryInfoType) Valid() bool { + switch e { + case File: + return true + default: + return false + } +} + +// CollapseResult Per-call statistics from a heap collapse +type CollapseResult struct { + // AlreadyHuge Chunks MADV_COLLAPSE accepted but were already hugepages (no work) + AlreadyHuge *int `json:"alreadyHuge,omitempty"` + + // Chunks 2 MiB chunks attempted + Chunks *int `json:"chunks,omitempty"` + + // Collapsed Chunks whose base pages were actually migrated into a new hugepage (real work) + Collapsed *int `json:"collapsed,omitempty"` + + // ElapsedMs Wall-clock time spent collapsing, in milliseconds + ElapsedMs *int64 `json:"elapsedMs,omitempty"` + + // Regions Anonymous read-write regions scanned + Regions *int `json:"regions,omitempty"` + + // Skipped Chunks that could not be collapsed (empty or ineligible) + Skipped *int `json:"skipped,omitempty"` +} + +// ComposeRequest defines model for ComposeRequest. +type ComposeRequest struct { + // Destination Destination file path for the composed file + Destination string `json:"destination"` + + // SourcePaths Ordered list of source file paths to concatenate + SourcePaths []string `json:"source_paths"` + + // Username User for setting ownership and resolving relative paths + Username *string `json:"username,omitempty"` +} + +// EntryInfo defines model for EntryInfo. +type EntryInfo struct { + // Metadata User-defined metadata stored as extended attributes on the file. + Metadata *map[string]string `json:"metadata,omitempty"` + + // Name Name of the file + Name string `json:"name"` + + // Path Path to the file + Path string `json:"path"` + + // Type Type of the file + Type EntryInfoType `json:"type"` +} + +// EntryInfoType Type of the file +type EntryInfoType string + +// EnvVars Environment variables to set +type EnvVars map[string]string + +// Error defines model for Error. +type Error struct { + // Code Error code + Code int `json:"code"` + + // Message Error message + Message string `json:"message"` +} + +// Metrics Resource usage metrics +type Metrics struct { + // CPUCount Number of CPU cores + CPUCount *int `json:"cpu_count,omitempty"` + + // CPUUsedPct CPU usage percentage + CPUUsedPct *float32 `json:"cpu_used_pct,omitempty"` + + // DiskTotal Total disk space in bytes + DiskTotal *int `json:"disk_total,omitempty"` + + // DiskUsed Used disk space in bytes + DiskUsed *int `json:"disk_used,omitempty"` + + // MemCache Cached memory (page cache) in bytes + MemCache *int `json:"mem_cache,omitempty"` + + // MemTotal Total virtual memory in bytes + MemTotal *int `json:"mem_total,omitempty"` + + // MemTotalMib Total virtual memory in MiB + MemTotalMib *int `json:"mem_total_mib,omitempty"` + + // MemUsed Used virtual memory in bytes + MemUsed *int `json:"mem_used,omitempty"` + + // MemUsedMib Used virtual memory in MiB + MemUsedMib *int `json:"mem_used_mib,omitempty"` + + // TS Unix timestamp in UTC for current sandbox time + TS *int64 `json:"ts,omitempty"` +} + +// VolumeMount Volume mount configuration +type VolumeMount struct { + // NfsTarget Server target address + NfsTarget string `json:"nfs_target"` + + // Path Mount path inside the sandbox + Path string `json:"path"` +} + +// FilePath defines model for FilePath. +type FilePath = string + +// Signature defines model for Signature. +type Signature = string + +// SignatureExpiration defines model for SignatureExpiration. +type SignatureExpiration = int + +// User defines model for User. +type User = string + +// FileNotFound defines model for FileNotFound. +type FileNotFound = Error + +// InternalServerError defines model for InternalServerError. +type InternalServerError = Error + +// InvalidPath defines model for InvalidPath. +type InvalidPath = Error + +// InvalidUser defines model for InvalidUser. +type InvalidUser = Error + +// NotAcceptable defines model for NotAcceptable. +type NotAcceptable = Error + +// NotEnoughDiskSpace defines model for NotEnoughDiskSpace. +type NotEnoughDiskSpace = Error + +// UploadSuccess defines model for UploadSuccess. +type UploadSuccess = []EntryInfo + +// accessTokenAuthContextKey is the context key for AccessTokenAuth security scheme +type accessTokenAuthContextKey string + +// GetFilesParams defines parameters for GetFiles. +type GetFilesParams struct { + // Path Path to the file, URL encoded. Can be relative to the user's home directory (e.g. "file.txt" resolves to ~/file.txt). + Path *FilePath `form:"path,omitempty" json:"path,omitempty"` + + // Username User for setting file ownership and resolving relative paths. Defaults to the sandbox's default user. + Username *User `form:"username,omitempty" json:"username,omitempty"` + + // Signature Signature used for file access permission verification. + Signature *Signature `form:"signature,omitempty" json:"signature,omitempty"` + + // SignatureExpiration Unix timestamp (seconds) after which the signature expires. Only used with the signature parameter. + SignatureExpiration *SignatureExpiration `form:"signature_expiration,omitempty" json:"signature_expiration,omitempty"` +} + +// PostFilesMultipartBody defines parameters for PostFiles. +type PostFilesMultipartBody struct { + File *openapi_types.File `json:"file,omitempty"` +} + +// PostFilesParams defines parameters for PostFiles. +type PostFilesParams struct { + // Path Path to the file, URL encoded. Can be relative to the user's home directory (e.g. "file.txt" resolves to ~/file.txt). + Path *FilePath `form:"path,omitempty" json:"path,omitempty"` + + // Username User for setting file ownership and resolving relative paths. Defaults to the sandbox's default user. + Username *User `form:"username,omitempty" json:"username,omitempty"` + + // Signature Signature used for file access permission verification. + Signature *Signature `form:"signature,omitempty" json:"signature,omitempty"` + + // SignatureExpiration Unix timestamp (seconds) after which the signature expires. Only used with the signature parameter. + SignatureExpiration *SignatureExpiration `form:"signature_expiration,omitempty" json:"signature_expiration,omitempty"` +} + +// PostInitJSONBody defines parameters for PostInit. +type PostInitJSONBody struct { + // AccessToken Access token for secure access to envd service + AccessToken *string `json:"accessToken,omitempty"` + + // CaBundle PEM-encoded CA certificates to install into the system trust store (may contain multiple concatenated PEM blocks) + CaBundle *string `json:"caBundle,omitempty"` + + // DefaultUser The default user to use for operations + DefaultUser *string `json:"defaultUser,omitempty"` + + // DefaultWorkdir The default working directory to use for operations + DefaultWorkdir *string `json:"defaultWorkdir,omitempty"` + + // EnvVars Environment variables to set + EnvVars *EnvVars `json:"envVars,omitempty"` + + // HyperloopIP IP address of the hyperloop server to connect to + HyperloopIP *string `json:"hyperloopIP,omitempty"` + + // LifecycleID Lifecycle ID of the sandbox + LifecycleID *string `json:"lifecycleID,omitempty"` + + // Timestamp The current timestamp in RFC3339 format + Timestamp *time.Time `json:"timestamp,omitempty"` + VolumeMounts *[]VolumeMount `json:"volumeMounts,omitempty"` +} + +// PostFilesMultipartRequestBody defines body for PostFiles for multipart/form-data ContentType. +type PostFilesMultipartRequestBody PostFilesMultipartBody + +// PostFilesComposeJSONRequestBody defines body for PostFilesCompose for application/json ContentType. +type PostFilesComposeJSONRequestBody = ComposeRequest + +// PostInitJSONRequestBody defines body for PostInit for application/json ContentType. +type PostInitJSONRequestBody PostInitJSONBody + +// RequestEditorFn is the function signature for the RequestEditor callback function +type RequestEditorFn func(ctx context.Context, req *http.Request) error + +// Doer performs HTTP requests. +// +// The standard http.Client implements this interface. +type HttpRequestDoer interface { + Do(req *http.Request) (*http.Response, error) +} + +// Client which conforms to the OpenAPI3 specification for this service. +type Client struct { + // The endpoint of the server conforming to this interface, with scheme, + // https://api.deepmap.com for example. This can contain a path relative + // to the server, such as https://api.deepmap.com/dev-test, and all the + // paths in the swagger spec will be appended to the server. + Server string + + // Doer for performing requests, typically a *http.Client with any + // customized settings, such as certificate chains. + Client HttpRequestDoer + + // A list of callbacks for modifying requests which are generated before sending over + // the network. + RequestEditors []RequestEditorFn +} + +// ClientOption allows setting custom parameters during construction +type ClientOption func(*Client) error + +// Creates a new Client, with reasonable defaults +func NewClient(server string, opts ...ClientOption) (*Client, error) { + // create a client with sane default values + client := Client{ + Server: server, + } + // mutate client and add all optional params + for _, o := range opts { + if err := o(&client); err != nil { + return nil, err + } + } + // ensure the server URL always has a trailing slash + if !strings.HasSuffix(client.Server, "/") { + client.Server += "/" + } + // create httpClient, if not already present + if client.Client == nil { + client.Client = &http.Client{} + } + return &client, nil +} + +// WithHTTPClient allows overriding the default Doer, which is +// automatically created using http.Client. This is useful for tests. +func WithHTTPClient(doer HttpRequestDoer) ClientOption { + return func(c *Client) error { + c.Client = doer + return nil + } +} + +// WithRequestEditorFn allows setting up a callback function, which will be +// called right before sending the request. This can be used to mutate the request. +func WithRequestEditorFn(fn RequestEditorFn) ClientOption { + return func(c *Client) error { + c.RequestEditors = append(c.RequestEditors, fn) + return nil + } +} + +// The interface specification for the client above. +type ClientInterface interface { + // PostCollapse request + PostCollapse(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetEnvs request + GetEnvs(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetFiles request + GetFiles(ctx context.Context, params *GetFilesParams, reqEditors ...RequestEditorFn) (*http.Response, error) + + // PostFilesWithBody request with any body + PostFilesWithBody(ctx context.Context, params *PostFilesParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + // PostFilesComposeWithBody request with any body + PostFilesComposeWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + PostFilesCompose(ctx context.Context, body PostFilesComposeJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // PostFreeze request + PostFreeze(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // PostFsfreeze request + PostFsfreeze(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // PostFsthaw request + PostFsthaw(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetHealth request + GetHealth(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // PostInitWithBody request with any body + PostInitWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + PostInit(ctx context.Context, body PostInitJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetMetrics request + GetMetrics(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // PostUnfreeze request + PostUnfreeze(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) +} + +func (c *Client) PostCollapse(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostCollapseRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) GetEnvs(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetEnvsRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) GetFiles(ctx context.Context, params *GetFilesParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetFilesRequest(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) PostFilesWithBody(ctx context.Context, params *PostFilesParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostFilesRequestWithBody(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) PostFilesComposeWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostFilesComposeRequestWithBody(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) PostFilesCompose(ctx context.Context, body PostFilesComposeJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostFilesComposeRequest(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) PostFreeze(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostFreezeRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) PostFsfreeze(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostFsfreezeRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) PostFsthaw(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostFsthawRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) GetHealth(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetHealthRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) PostInitWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostInitRequestWithBody(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) PostInit(ctx context.Context, body PostInitJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostInitRequest(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) GetMetrics(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetMetricsRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) PostUnfreeze(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewPostUnfreezeRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +// NewPostCollapseRequest generates requests for PostCollapse +func NewPostCollapseRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/collapse") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetEnvsRequest generates requests for GetEnvs +func NewGetEnvsRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/envs") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetFilesRequest generates requests for GetFiles +func NewGetFilesRequest(server string, params *GetFilesParams) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/files") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.Path != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "path", *params.Path, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Username != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "username", *params.Username, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Signature != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "signature", *params.Signature, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.SignatureExpiration != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "signature_expiration", *params.SignatureExpiration, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewPostFilesRequestWithBody generates requests for PostFiles with any type of body +func NewPostFilesRequestWithBody(server string, params *PostFilesParams, 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("/files") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + // queryValues collects non-styled parameters (passthrough, JSON) + // that are safe to round-trip through url.Values.Encode(). + queryValues := queryURL.Query() + // rawQueryFragments collects pre-encoded query fragments from + // styled parameters, preserving literal commas as delimiters + // per the OpenAPI spec (e.g. "color=blue,black,brown"). + var rawQueryFragments []string + + if params.Path != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "path", *params.Path, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Username != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "username", *params.Username, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.Signature != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "signature", *params.Signature, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "string", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if params.SignatureExpiration != nil { + + if queryFrag, err := runtime.StyleParamWithOptions("form", true, "signature_expiration", *params.SignatureExpiration, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationQuery, Type: "integer", Format: ""}); err != nil { + return nil, err + } else { + for _, qp := range strings.Split(queryFrag, "&") { + rawQueryFragments = append(rawQueryFragments, qp) + } + } + + } + + if encoded := queryValues.Encode(); encoded != "" { + rawQueryFragments = append(rawQueryFragments, encoded) + } + queryURL.RawQuery = strings.Join(rawQueryFragments, "&") + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewPostFilesComposeRequest calls the generic PostFilesCompose builder with application/json body +func NewPostFilesComposeRequest(server string, body PostFilesComposeJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewPostFilesComposeRequestWithBody(server, "application/json", bodyReader) +} + +// NewPostFilesComposeRequestWithBody generates requests for PostFilesCompose with any type of body +func NewPostFilesComposeRequestWithBody(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("/files/compose") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewPostFreezeRequest generates requests for PostFreeze +func NewPostFreezeRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/freeze") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewPostFsfreezeRequest generates requests for PostFsfreeze +func NewPostFsfreezeRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/fsfreeze") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewPostFsthawRequest generates requests for PostFsthaw +func NewPostFsthawRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/fsthaw") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetHealthRequest generates requests for GetHealth +func NewGetHealthRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/health") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewPostInitRequest calls the generic PostInit builder with application/json body +func NewPostInitRequest(server string, body PostInitJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewPostInitRequestWithBody(server, "application/json", bodyReader) +} + +// NewPostInitRequestWithBody generates requests for PostInit with any type of body +func NewPostInitRequestWithBody(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("/init") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + +// NewGetMetricsRequest generates requests for GetMetrics +func NewGetMetricsRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/metrics") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewPostUnfreezeRequest generates requests for PostUnfreeze +func NewPostUnfreezeRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/unfreeze") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPost, queryURL.String(), nil) + if err != nil { + return nil, err + } + + 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 +} + +// ClientWithResponses builds on ClientInterface to offer response payloads +type ClientWithResponses struct { + ClientInterface +} + +// 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 +} + +// 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 + } +} + +// ClientWithResponsesInterface is the interface specification for the client with responses above. +type ClientWithResponsesInterface interface { + // PostCollapseWithResponse request + PostCollapseWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*PostCollapseResponse, error) + + // GetEnvsWithResponse request + GetEnvsWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetEnvsResponse, error) + + // GetFilesWithResponse request + GetFilesWithResponse(ctx context.Context, params *GetFilesParams, reqEditors ...RequestEditorFn) (*GetFilesResponse, error) + + // PostFilesWithBodyWithResponse request with any body + PostFilesWithBodyWithResponse(ctx context.Context, params *PostFilesParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostFilesResponse, error) + + // PostFilesComposeWithBodyWithResponse request with any body + PostFilesComposeWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostFilesComposeResponse, error) + + PostFilesComposeWithResponse(ctx context.Context, body PostFilesComposeJSONRequestBody, reqEditors ...RequestEditorFn) (*PostFilesComposeResponse, error) + + // PostFreezeWithResponse request + PostFreezeWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*PostFreezeResponse, error) + + // PostFsfreezeWithResponse request + PostFsfreezeWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*PostFsfreezeResponse, error) + + // PostFsthawWithResponse request + PostFsthawWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*PostFsthawResponse, error) + + // GetHealthWithResponse request + GetHealthWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetHealthResponse, error) + + // PostInitWithBodyWithResponse request with any body + PostInitWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostInitResponse, error) + + PostInitWithResponse(ctx context.Context, body PostInitJSONRequestBody, reqEditors ...RequestEditorFn) (*PostInitResponse, error) + + // GetMetricsWithResponse request + GetMetricsWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetMetricsResponse, error) + + // PostUnfreezeWithResponse request + PostUnfreezeWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*PostUnfreezeResponse, error) +} + +type PostCollapseResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *CollapseResult + JSON500 *InternalServerError +} + +// Status returns HTTPResponse.Status +func (r PostCollapseResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PostCollapseResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r PostCollapseResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetEnvsResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *EnvVars +} + +// Status returns HTTPResponse.Status +func (r GetEnvsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetEnvsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetEnvsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetFilesResponse struct { + Body []byte + HTTPResponse *http.Response + JSON400 *InvalidPath + JSON401 *InvalidUser + JSON404 *FileNotFound + JSON406 *NotAcceptable + JSON500 *InternalServerError +} + +// Status returns HTTPResponse.Status +func (r GetFilesResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetFilesResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetFilesResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type PostFilesResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *UploadSuccess + JSON400 *InvalidPath + JSON401 *InvalidUser + JSON500 *InternalServerError + JSON507 *NotEnoughDiskSpace +} + +// Status returns HTTPResponse.Status +func (r PostFilesResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PostFilesResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r PostFilesResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type PostFilesComposeResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *EntryInfo + JSON400 *InvalidPath + JSON401 *InvalidUser + JSON404 *FileNotFound + JSON500 *InternalServerError + JSON507 *NotEnoughDiskSpace +} + +// Status returns HTTPResponse.Status +func (r PostFilesComposeResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PostFilesComposeResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r PostFilesComposeResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type PostFreezeResponse struct { + Body []byte + HTTPResponse *http.Response + JSON500 *InternalServerError +} + +// Status returns HTTPResponse.Status +func (r PostFreezeResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PostFreezeResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r PostFreezeResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type PostFsfreezeResponse struct { + Body []byte + HTTPResponse *http.Response + JSON500 *InternalServerError +} + +// Status returns HTTPResponse.Status +func (r PostFsfreezeResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PostFsfreezeResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r PostFsfreezeResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type PostFsthawResponse struct { + Body []byte + HTTPResponse *http.Response + JSON500 *InternalServerError +} + +// Status returns HTTPResponse.Status +func (r PostFsthawResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PostFsthawResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r PostFsthawResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetHealthResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// Status returns HTTPResponse.Status +func (r GetHealthResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetHealthResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetHealthResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type PostInitResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// Status returns HTTPResponse.Status +func (r PostInitResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PostInitResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r PostInitResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetMetricsResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *Metrics +} + +// Status returns HTTPResponse.Status +func (r GetMetricsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetMetricsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetMetricsResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type PostUnfreezeResponse struct { + Body []byte + HTTPResponse *http.Response + JSON500 *InternalServerError +} + +// Status returns HTTPResponse.Status +func (r PostUnfreezeResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r PostUnfreezeResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r PostUnfreezeResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +// PostCollapseWithResponse request returning *PostCollapseResponse +func (c *ClientWithResponses) PostCollapseWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*PostCollapseResponse, error) { + rsp, err := c.PostCollapse(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostCollapseResponse(rsp) +} + +// GetEnvsWithResponse request returning *GetEnvsResponse +func (c *ClientWithResponses) GetEnvsWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetEnvsResponse, error) { + rsp, err := c.GetEnvs(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetEnvsResponse(rsp) +} + +// GetFilesWithResponse request returning *GetFilesResponse +func (c *ClientWithResponses) GetFilesWithResponse(ctx context.Context, params *GetFilesParams, reqEditors ...RequestEditorFn) (*GetFilesResponse, error) { + rsp, err := c.GetFiles(ctx, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetFilesResponse(rsp) +} + +// PostFilesWithBodyWithResponse request with arbitrary body returning *PostFilesResponse +func (c *ClientWithResponses) PostFilesWithBodyWithResponse(ctx context.Context, params *PostFilesParams, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostFilesResponse, error) { + rsp, err := c.PostFilesWithBody(ctx, params, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostFilesResponse(rsp) +} + +// PostFilesComposeWithBodyWithResponse request with arbitrary body returning *PostFilesComposeResponse +func (c *ClientWithResponses) PostFilesComposeWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostFilesComposeResponse, error) { + rsp, err := c.PostFilesComposeWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostFilesComposeResponse(rsp) +} + +func (c *ClientWithResponses) PostFilesComposeWithResponse(ctx context.Context, body PostFilesComposeJSONRequestBody, reqEditors ...RequestEditorFn) (*PostFilesComposeResponse, error) { + rsp, err := c.PostFilesCompose(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostFilesComposeResponse(rsp) +} + +// PostFreezeWithResponse request returning *PostFreezeResponse +func (c *ClientWithResponses) PostFreezeWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*PostFreezeResponse, error) { + rsp, err := c.PostFreeze(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostFreezeResponse(rsp) +} + +// PostFsfreezeWithResponse request returning *PostFsfreezeResponse +func (c *ClientWithResponses) PostFsfreezeWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*PostFsfreezeResponse, error) { + rsp, err := c.PostFsfreeze(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostFsfreezeResponse(rsp) +} + +// PostFsthawWithResponse request returning *PostFsthawResponse +func (c *ClientWithResponses) PostFsthawWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*PostFsthawResponse, error) { + rsp, err := c.PostFsthaw(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostFsthawResponse(rsp) +} + +// GetHealthWithResponse request returning *GetHealthResponse +func (c *ClientWithResponses) GetHealthWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetHealthResponse, error) { + rsp, err := c.GetHealth(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetHealthResponse(rsp) +} + +// PostInitWithBodyWithResponse request with arbitrary body returning *PostInitResponse +func (c *ClientWithResponses) PostInitWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostInitResponse, error) { + rsp, err := c.PostInitWithBody(ctx, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostInitResponse(rsp) +} + +func (c *ClientWithResponses) PostInitWithResponse(ctx context.Context, body PostInitJSONRequestBody, reqEditors ...RequestEditorFn) (*PostInitResponse, error) { + rsp, err := c.PostInit(ctx, body, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostInitResponse(rsp) +} + +// GetMetricsWithResponse request returning *GetMetricsResponse +func (c *ClientWithResponses) GetMetricsWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*GetMetricsResponse, error) { + rsp, err := c.GetMetrics(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetMetricsResponse(rsp) +} + +// PostUnfreezeWithResponse request returning *PostUnfreezeResponse +func (c *ClientWithResponses) PostUnfreezeWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*PostUnfreezeResponse, error) { + rsp, err := c.PostUnfreeze(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParsePostUnfreezeResponse(rsp) +} + +// ParsePostCollapseResponse parses an HTTP response from a PostCollapseWithResponse call +func ParsePostCollapseResponse(rsp *http.Response) (*PostCollapseResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PostCollapseResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest CollapseResult + 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 InternalServerError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseGetEnvsResponse parses an HTTP response from a GetEnvsWithResponse call +func ParseGetEnvsResponse(rsp *http.Response) (*GetEnvsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetEnvsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest EnvVars + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + } + + return response, nil +} + +// ParseGetFilesResponse parses an HTTP response from a GetFilesWithResponse call +func ParseGetFilesResponse(rsp *http.Response) (*GetFilesResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetFilesResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest InvalidPath + 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 == 401: + var dest InvalidUser + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest FileNotFound + 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 == 406: + var dest NotAcceptable + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON406 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest InternalServerError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParsePostFilesResponse parses an HTTP response from a PostFilesWithResponse call +func ParsePostFilesResponse(rsp *http.Response) (*PostFilesResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PostFilesResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest UploadSuccess + 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 InvalidPath + 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 == 401: + var dest InvalidUser + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest InternalServerError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 507: + var dest NotEnoughDiskSpace + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON507 = &dest + + } + + return response, nil +} + +// ParsePostFilesComposeResponse parses an HTTP response from a PostFilesComposeWithResponse call +func ParsePostFilesComposeResponse(rsp *http.Response) (*PostFilesComposeResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PostFilesComposeResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest EntryInfo + 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 InvalidPath + 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 == 401: + var dest InvalidUser + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON401 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 404: + var dest FileNotFound + 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 InternalServerError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 507: + var dest NotEnoughDiskSpace + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON507 = &dest + + } + + return response, nil +} + +// ParsePostFreezeResponse parses an HTTP response from a PostFreezeWithResponse call +func ParsePostFreezeResponse(rsp *http.Response) (*PostFreezeResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PostFreezeResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest InternalServerError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParsePostFsfreezeResponse parses an HTTP response from a PostFsfreezeWithResponse call +func ParsePostFsfreezeResponse(rsp *http.Response) (*PostFsfreezeResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PostFsfreezeResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest InternalServerError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParsePostFsthawResponse parses an HTTP response from a PostFsthawWithResponse call +func ParsePostFsthawResponse(rsp *http.Response) (*PostFsthawResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PostFsthawResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest InternalServerError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} + +// ParseGetHealthResponse parses an HTTP response from a GetHealthWithResponse call +func ParseGetHealthResponse(rsp *http.Response) (*GetHealthResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetHealthResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParsePostInitResponse parses an HTTP response from a PostInitWithResponse call +func ParsePostInitResponse(rsp *http.Response) (*PostInitResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PostInitResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseGetMetricsResponse parses an HTTP response from a GetMetricsWithResponse call +func ParseGetMetricsResponse(rsp *http.Response) (*GetMetricsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetMetricsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest Metrics + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + } + + return response, nil +} + +// ParsePostUnfreezeResponse parses an HTTP response from a PostUnfreezeWithResponse call +func ParsePostUnfreezeResponse(rsp *http.Response) (*PostUnfreezeResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &PostUnfreezeResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest InternalServerError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + } + + return response, nil +} diff --git a/packages/go-sdk/internal/gen/envdapi/oapi-codegen.yaml b/packages/go-sdk/internal/gen/envdapi/oapi-codegen.yaml new file mode 100644 index 000000000..fa7f3b3d8 --- /dev/null +++ b/packages/go-sdk/internal/gen/envdapi/oapi-codegen.yaml @@ -0,0 +1,7 @@ +package: envdapi +generate: + models: true + client: true +output: packages/go-sdk/internal/gen/envdapi/client.gen.go +output-options: + name-normalizer: ToCamelCaseWithInitialisms diff --git a/packages/go-sdk/models.go b/packages/go-sdk/models.go new file mode 100644 index 000000000..6f8575354 --- /dev/null +++ b/packages/go-sdk/models.go @@ -0,0 +1,223 @@ +package agentbox + +import ( + "encoding/json" + "fmt" + "time" +) + +// SandboxState is the lifecycle state of a sandbox. +type SandboxState string + +const ( + SandboxRunning SandboxState = "running" + SandboxPaused SandboxState = "paused" +) + +// SandboxMetadata contains user-defined sandbox metadata. +type SandboxMetadata map[string]string + +// SandboxNetworkConfig configures sandbox egress and public traffic. +type SandboxNetworkConfig struct { + AllowOut *[]string `json:"allowOut,omitzero"` + AllowPublicTraffic *bool `json:"allowPublicTraffic,omitzero"` + DenyOut *[]string `json:"denyOut,omitzero"` + MaskRequestHost *string `json:"maskRequestHost,omitzero"` + Rules *map[string][]SandboxNetworkRule `json:"rules,omitzero"` +} + +// SandboxNetworkRule applies request transformations to matching traffic. +type SandboxNetworkRule struct { + Transform *SandboxNetworkTransform `json:"transform,omitzero"` +} + +// SandboxNetworkTransform describes headers injected into matching requests. +type SandboxNetworkTransform struct { + Headers *map[string]string `json:"headers,omitzero"` +} + +// SandboxIAM configures workload identity tokens. +type SandboxIAM struct { + Tokens *SandboxIAMTokens `json:"tokens,omitzero"` +} + +// SandboxIAMTokens contains named workload identity token definitions. +type SandboxIAMTokens map[string]SandboxIAMToken + +// SandboxIAMToken configures one workload identity token. +type SandboxIAMToken struct { + Audience string `json:"audience"` + TokenType string `json:"tokenType"` +} + +// SandboxLifecycle describes timeout and auto-resume behavior. +type SandboxLifecycle struct { + AutoResume bool `json:"autoResume"` + OnTimeout string `json:"onTimeout"` +} + +// SandboxInfo contains current sandbox state and configuration. +type SandboxInfo struct { + Alias *string `json:"alias,omitzero"` + AllowInternetAccess *bool `json:"allowInternetAccess,omitzero"` + CPUCount int32 `json:"cpuCount"` + DiskSizeMB int32 `json:"diskSizeMB"` + Domain *string `json:"domain,omitzero"` + EndAt time.Time `json:"endAt"` + EnvdVersion string `json:"envdVersion"` + Lifecycle *SandboxLifecycle `json:"lifecycle,omitzero"` + MemoryMB int32 `json:"memoryMB"` + Metadata *SandboxMetadata `json:"metadata,omitzero"` + Network *SandboxNetworkConfig `json:"network,omitzero"` + SandboxID string `json:"sandboxID"` + StartedAt time.Time `json:"startedAt"` + State SandboxState `json:"state"` + TemplateID string `json:"templateID"` +} + +// ListedSandbox is a compact sandbox list entry. +type ListedSandbox struct { + Alias *string `json:"alias,omitzero"` + CPUCount int32 `json:"cpuCount"` + DiskSizeMB int32 `json:"diskSizeMB"` + EndAt time.Time `json:"endAt"` + EnvdVersion string `json:"envdVersion"` + MemoryMB int32 `json:"memoryMB"` + Metadata *SandboxMetadata `json:"metadata,omitzero"` + SandboxID string `json:"sandboxID"` + StartedAt time.Time `json:"startedAt"` + State SandboxState `json:"state"` + TemplateID string `json:"templateID"` +} + +// SandboxMetric is one timestamped resource-usage sample. +type SandboxMetric struct { + CPUCount int32 `json:"cpuCount"` + CPUUsedPct float32 `json:"cpuUsedPct"` + DiskTotal int64 `json:"diskTotal"` + DiskUsed int64 `json:"diskUsed"` + MemCache int64 `json:"memCache"` + MemTotal int64 `json:"memTotal"` + MemUsed int64 `json:"memUsed"` + TimestampUnix int64 `json:"timestampUnix"` +} + +// SnapshotInfo identifies a saved sandbox snapshot. +type SnapshotInfo struct { + Names []string `json:"names"` + SnapshotID string `json:"snapshotID"` +} + +// SandboxLogEntry is one structured sandbox log record. +type SandboxLogEntry struct { + Fields map[string]string `json:"fields"` + ID *string `json:"id,omitzero"` + Level string `json:"level"` + Message string `json:"message"` + Timestamp time.Time `json:"timestamp"` +} + +// TemplateBuildStatus is a template build state. +type TemplateBuildStatus string + +const ( + BuildWaiting TemplateBuildStatus = "waiting" + BuildBuilding TemplateBuildStatus = "building" + BuildReady TemplateBuildStatus = "ready" + BuildFailed TemplateBuildStatus = "error" +) + +// BuildLogEntry is one structured template build log record. +type BuildLogEntry struct { + ID *string `json:"id,omitzero"` + Level string `json:"level"` + Message string `json:"message"` + Step *string `json:"step,omitzero"` + Timestamp time.Time `json:"timestamp"` +} + +// BuildStatusReason explains a terminal template build status. +type BuildStatusReason struct { + LogEntries *[]BuildLogEntry `json:"logEntries,omitzero"` + Message string `json:"message"` + Step *string `json:"step,omitzero"` +} + +// TemplateBuildInfo contains the latest status and logs for a build. +type TemplateBuildInfo struct { + BuildID string `json:"buildID"` + LogEntries []BuildLogEntry `json:"logEntries"` + Logs []string `json:"logs"` + Reason *BuildStatusReason `json:"reason,omitzero"` + Status TemplateBuildStatus `json:"status"` + TemplateID string `json:"templateID"` +} + +// TemplateBuild describes one historical template build. +type TemplateBuild struct { + BuildID string `json:"buildID"` + CPUCount int32 `json:"cpuCount"` + CreatedAt time.Time `json:"createdAt"` + DiskSizeMB *int32 `json:"diskSizeMB,omitzero"` + EnvdVersion *string `json:"envdVersion,omitzero"` + FinishedAt *time.Time `json:"finishedAt,omitzero"` + MemoryMB int32 `json:"memoryMB"` + Status TemplateBuildStatus `json:"status"` + UpdatedAt time.Time `json:"updatedAt"` +} + +// TeamUser identifies the user who created a template. +type TeamUser struct { + ID string `json:"id"` +} + +// TemplateInfo is a compact template list entry. +type TemplateInfo struct { + BuildCount int32 `json:"buildCount"` + BuildID string `json:"buildID"` + BuildStatus TemplateBuildStatus `json:"buildStatus"` + CPUCount int32 `json:"cpuCount"` + CreatedAt time.Time `json:"createdAt"` + CreatedBy *TeamUser `json:"createdBy,omitzero"` + DiskSizeMB int32 `json:"diskSizeMB"` + EnvdVersion string `json:"envdVersion"` + LastSpawnedAt *time.Time `json:"lastSpawnedAt,omitzero"` + MemoryMB int32 `json:"memoryMB"` + Names []string `json:"names"` + Public bool `json:"public"` + SpawnCount int64 `json:"spawnCount"` + TemplateID string `json:"templateID"` + UpdatedAt time.Time `json:"updatedAt"` +} + +// TemplateWithBuilds contains a template and its build history. +type TemplateWithBuilds struct { + Builds []TemplateBuild `json:"builds"` + CreatedAt time.Time `json:"createdAt"` + LastSpawnedAt *time.Time `json:"lastSpawnedAt,omitzero"` + Names []string `json:"names"` + Public bool `json:"public"` + SpawnCount int64 `json:"spawnCount"` + TemplateID string `json:"templateID"` + UpdatedAt time.Time `json:"updatedAt"` +} + +// Page contains one page and an optional opaque continuation token. +type Page[T any] struct { + Items []T + NextToken string +} + +// convertModel deliberately copies only the fields in the public model. This +// keeps generated API changes internal until the public SDK model is updated. +func convertModel[Target any](source any) (Target, error) { + var target Target + data, err := json.Marshal(source) + if err != nil { + return target, fmt.Errorf("agentbox: encode API model: %w", err) + } + if err := json.Unmarshal(data, &target); err != nil { + return target, fmt.Errorf("agentbox: decode API model: %w", err) + } + return target, nil +} diff --git a/packages/go-sdk/pty.go b/packages/go-sdk/pty.go new file mode 100644 index 000000000..bb18137d1 --- /dev/null +++ b/packages/go-sdk/pty.go @@ -0,0 +1,101 @@ +package agentbox + +import ( + "context" + + "connectrpc.com/connect" + process "github.com/abox-dev/sdk/packages/go-sdk/internal/gen/envd/process" +) + +// PTYOptions configures an interactive terminal. +type PTYOptions struct { + Args []string + Env map[string]string + Cwd string + Tag string + Cols uint32 + Rows uint32 + OnPTY func([]byte) +} + +// PTYService manages pseudo-terminal processes. +type PTYService struct{ commands *CommandService } + +// Create starts a process attached to a pseudo-terminal. +func (service *PTYService) Create(ctx context.Context, command string, options *PTYOptions) (*CommandHandle, error) { + if command == "" { + return nil, &InvalidArgumentError{Message: "command cannot be empty"} + } + if options == nil { + options = &PTYOptions{} + } + cols, rows := options.Cols, options.Rows + if cols == 0 { + cols = 80 + } + if rows == 0 { + rows = 24 + } + config := &process.ProcessConfig{Cmd: command, Args: options.Args, Envs: options.Env} + if options.Cwd != "" { + config.Cwd = &options.Cwd + } + request := connect.NewRequest(&process.StartRequest{Process: config, Pty: &process.PTY{Size: &process.PTY_Size{Cols: cols, Rows: rows}}}) + if options.Tag != "" { + request.Msg.Tag = &options.Tag + } + service.commands.addHeaders(request.Header()) + stream, err := service.commands.client.Start(ctx, request) + if err != nil { + return nil, connectError(err) + } + handle := newCommandHandle(service.commands, options.Tag) + go handle.receive(ctx, func() (*process.ProcessEvent, bool) { + if !stream.Receive() { + return nil, false + } + return stream.Msg().GetEvent(), true + }, stream.Err, stream.Close, outputCallbacks{pty: options.OnPTY}) + return handle, nil +} + +// Connect attaches to an existing PTY process. +func (service *PTYService) Connect(ctx context.Context, pid uint32, tag string) (*CommandHandle, error) { + return service.commands.Connect(ctx, pid, tag) +} + +// Input sends terminal input. +func (service *PTYService) Input(ctx context.Context, handle *CommandHandle, data []byte) error { + pid, err := handle.PID(ctx) + if err != nil { + return err + } + request := connect.NewRequest(&process.SendInputRequest{Process: &process.ProcessSelector{Selector: &process.ProcessSelector_Pid{Pid: pid}}, Input: &process.ProcessInput{Input: &process.ProcessInput_Pty{Pty: data}}}) + service.commands.addHeaders(request.Header()) + requestCtx, cancel := service.commands.sandbox.unaryContext(ctx) + defer cancel() + _, err = service.commands.client.SendInput(requestCtx, request) + return connectError(err) +} + +// Resize changes terminal dimensions. +func (service *PTYService) Resize(ctx context.Context, handle *CommandHandle, cols, rows uint32) error { + if cols == 0 || rows == 0 { + return &InvalidArgumentError{Message: "PTY columns and rows must be positive"} + } + pid, err := handle.PID(ctx) + if err != nil { + return err + } + request := connect.NewRequest(&process.UpdateRequest{Process: &process.ProcessSelector{Selector: &process.ProcessSelector_Pid{Pid: pid}}, Pty: &process.PTY{Size: &process.PTY_Size{Cols: cols, Rows: rows}}}) + service.commands.addHeaders(request.Header()) + requestCtx, cancel := service.commands.sandbox.unaryContext(ctx) + defer cancel() + _, err = service.commands.client.Update(requestCtx, request) + return connectError(err) +} + +// Kill stops the terminal process. +func (service *PTYService) Kill(ctx context.Context, handle *CommandHandle) error { + return handle.Kill(ctx) +} diff --git a/packages/go-sdk/sandbox.go b/packages/go-sdk/sandbox.go new file mode 100644 index 000000000..368fd0fb5 --- /dev/null +++ b/packages/go-sdk/sandbox.go @@ -0,0 +1,758 @@ +package agentbox + +import ( + "context" + "crypto/sha256" + "encoding/base64" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "slices" + "strconv" + "strings" + "time" + + api "github.com/abox-dev/sdk/packages/go-sdk/internal/gen/api" +) + +// CreateSandboxOptions configures a new sandbox. +type CreateSandboxOptions struct { + Template string + Timeout time.Duration + AutoPause *bool + AutoPauseMemory *bool + AutoResume *bool + Secure *bool + AllowInternetAccess *bool + Env map[string]string + Metadata map[string]string + Network *SandboxNetworkConfig + IAM *SandboxIAM +} + +// ConnectSandboxOptions configures connecting or resuming a sandbox. +type ConnectSandboxOptions struct{ Timeout time.Duration } + +// ListSandboxOptions filters one sandbox list page. +type ListSandboxOptions struct { + Metadata map[string]string + States []SandboxState + NextToken string + Limit int +} + +// MetricsOptions selects a metrics interval. +type MetricsOptions struct{ Start, End time.Time } + +// SandboxLogOptions filters one page of sandbox logs. +type SandboxLogOptions struct { + Cursor string + Timestamp int64 + Limit int + Direction string + Level string + Search string +} + +// PauseOptions configures snapshot behavior while pausing. +type PauseOptions struct{ Memory *bool } + +// ForkOptions configures sandbox forks. +type ForkOptions struct { + Count int + Timeout time.Duration +} + +// ForkResult is one ordered fork outcome. Exactly one of Sandbox or Err is set. +type ForkResult struct { + Sandbox *Sandbox + Err error +} + +// SnapshotListOptions filters and paginates snapshots. +type SnapshotListOptions struct { + SandboxID string + Name string + NextToken string + Limit int +} + +// SandboxRequestOptions configures a request to a service inside a sandbox. +type SandboxRequestOptions struct { + Direct bool + Headers http.Header + ContentType string +} + +// SandboxService manages sandboxes owned by a client. +type SandboxService struct{ client *Client } + +// Sandbox is a connected AgentBox sandbox. +type Sandbox struct { + client *Client + ID string + TemplateID string + Alias string + Domain string + EnvdVersion string + envdAccessToken string + trafficAccessToken string + + Commands *CommandService + PTY *PTYService + Files *FileService +} + +// RequestTimeout returns the default unary request timeout configured on the client. +func (sandbox *Sandbox) RequestTimeout() time.Duration { return sandbox.client.config.requestTimeout } + +func (sandbox *Sandbox) unaryContext(ctx context.Context) (context.Context, context.CancelFunc) { + return withRequestTimeout(ctx, sandbox.RequestTimeout()) +} + +// String returns a credential-free sandbox description. +func (sandbox Sandbox) String() string { + return fmt.Sprintf("Sandbox{ID:%q, TemplateID:%q, Alias:%q, Domain:%q, EnvdVersion:%q}", sandbox.ID, sandbox.TemplateID, sandbox.Alias, sandbox.Domain, sandbox.EnvdVersion) +} + +// GoString returns a credential-free sandbox description for %#v formatting. +func (sandbox Sandbox) GoString() string { return sandbox.String() } + +// Create starts and connects to a sandbox. +func (service *SandboxService) Create(ctx context.Context, options *CreateSandboxOptions) (*Sandbox, error) { + if options == nil { + options = &CreateSandboxOptions{} + } + if options.IAM != nil && options.IAM.Tokens != nil { + for name := range *options.IAM.Tokens { + if err := ValidateIAMTokenName(name); err != nil { + return nil, err + } + } + } + template := options.Template + if template == "" { + template = "base" + } + timeout, err := durationSeconds(options.Timeout, defaultSandboxTimeout) + if err != nil { + return nil, err + } + var network *api.SandboxNetworkConfig + if options.Network != nil { + converted, err := convertModel[api.SandboxNetworkConfig](*options.Network) + if err != nil { + return nil, err + } + network = &converted + } + var iam *api.SandboxIam + if options.IAM != nil { + converted, err := convertModel[api.SandboxIam](*options.IAM) + if err != nil { + return nil, err + } + iam = &converted + } + body := api.NewSandbox{ + TemplateID: template, Timeout: &timeout, AutoPause: options.AutoPause, + AutoPauseMemory: options.AutoPauseMemory, Secure: options.Secure, + AllowInternetAccess: options.AllowInternetAccess, Network: network, + Iam: iam, + } + if options.AutoResume != nil { + body.AutoResume = &api.SandboxAutoResumeConfig{Enabled: *options.AutoResume} + } + if options.Env != nil { + env := api.EnvVars(options.Env) + body.EnvVars = &env + } + if options.Metadata != nil { + metadata := api.SandboxMetadata(options.Metadata) + body.Metadata = &metadata + } + requestCtx, cancel := withRequestTimeout(ctx, service.client.config.requestTimeout) + defer cancel() + response, err := service.client.api.PostSandboxesWithResponse(requestCtx, body) + if err != nil { + return nil, normalizeRequestError(err) + } + if response.JSON201 == nil { + return nil, decodeStatusError(response.StatusCode(), response.Status(), response.Body) + } + return service.client.sandboxFromAPI(*response.JSON201), nil +} + +// Connect connects to or resumes a sandbox. +func (service *SandboxService) Connect(ctx context.Context, id string, options *ConnectSandboxOptions) (*Sandbox, error) { + if strings.TrimSpace(id) == "" { + return nil, &InvalidArgumentError{Message: "sandbox ID cannot be empty"} + } + timeout := defaultSandboxTimeout + if options != nil && options.Timeout != 0 { + timeout = options.Timeout + } + seconds, err := durationSeconds(timeout, defaultSandboxTimeout) + if err != nil { + return nil, err + } + requestCtx, cancel := withRequestTimeout(ctx, service.client.config.requestTimeout) + defer cancel() + response, err := service.client.api.PostSandboxesSandboxIDConnectWithResponse(requestCtx, id, api.ConnectSandbox{Timeout: seconds}) + if err != nil { + return nil, normalizeRequestError(err) + } + value := response.JSON200 + if value == nil { + value = response.JSON201 + } + if value == nil { + return nil, decodeStatusError(response.StatusCode(), response.Status(), response.Body) + } + return service.client.sandboxFromAPI(*value), nil +} + +// List returns one list page. The continuation token is read from X-Next-Token. +func (service *SandboxService) List(ctx context.Context, options *ListSandboxOptions) (Page[ListedSandbox], error) { + parameters := &api.GetV2SandboxesParams{} + if options != nil { + if len(options.Metadata) > 0 { + values := url.Values{} + for key, value := range options.Metadata { + values.Set(key, value) + } + encoded := values.Encode() + parameters.Metadata = &encoded + } + if len(options.States) > 0 { + states := make([]api.SandboxState, len(options.States)) + for index, state := range options.States { + states[index] = api.SandboxState(state) + } + parameters.State = &states + } + if options.NextToken != "" { + parameters.NextToken = &options.NextToken + } + if options.Limit > 0 { + limit := int32(options.Limit) + parameters.Limit = &limit + } + } + requestCtx, cancel := withRequestTimeout(ctx, service.client.config.requestTimeout) + defer cancel() + response, err := service.client.api.GetV2SandboxesWithResponse(requestCtx, parameters) + if err != nil { + return Page[ListedSandbox]{}, normalizeRequestError(err) + } + if response.JSON200 == nil { + return Page[ListedSandbox]{}, decodeStatusError(response.StatusCode(), response.Status(), response.Body) + } + items, err := convertModel[[]ListedSandbox](*response.JSON200) + return Page[ListedSandbox]{Items: items, NextToken: response.HTTPResponse.Header.Get("X-Next-Token")}, err +} + +// Info returns current sandbox state and configuration. +func (service *SandboxService) Info(ctx context.Context, id string) (*SandboxInfo, error) { + requestCtx, cancel := withRequestTimeout(ctx, service.client.config.requestTimeout) + defer cancel() + response, err := service.client.api.GetSandboxesSandboxIDWithResponse(requestCtx, id) + if err != nil { + return nil, normalizeRequestError(err) + } + if response.JSON200 == nil { + return nil, decodeStatusError(response.StatusCode(), response.Status(), response.Body) + } + result, err := convertModel[SandboxInfo](*response.JSON200) + return &result, err +} + +// Kill permanently stops a sandbox. It returns false when it did not exist. +func (service *SandboxService) Kill(ctx context.Context, id string) (bool, error) { + if service.client.config.debug { + return true, nil + } + requestCtx, cancel := withRequestTimeout(ctx, service.client.config.requestTimeout) + defer cancel() + response, err := service.client.api.DeleteSandboxesSandboxID(requestCtx, id) + if err != nil { + return false, normalizeRequestError(err) + } + defer response.Body.Close() + if response.StatusCode == http.StatusNotFound { + return false, nil + } + if response.StatusCode != http.StatusNoContent { + return false, decodeHTTPError(response) + } + return true, nil +} + +// Snapshots returns one page of snapshots. +func (service *SandboxService) Snapshots(ctx context.Context, options *SnapshotListOptions) (Page[SnapshotInfo], error) { + parameters := &api.GetSnapshotsParams{} + if options != nil { + if options.SandboxID != "" { + parameters.SandboxID = &options.SandboxID + } + if options.Name != "" { + parameters.Name = &options.Name + } + if options.NextToken != "" { + parameters.NextToken = &options.NextToken + } + if options.Limit > 0 { + value := int32(options.Limit) + parameters.Limit = &value + } + } + requestCtx, cancel := withRequestTimeout(ctx, service.client.config.requestTimeout) + defer cancel() + response, err := service.client.api.GetSnapshotsWithResponse(requestCtx, parameters) + if err != nil { + return Page[SnapshotInfo]{}, normalizeRequestError(err) + } + if response.JSON200 == nil { + return Page[SnapshotInfo]{}, decodeStatusError(response.StatusCode(), response.Status(), response.Body) + } + items, err := convertModel[[]SnapshotInfo](*response.JSON200) + return Page[SnapshotInfo]{Items: items, NextToken: response.HTTPResponse.Header.Get("X-Next-Token")}, err +} + +// DeleteSnapshot deletes a snapshot. It returns false when it did not exist. +func (service *SandboxService) DeleteSnapshot(ctx context.Context, snapshotID string) (bool, error) { + if strings.TrimSpace(snapshotID) == "" { + return false, &InvalidArgumentError{Message: "snapshot ID cannot be empty"} + } + requestCtx, cancel := withRequestTimeout(ctx, service.client.config.requestTimeout) + defer cancel() + response, err := service.client.api.DeleteTemplatesTemplateIDWithResponse(requestCtx, snapshotID) + if err != nil { + return false, normalizeRequestError(err) + } + if response.StatusCode() == http.StatusNotFound { + return false, nil + } + if response.StatusCode() < 200 || response.StatusCode() >= 300 { + return false, decodeStatusError(response.StatusCode(), response.Status(), response.Body) + } + return true, nil +} + +// Metrics returns the latest metrics for the requested sandbox IDs. +func (service *SandboxService) Metrics(ctx context.Context, sandboxIDs ...string) (map[string]SandboxMetric, error) { + if len(sandboxIDs) == 0 { + return nil, &InvalidArgumentError{Message: "at least one sandbox ID is required"} + } + requestCtx, cancel := withRequestTimeout(ctx, service.client.config.requestTimeout) + defer cancel() + response, err := service.client.api.GetSandboxesMetricsWithResponse(requestCtx, &api.GetSandboxesMetricsParams{SandboxIds: sandboxIDs}) + if err != nil { + return nil, normalizeRequestError(err) + } + if response.JSON200 == nil { + return nil, decodeStatusError(response.StatusCode(), response.Status(), response.Body) + } + return convertModel[map[string]SandboxMetric](response.JSON200.Sandboxes) +} + +// Logs returns one page of structured sandbox logs. +func (service *SandboxService) Logs(ctx context.Context, id string, options *SandboxLogOptions) (Page[SandboxLogEntry], error) { + params := &api.GetV2SandboxesSandboxIDLogsParams{} + if options != nil { + if options.Cursor != "" { + params.PageCursor = &options.Cursor + } + if options.Timestamp != 0 { + params.Cursor = &options.Timestamp + } + if options.Limit > 0 { + limit := int32(options.Limit) + params.Limit = &limit + } + if options.Direction != "" { + value := api.LogsDirection(options.Direction) + params.Direction = &value + } + if options.Level != "" { + value := api.LogLevel(options.Level) + params.Level = &value + } + if options.Search != "" { + params.Search = &options.Search + } + } + requestCtx, cancel := withRequestTimeout(ctx, service.client.config.requestTimeout) + defer cancel() + response, err := service.client.api.GetV2SandboxesSandboxIDLogsWithResponse(requestCtx, id, params) + if err != nil { + return Page[SandboxLogEntry]{}, normalizeRequestError(err) + } + if response.JSON200 == nil { + return Page[SandboxLogEntry]{}, decodeStatusError(response.StatusCode(), response.Status(), response.Body) + } + items, err := convertModel[[]SandboxLogEntry](response.JSON200.Logs) + if err != nil { + return Page[SandboxLogEntry]{}, err + } + page := Page[SandboxLogEntry]{Items: items} + if response.JSON200.NextCursor != nil { + page.NextToken = *response.JSON200.NextCursor + } + return page, nil +} + +// Info returns information about this sandbox. +func (sandbox *Sandbox) Info(ctx context.Context) (*SandboxInfo, error) { + return sandbox.client.Sandboxes.Info(ctx, sandbox.ID) +} + +// Logs returns one page of sandbox logs. +func (sandbox *Sandbox) Logs(ctx context.Context, options *SandboxLogOptions) (Page[SandboxLogEntry], error) { + return sandbox.client.Sandboxes.Logs(ctx, sandbox.ID, options) +} + +// Kill permanently stops this sandbox. It returns false when it was not found. +func (sandbox *Sandbox) Kill(ctx context.Context) (bool, error) { + return sandbox.client.Sandboxes.Kill(ctx, sandbox.ID) +} + +// IsRunning reports whether envd is reachable. A 502 response means the +// sandbox is no longer running. +func (sandbox *Sandbox) IsRunning(ctx context.Context) (bool, error) { + requestCtx, cancel := sandbox.unaryContext(ctx) + defer cancel() + response, err := sandbox.Request(requestCtx, envdPort, http.MethodGet, "/health", nil, false) + if err != nil { + return false, err + } + defer response.Body.Close() + if response.StatusCode == http.StatusBadGateway { + return false, nil + } + if response.StatusCode < 200 || response.StatusCode >= 300 { + return false, decodeHTTPError(response) + } + return true, nil +} + +// SetTimeout changes the sandbox expiration timeout from now. +func (sandbox *Sandbox) SetTimeout(ctx context.Context, timeout time.Duration) error { + seconds, err := durationSeconds(timeout, 0) + if err != nil { + return err + } + requestCtx, cancel := withRequestTimeout(ctx, sandbox.client.config.requestTimeout) + defer cancel() + response, err := sandbox.client.api.PostSandboxesSandboxIDTimeoutWithResponse(requestCtx, sandbox.ID, api.SandboxTimeoutRequest{Timeout: seconds}) + if err != nil { + return normalizeRequestError(err) + } + if response.StatusCode() < 200 || response.StatusCode() >= 300 { + return decodeStatusError(response.StatusCode(), response.Status(), response.Body) + } + return nil +} + +// KeepAlive extends the sandbox lifetime. A zero duration uses the server default. +func (sandbox *Sandbox) KeepAlive(ctx context.Context, duration time.Duration) error { + body := api.SandboxRefreshRequest{} + if duration != 0 { + seconds, err := durationSecondsInt(duration) + if err != nil { + return err + } + body.Duration = &seconds + } + requestCtx, cancel := withRequestTimeout(ctx, sandbox.client.config.requestTimeout) + defer cancel() + response, err := sandbox.client.api.PostSandboxesSandboxIDRefreshesWithResponse(requestCtx, sandbox.ID, body) + if err != nil { + return normalizeRequestError(err) + } + if response.StatusCode() < 200 || response.StatusCode() >= 300 { + return decodeStatusError(response.StatusCode(), response.Status(), response.Body) + } + return nil +} + +// Pause pauses the sandbox, optionally retaining memory. +func (sandbox *Sandbox) Pause(ctx context.Context, options *PauseOptions) error { + body := api.SandboxPauseRequest{} + if options != nil { + body.Memory = options.Memory + } + requestCtx, cancel := withRequestTimeout(ctx, sandbox.client.config.requestTimeout) + defer cancel() + response, err := sandbox.client.api.PostSandboxesSandboxIDPauseWithResponse(requestCtx, sandbox.ID, body) + if err != nil { + return normalizeRequestError(err) + } + if response.StatusCode() < 200 || response.StatusCode() >= 300 { + return decodeStatusError(response.StatusCode(), response.Status(), response.Body) + } + return nil +} + +// UpdateNetwork atomically replaces sandbox egress rules. +func (sandbox *Sandbox) UpdateNetwork(ctx context.Context, config SandboxNetworkConfig, allowInternetAccess *bool) error { + body, err := convertModel[api.SandboxNetworkUpdateConfig](config) + if err != nil { + return err + } + body.AllowInternetAccess = allowInternetAccess + requestCtx, cancel := withRequestTimeout(ctx, sandbox.client.config.requestTimeout) + defer cancel() + response, err := sandbox.client.api.PutSandboxesSandboxIDNetworkWithResponse(requestCtx, sandbox.ID, body) + if err != nil { + return normalizeRequestError(err) + } + if response.StatusCode() < 200 || response.StatusCode() >= 300 { + return decodeStatusError(response.StatusCode(), response.Status(), response.Body) + } + return nil +} + +// Metrics returns sandbox metrics for the requested interval. +func (sandbox *Sandbox) Metrics(ctx context.Context, options *MetricsOptions) ([]SandboxMetric, error) { + parameters := &api.GetSandboxesSandboxIDMetricsParams{} + if options != nil { + if !options.Start.IsZero() { + v := options.Start.Unix() + parameters.Start = &v + } + if !options.End.IsZero() { + v := options.End.Unix() + parameters.End = &v + } + } + requestCtx, cancel := withRequestTimeout(ctx, sandbox.client.config.requestTimeout) + defer cancel() + response, err := sandbox.client.api.GetSandboxesSandboxIDMetricsWithResponse(requestCtx, sandbox.ID, parameters) + if err != nil { + return nil, normalizeRequestError(err) + } + if response.JSON200 == nil { + return nil, decodeStatusError(response.StatusCode(), response.Status(), response.Body) + } + return convertModel[[]SandboxMetric](*response.JSON200) +} + +// Fork creates one or more sandboxes from this sandbox's current state. +func (sandbox *Sandbox) Fork(ctx context.Context, options *ForkOptions) ([]ForkResult, error) { + body := api.SandboxForkRequest{} + if options != nil { + if options.Count > 0 { + count := int32(options.Count) + body.Count = &count + } + if options.Timeout != 0 { + seconds, err := durationSeconds(options.Timeout, 0) + if err != nil { + return nil, err + } + body.Timeout = &seconds + } + } + requestCtx, cancel := withRequestTimeout(ctx, sandbox.client.config.requestTimeout) + defer cancel() + response, err := sandbox.client.api.PostSandboxesSandboxIDForkWithResponse(requestCtx, sandbox.ID, body) + if err != nil { + return nil, normalizeRequestError(err) + } + if response.JSON201 == nil { + return nil, decodeStatusError(response.StatusCode(), response.Status(), response.Body) + } + result := make([]ForkResult, 0, len(*response.JSON201)) + for _, item := range *response.JSON201 { + if item.Sandbox != nil { + result = append(result, ForkResult{Sandbox: sandbox.client.sandboxFromAPI(*item.Sandbox)}) + continue + } + if item.Error != nil { + result = append(result, ForkResult{Err: decodeStatusError(int(item.Error.Code), strconv.Itoa(int(item.Error.Code)), []byte(item.Error.Message))}) + continue + } + result = append(result, ForkResult{Err: &SandboxError{APIError: APIError{Message: "fork result contained neither sandbox nor error"}}}) + } + return result, nil +} + +// CreateSnapshot stores this sandbox as a template snapshot. +func (sandbox *Sandbox) CreateSnapshot(ctx context.Context, name string) (*SnapshotInfo, error) { + body := api.SandboxSnapshotRequest{} + if name != "" { + body.Name = &name + } + requestCtx, cancel := withRequestTimeout(ctx, sandbox.client.config.requestTimeout) + defer cancel() + response, err := sandbox.client.api.PostSandboxesSandboxIDSnapshotsWithResponse(requestCtx, sandbox.ID, body) + if err != nil { + return nil, normalizeRequestError(err) + } + if response.JSON201 == nil { + return nil, decodeStatusError(response.StatusCode(), response.Status(), response.Body) + } + result, err := convertModel[SnapshotInfo](*response.JSON201) + return &result, err +} + +// Host returns the public hostname for a port exposed by the sandbox. +func (sandbox *Sandbox) Host(port int) string { + return fmt.Sprintf("%d-%s.%s", port, sandbox.ID, sandbox.Domain) +} + +// Request performs an authenticated request against a service inside the sandbox. +// Direct bypasses the stable proxy hostname while retaining AgentBox routing headers. +func (sandbox *Sandbox) Request(ctx context.Context, port int, method, path string, body io.Reader, direct bool) (*http.Response, error) { + return sandbox.RequestWithOptions(ctx, port, method, path, body, &SandboxRequestOptions{Direct: direct}) +} + +// RequestWithOptions performs an authenticated request with custom headers. +func (sandbox *Sandbox) RequestWithOptions(ctx context.Context, port int, method, path string, body io.Reader, options *SandboxRequestOptions) (*http.Response, error) { + if options == nil { + options = &SandboxRequestOptions{} + } + endpoint := strings.TrimRight(sandbox.envdURL(port, options.Direct), "/") + "/" + strings.TrimLeft(path, "/") + request, err := http.NewRequestWithContext(ctx, method, endpoint, body) + if err != nil { + return nil, fmt.Errorf("agentbox: create sandbox request: %w", err) + } + request.Header = sandbox.envdHeaders(port) + for key, values := range options.Headers { + request.Header[key] = slices.Clone(values) + } + if options.ContentType != "" { + request.Header.Set("Content-Type", options.ContentType) + } else if body != nil { + request.Header.Set("Content-Type", "application/json") + } + response, err := sandbox.client.httpClient.Do(request) + if err != nil { + return nil, normalizeRequestError(err) + } + return response, nil +} + +func (client *Client) sandboxFromAPI(value api.Sandbox) *Sandbox { + domain := client.config.domain + if value.Domain != nil && *value.Domain != "" { + domain = *value.Domain + } + sandbox := &Sandbox{client: client, ID: value.SandboxID, TemplateID: value.TemplateID, EnvdVersion: value.EnvdVersion, Domain: domain} + if value.Alias != nil { + sandbox.Alias = *value.Alias + } + if value.EnvdAccessToken != nil { + sandbox.envdAccessToken = *value.EnvdAccessToken + } + if value.TrafficAccessToken != nil { + sandbox.trafficAccessToken = *value.TrafficAccessToken + } + sandbox.Commands = newCommandService(sandbox) + sandbox.PTY = &PTYService{commands: sandbox.Commands} + sandbox.Files = newFileService(sandbox) + return sandbox +} + +func (sandbox *Sandbox) envdURL(port int, direct bool) string { + if sandbox.client.config.debug { + return fmt.Sprintf("http://localhost:%d", port) + } + if sandbox.client.config.sandboxURL != "" { + parsed, _ := url.Parse(sandbox.client.config.sandboxURL) + if direct { + return parsed.Scheme + "://" + fmt.Sprintf("%d-%s.%s", port, sandbox.ID, parsed.Host) + } + return sandbox.client.config.sandboxURL + } + if !direct && sandbox.Domain == defaultDomain { + return "https://sandbox." + sandbox.Domain + } + return "https://" + sandbox.Host(port) +} + +func (sandbox *Sandbox) envdHeaders(port int) http.Header { + headers := make(http.Header) + headers.Set("Agentbox-Sandbox-Id", sandbox.ID) + headers.Set("Agentbox-Sandbox-Port", strconv.Itoa(port)) + if sandbox.envdAccessToken != "" { + headers.Set("X-Access-Token", sandbox.envdAccessToken) + } + if sandbox.trafficAccessToken != "" { + headers.Set("Agentbox-Traffic-Access-Token", sandbox.trafficAccessToken) + } + return headers +} + +func (sandbox *Sandbox) resolveUser(user string) string { + if user == "" && !envdAtLeast(sandbox.EnvdVersion, 0, 4, 0) { + return "user" + } + return user +} + +func envdAtLeast(version string, major, minor, patch int) bool { + version = strings.TrimPrefix(strings.TrimSpace(version), "v") + if prerelease, _, found := strings.Cut(version, "-"); found { + version = prerelease + } + parts := strings.Split(version, ".") + if len(parts) == 0 || len(parts) > 3 { + return true + } + actual := [3]int{} + for index, part := range parts { + value, err := strconv.Atoi(part) + if err != nil || value < 0 { + return true + } + actual[index] = value + } + required := [3]int{major, minor, patch} + for index := range actual { + if actual[index] != required[index] { + return actual[index] > required[index] + } + } + return true +} + +func durationSeconds(value, fallback time.Duration) (int32, error) { + if value == 0 { + value = fallback + } + if value <= 0 || value > time.Duration(^uint32(0)>>1)*time.Second { + return 0, &InvalidArgumentError{Message: "duration must be positive and fit in seconds"} + } + return int32(value / time.Second), nil +} +func durationSecondsInt(value time.Duration) (int, error) { + seconds, err := durationSeconds(value, 0) + return int(seconds), err +} +func normalizeRequestError(err error) error { + if errors.Is(err, context.DeadlineExceeded) { + return &TimeoutError{APIError: APIError{Message: "request timed out", Cause: err}} + } + if isConnectionError(err) { + return &SandboxError{APIError: APIError{Message: "connection failed", Cause: err}} + } + return err +} + +func fileSignature(path, operation, user, token string, expiration time.Time) (string, int64, error) { + if token == "" { + return "", 0, &AuthenticationError{APIError: APIError{Message: "envd access token is required for signed URLs"}} + } + raw := path + ":" + operation + ":" + user + ":" + token + unix := int64(0) + if !expiration.IsZero() { + unix = expiration.Unix() + raw += ":" + strconv.FormatInt(unix, 10) + } + digest := sha256.Sum256([]byte(raw)) + return "v1_" + strings.TrimRight(base64.StdEncoding.EncodeToString(digest[:]), "="), unix, nil +} diff --git a/packages/go-sdk/sandbox_test.go b/packages/go-sdk/sandbox_test.go new file mode 100644 index 000000000..aec272223 --- /dev/null +++ b/packages/go-sdk/sandbox_test.go @@ -0,0 +1,244 @@ +package agentbox + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "strings" + "testing" + "time" + + api "github.com/abox-dev/sdk/packages/go-sdk/internal/gen/api" +) + +func TestSandboxLifecycleAPI(t *testing.T) { + requests := make(chan *http.Request, 32) + server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + requests <- request.Clone(request.Context()) + writer.Header().Set("Content-Type", "application/json") + switch { + case request.Method == http.MethodPost && request.URL.Path == "/sandboxes": + writer.WriteHeader(201) + fmt.Fprint(writer, `{"sandboxID":"sbx","templateID":"base","envdVersion":"1.0","domain":"example.test","envdAccessToken":"envd","trafficAccessToken":"traffic"}`) + case request.URL.Path == "/v2/sandboxes": + fmt.Fprint(writer, `[{"sandboxID":"sbx","templateID":"base","envdVersion":"1","cpuCount":2,"memoryMB":1024,"diskSizeMB":1024,"startedAt":"2024-01-01T00:00:00Z","endAt":"2024-01-01T01:00:00Z","state":"running"}]`) + case request.URL.Path == "/sandboxes/sbx" && request.Method == http.MethodGet: + fmt.Fprint(writer, `{"sandboxID":"sbx","templateID":"base","envdVersion":"1","cpuCount":2,"memoryMB":1024,"diskSizeMB":1024,"startedAt":"2024-01-01T00:00:00Z","endAt":"2024-01-01T01:00:00Z","state":"running"}`) + case request.URL.Path == "/sandboxes/sbx/connect": + writer.WriteHeader(200) + fmt.Fprint(writer, `{"sandboxID":"sbx","templateID":"base","envdVersion":"1"}`) + case request.URL.Path == "/sandboxes/sbx/metrics": + fmt.Fprint(writer, `[{"timestampUnix":1,"cpuCount":2,"cpuUsedPct":1,"memUsed":1,"memTotal":2,"memCache":0,"diskUsed":1,"diskTotal":2}]`) + case request.URL.Path == "/sandboxes/metrics": + fmt.Fprint(writer, `{"sandboxes":{"sbx":{"timestampUnix":1,"cpuCount":2,"cpuUsedPct":1,"memUsed":1,"memTotal":2,"memCache":0,"diskUsed":1,"diskTotal":2}}}`) + case request.URL.Path == "/v2/sandboxes/sbx/logs": + fmt.Fprint(writer, `{"logs":[{"timestamp":"2024-01-01T00:00:00Z","level":"info","message":"ready","fields":{}}],"nextCursor":"next"}`) + case request.URL.Path == "/sandboxes/sbx/fork": + writer.WriteHeader(201) + fmt.Fprint(writer, `[{"sandbox":{"sandboxID":"fork","templateID":"base","envdVersion":"1"}},{"error":{"code":409,"message":"failed"}}]`) + case request.URL.Path == "/sandboxes/sbx/snapshots": + writer.WriteHeader(201) + fmt.Fprint(writer, `{"snapshotID":"snap:default","names":["snap:default"]}`) + case request.URL.Path == "/snapshots": + fmt.Fprint(writer, `[{"snapshotID":"snap:default","names":["snap:default"]}]`) + case request.Method == http.MethodDelete && request.URL.Path == "/templates/missing": + writer.WriteHeader(http.StatusNotFound) + case request.Method == http.MethodDelete && request.URL.Path == "/sandboxes/missing": + writer.WriteHeader(http.StatusNotFound) + case request.Method == http.MethodDelete: + writer.WriteHeader(http.StatusNoContent) + default: + writer.WriteHeader(http.StatusNoContent) + } + })) + defer server.Close() + client, err := NewClient(WithAPIURL(server.URL)) + if err != nil { + t.Fatal(err) + } + ctx := context.Background() + sandbox, err := client.Sandboxes.Create(ctx, &CreateSandboxOptions{Template: "base", Timeout: time.Minute, Env: map[string]string{"A": "B"}, Metadata: map[string]string{"m": "v"}}) + if err != nil { + t.Fatal(err) + } + if sandbox.ID != "sbx" || sandbox.Host(8080) != "8080-sbx.example.test" || sandbox.Commands == nil || sandbox.Files == nil || sandbox.PTY == nil { + t.Fatalf("bad sandbox: %#v", sandbox) + } + if page, err := client.Sandboxes.List(ctx, &ListSandboxOptions{Metadata: map[string]string{"app": "test"}, States: []SandboxState{SandboxRunning}, NextToken: "token", Limit: 10}); err != nil || len(page.Items) != 1 { + t.Fatalf("list: %v %#v", err, page) + } + if _, err := client.Sandboxes.Connect(ctx, "sbx", &ConnectSandboxOptions{Timeout: time.Minute}); err != nil { + t.Fatal(err) + } + if _, err := sandbox.Info(ctx); err != nil { + t.Fatal(err) + } + if metrics, err := sandbox.Metrics(ctx, &MetricsOptions{Start: time.Unix(1, 0), End: time.Unix(2, 0)}); err != nil || len(metrics) != 1 { + t.Fatalf("metrics: %v", err) + } + if metrics, err := client.Sandboxes.Metrics(ctx, "sbx"); err != nil || len(metrics) != 1 { + t.Fatalf("all metrics: %v", err) + } + if logs, err := sandbox.Logs(ctx, &SandboxLogOptions{Cursor: "cursor", Timestamp: 1, Limit: 2, Direction: "forward", Level: "info", Search: "ready"}); err != nil || logs.NextToken != "next" { + t.Fatalf("logs: %v %#v", err, logs) + } + if err := sandbox.SetTimeout(ctx, time.Minute); err != nil { + t.Fatal(err) + } + if err := sandbox.KeepAlive(ctx, time.Minute); err != nil { + t.Fatal(err) + } + if err := sandbox.Pause(ctx, &PauseOptions{}); err != nil { + t.Fatal(err) + } + if err := sandbox.UpdateNetwork(ctx, SandboxNetworkConfig{}, nil); err != nil { + t.Fatal(err) + } + if forks, err := sandbox.Fork(ctx, &ForkOptions{Count: 2, Timeout: time.Minute}); err != nil || len(forks) != 2 || forks[0].Sandbox == nil || forks[1].Err == nil { + t.Fatalf("fork: %v %#v", err, forks) + } + if snapshot, err := sandbox.CreateSnapshot(ctx, "snap"); err != nil || snapshot.SnapshotID == "" { + t.Fatalf("snapshot: %v", err) + } + if snapshots, err := client.Sandboxes.Snapshots(ctx, &SnapshotListOptions{SandboxID: "sbx", Name: "snap", Limit: 10}); err != nil || len(snapshots.Items) != 1 { + t.Fatalf("snapshots: %v", err) + } + if deleted, err := client.Sandboxes.DeleteSnapshot(ctx, "snap"); err != nil || !deleted { + t.Fatalf("delete snapshot: %v %v", deleted, err) + } + if deleted, err := client.Sandboxes.DeleteSnapshot(ctx, "missing"); err != nil || deleted { + t.Fatalf("missing snapshot: %v %v", deleted, err) + } + if killed, err := sandbox.Kill(ctx); err != nil || !killed { + t.Fatal(err) + } + if killed, err := client.Sandboxes.Kill(ctx, "missing"); err != nil || killed { + t.Fatalf("missing sandbox kill: %v %v", killed, err) + } + close(requests) + for request := range requests { + if request.Header.Get("User-Agent") == "" { + t.Fatal("missing user agent") + } + } +} + +func TestSandboxValidationAndSigning(t *testing.T) { + client, _ := NewClient(WithAPIURL("http://localhost")) + debugClient, _ := NewClient(WithAPIURL("http://localhost"), WithDebug(true)) + if killed, err := debugClient.Sandboxes.Kill(t.Context(), "debug"); err != nil || !killed { + t.Fatalf("debug kill: %v %v", killed, err) + } + if _, err := client.Sandboxes.Connect(context.Background(), "", nil); err == nil { + t.Fatal("expected validation") + } + if _, err := client.Sandboxes.Metrics(context.Background()); err == nil { + t.Fatal("expected metrics validation") + } + if _, err := durationSeconds(-time.Second, 0); err == nil { + t.Fatal("expected duration validation") + } + if _, err := client.Sandboxes.DeleteSnapshot(context.Background(), ""); err == nil { + t.Fatal("expected snapshot validation") + } + placeholders, err := IAMTokenPlaceholders("aws", "gcp") + if err != nil || placeholders["aws"] != "${agentbox.identity.tokens.aws}" { + t.Fatalf("IAM placeholders: %#v %v", placeholders, err) + } + for _, name := range []string{"", "bad}", "bad\nname"} { + if _, err := IAMTokenPlaceholder(name); err == nil { + t.Fatalf("expected invalid IAM name %q", name) + } + } + tokens := SandboxIAMTokens{"bad{": {Audience: "aud", TokenType: "jwt"}} + if _, err := client.Sandboxes.Create(context.Background(), &CreateSandboxOptions{IAM: &SandboxIAM{Tokens: &tokens}}); err == nil { + t.Fatal("expected invalid IAM config") + } + sandbox := client.sandboxFromAPI(api.Sandbox{SandboxID: "id", TemplateID: "base", EnvdVersion: "1"}) + if _, err := sandbox.Files.SignedReadURL("/x", "", time.Time{}); err == nil { + t.Fatal("expected missing token") + } + sandbox.envdAccessToken = "secret" + sandbox.trafficAccessToken = "traffic-secret" + if rendered := fmt.Sprintf("%+v %#v", sandbox, sandbox); strings.Contains(rendered, "secret") { + t.Fatalf("sandbox formatting leaked credentials: %s", rendered) + } + read, err := sandbox.Files.SignedReadURL("/x", "user", time.Unix(100, 0)) + if err != nil || !strings.Contains(read, "signature=v1_") || !strings.Contains(read, "signature_expiration=100") { + t.Fatalf("signed URL: %s %v", read, err) + } + write, err := sandbox.Files.SignedWriteURL("/x", "", time.Time{}) + if err != nil || write == read { + t.Fatalf("signed write URL: %v", err) + } +} + +func TestSandboxHealthAndModelConversionFailures(t *testing.T) { + status := http.StatusBadGateway + server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, _ *http.Request) { + writer.WriteHeader(status) + })) + defer server.Close() + client, err := NewClient(WithAPIURL(server.URL), WithSandboxURL(server.URL)) + if err != nil { + t.Fatal(err) + } + sandbox := client.sandboxFromAPI(api.Sandbox{SandboxID: "id", TemplateID: "base", EnvdVersion: "1"}) + if running, err := sandbox.IsRunning(t.Context()); err != nil || running { + t.Fatalf("bad gateway health: %v %v", running, err) + } + status = http.StatusInternalServerError + if _, err := sandbox.IsRunning(t.Context()); err == nil { + t.Fatal("expected health error") + } + if _, err := convertModel[any](make(chan int)); err == nil { + t.Fatal("expected model encode error") + } + if _, err := convertModel[chan int]("invalid"); err == nil { + t.Fatal("expected model decode error") + } +} + +func decodeRequest(t *testing.T, request *http.Request, value any) { + t.Helper() + if err := json.NewDecoder(request.Body).Decode(value); err != nil { + t.Fatal(err) + } +} + +func TestControlPlaneFailures(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + writer.Header().Set("Content-Type", "application/json") + writer.WriteHeader(http.StatusInternalServerError) + fmt.Fprint(writer, `{"message":"failure"}`) + })) + defer server.Close() + client, _ := NewClient(WithAPIURL(server.URL)) + ctx := context.Background() + sandbox := client.sandboxFromAPI(api.Sandbox{SandboxID: "id", TemplateID: "base", EnvdVersion: "1"}) + calls := []func() error{ + func() error { _, err := client.Sandboxes.Create(ctx, nil); return err }, + func() error { _, err := client.Sandboxes.Connect(ctx, "id", nil); return err }, + func() error { _, err := client.Sandboxes.List(ctx, nil); return err }, + func() error { _, err := client.Sandboxes.Info(ctx, "id"); return err }, + func() error { _, err := client.Sandboxes.Kill(ctx, "id"); return err }, + func() error { _, err := client.Sandboxes.Snapshots(ctx, nil); return err }, + func() error { _, err := client.Sandboxes.DeleteSnapshot(ctx, "id"); return err }, + func() error { _, err := client.Sandboxes.Metrics(ctx, "id"); return err }, + func() error { _, err := client.Sandboxes.Logs(ctx, "id", nil); return err }, + func() error { return sandbox.SetTimeout(ctx, time.Second) }, + func() error { return sandbox.KeepAlive(ctx, 0) }, + func() error { return sandbox.Pause(ctx, nil) }, + func() error { return sandbox.UpdateNetwork(ctx, SandboxNetworkConfig{}, nil) }, + func() error { _, err := sandbox.Metrics(ctx, nil); return err }, + func() error { _, err := sandbox.Fork(ctx, nil); return err }, + func() error { _, err := sandbox.CreateSnapshot(ctx, ""); return err }, + } + for index, call := range calls { + if err := call(); err == nil { + t.Fatalf("call %d unexpectedly succeeded", index) + } + } +} diff --git a/packages/go-sdk/scripts/check-go-coverage.sh b/packages/go-sdk/scripts/check-go-coverage.sh new file mode 100755 index 000000000..07e5e7032 --- /dev/null +++ b/packages/go-sdk/scripts/check-go-coverage.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +set -euo pipefail + +root_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) +profile=$(mktemp) +trap 'rm -f "$profile"' EXIT + +cd "$root_dir" +test_packages=$(go list ./... | grep -v -E '/(internal/gen|tests)/') +go test -covermode=atomic -coverprofile="$profile" $test_packages +coverage=$(go tool cover -func="$profile" | awk '/^total:/ {gsub(/%/, "", $3); print $3}') + +awk -v coverage="$coverage" 'BEGIN { if (coverage + 0 < 90) exit 1 }' || { + printf 'Go statement coverage %s%% is below 90%%\n' "$coverage" >&2 + exit 1 +} +printf 'Go statement coverage: %s%%\n' "$coverage" diff --git a/packages/go-sdk/scripts/check-go-format.sh b/packages/go-sdk/scripts/check-go-format.sh new file mode 100755 index 000000000..f428132d8 --- /dev/null +++ b/packages/go-sdk/scripts/check-go-format.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail + +unformatted=$(gofmt -l -- $(find . -name '*.go' -type f -not -path './.git/*')) +if [[ -n "$unformatted" ]]; then + printf 'Go files need formatting:\n%s\n' "$unformatted" >&2 + exit 1 +fi diff --git a/packages/go-sdk/scripts/filter-go-envd.py b/packages/go-sdk/scripts/filter-go-envd.py new file mode 100755 index 000000000..8c13321cd --- /dev/null +++ b/packages/go-sdk/scripts/filter-go-envd.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python3 +"""Remove language-specific type overrides from the derived Go envd spec.""" + +from pathlib import Path +import re +import sys + + +path = Path(sys.argv[1]) +contents = path.read_text() +filtered, replacements = re.subn(r"^\s*x-go-type:\s*SecureToken\s*$\n?", "", contents, flags=re.MULTILINE) +if replacements == 0: + raise SystemExit("SecureToken x-go-type override was not found in the envd spec") +path.write_text(filtered) diff --git a/packages/go-sdk/scripts/test-go-consumer.sh b/packages/go-sdk/scripts/test-go-consumer.sh new file mode 100755 index 000000000..e87b8014b --- /dev/null +++ b/packages/go-sdk/scripts/test-go-consumer.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash +set -euo pipefail + +root_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) +consumer_dir=$(mktemp -d) +trap 'rm -rf "$consumer_dir"' EXIT + +cd "$consumer_dir" +go mod init example.com/agentbox-consumer >/dev/null +go mod edit -replace github.com/abox-dev/sdk/packages/go-sdk="$root_dir" +go mod edit -require github.com/abox-dev/sdk/packages/go-sdk@v0.0.0 +cp "$root_dir/tests/consumer/consumer_test.go" . +go mod tidy +go test ./... diff --git a/packages/go-sdk/template.go b/packages/go-sdk/template.go new file mode 100644 index 000000000..298cd3374 --- /dev/null +++ b/packages/go-sdk/template.go @@ -0,0 +1,906 @@ +package agentbox + +import ( + "context" + "encoding/json" + "fmt" + "maps" + "net/http" + "os" + "path/filepath" + "slices" + "strconv" + "strings" + "time" + + api "github.com/abox-dev/sdk/packages/go-sdk/internal/gen/api" +) + +const defaultBaseImage = "docker.io/retailcrm/agentbox-base:v0.0.1@sha256:493b753044eb82f90b99afb3974033591ccbd7037b39e7963dfe49f38790376e" + +// TemplateStep is one layer in a template build. +type TemplateStep struct { + Type string `json:"type"` + Args []string `json:"args"` + Force bool `json:"force"` + FilesHash string `json:"filesHash,omitempty"` + ForceUpload bool `json:"forceUpload,omitempty"` + ResolveSymlinks bool `json:"resolveSymlinks,omitempty"` + Gzip bool `json:"gzip,omitempty"` +} + +// CopyOptions configures a COPY template layer. +type CopyOptions struct { + User string + Mode os.FileMode + ForceUpload bool + ResolveSymlinks bool + Gzip *bool +} + +// PackageInstallOptions configures npm/bun installs. +type PackageInstallOptions struct{ Global, Dev bool } + +// AptInstallOptions configures apt-get. +type AptInstallOptions struct{ NoInstallRecommends, FixMissing bool } + +// GitCloneOptions configures a git clone layer. +type GitCloneOptions struct { + Path, Branch, User string + Depth int +} + +// TemplateBuilder builds a declarative template definition. +type TemplateBuilder struct { + contextPath string + ignore []string + baseImage string + baseTemplate string + registry *api.FromImageRegistry + startCmd string + readyCmd string + force bool + forceNext bool + steps []TemplateStep + err error +} + +// NewTemplate starts a template definition. Empty contextPath uses the current directory. +func NewTemplate(contextPath string, ignore ...string) *TemplateBuilder { + if contextPath == "" { + contextPath = "." + } + return &TemplateBuilder{contextPath: contextPath, ignore: slices.Clone(ignore), baseImage: defaultBaseImage} +} + +func (builder *TemplateBuilder) fromImage(image string) *TemplateBuilder { + if image == "" { + builder.err = &InvalidArgumentError{Message: "base image cannot be empty"} + return builder + } + builder.baseImage, builder.baseTemplate = image, "" + if builder.forceNext { + builder.force = true + } + return builder +} + +// FromImage selects an OCI image. +func (builder *TemplateBuilder) FromImage(image string) *TemplateBuilder { + return builder.fromImage(image) +} +func (builder *TemplateBuilder) FromDebian(variant string) *TemplateBuilder { + if variant == "" { + variant = "stable" + } + return builder.fromImage("debian:" + variant) +} +func (builder *TemplateBuilder) FromUbuntu(variant string) *TemplateBuilder { + if variant == "" { + variant = "latest" + } + return builder.fromImage("ubuntu:" + variant) +} +func (builder *TemplateBuilder) FromFedora(variant string) *TemplateBuilder { + if variant == "" { + variant = "44" + } + return builder.fromImage("fedora:" + variant) +} +func (builder *TemplateBuilder) FromAlpine(variant string) *TemplateBuilder { + if variant == "" { + variant = "3.24" + } + return builder.fromImage("alpine:" + variant) +} +func (builder *TemplateBuilder) FromArch(variant string) *TemplateBuilder { + if variant == "" { + variant = "latest" + } + return builder.fromImage("archlinux:" + variant) +} +func (builder *TemplateBuilder) FromPython(version string) *TemplateBuilder { + if version == "" { + version = "3" + } + return builder.fromImage("python:" + version) +} +func (builder *TemplateBuilder) FromNode(variant string) *TemplateBuilder { + if variant == "" { + variant = "lts" + } + return builder.fromImage("node:" + variant) +} +func (builder *TemplateBuilder) FromBun(variant string) *TemplateBuilder { + if variant == "" { + variant = "latest" + } + return builder.fromImage("oven/bun:" + variant) +} +func (builder *TemplateBuilder) FromBase() *TemplateBuilder { + return builder.fromImage(defaultBaseImage) +} + +// FromTemplate selects another AgentBox template. +func (builder *TemplateBuilder) FromTemplate(template string) *TemplateBuilder { + builder.baseTemplate, builder.baseImage = template, "" + if builder.forceNext { + builder.force = true + } + return builder +} + +// FromRegistry selects a password-authenticated OCI registry image. +func (builder *TemplateBuilder) FromRegistry(image, username, password string) *TemplateBuilder { + builder.fromImage(image) + var registry api.FromImageRegistry + if err := registry.FromGeneralRegistry(api.GeneralRegistry{Type: api.Registry, Username: username, Password: password}); err != nil { + builder.err = err + } + builder.registry = ®istry + return builder +} + +// FromAWSRegistry selects an AWS ECR image. +func (builder *TemplateBuilder) FromAWSRegistry(image, accessKeyID, secretAccessKey, region string) *TemplateBuilder { + builder.fromImage(image) + var registry api.FromImageRegistry + if err := registry.FromAWSRegistry(api.AWSRegistry{Type: api.Aws, AwsAccessKeyID: accessKeyID, AwsSecretAccessKey: secretAccessKey, AwsRegion: region}); err != nil { + builder.err = err + } + builder.registry = ®istry + return builder +} + +// FromGCPRegistry selects a GCP Artifact Registry image. +func (builder *TemplateBuilder) FromGCPRegistry(image, serviceAccountJSON string) *TemplateBuilder { + builder.fromImage(image) + var registry api.FromImageRegistry + if err := registry.FromGCPRegistry(api.GCPRegistry{Type: api.Gcp, ServiceAccountJSON: serviceAccountJSON}); err != nil { + builder.err = err + } + builder.registry = ®istry + return builder +} + +// FromDockerfile parses common FROM/RUN/COPY/ENV/WORKDIR/USER directives. +func (builder *TemplateBuilder) FromDockerfile(contentOrPath string) *TemplateBuilder { + content := contentOrPath + if data, err := os.ReadFile(filepath.Join(builder.contextPath, contentOrPath)); err == nil { + content = string(data) + } + lines := joinDockerfileLines(content) + fromCount := 0 + for _, line := range lines { + if fields := strings.Fields(line); len(fields) > 0 && strings.EqualFold(fields[0], "FROM") { + fromCount++ + } + } + if fromCount != 1 { + builder.err = &InvalidArgumentError{Message: "Dockerfile must contain exactly one FROM instruction"} + return builder + } + builder.User("root").Workdir("/") + userChanged, workdirChanged := false, false + for _, line := range lines { + fields := strings.Fields(line) + if len(fields) == 0 || strings.HasPrefix(fields[0], "#") { + continue + } + directive := strings.ToUpper(fields[0]) + rest := strings.TrimSpace(line[len(fields[0]):]) + switch directive { + case "FROM": + values, err := splitDockerWords(rest) + if err != nil { + builder.err = err + continue + } + if len(values) == 0 { + builder.err = &InvalidArgumentError{Message: "invalid Dockerfile FROM"} + } else { + builder.FromImage(values[0]) + } + case "RUN": + builder.Run(rest) + case "WORKDIR": + builder.Workdir(rest) + workdirChanged = true + case "USER": + builder.User(rest) + userChanged = true + case "ENV", "ARG": + values, err := splitDockerWords(rest) + if err != nil { + builder.err = err + continue + } + env := map[string]string{} + for index := 0; index < len(values); index++ { + key, value, found := strings.Cut(values[index], "=") + if found { + env[key] = value + } else if index+1 < len(values) { + env[key] = values[index+1] + index++ + } else if directive == "ARG" { + env[key] = "" + } + } + builder.Env(env) + case "COPY", "ADD": + values, err := splitDockerWords(rest) + if err != nil { + builder.err = err + continue + } + copyOptions := &CopyOptions{} + for len(values) > 0 && strings.HasPrefix(values[0], "--") { + if owner, ok := strings.CutPrefix(values[0], "--chown="); ok { + copyOptions.User = owner + } + values = values[1:] + } + if len(values) < 2 { + builder.err = &InvalidArgumentError{Message: "invalid Dockerfile " + directive} + } else { + for _, source := range values[:len(values)-1] { + builder.Copy(source, values[len(values)-1], copyOptions) + } + } + case "CMD", "ENTRYPOINT": + command := rest + var execArgs []string + if json.Unmarshal([]byte(rest), &execArgs) == nil { + command = strings.Join(execArgs, " ") + } + builder.Start(command, WaitForTimeout(20*time.Second)) + case "EXPOSE", "VOLUME": + // These directives do not require a template build operation. + default: + // Match the other SDKs: ignore unknown Dockerfile directives. + } + } + if !userChanged { + builder.User("user") + } + if !workdirChanged { + builder.Workdir("/home/user") + } + return builder +} + +func (builder *TemplateBuilder) add(kind string, args ...string) *TemplateBuilder { + builder.steps = append(builder.steps, TemplateStep{Type: kind, Args: args, Force: builder.forceNext}) + return builder +} + +// Copy adds a file or directory from the context. +func (builder *TemplateBuilder) Copy(source, destination string, options *CopyOptions) *TemplateBuilder { + if filepath.IsAbs(source) || strings.HasPrefix(filepath.Clean(source), "..") { + builder.err = &InvalidArgumentError{Message: "copy source must stay inside template context"} + return builder + } + step := TemplateStep{Type: "COPY", Args: []string{source, destination, "", ""}, Force: builder.forceNext, Gzip: true} + if options != nil { + step.Args[2] = options.User + if options.Mode != 0 { + step.Args[3] = fmt.Sprintf("%04o", options.Mode.Perm()) + } + step.ForceUpload, step.ResolveSymlinks = options.ForceUpload, options.ResolveSymlinks + step.Force = step.Force || options.ForceUpload + if options.Gzip != nil { + step.Gzip = *options.Gzip + } + } + builder.steps = append(builder.steps, step) + return builder +} + +func (builder *TemplateBuilder) Run(commands ...string) *TemplateBuilder { + return builder.add("RUN", strings.Join(commands, " && ")) +} +func (builder *TemplateBuilder) RunAs(user string, commands ...string) *TemplateBuilder { + return builder.add("RUN", strings.Join(commands, " && "), user) +} +func (builder *TemplateBuilder) Workdir(path string) *TemplateBuilder { + return builder.add("WORKDIR", path) +} +func (builder *TemplateBuilder) User(user string) *TemplateBuilder { return builder.add("USER", user) } +func (builder *TemplateBuilder) Env(values map[string]string) *TemplateBuilder { + args := make([]string, 0, len(values)*2) + keys := slices.Sorted(maps.Keys(values)) + for _, key := range keys { + args = append(args, key, values[key]) + } + return builder.add("ENV", args...) +} +func (builder *TemplateBuilder) Remove(paths ...string) *TemplateBuilder { + quoted := make([]string, len(paths)) + for index, path := range paths { + quoted[index] = shellQuote(path) + } + return builder.Run("rm -rf " + strings.Join(quoted, " ")) +} +func (builder *TemplateBuilder) Rename(source, destination string) *TemplateBuilder { + return builder.Run("mv " + shellQuote(source) + " " + shellQuote(destination)) +} +func (builder *TemplateBuilder) MakeDir(paths ...string) *TemplateBuilder { + quoted := make([]string, len(paths)) + for index, path := range paths { + quoted[index] = shellQuote(path) + } + return builder.Run("mkdir -p " + strings.Join(quoted, " ")) +} +func (builder *TemplateBuilder) Symlink(source, destination string) *TemplateBuilder { + return builder.Run("ln -s " + shellQuote(source) + " " + shellQuote(destination)) +} +func (builder *TemplateBuilder) PipInstall(packages ...string) *TemplateBuilder { + if len(packages) == 0 { + packages = []string{"."} + } + return builder.RunAs("root", "pip install "+strings.Join(packages, " ")) +} +func (builder *TemplateBuilder) NPMInstall(options PackageInstallOptions, packages ...string) *TemplateBuilder { + flags := "" + if options.Global { + flags += " -g" + } + if options.Dev { + flags += " --save-dev" + } + user := "" + if options.Global { + user = "root" + } + return builder.RunAs(user, strings.TrimSpace("npm install"+flags+" "+strings.Join(packages, " "))) +} +func (builder *TemplateBuilder) BunInstall(options PackageInstallOptions, packages ...string) *TemplateBuilder { + flags := "" + if options.Global { + flags += " -g" + } + if options.Dev { + flags += " --dev" + } + user := "" + if options.Global { + user = "root" + } + return builder.RunAs(user, strings.TrimSpace("bun install"+flags+" "+strings.Join(packages, " "))) +} +func (builder *TemplateBuilder) AptInstall(options AptInstallOptions, packages ...string) *TemplateBuilder { + flags := "" + if options.NoInstallRecommends { + flags += " --no-install-recommends" + } + if options.FixMissing { + flags += " --fix-missing" + } + return builder.RunAs("root", "apt-get update", "DEBIAN_FRONTEND=noninteractive DEBCONF_NOWARNINGS=yes apt-get install -y"+flags+" "+strings.Join(packages, " ")) +} +func (builder *TemplateBuilder) GitClone(repository string, options *GitCloneOptions) *TemplateBuilder { + args := []string{"git clone", shellQuote(repository)} + user := "" + if options != nil { + user = options.User + if options.Branch != "" { + args = append(args, "--branch", shellQuote(options.Branch), "--single-branch") + } + if options.Depth > 0 { + args = append(args, "--depth", strconv.Itoa(options.Depth)) + } + if options.Path != "" { + args = append(args, shellQuote(options.Path)) + } + } + return builder.RunAs(user, strings.Join(args, " ")) +} + +// SkipCache forces this and all subsequent layers. +func (builder *TemplateBuilder) SkipCache() *TemplateBuilder { + builder.forceNext = true + return builder +} +func (builder *TemplateBuilder) Start(command, readyCommand string) *TemplateBuilder { + builder.startCmd, builder.readyCmd = command, readyCommand + return builder +} +func (builder *TemplateBuilder) Ready(command string) *TemplateBuilder { + builder.readyCmd = command + return builder +} +func WaitForPort(port int) string { + return fmt.Sprintf(`[ -n "$(ss -Htuln sport = :%d)" ]`, port) +} +func WaitForURL(value string, status int) string { + return fmt.Sprintf(`curl -s -o /dev/null -w "%%{http_code}" %s | grep -q "%d"`, shellQuote(value), status) +} +func WaitForFile(path string) string { return "test -e " + shellQuote(path) } +func WaitForProcess(process string) string { return "pgrep " + shellQuote(process) + " >/dev/null" } + +// WaitForTimeout waits a fixed duration before marking a service ready. +func WaitForTimeout(timeout time.Duration) string { + seconds := max(1, int(timeout/time.Second)) + return "sleep " + strconv.Itoa(seconds) +} + +// JSON returns the build request representation without computed copy hashes. +func (builder *TemplateBuilder) JSON() ([]byte, error) { + if builder.err != nil { + return nil, builder.err + } + return json.Marshal(builder.request(nil)) +} + +// Dockerfile returns a human-readable equivalent definition. +func (builder *TemplateBuilder) Dockerfile() string { + var output strings.Builder + if builder.baseImage != "" { + fmt.Fprintf(&output, "FROM %s\n", builder.baseImage) + } + for _, step := range builder.steps { + switch step.Type { + case "RUN": + fmt.Fprintf(&output, "RUN %s\n", step.Args[0]) + case "COPY": + fmt.Fprintf(&output, "COPY %s %s\n", step.Args[0], step.Args[1]) + case "WORKDIR", "USER": + fmt.Fprintf(&output, "%s %s\n", step.Type, step.Args[0]) + case "ENV": + for index := 0; index+1 < len(step.Args); index += 2 { + fmt.Fprintf(&output, "ENV %s=%s\n", step.Args[index], step.Args[index+1]) + } + } + } + return output.String() +} + +func (builder *TemplateBuilder) request(steps []api.TemplateStep) api.TemplateBuildStartV2 { + if steps == nil { + steps = make([]api.TemplateStep, len(builder.steps)) + for index, step := range builder.steps { + args := slices.Clone(step.Args) + force := step.Force + steps[index] = api.TemplateStep{Type: step.Type, Args: &args, Force: &force} + if step.FilesHash != "" { + steps[index].FilesHash = &step.FilesHash + } + } + } + force := builder.force + request := api.TemplateBuildStartV2{Steps: &steps, Force: &force} + if builder.baseImage != "" { + request.FromImage = &builder.baseImage + } + if builder.baseTemplate != "" { + request.FromTemplate = &builder.baseTemplate + } + if builder.registry != nil { + request.FromImageRegistry = builder.registry + } + if builder.startCmd != "" { + request.StartCmd = &builder.startCmd + } + if builder.readyCmd != "" { + request.ReadyCmd = &builder.readyCmd + } + return request +} + +// TemplateService manages AgentBox templates. +type TemplateService struct{ client *Client } +type TemplateBuildOptions struct { + Tags []string + CPUCount, MemoryMB int + SkipCache bool + PollInterval time.Duration + OnLog func(BuildLogEntry) +} +type TemplateBuildRef struct { + Name string + Tags []string + TemplateID, BuildID string +} +type TemplateListOptions struct { + TeamID, NextToken string + Limit int +} + +// TemplateInfoOptions paginates a template's build history. +type TemplateInfoOptions struct { + NextToken string + Limit int +} +type TemplateLogOptions struct { + Cursor string + Timestamp int64 + Limit int + Direction, Level, Source string +} +type TemplateTag struct { + Tag, BuildID string + CreatedAt time.Time +} + +// BuildInBackground uploads COPY contexts and starts a build. +func (service *TemplateService) BuildInBackground(ctx context.Context, builder *TemplateBuilder, name string, options *TemplateBuildOptions) (*TemplateBuildRef, error) { + if builder == nil || name == "" { + return nil, &InvalidArgumentError{Message: "template and name are required"} + } + if builder.err != nil { + return nil, builder.err + } + if options == nil { + options = &TemplateBuildOptions{} + } + cpu, memory := int32(options.CPUCount), int32(options.MemoryMB) + if cpu == 0 { + cpu = 2 + } + if memory == 0 { + memory = 1024 + } + builder.force = builder.force || options.SkipCache + body := api.TemplateBuildRequestV3{Name: &name, CPUCount: &cpu, MemoryMB: &memory} + if len(options.Tags) > 0 { + tags := slices.Clone(options.Tags) + body.Tags = &tags + } + requestCtx, cancel := withRequestTimeout(ctx, service.client.config.requestTimeout) + defer cancel() + response, err := service.client.api.PostV3TemplatesWithResponse(requestCtx, body) + if err != nil { + return nil, normalizeRequestError(err) + } + if response.JSON202 == nil { + return nil, &BuildError{APIError: APIError{StatusCode: response.StatusCode(), Message: string(response.Body)}} + } + value := response.JSON202 + steps, err := service.prepareCopySteps(ctx, builder, value.TemplateID) + if err != nil { + return nil, err + } + triggerCtx, triggerCancel := withRequestTimeout(ctx, service.client.config.requestTimeout) + defer triggerCancel() + trigger, err := service.client.api.PostV2TemplatesTemplateIDBuildsBuildIDWithResponse(triggerCtx, value.TemplateID, value.BuildID, builder.request(steps)) + if err != nil { + return nil, normalizeRequestError(err) + } + if trigger.StatusCode() < 200 || trigger.StatusCode() >= 300 { + return nil, &BuildError{APIError: APIError{StatusCode: trigger.StatusCode(), Message: string(trigger.Body)}} + } + return &TemplateBuildRef{Name: name, Tags: value.Tags, TemplateID: value.TemplateID, BuildID: value.BuildID}, nil +} + +// Build starts a build and waits for a terminal state. +func (service *TemplateService) Build(ctx context.Context, builder *TemplateBuilder, name string, options *TemplateBuildOptions) (*TemplateBuildRef, error) { + reference, err := service.BuildInBackground(ctx, builder, name, options) + if err != nil { + return nil, err + } + interval := 200 * time.Millisecond + if options != nil && options.PollInterval > 0 { + interval = options.PollInterval + } + offset := 0 + for { + status, err := service.BuildStatus(ctx, reference.TemplateID, reference.BuildID, offset) + if err != nil { + return nil, err + } + offset += len(status.LogEntries) + if options != nil && options.OnLog != nil { + for _, entry := range status.LogEntries { + options.OnLog(entry) + } + } + switch status.Status { + case BuildReady: + return reference, nil + case BuildFailed: + message := "template build failed" + if status.Reason != nil { + message = status.Reason.Message + } + return nil, &BuildError{APIError: APIError{Message: message}} + } + timer := time.NewTimer(interval) + select { + case <-ctx.Done(): + timer.Stop() + return nil, ctx.Err() + case <-timer.C: + } + } +} + +func (service *TemplateService) BuildStatus(ctx context.Context, templateID, buildID string, logsOffset int) (*TemplateBuildInfo, error) { + offset := int32(logsOffset) + requestCtx, cancel := withRequestTimeout(ctx, service.client.config.requestTimeout) + defer cancel() + response, err := service.client.api.GetTemplatesTemplateIDBuildsBuildIDStatusWithResponse(requestCtx, templateID, buildID, &api.GetTemplatesTemplateIDBuildsBuildIDStatusParams{LogsOffset: &offset}) + if err != nil { + return nil, normalizeRequestError(err) + } + if response.JSON200 == nil { + return nil, &BuildError{APIError: APIError{StatusCode: response.StatusCode(), Message: string(response.Body)}} + } + result, err := convertModel[TemplateBuildInfo](*response.JSON200) + return &result, err +} +func (service *TemplateService) Exists(ctx context.Context, alias string) (bool, error) { + requestCtx, cancel := withRequestTimeout(ctx, service.client.config.requestTimeout) + defer cancel() + response, err := service.client.api.GetTemplatesAliasesAliasWithResponse(requestCtx, alias) + if err != nil { + return false, normalizeRequestError(err) + } + if response.StatusCode() == http.StatusNotFound { + return false, nil + } + if response.StatusCode() == http.StatusForbidden { + return true, nil + } + if response.JSON200 == nil { + return false, &TemplateError{APIError: APIError{StatusCode: response.StatusCode(), Message: string(response.Body)}} + } + return true, nil +} +func (service *TemplateService) AssignTags(ctx context.Context, target string, tags []string) (string, error) { + requestCtx, cancel := withRequestTimeout(ctx, service.client.config.requestTimeout) + defer cancel() + response, err := service.client.api.PostTemplatesTagsWithResponse(requestCtx, api.AssignTemplateTagsRequest{Target: target, Tags: tags}) + if err != nil { + return "", normalizeRequestError(err) + } + if response.JSON201 == nil { + return "", &TemplateError{APIError: APIError{StatusCode: response.StatusCode(), Message: string(response.Body)}} + } + return response.JSON201.BuildID.String(), nil +} +func (service *TemplateService) RemoveTags(ctx context.Context, name string, tags []string) error { + requestCtx, cancel := withRequestTimeout(ctx, service.client.config.requestTimeout) + defer cancel() + response, err := service.client.api.DeleteTemplatesTagsWithResponse(requestCtx, api.DeleteTemplateTagsRequest{Name: name, Tags: tags}) + if err != nil { + return normalizeRequestError(err) + } + if response.StatusCode() < 200 || response.StatusCode() >= 300 { + return &TemplateError{APIError: APIError{StatusCode: response.StatusCode(), Message: string(response.Body)}} + } + return nil +} +func (service *TemplateService) Tags(ctx context.Context, templateID string) ([]TemplateTag, error) { + requestCtx, cancel := withRequestTimeout(ctx, service.client.config.requestTimeout) + defer cancel() + response, err := service.client.api.GetTemplatesTemplateIDTagsWithResponse(requestCtx, templateID) + if err != nil { + return nil, normalizeRequestError(err) + } + if response.JSON200 == nil { + return nil, &TemplateError{APIError: APIError{StatusCode: response.StatusCode(), Message: string(response.Body)}} + } + result := make([]TemplateTag, 0, len(*response.JSON200)) + for _, tag := range *response.JSON200 { + result = append(result, TemplateTag{Tag: tag.Tag, BuildID: tag.BuildID.String(), CreatedAt: tag.CreatedAt}) + } + return result, nil +} + +// List returns one template page. +func (service *TemplateService) List(ctx context.Context, options *TemplateListOptions) (Page[TemplateInfo], error) { + params := &api.GetV2TemplatesParams{} + if options != nil { + if options.TeamID != "" { + params.TeamID = &options.TeamID + } + if options.NextToken != "" { + params.NextToken = &options.NextToken + } + if options.Limit > 0 { + limit := int32(options.Limit) + params.Limit = &limit + } + } + requestCtx, cancel := withRequestTimeout(ctx, service.client.config.requestTimeout) + defer cancel() + response, err := service.client.api.GetV2TemplatesWithResponse(requestCtx, params) + if err != nil { + return Page[TemplateInfo]{}, normalizeRequestError(err) + } + if response.JSON200 == nil { + return Page[TemplateInfo]{}, &TemplateError{APIError: APIError{StatusCode: response.StatusCode(), Message: string(response.Body)}} + } + items, err := convertModel[[]TemplateInfo](*response.JSON200) + return Page[TemplateInfo]{Items: items, NextToken: response.HTTPResponse.Header.Get("X-Next-Token")}, err +} + +// Info returns a template and its build history. +func (service *TemplateService) Info(ctx context.Context, templateID string, options *TemplateInfoOptions) (*TemplateWithBuilds, error) { + params := &api.GetTemplatesTemplateIDParams{} + if options != nil { + if options.NextToken != "" { + params.NextToken = &options.NextToken + } + if options.Limit > 0 { + value := int32(options.Limit) + params.Limit = &value + } + } + requestCtx, cancel := withRequestTimeout(ctx, service.client.config.requestTimeout) + defer cancel() + response, err := service.client.api.GetTemplatesTemplateIDWithResponse(requestCtx, templateID, params) + if err != nil { + return nil, normalizeRequestError(err) + } + if response.JSON200 == nil { + return nil, &TemplateError{APIError: APIError{StatusCode: response.StatusCode(), Message: string(response.Body)}} + } + result, err := convertModel[TemplateWithBuilds](*response.JSON200) + return &result, err +} + +// Delete removes a template. +func (service *TemplateService) Delete(ctx context.Context, templateID string) error { + requestCtx, cancel := withRequestTimeout(ctx, service.client.config.requestTimeout) + defer cancel() + response, err := service.client.api.DeleteTemplatesTemplateIDWithResponse(requestCtx, templateID) + if err != nil { + return normalizeRequestError(err) + } + if response.StatusCode() < 200 || response.StatusCode() >= 300 { + return &TemplateError{APIError: APIError{StatusCode: response.StatusCode(), Message: string(response.Body)}} + } + return nil +} + +// SetPublic changes template visibility and returns its names. +func (service *TemplateService) SetPublic(ctx context.Context, templateID string, public bool) ([]string, error) { + requestCtx, cancel := withRequestTimeout(ctx, service.client.config.requestTimeout) + defer cancel() + response, err := service.client.api.PatchV2TemplatesTemplateIDWithResponse(requestCtx, templateID, api.TemplateUpdateRequest{Public: &public}) + if err != nil { + return nil, normalizeRequestError(err) + } + if response.JSON200 == nil { + return nil, &TemplateError{APIError: APIError{StatusCode: response.StatusCode(), Message: string(response.Body)}} + } + return response.JSON200.Names, nil +} + +// BuildLogs returns one page of structured build logs. +func (service *TemplateService) BuildLogs(ctx context.Context, templateID, buildID string, options *TemplateLogOptions) (Page[BuildLogEntry], error) { + params := &api.GetTemplatesTemplateIDBuildsBuildIDLogsParams{} + if options != nil { + if options.Cursor != "" { + params.PageCursor = &options.Cursor + } + if options.Timestamp != 0 { + params.Cursor = &options.Timestamp + } + if options.Limit > 0 { + limit := int32(options.Limit) + params.Limit = &limit + } + if options.Direction != "" { + value := api.LogsDirection(options.Direction) + params.Direction = &value + } + if options.Level != "" { + value := api.LogLevel(options.Level) + params.Level = &value + } + if options.Source != "" { + value := api.LogsSource(options.Source) + params.Source = &value + } + } + requestCtx, cancel := withRequestTimeout(ctx, service.client.config.requestTimeout) + defer cancel() + response, err := service.client.api.GetTemplatesTemplateIDBuildsBuildIDLogsWithResponse(requestCtx, templateID, buildID, params) + if err != nil { + return Page[BuildLogEntry]{}, normalizeRequestError(err) + } + if response.JSON200 == nil { + return Page[BuildLogEntry]{}, &BuildError{APIError: APIError{StatusCode: response.StatusCode(), Message: string(response.Body)}} + } + items, err := convertModel[[]BuildLogEntry](response.JSON200.Logs) + if err != nil { + return Page[BuildLogEntry]{}, err + } + page := Page[BuildLogEntry]{Items: items} + if response.JSON200.NextCursor != nil { + page.NextToken = *response.JSON200.NextCursor + } + return page, nil +} + +func shellQuote(value string) string { return "'" + strings.ReplaceAll(value, "'", "'\"'\"'") + "'" } + +func splitDockerWords(value string) ([]string, error) { + words := make([]string, 0) + var current strings.Builder + var quote rune + escaped := false + flush := func() { + if current.Len() > 0 { + words = append(words, current.String()) + current.Reset() + } + } + for _, char := range value { + if escaped { + current.WriteRune(char) + escaped = false + continue + } + if char == '\\' && quote != '\'' { + escaped = true + continue + } + if quote != 0 { + if char == quote { + quote = 0 + } else { + current.WriteRune(char) + } + continue + } + switch char { + case '\'', '"': + quote = char + case ' ', '\t', '\r', '\n': + flush() + default: + current.WriteRune(char) + } + } + if escaped || quote != 0 { + return nil, &InvalidArgumentError{Message: "unterminated Dockerfile escape or quote"} + } + flush() + return words, nil +} + +func joinDockerfileLines(content string) []string { + raw := strings.Split(strings.ReplaceAll(content, "\r\n", "\n"), "\n") + result := make([]string, 0, len(raw)) + current := "" + for _, line := range raw { + line = strings.TrimSpace(line) + current += line + if strings.HasSuffix(current, "\\") { + current = strings.TrimSuffix(current, "\\") + " " + continue + } + if current != "" { + result = append(result, current) + } + current = "" + } + if current != "" { + result = append(result, current) + } + return result +} diff --git a/packages/go-sdk/template_archive.go b/packages/go-sdk/template_archive.go new file mode 100644 index 000000000..edd65adb7 --- /dev/null +++ b/packages/go-sdk/template_archive.go @@ -0,0 +1,183 @@ +package agentbox + +import ( + "archive/tar" + "bytes" + "compress/gzip" + "context" + "crypto/sha256" + "fmt" + "io" + "net/http" + "os" + "path/filepath" + "slices" + "strings" + "time" + + api "github.com/abox-dev/sdk/packages/go-sdk/internal/gen/api" +) + +func (service *TemplateService) prepareCopySteps(ctx context.Context, builder *TemplateBuilder, templateID string) ([]api.TemplateStep, error) { + steps := make([]api.TemplateStep, len(builder.steps)) + for index, step := range builder.steps { + args := slices.Clone(step.Args) + force := step.Force + steps[index] = api.TemplateStep{Type: step.Type, Args: &args, Force: &force} + if step.Type != "COPY" { + continue + } + archive, hash, err := archiveCopy(builder.contextPath, step.Args[0], builder.ignore, step.ResolveSymlinks, step.Gzip) + if err != nil { + return nil, &FileUploadError{APIError: APIError{Message: "prepare " + step.Args[0], Cause: err}} + } + steps[index].FilesHash = &hash + requestCtx, cancel := withRequestTimeout(ctx, service.client.config.requestTimeout) + response, err := service.client.api.GetTemplatesTemplateIDFilesHashWithResponse(requestCtx, templateID, hash) + cancel() + if err != nil { + return nil, normalizeRequestError(err) + } + if response.JSON201 == nil { + return nil, &FileUploadError{APIError: APIError{StatusCode: response.StatusCode(), Message: string(response.Body)}} + } + if response.JSON201.Present && !step.ForceUpload { + continue + } + if response.JSON201.URL == nil || *response.JSON201.URL == "" { + return nil, &FileUploadError{APIError: APIError{Message: "upload URL is missing"}} + } + request, err := http.NewRequestWithContext(ctx, http.MethodPut, *response.JSON201.URL, bytes.NewReader(archive)) + if err != nil { + return nil, err + } + request.ContentLength = int64(len(archive)) + upload, err := service.client.httpClient.Do(request) + if err != nil { + return nil, &FileUploadError{APIError: APIError{Message: "upload failed", Cause: err}} + } + _, _ = io.Copy(io.Discard, upload.Body) + upload.Body.Close() + if upload.StatusCode < 200 || upload.StatusCode >= 300 { + return nil, &FileUploadError{APIError: APIError{StatusCode: upload.StatusCode, Message: upload.Status}} + } + } + return steps, nil +} + +func archiveCopy(contextPath, source string, ignore []string, resolveSymlinks, useGzip bool) ([]byte, string, error) { + root, err := filepath.Abs(contextPath) + if err != nil { + return nil, "", err + } + path := filepath.Join(root, filepath.Clean(source)) + if !strings.HasPrefix(path, root+string(filepath.Separator)) && path != root { + return nil, "", fmt.Errorf("source escapes context: %s", source) + } + var buffer bytes.Buffer + var output io.Writer = &buffer + var zipper *gzip.Writer + if useGzip { + zipper, _ = gzip.NewWriterLevel(&buffer, gzip.BestSpeed) + zipper.Header.ModTime = time.Unix(0, 0) + zipper.Header.OS = 255 + output = zipper + } + archive := tar.NewWriter(output) + paths := make([]string, 0) + err = filepath.WalkDir(path, func(current string, entry os.DirEntry, walkErr error) error { + if walkErr != nil { + return walkErr + } + relative, err := filepath.Rel(root, current) + if err != nil { + return err + } + if ignored(relative, ignore) { + if entry.IsDir() { + return filepath.SkipDir + } + return nil + } + paths = append(paths, current) + return nil + }) + if err != nil { + return nil, "", err + } + slices.Sort(paths) + for _, current := range paths { + info, err := os.Lstat(current) + if err != nil { + return nil, "", err + } + link := "" + if info.Mode()&os.ModeSymlink != 0 { + link, err = os.Readlink(current) + if err != nil { + return nil, "", err + } + if resolveSymlinks { + info, err = os.Stat(current) + if err != nil { + return nil, "", err + } + link = "" + } + } + header, err := tar.FileInfoHeader(info, link) + if err != nil { + return nil, "", err + } + header.Name, _ = filepath.Rel(root, current) + header.Name = filepath.ToSlash(header.Name) + header.ModTime = time.Unix(0, 0) + header.AccessTime = time.Time{} + header.ChangeTime = time.Time{} + header.Uid, header.Gid, header.Uname, header.Gname = 0, 0, "", "" + if err := archive.WriteHeader(header); err != nil { + return nil, "", err + } + if info.Mode().IsRegular() { + file, err := os.Open(current) + if err != nil { + return nil, "", err + } + _, copyErr := io.Copy(archive, file) + closeErr := file.Close() + if copyErr != nil { + return nil, "", copyErr + } + if closeErr != nil { + return nil, "", closeErr + } + } + } + if err := archive.Close(); err != nil { + return nil, "", err + } + if zipper != nil { + if err := zipper.Close(); err != nil { + return nil, "", err + } + } + digest := sha256.Sum256(buffer.Bytes()) + return buffer.Bytes(), fmt.Sprintf("%x", digest[:]), nil +} + +func ignored(path string, patterns []string) bool { + path = filepath.ToSlash(path) + for _, pattern := range patterns { + pattern = strings.TrimSpace(pattern) + if pattern == "" || strings.HasPrefix(pattern, "#") { + continue + } + if matched, _ := filepath.Match(pattern, path); matched { + return true + } + if matched, _ := filepath.Match(pattern, filepath.Base(path)); matched { + return true + } + } + return false +} diff --git a/packages/go-sdk/template_test.go b/packages/go-sdk/template_test.go new file mode 100644 index 000000000..b52ffc82f --- /dev/null +++ b/packages/go-sdk/template_test.go @@ -0,0 +1,281 @@ +package agentbox + +import ( + "context" + "errors" + "fmt" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "strings" + "testing" + "time" +) + +func TestTemplateBuilder(t *testing.T) { + contextPath := t.TempDir() + if err := os.WriteFile(filepath.Join(contextPath, "hello.txt"), []byte("hello"), 0o600); err != nil { + t.Fatal(err) + } + gzipDisabled := false + builder := NewTemplate(contextPath, "*.ignore").FromPython("3.13").Copy("hello.txt", "/app/", &CopyOptions{User: "user", Mode: 0o640, ForceUpload: true, Gzip: &gzipDisabled}).Workdir("/app").User("user").Env(map[string]string{"B": "2", "A": "1"}).Run("echo one", "echo two").MakeDir("/tmp/x").Rename("a", "b").Symlink("b", "c").Remove("c").PipInstall("requests").NPMInstall(PackageInstallOptions{Dev: true}, "typescript").BunInstall(PackageInstallOptions{Global: true}, "tsx").AptInstall(AptInstallOptions{NoInstallRecommends: true}, "git").GitClone("https://example.test/repo.git", &GitCloneOptions{Path: "repo", Branch: "main", Depth: 1}).Start("python app.py", WaitForPort(8000)).Ready(WaitForURL("http://localhost", 200)).SkipCache() + data, err := builder.JSON() + if err != nil { + t.Fatal(err) + } + if !strings.Contains(string(data), `"fromImage":"python:3.13"`) || strings.Contains(string(data), `"filesHash"`) { + t.Fatalf("unexpected JSON: %s", data) + } + dockerfile := builder.Dockerfile() + for _, expected := range []string{"FROM python:3.13", "COPY hello.txt /app/", "WORKDIR /app", "USER user", "RUN echo one && echo two", "ENV A=1"} { + if !strings.Contains(dockerfile, expected) { + t.Fatalf("missing %q in %s", expected, dockerfile) + } + } + if WaitForFile("/tmp/ready") == "" || WaitForProcess("nginx") == "" { + t.Fatal("ready helpers empty") + } + if WaitForTimeout(0) != "sleep 1" { + t.Fatal("timeout helper") + } + if _, err := NewTemplate(contextPath).Copy("../escape", "/", nil).JSON(); err == nil { + t.Fatal("expected escaping copy error") + } + if _, err := NewTemplate(contextPath).FromImage("").JSON(); err == nil { + t.Fatal("expected image error") + } + parsed := NewTemplate(contextPath).FromDockerfile("FROM alpine:3.24\nENV A=1 B 2\nWORKDIR /app\nUSER app\nRUN echo ok\nCOPY hello.txt /app/") + if _, err := parsed.JSON(); err != nil || !strings.Contains(parsed.Dockerfile(), "FROM alpine:3.24") { + t.Fatalf("dockerfile parse: %v", err) + } + quoted := NewTemplate(contextPath).FromDockerfile("FROM alpine:3.24\nENV A=\"hello world\"\nCOPY \"hello world.txt\" /app/") + if _, err := quoted.JSON(); err != nil || quoted.steps[2].Args[1] != "hello world" || quoted.steps[3].Args[0] != "hello world.txt" { + t.Fatalf("quoted Dockerfile parse: %#v %v", quoted.steps, err) + } + if _, err := NewTemplate(contextPath).FromDockerfile("FROM alpine\nENV A='unterminated").JSON(); err == nil { + t.Fatal("expected unterminated quote error") + } + if _, err := NewTemplate(contextPath).FromDockerfile("EXPOSE 80").JSON(); err == nil { + t.Fatal("expected missing FROM") + } + if _, err := NewTemplate(contextPath).FromDockerfile("FROM a\nFROM b").JSON(); err == nil { + t.Fatal("expected multi-stage error") + } + if _, err := NewTemplate(contextPath).FromDockerfile("FROM alpine\nARG FLAG\nCOPY --chown=user hello.txt other.txt /app/\nEXPOSE 80\nVOLUME /data\nCMD [\"echo\",\"ok\"]\nLABEL x=y").JSON(); err != nil { + t.Fatal(err) + } + NewTemplate("").FromBase().FromDebian("").FromUbuntu("").FromFedora("").FromAlpine("").FromArch("").FromNode("").FromBun("").FromTemplate("base").FromRegistry("image", "u", "p").FromAWSRegistry("image", "a", "s", "r").FromGCPRegistry("image", `{}`) + NewTemplate(contextPath).FromPython("").SkipCache().FromTemplate("base").PipInstall().NPMInstall(PackageInstallOptions{Global: true}).BunInstall(PackageInstallOptions{Dev: true}) + if err := os.WriteFile(filepath.Join(contextPath, "Dockerfile"), []byte("FROM alpine:3.24\\\n\nRUN echo ok\n"), 0o600); err != nil { + t.Fatal(err) + } + if _, err := NewTemplate(contextPath).FromDockerfile("Dockerfile").JSON(); err != nil { + t.Fatal(err) + } +} + +func TestTemplateArchiveDeterministic(t *testing.T) { + directory := t.TempDir() + if err := os.Mkdir(filepath.Join(directory, "dir"), 0o755); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(directory, "dir", "a.txt"), []byte("A"), 0o644); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(directory, "skip.ignore"), []byte("skip"), 0o644); err != nil { + t.Fatal(err) + } + first, hash1, err := archiveCopy(directory, ".", []string{"*.ignore"}, false, true) + if err != nil { + t.Fatal(err) + } + second, hash2, err := archiveCopy(directory, ".", []string{"*.ignore"}, false, true) + if err != nil { + t.Fatal(err) + } + if hash1 != hash2 || string(first) != string(second) { + t.Fatal("archive is not deterministic") + } + if _, _, err := archiveCopy(directory, "../escape", nil, false, false); err == nil { + t.Fatal("expected escape error") + } + if !ignored("anything", []string{"", "# comment", "any*"}) { + t.Fatal("ignore pattern not applied") + } + if err := os.Symlink("dir/a.txt", filepath.Join(directory, "link")); err != nil { + t.Fatal(err) + } + if _, _, err := archiveCopy(directory, "link", nil, true, false); err != nil { + t.Fatal(err) + } +} + +func TestTemplateService(t *testing.T) { + contextPath := t.TempDir() + if err := os.WriteFile(filepath.Join(contextPath, "hello.txt"), []byte("hello"), 0o600); err != nil { + t.Fatal(err) + } + statusCalls := 0 + uploaded := false + cachePresent := false + var server *httptest.Server + server = httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + writer.Header().Set("Content-Type", "application/json") + switch { + case request.URL.Path == "/v3/templates": + writer.WriteHeader(http.StatusAccepted) + fmt.Fprint(writer, `{"templateID":"tpl","buildID":"build","names":["name"],"public":false,"tags":["v1"]}`) + case strings.HasPrefix(request.URL.Path, "/templates/tpl/files/"): + writer.WriteHeader(http.StatusCreated) + fmt.Fprintf(writer, `{"present":%t,"url":%q}`, cachePresent, server.URL+"/upload") + case request.URL.Path == "/upload": + uploaded = request.ContentLength > 0 + writer.WriteHeader(http.StatusOK) + case request.URL.Path == "/v2/templates/tpl/builds/build": + writer.WriteHeader(http.StatusAccepted) + case request.URL.Path == "/templates/tpl/builds/build/status": + statusCalls++ + status := "building" + if statusCalls > 1 { + status = "ready" + } + fmt.Fprintf(writer, `{"templateID":"tpl","buildID":"build","status":%q,"logs":[],"logEntries":[]}`, status) + case request.URL.Path == "/templates/aliases/missing": + writer.WriteHeader(http.StatusNotFound) + fmt.Fprint(writer, `{"message":"missing"}`) + case request.URL.Path == "/templates/aliases/forbidden": + writer.WriteHeader(http.StatusForbidden) + fmt.Fprint(writer, `{"message":"forbidden"}`) + case request.URL.Path == "/templates/aliases/name": + fmt.Fprint(writer, `{"templateID":"tpl","public":false}`) + case request.URL.Path == "/v2/templates": + writer.Header().Set("X-Next-Token", "next") + fmt.Fprint(writer, `[]`) + case request.URL.Path == "/templates/tpl" && request.Method == http.MethodGet: + fmt.Fprint(writer, `{"templateID":"tpl","names":["name"],"builds":[],"public":false,"spawnCount":0,"createdAt":"2024-01-01T00:00:00Z","updatedAt":"2024-01-01T00:00:00Z"}`) + case request.URL.Path == "/templates/tpl" && request.Method == http.MethodDelete: + writer.WriteHeader(http.StatusNoContent) + case request.URL.Path == "/v2/templates/tpl": + fmt.Fprint(writer, `{"names":["name"]}`) + case request.URL.Path == "/templates/tags" && request.Method == http.MethodPost: + writer.WriteHeader(http.StatusCreated) + fmt.Fprint(writer, `{"buildID":"00000000-0000-0000-0000-000000000001","tags":["v1"]}`) + case request.URL.Path == "/templates/tags" && request.Method == http.MethodDelete: + writer.WriteHeader(http.StatusNoContent) + case request.URL.Path == "/templates/tpl/tags": + fmt.Fprint(writer, `[{"tag":"v1","buildID":"00000000-0000-0000-0000-000000000001","createdAt":"2024-01-01T00:00:00Z"}]`) + case request.URL.Path == "/templates/tpl/builds/build/logs": + fmt.Fprint(writer, `{"logs":[],"nextCursor":"cursor"}`) + default: + http.NotFound(writer, request) + } + })) + defer server.Close() + client, err := NewClient(WithAPIURL(server.URL)) + if err != nil { + t.Fatal(err) + } + builder := NewTemplate(contextPath).FromAlpine("").Copy("hello.txt", "/app/", nil) + ref, err := client.Templates.Build(context.Background(), builder, "name", &TemplateBuildOptions{Tags: []string{"v1"}, PollInterval: time.Millisecond}) + if err != nil || ref.TemplateID != "tpl" || !uploaded { + t.Fatalf("build: %#v %v uploaded=%v", ref, err, uploaded) + } + if exists, err := client.Templates.Exists(context.Background(), "name"); err != nil || !exists { + t.Fatalf("exists: %v %v", exists, err) + } + if exists, err := client.Templates.Exists(context.Background(), "missing"); err != nil || exists { + t.Fatalf("missing exists: %v %v", exists, err) + } + if exists, err := client.Templates.Exists(context.Background(), "forbidden"); err != nil || !exists { + t.Fatalf("forbidden exists: %v %v", exists, err) + } + cachePresent = true + if _, err := client.Templates.BuildInBackground(context.Background(), NewTemplate(contextPath).Copy("hello.txt", "/cached/", nil), "cached", nil); err != nil { + t.Fatal(err) + } + if page, err := client.Templates.List(context.Background(), &TemplateListOptions{Limit: 10}); err != nil || page.NextToken != "next" { + t.Fatalf("list: %#v %v", page, err) + } + if _, err := client.Templates.Info(context.Background(), "tpl", &TemplateInfoOptions{Limit: 10}); err != nil { + t.Fatal(err) + } + if _, err := client.Templates.SetPublic(context.Background(), "tpl", true); err != nil { + t.Fatal(err) + } + if id, err := client.Templates.AssignTags(context.Background(), "name", []string{"v1"}); err != nil || id == "" { + t.Fatalf("tags: %s %v", id, err) + } + if tags, err := client.Templates.Tags(context.Background(), "tpl"); err != nil || len(tags) != 1 { + t.Fatalf("tags list: %v", err) + } + if err := client.Templates.RemoveTags(context.Background(), "name", []string{"v1"}); err != nil { + t.Fatal(err) + } + if logs, err := client.Templates.BuildLogs(context.Background(), "tpl", "build", &TemplateLogOptions{Limit: 10, Direction: "forward", Level: "info", Source: "persistent"}); err != nil || logs.NextToken != "cursor" { + t.Fatalf("logs: %#v %v", logs, err) + } + if err := client.Templates.Delete(context.Background(), "tpl"); err != nil { + t.Fatal(err) + } +} + +func TestTemplateServiceFailures(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + writer.Header().Set("Content-Type", "application/json") + writer.WriteHeader(http.StatusInternalServerError) + fmt.Fprint(writer, `{"message":"failure"}`) + })) + defer server.Close() + client, _ := NewClient(WithAPIURL(server.URL)) + ctx := context.Background() + builder := NewTemplate(t.TempDir()) + calls := []func() error{ + func() error { _, err := client.Templates.BuildInBackground(ctx, builder, "name", nil); return err }, + func() error { _, err := client.Templates.BuildStatus(ctx, "tpl", "build", 0); return err }, + func() error { _, err := client.Templates.Exists(ctx, "name"); return err }, + func() error { _, err := client.Templates.List(ctx, nil); return err }, + func() error { _, err := client.Templates.Info(ctx, "tpl", nil); return err }, + func() error { return client.Templates.Delete(ctx, "tpl") }, + func() error { _, err := client.Templates.SetPublic(ctx, "tpl", true); return err }, + func() error { _, err := client.Templates.AssignTags(ctx, "name", nil); return err }, + func() error { return client.Templates.RemoveTags(ctx, "name", nil) }, + func() error { _, err := client.Templates.Tags(ctx, "tpl"); return err }, + func() error { _, err := client.Templates.BuildLogs(ctx, "tpl", "build", nil); return err }, + } + for index, call := range calls { + if err := call(); err == nil { + t.Fatalf("call %d unexpectedly succeeded", index) + } + } + if _, err := client.Templates.BuildInBackground(ctx, nil, "", nil); err == nil { + t.Fatal("expected builder validation") + } +} + +func TestTemplateBuildTerminalFailure(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) { + writer.Header().Set("Content-Type", "application/json") + switch { + case request.URL.Path == "/v3/templates": + writer.WriteHeader(http.StatusAccepted) + fmt.Fprint(writer, `{"templateID":"tpl","buildID":"build","names":[],"public":false,"tags":[]}`) + case request.URL.Path == "/v2/templates/tpl/builds/build": + writer.WriteHeader(http.StatusAccepted) + case request.URL.Path == "/templates/tpl/builds/build/status": + fmt.Fprint(writer, `{"templateID":"tpl","buildID":"build","status":"error","logs":[],"logEntries":[],"reason":{"message":"broken"}}`) + default: + http.NotFound(writer, request) + } + })) + defer server.Close() + client, _ := NewClient(WithAPIURL(server.URL)) + if _, err := client.Templates.Build(context.Background(), NewTemplate(t.TempDir()), "name", nil); err == nil { + t.Fatal("expected build failure") + } else { + var build *BuildError + if !errors.As(err, &build) { + t.Fatalf("error type %T", err) + } + } +} diff --git a/packages/go-sdk/tests/consumer/consumer_test.go b/packages/go-sdk/tests/consumer/consumer_test.go new file mode 100644 index 000000000..14c2e3120 --- /dev/null +++ b/packages/go-sdk/tests/consumer/consumer_test.go @@ -0,0 +1,23 @@ +package consumer_test + +import ( + "testing" + + "github.com/abox-dev/sdk/packages/go-sdk" + "github.com/abox-dev/sdk/packages/go-sdk/codeinterpreter" +) + +func TestPublicPackagesCompile(t *testing.T) { + client, err := agentbox.NewClient(agentbox.WithAPIKey("test")) + if err != nil { + t.Fatal(err) + } + if client.Sandboxes == nil || client.Templates == nil || codeinterpreter.DefaultTemplate == "" { + t.Fatal("public services are unavailable") + } + _ = agentbox.SandboxInfo{State: agentbox.SandboxRunning} + _ = agentbox.SnapshotListOptions{Limit: 10} + _ = agentbox.TemplateInfoOptions{Limit: 10} + _ = agentbox.ForkResult{} + _ = agentbox.SandboxRequestOptions{} +} diff --git a/packages/go-sdk/transport.go b/packages/go-sdk/transport.go new file mode 100644 index 000000000..369b61d0b --- /dev/null +++ b/packages/go-sdk/transport.go @@ -0,0 +1,193 @@ +package agentbox + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "io" + "log/slog" + "net" + "net/http" + "net/url" + "os" + "slices" + "strconv" + "strings" + "time" +) + +const maxErrorBody = 1 << 20 + +type sdkTransport struct { + base http.RoundTripper + headers http.Header + apiKey string + apiHost string + logger *slog.Logger +} + +func (transport *sdkTransport) RoundTrip(request *http.Request) (*http.Response, error) { + clone := request.Clone(request.Context()) + clone.Header = request.Header.Clone() + if clone.URL.Host == transport.apiHost { + for key, values := range transport.headers { + if _, exists := clone.Header[key]; !exists { + clone.Header[key] = slices.Clone(values) + } + } + if transport.apiKey != "" && clone.Header.Get("X-API-KEY") == "" { + clone.Header.Set("X-API-KEY", transport.apiKey) + } + } + if clone.Header.Get("User-Agent") == "" { + clone.Header.Set("User-Agent", "agentbox-go-sdk/"+Version) + } + started := time.Now() + response, err := transport.base.RoundTrip(clone) + if transport.logger != nil { + safeURL := clone.URL.Scheme + "://" + clone.URL.Host + clone.URL.EscapedPath() + attributes := []any{"method", clone.Method, "url", safeURL, "duration", time.Since(started)} + if response != nil { + attributes = append(attributes, "status", response.StatusCode) + } + if err != nil { + attributes = append(attributes, "error", err) + } + transport.logger.DebugContext(request.Context(), "AgentBox HTTP request", attributes...) + } + return response, err +} + +func newHTTPClient(config clientConfig) *http.Client { + apiURL, _ := url.Parse(config.apiURL) + apiHost := "" + if apiURL != nil { + apiHost = apiURL.Host + } + if config.httpClient != nil { + client := *config.httpClient + client.Transport = &sdkTransport{base: roundTripper(client.Transport), headers: config.headers.Clone(), apiKey: config.apiKey, apiHost: apiHost, logger: config.logger} + return &client + } + base := http.DefaultTransport.(*http.Transport).Clone() + base.ForceAttemptHTTP2 = true + base.MaxIdleConns = envPositiveInt("AGENTBOX_MAX_CONNECTIONS", 200) + base.MaxIdleConnsPerHost = envPositiveInt("AGENTBOX_MAX_KEEPALIVE_CONNECTIONS", 20) + base.IdleConnTimeout = envDurationSeconds("AGENTBOX_KEEPALIVE_EXPIRY", 300*time.Second) + if config.proxyURL != nil { + base.Proxy = http.ProxyURL(config.proxyURL) + } + return &http.Client{Transport: &sdkTransport{base: base, headers: config.headers.Clone(), apiKey: config.apiKey, apiHost: apiHost, logger: config.logger}} +} + +// newEnvdHTTPClient enables prior-knowledge h2c for local debug envd. Production +// envd uses HTTPS and negotiates HTTP/2 normally. A caller-supplied client or +// proxy remains authoritative because it may provide its own routing transport. +func newEnvdHTTPClient(config clientConfig, standard *http.Client) *http.Client { + if !config.debug || config.httpClient != nil || config.proxyURL != nil { + return standard + } + base := http.DefaultTransport.(*http.Transport).Clone() + base.MaxIdleConns = envPositiveInt("AGENTBOX_MAX_CONNECTIONS", 200) + base.MaxIdleConnsPerHost = envPositiveInt("AGENTBOX_MAX_KEEPALIVE_CONNECTIONS", 20) + base.IdleConnTimeout = envDurationSeconds("AGENTBOX_KEEPALIVE_EXPIRY", 300*time.Second) + base.Protocols = new(http.Protocols) + base.Protocols.SetUnencryptedHTTP2(true) + return &http.Client{Transport: &sdkTransport{base: base, apiHost: transportAPIHost(config.apiURL), logger: config.logger}} +} + +func transportAPIHost(value string) string { + parsed, _ := url.Parse(value) + if parsed == nil { + return "" + } + return parsed.Host +} + +func roundTripper(value http.RoundTripper) http.RoundTripper { + if value == nil { + return http.DefaultTransport + } + return value +} + +func envPositiveInt(name string, fallback int) int { + value, err := strconv.Atoi(osEnv(name)) + if err != nil || value <= 0 { + return fallback + } + return value +} + +func envDurationSeconds(name string, fallback time.Duration) time.Duration { + value, err := strconv.ParseFloat(osEnv(name), 64) + if err != nil || value <= 0 { + return fallback + } + return time.Duration(value * float64(time.Second)) +} + +var osEnv = func(name string) string { return strings.TrimSpace(getenv(name)) } +var getenv = os.Getenv + +func withRequestTimeout(ctx context.Context, timeout time.Duration) (context.Context, context.CancelFunc) { + if timeout == 0 { + return context.WithCancel(ctx) + } + return context.WithTimeout(ctx, timeout) +} + +func decodeHTTPError(response *http.Response) error { + body, _ := io.ReadAll(io.LimitReader(response.Body, maxErrorBody)) + return decodeStatusError(response.StatusCode, response.Status, body) +} + +func decodeFileHTTPError(response *http.Response) error { + err := decodeHTTPError(response) + if response.StatusCode != http.StatusNotFound { + return err + } + var missing *SandboxNotFoundError + if errors.As(err, &missing) { + return &FileNotFoundError{APIError: missing.APIError} + } + return err +} + +func decodeStatusError(statusCode int, status string, body []byte) error { + message := strings.TrimSpace(string(body)) + var payload struct { + Message string `json:"message"` + Code any `json:"code"` + } + if json.Unmarshal(body, &payload) == nil && payload.Message != "" { + message = payload.Message + } + if message == "" { + message = status + } + apiError := APIError{StatusCode: statusCode, Message: message} + if payload.Code != nil { + apiError.Code = fmt.Sprint(payload.Code) + } + switch statusCode { + case http.StatusUnauthorized, http.StatusForbidden: + return &AuthenticationError{APIError: apiError} + case http.StatusNotFound: + return &SandboxNotFoundError{APIError: apiError} + case http.StatusRequestEntityTooLarge: + return &NotEnoughSpaceError{APIError: apiError} + case http.StatusTooManyRequests: + return &RateLimitError{APIError: apiError} + case http.StatusBadGateway, http.StatusGatewayTimeout: + return &TimeoutError{APIError: apiError} + default: + return &SandboxError{APIError: apiError} + } +} + +func isConnectionError(err error) bool { + var networkError net.Error + return errors.As(err, &networkError) +} diff --git a/packages/go-sdk/version.go b/packages/go-sdk/version.go new file mode 100644 index 000000000..948c0f829 --- /dev/null +++ b/packages/go-sdk/version.go @@ -0,0 +1,4 @@ +package agentbox + +// Version is the AgentBox SDK release version. +const Version = "0.1.1" diff --git a/redocly.yaml b/redocly.yaml index 65f960ac0..a43173a58 100644 --- a/redocly.yaml +++ b/redocly.yaml @@ -40,6 +40,17 @@ apis: filter-out: property: x-not-implemented value: [true] + go-sdk: + root: spec/openapi.yml + decorators: + filter-in: + property: tags + value: [sandboxes, snapshots, templates, tags] + matchStrategy: any + applyTo: Operation + filter-out: + property: x-not-implemented + value: [true] envd: root: spec/envd/envd.yaml decorators: diff --git a/scripts/check-release-versions.mjs b/scripts/check-release-versions.mjs index c230ae363..7fafb23ef 100644 --- a/scripts/check-release-versions.mjs +++ b/scripts/check-release-versions.mjs @@ -19,6 +19,14 @@ for (const manifest of manifests) { throw new Error(`${manifest}: expected ${expected}, got ${value}`) } +const goVersion = fs + .readFileSync('packages/go-sdk/version.go', 'utf8') + .match(/^const Version = "([^"]+)"/m)?.[1] +if (goVersion !== expected) + throw new Error( + `packages/go-sdk/version.go: expected ${expected}, got ${goVersion}` + ) + for (const manifest of [ 'packages/python-sdk/pyproject.toml', 'packages/code-interpreter-python/pyproject.toml', @@ -63,3 +71,7 @@ for (const directory of [ throw new Error(`${directory}/NOTICE is missing`) } } + +if (!fs.existsSync('packages/go-sdk/LICENSE')) { + throw new Error('packages/go-sdk/LICENSE is missing') +} diff --git a/scripts/set-release-version.mjs b/scripts/set-release-version.mjs index 24ceeb799..3ea7dcc73 100644 --- a/scripts/set-release-version.mjs +++ b/scripts/set-release-version.mjs @@ -33,6 +33,12 @@ for (const relative of pythonManifests) { if (!current) throw new Error(`Cannot find version in ${relative}`) currentVersions.add(current) } +const goVersionFile = path.join(root, 'packages/go-sdk/version.go') +const goVersionContent = fs.readFileSync(goVersionFile, 'utf8') +const goVersion = goVersionContent.match(/^const Version = "([^"]+)"/m)?.[1] +if (!goVersion) + throw new Error('Cannot find Version in packages/go-sdk/version.go') +currentVersions.add(goVersion) if (currentVersions.size !== 1) { throw new Error( `Package versions are already inconsistent: ${[...currentVersions].join(', ')}` @@ -53,5 +59,12 @@ for (const relative of pythonManifests) { content.replace(/^version\s*=\s*"[^"]+"/m, `version = "${version}"`) ) } +fs.writeFileSync( + goVersionFile, + goVersionContent.replace( + /^const Version = "[^"]+"/m, + `const Version = "${version}"` + ) +) process.stdout.write(`Updated all AgentBox SDK packages to ${version}\n`) diff --git a/scripts/test-published-runtime.sh b/scripts/test-published-runtime.sh index a30972a5f..6665d77a3 100755 --- a/scripts/test-published-runtime.sh +++ b/scripts/test-published-runtime.sh @@ -42,3 +42,12 @@ uv pip show --python "$test_dir/python/bin/python" \ "$test_dir/python/bin/python" "$root_dir/tests/runtime/core-python.py" "$test_dir/python/bin/python" \ "$root_dir/tests/runtime/code_interpreter_python.py" + +mkdir -p "$test_dir/go" +cp "$root_dir/tests/runtime/go/main.go" "$test_dir/go/main.go" +cd "$test_dir/go" +go mod init example.com/agentbox-runtime-smoke >/dev/null +GOPROXY=https://proxy.golang.org go get \ + "github.com/abox-dev/sdk/packages/go-sdk@v$version" \ + "github.com/abox-dev/sdk/packages/go-sdk/codeinterpreter@v$version" >/dev/null +go run -tags runtime . diff --git a/scripts/test-release-runtime.sh b/scripts/test-release-runtime.sh index ac005bfdd..e93a6f46f 100755 --- a/scripts/test-release-runtime.sh +++ b/scripts/test-release-runtime.sh @@ -30,3 +30,12 @@ uv pip install --python "$test_dir/python/bin/python" \ "$release_dir"/pypi-code-interpreter/*.whl >/dev/null "$test_dir/python/bin/python" "$root_dir/tests/runtime/core-python.py" "$test_dir/python/bin/python" "$root_dir/tests/runtime/code_interpreter_python.py" + +mkdir -p "$test_dir/go" +cp "$root_dir/tests/runtime/go/main.go" "$test_dir/go/main.go" +cd "$test_dir/go" +go mod init example.com/agentbox-runtime-smoke >/dev/null +go mod edit -replace github.com/abox-dev/sdk/packages/go-sdk="$root_dir/packages/go-sdk" +go mod edit -require github.com/abox-dev/sdk/packages/go-sdk@v0.0.0 +go mod tidy +go run -tags runtime . diff --git a/spec/envd/buf-go.gen.yaml b/spec/envd/buf-go.gen.yaml new file mode 100644 index 000000000..5e37f3491 --- /dev/null +++ b/spec/envd/buf-go.gen.yaml @@ -0,0 +1,16 @@ +version: v1 +plugins: + - plugin: go + out: ../../packages/go-sdk/internal/gen/envd + opt: + - paths=source_relative + - plugin: connect-go + out: ../../packages/go-sdk/internal/gen/envd + opt: + - paths=source_relative + +managed: + enabled: true + optimize_for: SPEED + go_package_prefix: + default: github.com/abox-dev/sdk/packages/go-sdk/internal/gen/envd diff --git a/tests/runtime/go/main.go b/tests/runtime/go/main.go new file mode 100644 index 000000000..9ba73f58c --- /dev/null +++ b/tests/runtime/go/main.go @@ -0,0 +1,45 @@ +//go:build runtime + +package main + +import ( + "context" + "fmt" + "strings" + "time" + + "github.com/abox-dev/sdk/packages/go-sdk" + "github.com/abox-dev/sdk/packages/go-sdk/codeinterpreter" +) + +func main() { + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Minute) + defer cancel() + core, err := agentbox.NewClient() + must(err) + sandbox, err := core.Sandboxes.Create(ctx, &agentbox.CreateSandboxOptions{Timeout: 5 * time.Minute}) + must(err) + result, err := sandbox.Commands.Run(ctx, "printf", &agentbox.CommandOptions{Args: []string{"go-runtime-smoke"}}) + must(err) + if string(result.Stdout) != "go-runtime-smoke" { + panic(fmt.Sprintf("unexpected stdout %q", result.Stdout)) + } + must(sandbox.Kill(ctx)) + + interpreter, err := codeinterpreter.NewClient() + must(err) + codeSandbox, err := interpreter.Create(ctx, &agentbox.CreateSandboxOptions{Timeout: 5 * time.Minute}) + must(err) + execution, err := codeSandbox.RunCode(ctx, "40 + 2", nil) + must(err) + if !strings.Contains(execution.Text(), "42") { + panic(fmt.Sprintf("unexpected execution %#v", execution)) + } + must(codeSandbox.Kill(ctx)) +} + +func must(err error) { + if err != nil { + panic(err) + } +}