diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644
index 0000000..43fd5a7
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,15 @@
+// For format details, see https://aka.ms/devcontainer.json. For config options, see the
+// README at: https://github.com/devcontainers/templates/tree/main/src/debian
+{
+  "name": "Development",
+  "image": "mcr.microsoft.com/devcontainers/typescript-node:latest",
+  "features": {
+    "ghcr.io/devcontainers/features/node:1": {}
+  },
+  "postCreateCommand": "yarn install",
+  "customizations": {
+    "vscode": {
+      "extensions": ["esbenp.prettier-vscode"]
+    }
+  }
+}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..8c21a27
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,81 @@
+name: CI
+on:
+  push:
+    branches-ignore:
+      - 'generated'
+      - 'codegen/**'
+      - 'integrated/**'
+      - 'stl-preview-head/**'
+      - 'stl-preview-base/**'
+
+jobs:
+  lint:
+    timeout-minutes: 10
+    name: lint
+    runs-on: ${{ github.repository == 'stainless-sdks/gitpod-typescript' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
+    steps:
+      - uses: actions/checkout@v4
+
+      - name: Set up Node
+        uses: actions/setup-node@v4
+        with:
+          node-version: '18'
+
+      - name: Bootstrap
+        run: ./scripts/bootstrap
+
+      - name: Check types
+        run: ./scripts/lint
+
+  build:
+    timeout-minutes: 5
+    name: build
+    runs-on: ${{ github.repository == 'stainless-sdks/gitpod-typescript' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
+    permissions:
+      contents: read
+      id-token: write
+    steps:
+      - uses: actions/checkout@v4
+
+      - name: Set up Node
+        uses: actions/setup-node@v4
+        with:
+          node-version: '18'
+
+      - name: Bootstrap
+        run: ./scripts/bootstrap
+
+      - name: Check build
+        run: ./scripts/build
+
+      - name: Get GitHub OIDC Token
+        if: github.repository == 'stainless-sdks/gitpod-typescript'
+        id: github-oidc
+        uses: actions/github-script@v6
+        with:
+          script: core.setOutput('github_token', await core.getIDToken());
+
+      - name: Upload tarball
+        if: github.repository == 'stainless-sdks/gitpod-typescript'
+        env:
+          URL: https://pkg.stainless.com/s
+          AUTH: ${{ steps.github-oidc.outputs.github_token }}
+          SHA: ${{ github.sha }}
+        run: ./scripts/utils/upload-artifact.sh
+  test:
+    timeout-minutes: 10
+    name: test
+    runs-on: ${{ github.repository == 'stainless-sdks/gitpod-typescript' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
+    steps:
+      - uses: actions/checkout@v4
+
+      - name: Set up Node
+        uses: actions/setup-node@v4
+        with:
+          node-version: '18'
+
+      - name: Bootstrap
+        run: ./scripts/bootstrap
+
+      - name: Run tests
+        run: ./scripts/test
diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml
new file mode 100644
index 0000000..d11b246
--- /dev/null
+++ b/.github/workflows/publish-npm.yml
@@ -0,0 +1,32 @@
+# This workflow is triggered when a GitHub release is created.
+# It can also be run manually to re-publish to NPM in case it failed for some reason.
+# You can run this workflow by navigating to https://www.github.com/gitpod-io/gitpod-sdk-typescript/actions/workflows/publish-npm.yml
+name: Publish NPM
+on:
+  workflow_dispatch:
+
+  release:
+    types: [published]
+
+jobs:
+  publish:
+    name: publish
+    runs-on: ubuntu-latest
+
+    steps:
+      - uses: actions/checkout@v4
+
+      - name: Set up Node
+        uses: actions/setup-node@v3
+        with:
+          node-version: '18'
+
+      - name: Install dependencies
+        run: |
+          yarn install
+
+      - name: Publish to NPM
+        run: |
+          bash ./bin/publish-npm
+        env:
+          NPM_TOKEN: ${{ secrets.GITPOD_NPM_TOKEN || secrets.NPM_TOKEN }}
diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml
new file mode 100644
index 0000000..f875bac
--- /dev/null
+++ b/.github/workflows/release-doctor.yml
@@ -0,0 +1,21 @@
+name: Release Doctor
+on:
+  pull_request:
+    branches:
+      - main
+  workflow_dispatch:
+
+jobs:
+  release_doctor:
+    name: release doctor
+    runs-on: ubuntu-latest
+    if: github.repository == 'gitpod-io/gitpod-sdk-typescript' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next')
+
+    steps:
+      - uses: actions/checkout@v4
+
+      - name: Check release environment
+        run: |
+          bash ./bin/check-release-environment
+        env:
+          NPM_TOKEN: ${{ secrets.GITPOD_NPM_TOKEN || secrets.NPM_TOKEN }}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d98d51a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+.prism.log
+node_modules
+yarn-error.log
+codegen.log
+Brewfile.lock.json
+dist
+dist-deno
+/*.tgz
+.idea/
+
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 0000000..3548c5a
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,7 @@
+CHANGELOG.md
+/ecosystem-tests/*/**
+/node_modules
+/deno
+
+# don't format tsc output, will break source maps
+/dist
diff --git a/.prettierrc.json b/.prettierrc.json
new file mode 100644
index 0000000..af75ada
--- /dev/null
+++ b/.prettierrc.json
@@ -0,0 +1,7 @@
+{
+  "arrowParens": "always",
+  "experimentalTernaries": true,
+  "printWidth": 110,
+  "singleQuote": true,
+  "trailingComma": "all"
+}
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
new file mode 100644
index 0000000..f1c1e58
--- /dev/null
+++ b/.release-please-manifest.json
@@ -0,0 +1,3 @@
+{
+  ".": "0.5.0"
+}
diff --git a/.stats.yml b/.stats.yml
new file mode 100644
index 0000000..9cdc117
--- /dev/null
+++ b/.stats.yml
@@ -0,0 +1,4 @@
+configured_endpoints: 116
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-2e6ddfc9da00e33fcf13baf0b67012b97f051fa986658ff114fde989e56caa94.yml
+openapi_spec_hash: 5af02ea2008312d609394e548756e761
+config_hash: 60929489bdc1eaf979e7ef74fdd17b94
diff --git a/Brewfile b/Brewfile
new file mode 100644
index 0000000..e4feee6
--- /dev/null
+++ b/Brewfile
@@ -0,0 +1 @@
+brew "node"
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..60cc611
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,107 @@
+## Setting up the environment
+
+This repository uses [`yarn@v1`](https://classic.yarnpkg.com/lang/en/docs/install).
+Other package managers may work but are not officially supported for development.
+
+To set up the repository, run:
+
+```sh
+$ yarn
+$ yarn build
+```
+
+This will install all the required dependencies and build output files to `dist/`.
+
+## Modifying/Adding code
+
+Most of the SDK is generated code. Modifications to code will be persisted between generations, but may
+result in merge conflicts between manual patches and changes from the generator. The generator will never
+modify the contents of the `src/lib/` and `examples/` directories.
+
+## Adding and running examples
+
+All files in the `examples/` directory are not modified by the generator and can be freely edited or added to.
+
+```ts
+// add an example to examples/<your-example>.ts
+
+#!/usr/bin/env -S npm run tsn -T
+…
+```
+
+```sh
+$ chmod +x examples/<your-example>.ts
+# run the example against your api
+$ yarn tsn -T examples/<your-example>.ts
+```
+
+## Using the repository from source
+
+If you’d like to use the repository from source, you can either install from git or link to a cloned repository:
+
+To install via git:
+
+```sh
+$ npm install git+ssh://git@github.com:gitpod-io/gitpod-sdk-typescript.git
+```
+
+Alternatively, to link a local copy of the repo:
+
+```sh
+# Clone
+$ git clone https://www.github.com/gitpod-io/gitpod-sdk-typescript
+$ cd gitpod-sdk-typescript
+
+# With yarn
+$ yarn link
+$ cd ../my-package
+$ yarn link @gitpod/sdk
+
+# With pnpm
+$ pnpm link --global
+$ cd ../my-package
+$ pnpm link -—global @gitpod/sdk
+```
+
+## Running tests
+
+Most tests require you to [set up a mock server](https://github.com/stoplightio/prism) against the OpenAPI spec to run the tests.
+
+```sh
+$ npx prism mock path/to/your/openapi.yml
+```
+
+```sh
+$ yarn run test
+```
+
+## Linting and formatting
+
+This repository uses [prettier](https://www.npmjs.com/package/prettier) and
+[eslint](https://www.npmjs.com/package/eslint) to format the code in the repository.
+
+To lint:
+
+```sh
+$ yarn lint
+```
+
+To format and fix all lint issues automatically:
+
+```sh
+$ yarn fix
+```
+
+## Publishing and releases
+
+Changes made to this repository via the automated release PR pipeline should publish to npm automatically. If
+the changes aren't made through the automated pipeline, you may want to make releases manually.
+
+### Publish with a GitHub workflow
+
+You can release to package managers by using [the `Publish NPM` GitHub action](https://www.github.com/gitpod-io/gitpod-sdk-typescript/actions/workflows/publish-npm.yml). This requires a setup organization or repository secret to be set up.
+
+### Publish manually
+
+If you need to manually release a package, you can run the `bin/publish-npm` script with an `NPM_TOKEN` set on
+the environment.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..01fd4f2
--- /dev/null
+++ b/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 2025 Gitpod
+
+   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/README.md b/README.md
index 5882588..a2f50c6 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,403 @@
-# gitpod-typescript
\ No newline at end of file
+# Gitpod TypeScript API Library
+
+[![NPM version](https://img.shields.io/npm/v/@gitpod/sdk.svg)](https://npmjs.org/package/@gitpod/sdk) ![npm bundle size](https://img.shields.io/bundlephobia/minzip/@gitpod/sdk)
+
+This library provides convenient access to the Gitpod REST API from server-side TypeScript or JavaScript.
+
+The REST API documentation can be found on [docs.gitpod.io](https://docs.gitpod.io). The full API of this library can be found in [api.md](api.md).
+
+It is generated with [Stainless](https://www.stainless.com/).
+
+## Installation
+
+```sh
+npm install @gitpod/sdk
+```
+
+## Usage
+
+The full API of this library can be found in [api.md](api.md).
+
+<!-- prettier-ignore -->
+```js
+import Gitpod from '@gitpod/sdk';
+
+const client = new Gitpod({
+  bearerToken: process.env['GITPOD_API_KEY'], // This is the default and can be omitted
+});
+
+async function main() {
+  const response = await client.identity.getAuthenticatedIdentity();
+
+  console.log(response.organizationId);
+}
+
+main();
+```
+
+### Request & Response types
+
+This library includes TypeScript definitions for all request params and response fields. You may import and use them like so:
+
+<!-- prettier-ignore -->
+```ts
+import Gitpod from '@gitpod/sdk';
+
+const client = new Gitpod({
+  bearerToken: process.env['GITPOD_API_KEY'], // This is the default and can be omitted
+});
+
+async function main() {
+  const response: Gitpod.IdentityGetAuthenticatedIdentityResponse =
+    await client.identity.getAuthenticatedIdentity();
+}
+
+main();
+```
+
+Documentation for each method, request param, and response field are available in docstrings and will appear on hover in most modern editors.
+
+## Handling errors
+
+When the library is unable to connect to the API,
+or if the API returns a non-success status code (i.e., 4xx or 5xx response),
+a subclass of `APIError` will be thrown:
+
+<!-- prettier-ignore -->
+```ts
+async function main() {
+  const response = await client.identity.getAuthenticatedIdentity().catch(async (err) => {
+    if (err instanceof Gitpod.APIError) {
+      console.log(err.status); // 400
+      console.log(err.name); // BadRequestError
+      console.log(err.headers); // {server: 'nginx', ...}
+    } else {
+      throw err;
+    }
+  });
+}
+
+main();
+```
+
+Error codes are as follows:
+
+| Status Code | Error Type                 |
+| ----------- | -------------------------- |
+| 400         | `BadRequestError`          |
+| 401         | `AuthenticationError`      |
+| 403         | `PermissionDeniedError`    |
+| 404         | `NotFoundError`            |
+| 422         | `UnprocessableEntityError` |
+| 429         | `RateLimitError`           |
+| >=500       | `InternalServerError`      |
+| N/A         | `APIConnectionError`       |
+
+### Retries
+
+Certain errors will be automatically retried 2 times by default, with a short exponential backoff.
+Connection errors (for example, due to a network connectivity problem), 408 Request Timeout, 409 Conflict,
+429 Rate Limit, and >=500 Internal errors will all be retried by default.
+
+You can use the `maxRetries` option to configure or disable this:
+
+<!-- prettier-ignore -->
+```js
+// Configure the default for all requests:
+const client = new Gitpod({
+  maxRetries: 0, // default is 2
+});
+
+// Or, configure per-request:
+await client.identity.getAuthenticatedIdentity({
+  maxRetries: 5,
+});
+```
+
+### Timeouts
+
+Requests time out after 1 minute by default. You can configure this with a `timeout` option:
+
+<!-- prettier-ignore -->
+```ts
+// Configure the default for all requests:
+const client = new Gitpod({
+  timeout: 20 * 1000, // 20 seconds (default is 1 minute)
+});
+
+// Override per-request:
+await client.identity.getAuthenticatedIdentity({
+  timeout: 5 * 1000,
+});
+```
+
+On timeout, an `APIConnectionTimeoutError` is thrown.
+
+Note that requests which time out will be [retried twice by default](#retries).
+
+## Auto-pagination
+
+List methods in the Gitpod API are paginated.
+You can use the `for await … of` syntax to iterate through items across all pages:
+
+```ts
+async function fetchAllEnvironments(params) {
+  const allEnvironments = [];
+  // Automatically fetches more pages as needed.
+  for await (const environment of client.environments.list()) {
+    allEnvironments.push(environment);
+  }
+  return allEnvironments;
+}
+```
+
+Alternatively, you can request a single page at a time:
+
+```ts
+let page = await client.environments.list();
+for (const environment of page.environments) {
+  console.log(environment);
+}
+
+// Convenience methods are provided for manually paginating:
+while (page.hasNextPage()) {
+  page = await page.getNextPage();
+  // ...
+}
+```
+
+## Advanced Usage
+
+### Accessing raw Response data (e.g., headers)
+
+The "raw" `Response` returned by `fetch()` can be accessed through the `.asResponse()` method on the `APIPromise` type that all methods return.
+This method returns as soon as the headers for a successful response are received and does not consume the response body, so you are free to write custom parsing or streaming logic.
+
+You can also use the `.withResponse()` method to get the raw `Response` along with the parsed data.
+Unlike `.asResponse()` this method consumes the body, returning once it is parsed.
+
+<!-- prettier-ignore -->
+```ts
+const client = new Gitpod();
+
+const response = await client.identity.getAuthenticatedIdentity().asResponse();
+console.log(response.headers.get('X-My-Header'));
+console.log(response.statusText); // access the underlying Response object
+
+const { data: response, response: raw } = await client.identity.getAuthenticatedIdentity().withResponse();
+console.log(raw.headers.get('X-My-Header'));
+console.log(response.organizationId);
+```
+
+### Logging
+
+> [!IMPORTANT]
+> All log messages are intended for debugging only. The format and content of log messages
+> may change between releases.
+
+#### Log levels
+
+The log level can be configured in two ways:
+
+1. Via the `GITPOD_LOG` environment variable
+2. Using the `logLevel` client option (overrides the environment variable if set)
+
+```ts
+import Gitpod from '@gitpod/sdk';
+
+const client = new Gitpod({
+  logLevel: 'debug', // Show all log messages
+});
+```
+
+Available log levels, from most to least verbose:
+
+- `'debug'` - Show debug messages, info, warnings, and errors
+- `'info'` - Show info messages, warnings, and errors
+- `'warn'` - Show warnings and errors (default)
+- `'error'` - Show only errors
+- `'off'` - Disable all logging
+
+At the `'debug'` level, all HTTP requests and responses are logged, including headers and bodies.
+Some authentication-related headers are redacted, but sensitive data in request and response bodies
+may still be visible.
+
+#### Custom logger
+
+By default, this library logs to `globalThis.console`. You can also provide a custom logger.
+Most logging libraries are supported, including [pino](https://www.npmjs.com/package/pino), [winston](https://www.npmjs.com/package/winston), [bunyan](https://www.npmjs.com/package/bunyan), [consola](https://www.npmjs.com/package/consola), [signale](https://www.npmjs.com/package/signale), and [@std/log](https://jsr.io/@std/log). If your logger doesn't work, please open an issue.
+
+When providing a custom logger, the `logLevel` option still controls which messages are emitted, messages
+below the configured level will not be sent to your logger.
+
+```ts
+import Gitpod from '@gitpod/sdk';
+import pino from 'pino';
+
+const logger = pino();
+
+const client = new Gitpod({
+  logger: logger.child({ name: 'Gitpod' }),
+  logLevel: 'debug', // Send all messages to pino, allowing it to filter
+});
+```
+
+### Making custom/undocumented requests
+
+This library is typed for convenient access to the documented API. If you need to access undocumented
+endpoints, params, or response properties, the library can still be used.
+
+#### Undocumented endpoints
+
+To make requests to undocumented endpoints, you can use `client.get`, `client.post`, and other HTTP verbs.
+Options on the client, such as retries, will be respected when making these requests.
+
+```ts
+await client.post('/some/path', {
+  body: { some_prop: 'foo' },
+  query: { some_query_arg: 'bar' },
+});
+```
+
+#### Undocumented request params
+
+To make requests using undocumented parameters, you may use `// @ts-expect-error` on the undocumented
+parameter. This library doesn't validate at runtime that the request matches the type, so any extra values you
+send will be sent as-is.
+
+```ts
+client.foo.create({
+  foo: 'my_param',
+  bar: 12,
+  // @ts-expect-error baz is not yet public
+  baz: 'undocumented option',
+});
+```
+
+For requests with the `GET` verb, any extra params will be in the query, all other requests will send the
+extra param in the body.
+
+If you want to explicitly send an extra argument, you can do so with the `query`, `body`, and `headers` request
+options.
+
+#### Undocumented response properties
+
+To access undocumented response properties, you may access the response object with `// @ts-expect-error` on
+the response object, or cast the response object to the requisite type. Like the request params, we do not
+validate or strip extra properties from the response from the API.
+
+### Customizing the fetch client
+
+By default, this library expects a global `fetch` function is defined.
+
+If you want to use a different `fetch` function, you can either polyfill the global:
+
+```ts
+import fetch from 'my-fetch';
+
+globalThis.fetch = fetch;
+```
+
+Or pass it to the client:
+
+```ts
+import Gitpod from '@gitpod/sdk';
+import fetch from 'my-fetch';
+
+const client = new Gitpod({ fetch });
+```
+
+### Fetch options
+
+If you want to set custom `fetch` options without overriding the `fetch` function, you can provide a `fetchOptions` object when instantiating the client or making a request. (Request-specific options override client options.)
+
+```ts
+import Gitpod from '@gitpod/sdk';
+
+const client = new Gitpod({
+  fetchOptions: {
+    // `RequestInit` options
+  },
+});
+```
+
+#### Configuring proxies
+
+To modify proxy behavior, you can provide custom `fetchOptions` that add runtime-specific proxy
+options to requests:
+
+<img src="https://raw.githubusercontent.com/stainless-api/sdk-assets/refs/heads/main/node.svg" align="top" width="18" height="21"> **Node** <sup>[[docs](https://github.com/nodejs/undici/blob/main/docs/docs/api/ProxyAgent.md#example---proxyagent-with-fetch)]</sup>
+
+```ts
+import Gitpod from '@gitpod/sdk';
+import * as undici from 'undici';
+
+const proxyAgent = new undici.ProxyAgent('http://localhost:8888');
+const client = new Gitpod({
+  fetchOptions: {
+    dispatcher: proxyAgent,
+  },
+});
+```
+
+<img src="https://raw.githubusercontent.com/stainless-api/sdk-assets/refs/heads/main/bun.svg" align="top" width="18" height="21"> **Bun** <sup>[[docs](https://bun.sh/guides/http/proxy)]</sup>
+
+```ts
+import Gitpod from '@gitpod/sdk';
+
+const client = new Gitpod({
+  fetchOptions: {
+    proxy: 'http://localhost:8888',
+  },
+});
+```
+
+<img src="https://raw.githubusercontent.com/stainless-api/sdk-assets/refs/heads/main/deno.svg" align="top" width="18" height="21"> **Deno** <sup>[[docs](https://docs.deno.com/api/deno/~/Deno.createHttpClient)]</sup>
+
+```ts
+import Gitpod from 'npm:@gitpod/sdk';
+
+const httpClient = Deno.createHttpClient({ proxy: { url: 'http://localhost:8888' } });
+const client = new Gitpod({
+  fetchOptions: {
+    client: httpClient,
+  },
+});
+```
+
+## Frequently Asked Questions
+
+## Semantic versioning
+
+This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) conventions, though certain backwards-incompatible changes may be released as minor versions:
+
+1. Changes that only affect static types, without breaking runtime behavior.
+2. Changes to library internals which are technically public but not intended or documented for external use. _(Please open a GitHub issue to let us know if you are relying on such internals.)_
+3. Changes that we do not expect to impact the vast majority of users in practice.
+
+We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.
+
+We are keen for your feedback; please open an [issue](https://www.github.com/gitpod-io/gitpod-sdk-typescript/issues) with questions, bugs, or suggestions.
+
+## Requirements
+
+TypeScript >= 4.9 is supported.
+
+The following runtimes are supported:
+
+- Web browsers (Up-to-date Chrome, Firefox, Safari, Edge, and more)
+- Node.js 18 LTS or later ([non-EOL](https://endoflife.date/nodejs)) versions.
+- Deno v1.28.0 or higher.
+- Bun 1.0 or later.
+- Cloudflare Workers.
+- Vercel Edge Runtime.
+- Jest 28 or greater with the `"node"` environment (`"jsdom"` is not supported at this time).
+- Nitro v2.6 or greater.
+
+Note that React Native is not supported at this time.
+
+If you are interested in other runtime environments, please open or upvote an issue on GitHub.
+
+## Contributing
+
+See [the contributing documentation](./CONTRIBUTING.md).
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..2b0ed90
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,27 @@
+# Security Policy
+
+## Reporting Security Issues
+
+This SDK is generated by [Stainless Software Inc](http://stainless.com). Stainless takes security seriously, and encourages you to report any security vulnerability promptly so that appropriate action can be taken.
+
+To report a security issue, please contact the Stainless team at security@stainless.com.
+
+## Responsible Disclosure
+
+We appreciate the efforts of security researchers and individuals who help us maintain the security of
+SDKs we generate. If you believe you have found a security vulnerability, please adhere to responsible
+disclosure practices by allowing us a reasonable amount of time to investigate and address the issue
+before making any information public.
+
+## Reporting Non-SDK Related Security Issues
+
+If you encounter security issues that are not directly related to SDKs but pertain to the services
+or products provided by Gitpod please follow the respective company's security reporting guidelines.
+
+### Gitpod Terms and Policies
+
+Please contact dev-feedback@gitpod.com for any questions or concerns regarding security of our services.
+
+---
+
+Thank you for helping us keep the SDKs and systems they interact with secure.
diff --git a/api.md b/api.md
new file mode 100644
index 0000000..516cda9
--- /dev/null
+++ b/api.md
@@ -0,0 +1,535 @@
+# Shared
+
+Types:
+
+- <code><a href="./src/resources/shared.ts">AutomationTrigger</a></code>
+- <code><a href="./src/resources/shared.ts">EnvironmentClass</a></code>
+- <code><a href="./src/resources/shared.ts">ErrorCode</a></code>
+- <code><a href="./src/resources/shared.ts">FieldValue</a></code>
+- <code><a href="./src/resources/shared.ts">OrganizationRole</a></code>
+- <code><a href="./src/resources/shared.ts">Principal</a></code>
+- <code><a href="./src/resources/shared.ts">RunsOn</a></code>
+- <code><a href="./src/resources/shared.ts">Subject</a></code>
+- <code><a href="./src/resources/shared.ts">Task</a></code>
+- <code><a href="./src/resources/shared.ts">TaskExecution</a></code>
+- <code><a href="./src/resources/shared.ts">TaskExecutionMetadata</a></code>
+- <code><a href="./src/resources/shared.ts">TaskExecutionPhase</a></code>
+- <code><a href="./src/resources/shared.ts">TaskExecutionSpec</a></code>
+- <code><a href="./src/resources/shared.ts">TaskExecutionStatus</a></code>
+- <code><a href="./src/resources/shared.ts">TaskMetadata</a></code>
+- <code><a href="./src/resources/shared.ts">TaskSpec</a></code>
+- <code><a href="./src/resources/shared.ts">UserStatus</a></code>
+
+# Accounts
+
+Types:
+
+- <code><a href="./src/resources/accounts.ts">Account</a></code>
+- <code><a href="./src/resources/accounts.ts">AccountMembership</a></code>
+- <code><a href="./src/resources/accounts.ts">JoinableOrganization</a></code>
+- <code><a href="./src/resources/accounts.ts">LoginProvider</a></code>
+- <code><a href="./src/resources/accounts.ts">AccountRetrieveResponse</a></code>
+- <code><a href="./src/resources/accounts.ts">AccountDeleteResponse</a></code>
+- <code><a href="./src/resources/accounts.ts">AccountGetSSOLoginURLResponse</a></code>
+
+Methods:
+
+- <code title="post /gitpod.v1.AccountService/GetAccount">client.accounts.<a href="./src/resources/accounts.ts">retrieve</a>({ ...params }) -> AccountRetrieveResponse</code>
+- <code title="post /gitpod.v1.AccountService/DeleteAccount">client.accounts.<a href="./src/resources/accounts.ts">delete</a>({ ...params }) -> unknown</code>
+- <code title="post /gitpod.v1.AccountService/GetSSOLoginURL">client.accounts.<a href="./src/resources/accounts.ts">getSSOLoginURL</a>({ ...params }) -> AccountGetSSOLoginURLResponse</code>
+- <code title="post /gitpod.v1.AccountService/ListLoginProviders">client.accounts.<a href="./src/resources/accounts.ts">listLoginProviders</a>({ ...params }) -> LoginProvidersLoginProvidersPage</code>
+
+# Editors
+
+Types:
+
+- <code><a href="./src/resources/editors.ts">Editor</a></code>
+- <code><a href="./src/resources/editors.ts">EditorRetrieveResponse</a></code>
+- <code><a href="./src/resources/editors.ts">EditorResolveURLResponse</a></code>
+
+Methods:
+
+- <code title="post /gitpod.v1.EditorService/GetEditor">client.editors.<a href="./src/resources/editors.ts">retrieve</a>({ ...params }) -> EditorRetrieveResponse</code>
+- <code title="post /gitpod.v1.EditorService/ListEditors">client.editors.<a href="./src/resources/editors.ts">list</a>({ ...params }) -> EditorsEditorsPage</code>
+- <code title="post /gitpod.v1.EditorService/ResolveEditorURL">client.editors.<a href="./src/resources/editors.ts">resolveURL</a>({ ...params }) -> EditorResolveURLResponse</code>
+
+# Environments
+
+Types:
+
+- <code><a href="./src/resources/environments/environments.ts">AdmissionLevel</a></code>
+- <code><a href="./src/resources/environments/environments.ts">Environment</a></code>
+- <code><a href="./src/resources/environments/environments.ts">EnvironmentActivitySignal</a></code>
+- <code><a href="./src/resources/environments/environments.ts">EnvironmentMetadata</a></code>
+- <code><a href="./src/resources/environments/environments.ts">EnvironmentPhase</a></code>
+- <code><a href="./src/resources/environments/environments.ts">EnvironmentSpec</a></code>
+- <code><a href="./src/resources/environments/environments.ts">EnvironmentStatus</a></code>
+- <code><a href="./src/resources/environments/environments.ts">EnvironmentCreateResponse</a></code>
+- <code><a href="./src/resources/environments/environments.ts">EnvironmentRetrieveResponse</a></code>
+- <code><a href="./src/resources/environments/environments.ts">EnvironmentUpdateResponse</a></code>
+- <code><a href="./src/resources/environments/environments.ts">EnvironmentDeleteResponse</a></code>
+- <code><a href="./src/resources/environments/environments.ts">EnvironmentCreateEnvironmentTokenResponse</a></code>
+- <code><a href="./src/resources/environments/environments.ts">EnvironmentCreateFromProjectResponse</a></code>
+- <code><a href="./src/resources/environments/environments.ts">EnvironmentCreateLogsTokenResponse</a></code>
+- <code><a href="./src/resources/environments/environments.ts">EnvironmentMarkActiveResponse</a></code>
+- <code><a href="./src/resources/environments/environments.ts">EnvironmentStartResponse</a></code>
+- <code><a href="./src/resources/environments/environments.ts">EnvironmentStopResponse</a></code>
+
+Methods:
+
+- <code title="post /gitpod.v1.EnvironmentService/CreateEnvironment">client.environments.<a href="./src/resources/environments/environments.ts">create</a>({ ...params }) -> EnvironmentCreateResponse</code>
+- <code title="post /gitpod.v1.EnvironmentService/GetEnvironment">client.environments.<a href="./src/resources/environments/environments.ts">retrieve</a>({ ...params }) -> EnvironmentRetrieveResponse</code>
+- <code title="post /gitpod.v1.EnvironmentService/UpdateEnvironment">client.environments.<a href="./src/resources/environments/environments.ts">update</a>({ ...params }) -> unknown</code>
+- <code title="post /gitpod.v1.EnvironmentService/ListEnvironments">client.environments.<a href="./src/resources/environments/environments.ts">list</a>({ ...params }) -> EnvironmentsEnvironmentsPage</code>
+- <code title="post /gitpod.v1.EnvironmentService/DeleteEnvironment">client.environments.<a href="./src/resources/environments/environments.ts">delete</a>({ ...params }) -> unknown</code>
+- <code title="post /gitpod.v1.EnvironmentService/CreateEnvironmentAccessToken">client.environments.<a href="./src/resources/environments/environments.ts">createEnvironmentToken</a>({ ...params }) -> EnvironmentCreateEnvironmentTokenResponse</code>
+- <code title="post /gitpod.v1.EnvironmentService/CreateEnvironmentFromProject">client.environments.<a href="./src/resources/environments/environments.ts">createFromProject</a>({ ...params }) -> EnvironmentCreateFromProjectResponse</code>
+- <code title="post /gitpod.v1.EnvironmentService/CreateEnvironmentLogsToken">client.environments.<a href="./src/resources/environments/environments.ts">createLogsToken</a>({ ...params }) -> EnvironmentCreateLogsTokenResponse</code>
+- <code title="post /gitpod.v1.EnvironmentService/MarkEnvironmentActive">client.environments.<a href="./src/resources/environments/environments.ts">markActive</a>({ ...params }) -> unknown</code>
+- <code title="post /gitpod.v1.EnvironmentService/StartEnvironment">client.environments.<a href="./src/resources/environments/environments.ts">start</a>({ ...params }) -> unknown</code>
+- <code title="post /gitpod.v1.EnvironmentService/StopEnvironment">client.environments.<a href="./src/resources/environments/environments.ts">stop</a>({ ...params }) -> unknown</code>
+
+## Automations
+
+Types:
+
+- <code><a href="./src/resources/environments/automations/automations.ts">AutomationsFile</a></code>
+- <code><a href="./src/resources/environments/automations/automations.ts">AutomationUpsertResponse</a></code>
+
+Methods:
+
+- <code title="post /gitpod.v1.EnvironmentAutomationService/UpsertAutomationsFile">client.environments.automations.<a href="./src/resources/environments/automations/automations.ts">upsert</a>({ ...params }) -> AutomationUpsertResponse</code>
+
+### Services
+
+Types:
+
+- <code><a href="./src/resources/environments/automations/services.ts">Service</a></code>
+- <code><a href="./src/resources/environments/automations/services.ts">ServiceMetadata</a></code>
+- <code><a href="./src/resources/environments/automations/services.ts">ServicePhase</a></code>
+- <code><a href="./src/resources/environments/automations/services.ts">ServiceSpec</a></code>
+- <code><a href="./src/resources/environments/automations/services.ts">ServiceStatus</a></code>
+- <code><a href="./src/resources/environments/automations/services.ts">ServiceCreateResponse</a></code>
+- <code><a href="./src/resources/environments/automations/services.ts">ServiceRetrieveResponse</a></code>
+- <code><a href="./src/resources/environments/automations/services.ts">ServiceUpdateResponse</a></code>
+- <code><a href="./src/resources/environments/automations/services.ts">ServiceDeleteResponse</a></code>
+- <code><a href="./src/resources/environments/automations/services.ts">ServiceStartResponse</a></code>
+- <code><a href="./src/resources/environments/automations/services.ts">ServiceStopResponse</a></code>
+
+Methods:
+
+- <code title="post /gitpod.v1.EnvironmentAutomationService/CreateService">client.environments.automations.services.<a href="./src/resources/environments/automations/services.ts">create</a>({ ...params }) -> ServiceCreateResponse</code>
+- <code title="post /gitpod.v1.EnvironmentAutomationService/GetService">client.environments.automations.services.<a href="./src/resources/environments/automations/services.ts">retrieve</a>({ ...params }) -> ServiceRetrieveResponse</code>
+- <code title="post /gitpod.v1.EnvironmentAutomationService/UpdateService">client.environments.automations.services.<a href="./src/resources/environments/automations/services.ts">update</a>({ ...params }) -> unknown</code>
+- <code title="post /gitpod.v1.EnvironmentAutomationService/ListServices">client.environments.automations.services.<a href="./src/resources/environments/automations/services.ts">list</a>({ ...params }) -> ServicesServicesPage</code>
+- <code title="post /gitpod.v1.EnvironmentAutomationService/DeleteService">client.environments.automations.services.<a href="./src/resources/environments/automations/services.ts">delete</a>({ ...params }) -> unknown</code>
+- <code title="post /gitpod.v1.EnvironmentAutomationService/StartService">client.environments.automations.services.<a href="./src/resources/environments/automations/services.ts">start</a>({ ...params }) -> unknown</code>
+- <code title="post /gitpod.v1.EnvironmentAutomationService/StopService">client.environments.automations.services.<a href="./src/resources/environments/automations/services.ts">stop</a>({ ...params }) -> unknown</code>
+
+### Tasks
+
+Types:
+
+- <code><a href="./src/resources/environments/automations/tasks/tasks.ts">TaskCreateResponse</a></code>
+- <code><a href="./src/resources/environments/automations/tasks/tasks.ts">TaskRetrieveResponse</a></code>
+- <code><a href="./src/resources/environments/automations/tasks/tasks.ts">TaskUpdateResponse</a></code>
+- <code><a href="./src/resources/environments/automations/tasks/tasks.ts">TaskDeleteResponse</a></code>
+- <code><a href="./src/resources/environments/automations/tasks/tasks.ts">TaskStartResponse</a></code>
+
+Methods:
+
+- <code title="post /gitpod.v1.EnvironmentAutomationService/CreateTask">client.environments.automations.tasks.<a href="./src/resources/environments/automations/tasks/tasks.ts">create</a>({ ...params }) -> TaskCreateResponse</code>
+- <code title="post /gitpod.v1.EnvironmentAutomationService/GetTask">client.environments.automations.tasks.<a href="./src/resources/environments/automations/tasks/tasks.ts">retrieve</a>({ ...params }) -> TaskRetrieveResponse</code>
+- <code title="post /gitpod.v1.EnvironmentAutomationService/UpdateTask">client.environments.automations.tasks.<a href="./src/resources/environments/automations/tasks/tasks.ts">update</a>({ ...params }) -> unknown</code>
+- <code title="post /gitpod.v1.EnvironmentAutomationService/ListTasks">client.environments.automations.tasks.<a href="./src/resources/environments/automations/tasks/tasks.ts">list</a>({ ...params }) -> TasksTasksPage</code>
+- <code title="post /gitpod.v1.EnvironmentAutomationService/DeleteTask">client.environments.automations.tasks.<a href="./src/resources/environments/automations/tasks/tasks.ts">delete</a>({ ...params }) -> unknown</code>
+- <code title="post /gitpod.v1.EnvironmentAutomationService/StartTask">client.environments.automations.tasks.<a href="./src/resources/environments/automations/tasks/tasks.ts">start</a>({ ...params }) -> TaskStartResponse</code>
+
+#### Executions
+
+Types:
+
+- <code><a href="./src/resources/environments/automations/tasks/executions.ts">ExecutionRetrieveResponse</a></code>
+- <code><a href="./src/resources/environments/automations/tasks/executions.ts">ExecutionStopResponse</a></code>
+
+Methods:
+
+- <code title="post /gitpod.v1.EnvironmentAutomationService/GetTaskExecution">client.environments.automations.tasks.executions.<a href="./src/resources/environments/automations/tasks/executions.ts">retrieve</a>({ ...params }) -> ExecutionRetrieveResponse</code>
+- <code title="post /gitpod.v1.EnvironmentAutomationService/ListTaskExecutions">client.environments.automations.tasks.executions.<a href="./src/resources/environments/automations/tasks/executions.ts">list</a>({ ...params }) -> TaskExecutionsTaskExecutionsPage</code>
+- <code title="post /gitpod.v1.EnvironmentAutomationService/StopTaskExecution">client.environments.automations.tasks.executions.<a href="./src/resources/environments/automations/tasks/executions.ts">stop</a>({ ...params }) -> unknown</code>
+
+## Classes
+
+Methods:
+
+- <code title="post /gitpod.v1.EnvironmentService/ListEnvironmentClasses">client.environments.classes.<a href="./src/resources/environments/classes.ts">list</a>({ ...params }) -> EnvironmentClassesEnvironmentClassesPage</code>
+
+# Events
+
+Types:
+
+- <code><a href="./src/resources/events.ts">ResourceOperation</a></code>
+- <code><a href="./src/resources/events.ts">ResourceType</a></code>
+- <code><a href="./src/resources/events.ts">EventListResponse</a></code>
+- <code><a href="./src/resources/events.ts">EventWatchResponse</a></code>
+
+Methods:
+
+- <code title="post /gitpod.v1.EventService/ListAuditLogs">client.events.<a href="./src/resources/events.ts">list</a>({ ...params }) -> EventListResponsesEntriesPage</code>
+- <code title="post /gitpod.v1.EventService/WatchEvents">client.events.<a href="./src/resources/events.ts">watch</a>({ ...params }) -> EventWatchResponse</code>
+
+# Groups
+
+Types:
+
+- <code><a href="./src/resources/groups.ts">Group</a></code>
+
+Methods:
+
+- <code title="post /gitpod.v1.GroupService/ListGroups">client.groups.<a href="./src/resources/groups.ts">list</a>({ ...params }) -> GroupsGroupsPage</code>
+
+# Identity
+
+Types:
+
+- <code><a href="./src/resources/identity.ts">IDTokenVersion</a></code>
+- <code><a href="./src/resources/identity.ts">IdentityExchangeTokenResponse</a></code>
+- <code><a href="./src/resources/identity.ts">IdentityGetAuthenticatedIdentityResponse</a></code>
+- <code><a href="./src/resources/identity.ts">IdentityGetIDTokenResponse</a></code>
+
+Methods:
+
+- <code title="post /gitpod.v1.IdentityService/ExchangeToken">client.identity.<a href="./src/resources/identity.ts">exchangeToken</a>({ ...params }) -> IdentityExchangeTokenResponse</code>
+- <code title="post /gitpod.v1.IdentityService/GetAuthenticatedIdentity">client.identity.<a href="./src/resources/identity.ts">getAuthenticatedIdentity</a>({ ...params }) -> IdentityGetAuthenticatedIdentityResponse</code>
+- <code title="post /gitpod.v1.IdentityService/GetIDToken">client.identity.<a href="./src/resources/identity.ts">getIDToken</a>({ ...params }) -> IdentityGetIDTokenResponse</code>
+
+# Organizations
+
+Types:
+
+- <code><a href="./src/resources/organizations/organizations.ts">InviteDomains</a></code>
+- <code><a href="./src/resources/organizations/organizations.ts">Organization</a></code>
+- <code><a href="./src/resources/organizations/organizations.ts">OrganizationMember</a></code>
+- <code><a href="./src/resources/organizations/organizations.ts">OrganizationTier</a></code>
+- <code><a href="./src/resources/organizations/organizations.ts">OrganizationCreateResponse</a></code>
+- <code><a href="./src/resources/organizations/organizations.ts">OrganizationRetrieveResponse</a></code>
+- <code><a href="./src/resources/organizations/organizations.ts">OrganizationUpdateResponse</a></code>
+- <code><a href="./src/resources/organizations/organizations.ts">OrganizationDeleteResponse</a></code>
+- <code><a href="./src/resources/organizations/organizations.ts">OrganizationJoinResponse</a></code>
+- <code><a href="./src/resources/organizations/organizations.ts">OrganizationLeaveResponse</a></code>
+- <code><a href="./src/resources/organizations/organizations.ts">OrganizationSetRoleResponse</a></code>
+
+Methods:
+
+- <code title="post /gitpod.v1.OrganizationService/CreateOrganization">client.organizations.<a href="./src/resources/organizations/organizations.ts">create</a>({ ...params }) -> OrganizationCreateResponse</code>
+- <code title="post /gitpod.v1.OrganizationService/GetOrganization">client.organizations.<a href="./src/resources/organizations/organizations.ts">retrieve</a>({ ...params }) -> OrganizationRetrieveResponse</code>
+- <code title="post /gitpod.v1.OrganizationService/UpdateOrganization">client.organizations.<a href="./src/resources/organizations/organizations.ts">update</a>({ ...params }) -> OrganizationUpdateResponse</code>
+- <code title="post /gitpod.v1.OrganizationService/DeleteOrganization">client.organizations.<a href="./src/resources/organizations/organizations.ts">delete</a>({ ...params }) -> unknown</code>
+- <code title="post /gitpod.v1.OrganizationService/JoinOrganization">client.organizations.<a href="./src/resources/organizations/organizations.ts">join</a>({ ...params }) -> OrganizationJoinResponse</code>
+- <code title="post /gitpod.v1.OrganizationService/LeaveOrganization">client.organizations.<a href="./src/resources/organizations/organizations.ts">leave</a>({ ...params }) -> unknown</code>
+- <code title="post /gitpod.v1.OrganizationService/ListMembers">client.organizations.<a href="./src/resources/organizations/organizations.ts">listMembers</a>({ ...params }) -> OrganizationMembersMembersPage</code>
+- <code title="post /gitpod.v1.OrganizationService/SetRole">client.organizations.<a href="./src/resources/organizations/organizations.ts">setRole</a>({ ...params }) -> unknown</code>
+
+## DomainVerifications
+
+Types:
+
+- <code><a href="./src/resources/organizations/domain-verifications.ts">DomainVerification</a></code>
+- <code><a href="./src/resources/organizations/domain-verifications.ts">DomainVerificationState</a></code>
+- <code><a href="./src/resources/organizations/domain-verifications.ts">DomainVerificationCreateResponse</a></code>
+- <code><a href="./src/resources/organizations/domain-verifications.ts">DomainVerificationRetrieveResponse</a></code>
+- <code><a href="./src/resources/organizations/domain-verifications.ts">DomainVerificationDeleteResponse</a></code>
+- <code><a href="./src/resources/organizations/domain-verifications.ts">DomainVerificationVerifyResponse</a></code>
+
+Methods:
+
+- <code title="post /gitpod.v1.OrganizationService/CreateDomainVerification">client.organizations.domainVerifications.<a href="./src/resources/organizations/domain-verifications.ts">create</a>({ ...params }) -> DomainVerificationCreateResponse</code>
+- <code title="post /gitpod.v1.OrganizationService/GetDomainVerification">client.organizations.domainVerifications.<a href="./src/resources/organizations/domain-verifications.ts">retrieve</a>({ ...params }) -> DomainVerificationRetrieveResponse</code>
+- <code title="post /gitpod.v1.OrganizationService/ListDomainVerifications">client.organizations.domainVerifications.<a href="./src/resources/organizations/domain-verifications.ts">list</a>({ ...params }) -> DomainVerificationsDomainVerificationsPage</code>
+- <code title="post /gitpod.v1.OrganizationService/DeleteDomainVerification">client.organizations.domainVerifications.<a href="./src/resources/organizations/domain-verifications.ts">delete</a>({ ...params }) -> unknown</code>
+- <code title="post /gitpod.v1.OrganizationService/VerifyDomain">client.organizations.domainVerifications.<a href="./src/resources/organizations/domain-verifications.ts">verify</a>({ ...params }) -> DomainVerificationVerifyResponse</code>
+
+## Invites
+
+Types:
+
+- <code><a href="./src/resources/organizations/invites.ts">OrganizationInvite</a></code>
+- <code><a href="./src/resources/organizations/invites.ts">InviteCreateResponse</a></code>
+- <code><a href="./src/resources/organizations/invites.ts">InviteRetrieveResponse</a></code>
+- <code><a href="./src/resources/organizations/invites.ts">InviteGetSummaryResponse</a></code>
+
+Methods:
+
+- <code title="post /gitpod.v1.OrganizationService/CreateOrganizationInvite">client.organizations.invites.<a href="./src/resources/organizations/invites.ts">create</a>({ ...params }) -> InviteCreateResponse</code>
+- <code title="post /gitpod.v1.OrganizationService/GetOrganizationInvite">client.organizations.invites.<a href="./src/resources/organizations/invites.ts">retrieve</a>({ ...params }) -> InviteRetrieveResponse</code>
+- <code title="post /gitpod.v1.OrganizationService/GetOrganizationInviteSummary">client.organizations.invites.<a href="./src/resources/organizations/invites.ts">getSummary</a>({ ...params }) -> InviteGetSummaryResponse</code>
+
+## Policies
+
+Types:
+
+- <code><a href="./src/resources/organizations/policies.ts">OrganizationPolicies</a></code>
+- <code><a href="./src/resources/organizations/policies.ts">PolicyRetrieveResponse</a></code>
+- <code><a href="./src/resources/organizations/policies.ts">PolicyUpdateResponse</a></code>
+
+Methods:
+
+- <code title="post /gitpod.v1.OrganizationService/GetOrganizationPolicies">client.organizations.policies.<a href="./src/resources/organizations/policies.ts">retrieve</a>({ ...params }) -> PolicyRetrieveResponse</code>
+- <code title="post /gitpod.v1.OrganizationService/UpdateOrganizationPolicies">client.organizations.policies.<a href="./src/resources/organizations/policies.ts">update</a>({ ...params }) -> unknown</code>
+
+## SSOConfigurations
+
+Types:
+
+- <code><a href="./src/resources/organizations/sso-configurations.ts">ProviderType</a></code>
+- <code><a href="./src/resources/organizations/sso-configurations.ts">SSOConfiguration</a></code>
+- <code><a href="./src/resources/organizations/sso-configurations.ts">SSOConfigurationState</a></code>
+- <code><a href="./src/resources/organizations/sso-configurations.ts">SSOConfigurationCreateResponse</a></code>
+- <code><a href="./src/resources/organizations/sso-configurations.ts">SSOConfigurationRetrieveResponse</a></code>
+- <code><a href="./src/resources/organizations/sso-configurations.ts">SSOConfigurationUpdateResponse</a></code>
+- <code><a href="./src/resources/organizations/sso-configurations.ts">SSOConfigurationDeleteResponse</a></code>
+
+Methods:
+
+- <code title="post /gitpod.v1.OrganizationService/CreateSSOConfiguration">client.organizations.ssoConfigurations.<a href="./src/resources/organizations/sso-configurations.ts">create</a>({ ...params }) -> SSOConfigurationCreateResponse</code>
+- <code title="post /gitpod.v1.OrganizationService/GetSSOConfiguration">client.organizations.ssoConfigurations.<a href="./src/resources/organizations/sso-configurations.ts">retrieve</a>({ ...params }) -> SSOConfigurationRetrieveResponse</code>
+- <code title="post /gitpod.v1.OrganizationService/UpdateSSOConfiguration">client.organizations.ssoConfigurations.<a href="./src/resources/organizations/sso-configurations.ts">update</a>({ ...params }) -> unknown</code>
+- <code title="post /gitpod.v1.OrganizationService/ListSSOConfigurations">client.organizations.ssoConfigurations.<a href="./src/resources/organizations/sso-configurations.ts">list</a>({ ...params }) -> SSOConfigurationsSSOConfigurationsPage</code>
+- <code title="post /gitpod.v1.OrganizationService/DeleteSSOConfiguration">client.organizations.ssoConfigurations.<a href="./src/resources/organizations/sso-configurations.ts">delete</a>({ ...params }) -> unknown</code>
+
+# Projects
+
+Types:
+
+- <code><a href="./src/resources/projects/projects.ts">EnvironmentInitializer</a></code>
+- <code><a href="./src/resources/projects/projects.ts">Project</a></code>
+- <code><a href="./src/resources/projects/projects.ts">ProjectEnvironmentClass</a></code>
+- <code><a href="./src/resources/projects/projects.ts">ProjectMetadata</a></code>
+- <code><a href="./src/resources/projects/projects.ts">ProjectCreateResponse</a></code>
+- <code><a href="./src/resources/projects/projects.ts">ProjectRetrieveResponse</a></code>
+- <code><a href="./src/resources/projects/projects.ts">ProjectUpdateResponse</a></code>
+- <code><a href="./src/resources/projects/projects.ts">ProjectDeleteResponse</a></code>
+- <code><a href="./src/resources/projects/projects.ts">ProjectCreateFromEnvironmentResponse</a></code>
+
+Methods:
+
+- <code title="post /gitpod.v1.ProjectService/CreateProject">client.projects.<a href="./src/resources/projects/projects.ts">create</a>({ ...params }) -> ProjectCreateResponse</code>
+- <code title="post /gitpod.v1.ProjectService/GetProject">client.projects.<a href="./src/resources/projects/projects.ts">retrieve</a>({ ...params }) -> ProjectRetrieveResponse</code>
+- <code title="post /gitpod.v1.ProjectService/UpdateProject">client.projects.<a href="./src/resources/projects/projects.ts">update</a>({ ...params }) -> ProjectUpdateResponse</code>
+- <code title="post /gitpod.v1.ProjectService/ListProjects">client.projects.<a href="./src/resources/projects/projects.ts">list</a>({ ...params }) -> ProjectsProjectsPage</code>
+- <code title="post /gitpod.v1.ProjectService/DeleteProject">client.projects.<a href="./src/resources/projects/projects.ts">delete</a>({ ...params }) -> unknown</code>
+- <code title="post /gitpod.v1.ProjectService/CreateProjectFromEnvironment">client.projects.<a href="./src/resources/projects/projects.ts">createFromEnvironment</a>({ ...params }) -> ProjectCreateFromEnvironmentResponse</code>
+
+## Policies
+
+Types:
+
+- <code><a href="./src/resources/projects/policies.ts">ProjectPolicy</a></code>
+- <code><a href="./src/resources/projects/policies.ts">ProjectRole</a></code>
+- <code><a href="./src/resources/projects/policies.ts">PolicyCreateResponse</a></code>
+- <code><a href="./src/resources/projects/policies.ts">PolicyUpdateResponse</a></code>
+- <code><a href="./src/resources/projects/policies.ts">PolicyDeleteResponse</a></code>
+
+Methods:
+
+- <code title="post /gitpod.v1.ProjectService/CreateProjectPolicy">client.projects.policies.<a href="./src/resources/projects/policies.ts">create</a>({ ...params }) -> PolicyCreateResponse</code>
+- <code title="post /gitpod.v1.ProjectService/UpdateProjectPolicy">client.projects.policies.<a href="./src/resources/projects/policies.ts">update</a>({ ...params }) -> PolicyUpdateResponse</code>
+- <code title="post /gitpod.v1.ProjectService/ListProjectPolicies">client.projects.policies.<a href="./src/resources/projects/policies.ts">list</a>({ ...params }) -> ProjectPoliciesPoliciesPage</code>
+- <code title="post /gitpod.v1.ProjectService/DeleteProjectPolicy">client.projects.policies.<a href="./src/resources/projects/policies.ts">delete</a>({ ...params }) -> unknown</code>
+
+# Runners
+
+Types:
+
+- <code><a href="./src/resources/runners/runners.ts">LogLevel</a></code>
+- <code><a href="./src/resources/runners/runners.ts">MetricsConfiguration</a></code>
+- <code><a href="./src/resources/runners/runners.ts">Runner</a></code>
+- <code><a href="./src/resources/runners/runners.ts">RunnerCapability</a></code>
+- <code><a href="./src/resources/runners/runners.ts">RunnerConfiguration</a></code>
+- <code><a href="./src/resources/runners/runners.ts">RunnerKind</a></code>
+- <code><a href="./src/resources/runners/runners.ts">RunnerPhase</a></code>
+- <code><a href="./src/resources/runners/runners.ts">RunnerProvider</a></code>
+- <code><a href="./src/resources/runners/runners.ts">RunnerReleaseChannel</a></code>
+- <code><a href="./src/resources/runners/runners.ts">RunnerSpec</a></code>
+- <code><a href="./src/resources/runners/runners.ts">RunnerStatus</a></code>
+- <code><a href="./src/resources/runners/runners.ts">RunnerCreateResponse</a></code>
+- <code><a href="./src/resources/runners/runners.ts">RunnerRetrieveResponse</a></code>
+- <code><a href="./src/resources/runners/runners.ts">RunnerUpdateResponse</a></code>
+- <code><a href="./src/resources/runners/runners.ts">RunnerDeleteResponse</a></code>
+- <code><a href="./src/resources/runners/runners.ts">RunnerCheckAuthenticationForHostResponse</a></code>
+- <code><a href="./src/resources/runners/runners.ts">RunnerCreateRunnerTokenResponse</a></code>
+- <code><a href="./src/resources/runners/runners.ts">RunnerParseContextURLResponse</a></code>
+
+Methods:
+
+- <code title="post /gitpod.v1.RunnerService/CreateRunner">client.runners.<a href="./src/resources/runners/runners.ts">create</a>({ ...params }) -> RunnerCreateResponse</code>
+- <code title="post /gitpod.v1.RunnerService/GetRunner">client.runners.<a href="./src/resources/runners/runners.ts">retrieve</a>({ ...params }) -> RunnerRetrieveResponse</code>
+- <code title="post /gitpod.v1.RunnerService/UpdateRunner">client.runners.<a href="./src/resources/runners/runners.ts">update</a>({ ...params }) -> unknown</code>
+- <code title="post /gitpod.v1.RunnerService/ListRunners">client.runners.<a href="./src/resources/runners/runners.ts">list</a>({ ...params }) -> RunnersRunnersPage</code>
+- <code title="post /gitpod.v1.RunnerService/DeleteRunner">client.runners.<a href="./src/resources/runners/runners.ts">delete</a>({ ...params }) -> unknown</code>
+- <code title="post /gitpod.v1.RunnerService/CheckAuthenticationForHost">client.runners.<a href="./src/resources/runners/runners.ts">checkAuthenticationForHost</a>({ ...params }) -> RunnerCheckAuthenticationForHostResponse</code>
+- <code title="post /gitpod.v1.RunnerService/CreateRunnerToken">client.runners.<a href="./src/resources/runners/runners.ts">createRunnerToken</a>({ ...params }) -> RunnerCreateRunnerTokenResponse</code>
+- <code title="post /gitpod.v1.RunnerService/ParseContextURL">client.runners.<a href="./src/resources/runners/runners.ts">parseContextURL</a>({ ...params }) -> RunnerParseContextURLResponse</code>
+
+## Configurations
+
+Types:
+
+- <code><a href="./src/resources/runners/configurations/configurations.ts">EnvironmentClassValidationResult</a></code>
+- <code><a href="./src/resources/runners/configurations/configurations.ts">FieldValidationError</a></code>
+- <code><a href="./src/resources/runners/configurations/configurations.ts">ScmIntegrationValidationResult</a></code>
+- <code><a href="./src/resources/runners/configurations/configurations.ts">ConfigurationValidateResponse</a></code>
+
+Methods:
+
+- <code title="post /gitpod.v1.RunnerConfigurationService/ValidateRunnerConfiguration">client.runners.configurations.<a href="./src/resources/runners/configurations/configurations.ts">validate</a>({ ...params }) -> ConfigurationValidateResponse</code>
+
+### EnvironmentClasses
+
+Types:
+
+- <code><a href="./src/resources/runners/configurations/environment-classes.ts">EnvironmentClassCreateResponse</a></code>
+- <code><a href="./src/resources/runners/configurations/environment-classes.ts">EnvironmentClassRetrieveResponse</a></code>
+- <code><a href="./src/resources/runners/configurations/environment-classes.ts">EnvironmentClassUpdateResponse</a></code>
+
+Methods:
+
+- <code title="post /gitpod.v1.RunnerConfigurationService/CreateEnvironmentClass">client.runners.configurations.environmentClasses.<a href="./src/resources/runners/configurations/environment-classes.ts">create</a>({ ...params }) -> EnvironmentClassCreateResponse</code>
+- <code title="post /gitpod.v1.RunnerConfigurationService/GetEnvironmentClass">client.runners.configurations.environmentClasses.<a href="./src/resources/runners/configurations/environment-classes.ts">retrieve</a>({ ...params }) -> EnvironmentClassRetrieveResponse</code>
+- <code title="post /gitpod.v1.RunnerConfigurationService/UpdateEnvironmentClass">client.runners.configurations.environmentClasses.<a href="./src/resources/runners/configurations/environment-classes.ts">update</a>({ ...params }) -> unknown</code>
+- <code title="post /gitpod.v1.RunnerConfigurationService/ListEnvironmentClasses">client.runners.configurations.environmentClasses.<a href="./src/resources/runners/configurations/environment-classes.ts">list</a>({ ...params }) -> EnvironmentClassesEnvironmentClassesPage</code>
+
+### HostAuthenticationTokens
+
+Types:
+
+- <code><a href="./src/resources/runners/configurations/host-authentication-tokens.ts">HostAuthenticationToken</a></code>
+- <code><a href="./src/resources/runners/configurations/host-authentication-tokens.ts">HostAuthenticationTokenSource</a></code>
+- <code><a href="./src/resources/runners/configurations/host-authentication-tokens.ts">HostAuthenticationTokenCreateResponse</a></code>
+- <code><a href="./src/resources/runners/configurations/host-authentication-tokens.ts">HostAuthenticationTokenRetrieveResponse</a></code>
+- <code><a href="./src/resources/runners/configurations/host-authentication-tokens.ts">HostAuthenticationTokenUpdateResponse</a></code>
+- <code><a href="./src/resources/runners/configurations/host-authentication-tokens.ts">HostAuthenticationTokenDeleteResponse</a></code>
+
+Methods:
+
+- <code title="post /gitpod.v1.RunnerConfigurationService/CreateHostAuthenticationToken">client.runners.configurations.hostAuthenticationTokens.<a href="./src/resources/runners/configurations/host-authentication-tokens.ts">create</a>({ ...params }) -> HostAuthenticationTokenCreateResponse</code>
+- <code title="post /gitpod.v1.RunnerConfigurationService/GetHostAuthenticationToken">client.runners.configurations.hostAuthenticationTokens.<a href="./src/resources/runners/configurations/host-authentication-tokens.ts">retrieve</a>({ ...params }) -> HostAuthenticationTokenRetrieveResponse</code>
+- <code title="post /gitpod.v1.RunnerConfigurationService/UpdateHostAuthenticationToken">client.runners.configurations.hostAuthenticationTokens.<a href="./src/resources/runners/configurations/host-authentication-tokens.ts">update</a>({ ...params }) -> unknown</code>
+- <code title="post /gitpod.v1.RunnerConfigurationService/ListHostAuthenticationTokens">client.runners.configurations.hostAuthenticationTokens.<a href="./src/resources/runners/configurations/host-authentication-tokens.ts">list</a>({ ...params }) -> HostAuthenticationTokensTokensPage</code>
+- <code title="post /gitpod.v1.RunnerConfigurationService/DeleteHostAuthenticationToken">client.runners.configurations.hostAuthenticationTokens.<a href="./src/resources/runners/configurations/host-authentication-tokens.ts">delete</a>({ ...params }) -> unknown</code>
+
+### Schema
+
+Types:
+
+- <code><a href="./src/resources/runners/configurations/schema.ts">RunnerConfigurationSchema</a></code>
+- <code><a href="./src/resources/runners/configurations/schema.ts">SchemaRetrieveResponse</a></code>
+
+Methods:
+
+- <code title="post /gitpod.v1.RunnerConfigurationService/GetRunnerConfigurationSchema">client.runners.configurations.schema.<a href="./src/resources/runners/configurations/schema.ts">retrieve</a>({ ...params }) -> SchemaRetrieveResponse</code>
+
+### ScmIntegrations
+
+Types:
+
+- <code><a href="./src/resources/runners/configurations/scm-integrations.ts">ScmIntegration</a></code>
+- <code><a href="./src/resources/runners/configurations/scm-integrations.ts">ScmIntegrationOAuthConfig</a></code>
+- <code><a href="./src/resources/runners/configurations/scm-integrations.ts">ScmIntegrationCreateResponse</a></code>
+- <code><a href="./src/resources/runners/configurations/scm-integrations.ts">ScmIntegrationRetrieveResponse</a></code>
+- <code><a href="./src/resources/runners/configurations/scm-integrations.ts">ScmIntegrationUpdateResponse</a></code>
+- <code><a href="./src/resources/runners/configurations/scm-integrations.ts">ScmIntegrationDeleteResponse</a></code>
+
+Methods:
+
+- <code title="post /gitpod.v1.RunnerConfigurationService/CreateSCMIntegration">client.runners.configurations.scmIntegrations.<a href="./src/resources/runners/configurations/scm-integrations.ts">create</a>({ ...params }) -> ScmIntegrationCreateResponse</code>
+- <code title="post /gitpod.v1.RunnerConfigurationService/GetSCMIntegration">client.runners.configurations.scmIntegrations.<a href="./src/resources/runners/configurations/scm-integrations.ts">retrieve</a>({ ...params }) -> ScmIntegrationRetrieveResponse</code>
+- <code title="post /gitpod.v1.RunnerConfigurationService/UpdateSCMIntegration">client.runners.configurations.scmIntegrations.<a href="./src/resources/runners/configurations/scm-integrations.ts">update</a>({ ...params }) -> unknown</code>
+- <code title="post /gitpod.v1.RunnerConfigurationService/ListSCMIntegrations">client.runners.configurations.scmIntegrations.<a href="./src/resources/runners/configurations/scm-integrations.ts">list</a>({ ...params }) -> ScmIntegrationsIntegrationsPage</code>
+- <code title="post /gitpod.v1.RunnerConfigurationService/DeleteSCMIntegration">client.runners.configurations.scmIntegrations.<a href="./src/resources/runners/configurations/scm-integrations.ts">delete</a>({ ...params }) -> unknown</code>
+
+## Policies
+
+Types:
+
+- <code><a href="./src/resources/runners/policies.ts">RunnerPolicy</a></code>
+- <code><a href="./src/resources/runners/policies.ts">RunnerRole</a></code>
+- <code><a href="./src/resources/runners/policies.ts">PolicyCreateResponse</a></code>
+- <code><a href="./src/resources/runners/policies.ts">PolicyUpdateResponse</a></code>
+- <code><a href="./src/resources/runners/policies.ts">PolicyDeleteResponse</a></code>
+
+Methods:
+
+- <code title="post /gitpod.v1.RunnerService/CreateRunnerPolicy">client.runners.policies.<a href="./src/resources/runners/policies.ts">create</a>({ ...params }) -> PolicyCreateResponse</code>
+- <code title="post /gitpod.v1.RunnerService/UpdateRunnerPolicy">client.runners.policies.<a href="./src/resources/runners/policies.ts">update</a>({ ...params }) -> PolicyUpdateResponse</code>
+- <code title="post /gitpod.v1.RunnerService/ListRunnerPolicies">client.runners.policies.<a href="./src/resources/runners/policies.ts">list</a>({ ...params }) -> RunnerPoliciesPoliciesPage</code>
+- <code title="post /gitpod.v1.RunnerService/DeleteRunnerPolicy">client.runners.policies.<a href="./src/resources/runners/policies.ts">delete</a>({ ...params }) -> unknown</code>
+
+# Secrets
+
+Types:
+
+- <code><a href="./src/resources/secrets.ts">Secret</a></code>
+- <code><a href="./src/resources/secrets.ts">SecretScope</a></code>
+- <code><a href="./src/resources/secrets.ts">SecretCreateResponse</a></code>
+- <code><a href="./src/resources/secrets.ts">SecretDeleteResponse</a></code>
+- <code><a href="./src/resources/secrets.ts">SecretGetValueResponse</a></code>
+- <code><a href="./src/resources/secrets.ts">SecretUpdateValueResponse</a></code>
+
+Methods:
+
+- <code title="post /gitpod.v1.SecretService/CreateSecret">client.secrets.<a href="./src/resources/secrets.ts">create</a>({ ...params }) -> SecretCreateResponse</code>
+- <code title="post /gitpod.v1.SecretService/ListSecrets">client.secrets.<a href="./src/resources/secrets.ts">list</a>({ ...params }) -> SecretsSecretsPage</code>
+- <code title="post /gitpod.v1.SecretService/DeleteSecret">client.secrets.<a href="./src/resources/secrets.ts">delete</a>({ ...params }) -> unknown</code>
+- <code title="post /gitpod.v1.SecretService/GetSecretValue">client.secrets.<a href="./src/resources/secrets.ts">getValue</a>({ ...params }) -> SecretGetValueResponse</code>
+- <code title="post /gitpod.v1.SecretService/UpdateSecretValue">client.secrets.<a href="./src/resources/secrets.ts">updateValue</a>({ ...params }) -> unknown</code>
+
+# Usage
+
+Types:
+
+- <code><a href="./src/resources/usage.ts">EnvironmentSession</a></code>
+
+Methods:
+
+- <code title="post /gitpod.v1.UsageService/ListEnvironmentSessions">client.usage.<a href="./src/resources/usage.ts">listEnvironmentSessions</a>({ ...params }) -> EnvironmentSessionsSessionsPage</code>
+
+# Users
+
+Types:
+
+- <code><a href="./src/resources/users/users.ts">User</a></code>
+- <code><a href="./src/resources/users/users.ts">UserGetAuthenticatedUserResponse</a></code>
+- <code><a href="./src/resources/users/users.ts">UserSetSuspendedResponse</a></code>
+
+Methods:
+
+- <code title="post /gitpod.v1.UserService/GetAuthenticatedUser">client.users.<a href="./src/resources/users/users.ts">getAuthenticatedUser</a>({ ...params }) -> UserGetAuthenticatedUserResponse</code>
+- <code title="post /gitpod.v1.UserService/SetSuspended">client.users.<a href="./src/resources/users/users.ts">setSuspended</a>({ ...params }) -> unknown</code>
+
+## Dotfiles
+
+Types:
+
+- <code><a href="./src/resources/users/dotfiles.ts">DotfilesConfiguration</a></code>
+- <code><a href="./src/resources/users/dotfiles.ts">DotfileGetResponse</a></code>
+- <code><a href="./src/resources/users/dotfiles.ts">DotfileSetResponse</a></code>
+
+Methods:
+
+- <code title="post /gitpod.v1.UserService/GetDotfilesConfiguration">client.users.dotfiles.<a href="./src/resources/users/dotfiles.ts">get</a>({ ...params }) -> DotfileGetResponse</code>
+- <code title="post /gitpod.v1.UserService/SetDotfilesConfiguration">client.users.dotfiles.<a href="./src/resources/users/dotfiles.ts">set</a>({ ...params }) -> unknown</code>
+
+## Pats
+
+Types:
+
+- <code><a href="./src/resources/users/pats.ts">PersonalAccessToken</a></code>
+- <code><a href="./src/resources/users/pats.ts">PatDeleteResponse</a></code>
+- <code><a href="./src/resources/users/pats.ts">PatGetResponse</a></code>
+
+Methods:
+
+- <code title="post /gitpod.v1.UserService/ListPersonalAccessTokens">client.users.pats.<a href="./src/resources/users/pats.ts">list</a>({ ...params }) -> PersonalAccessTokensPersonalAccessTokensPage</code>
+- <code title="post /gitpod.v1.UserService/DeletePersonalAccessToken">client.users.pats.<a href="./src/resources/users/pats.ts">delete</a>({ ...params }) -> unknown</code>
+- <code title="post /gitpod.v1.UserService/GetPersonalAccessToken">client.users.pats.<a href="./src/resources/users/pats.ts">get</a>({ ...params }) -> PatGetResponse</code>
diff --git a/bin/check-release-environment b/bin/check-release-environment
new file mode 100644
index 0000000..0d4280c
--- /dev/null
+++ b/bin/check-release-environment
@@ -0,0 +1,22 @@
+#!/usr/bin/env bash
+
+errors=()
+
+if [ -z "${NPM_TOKEN}" ]; then
+  errors+=("The GITPOD_NPM_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets")
+fi
+
+lenErrors=${#errors[@]}
+
+if [[ lenErrors -gt 0 ]]; then
+  echo -e "Found the following errors in the release environment:\n"
+
+  for error in "${errors[@]}"; do
+    echo -e "- $error\n"
+  done
+
+  exit 1
+fi
+
+echo "The environment is ready to push releases!"
+
diff --git a/bin/publish-npm b/bin/publish-npm
new file mode 100644
index 0000000..4c21181
--- /dev/null
+++ b/bin/publish-npm
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+
+set -eux
+
+npm config set '//registry.npmjs.org/:_authToken' "$NPM_TOKEN"
+
+# Build the project
+yarn build
+
+# Navigate to the dist directory
+cd dist
+
+# Get the version from package.json
+VERSION="$(node -p "require('./package.json').version")"
+
+# Extract the pre-release tag if it exists
+if [[ "$VERSION" =~ -([a-zA-Z]+) ]]; then
+  # Extract the part before any dot in the pre-release identifier
+  TAG="${BASH_REMATCH[1]}"
+else
+  TAG="latest"
+fi
+
+# Publish with the appropriate tag
+yarn publish --access public --tag "$TAG"
diff --git a/eslint.config.mjs b/eslint.config.mjs
new file mode 100644
index 0000000..bd8e0cb
--- /dev/null
+++ b/eslint.config.mjs
@@ -0,0 +1,42 @@
+// @ts-check
+import tseslint from 'typescript-eslint';
+import unusedImports from 'eslint-plugin-unused-imports';
+import prettier from 'eslint-plugin-prettier';
+
+export default tseslint.config(
+  {
+    languageOptions: {
+      parser: tseslint.parser,
+      parserOptions: { sourceType: 'module' },
+    },
+    files: ['**/*.ts', '**/*.mts', '**/*.cts', '**/*.js', '**/*.mjs', '**/*.cjs'],
+    ignores: ['dist/**'],
+    plugins: {
+      '@typescript-eslint': tseslint.plugin,
+      'unused-imports': unusedImports,
+      prettier,
+    },
+    rules: {
+      'no-unused-vars': 'off',
+      'prettier/prettier': 'error',
+      'unused-imports/no-unused-imports': 'error',
+      'no-restricted-imports': [
+        'error',
+        {
+          patterns: [
+            {
+              regex: '^@gitpod/sdk(/.*)?',
+              message: 'Use a relative import, not a package import.',
+            },
+          ],
+        },
+      ],
+    },
+  },
+  {
+    files: ['tests/**', 'examples/**'],
+    rules: {
+      'no-restricted-imports': 'off',
+    },
+  },
+);
diff --git a/examples/.keep b/examples/.keep
new file mode 100644
index 0000000..0651c89
--- /dev/null
+++ b/examples/.keep
@@ -0,0 +1,4 @@
+File generated from our OpenAPI spec by Stainless.
+
+This directory can be used to store example files demonstrating usage of this SDK.
+It is ignored by Stainless code generation and its content (other than this keep file) won't be touched.
diff --git a/jest.config.ts b/jest.config.ts
new file mode 100644
index 0000000..d185444
--- /dev/null
+++ b/jest.config.ts
@@ -0,0 +1,22 @@
+import type { JestConfigWithTsJest } from 'ts-jest';
+
+const config: JestConfigWithTsJest = {
+  preset: 'ts-jest/presets/default-esm',
+  testEnvironment: 'node',
+  transform: {
+    '^.+\\.(t|j)sx?$': ['@swc/jest', { sourceMaps: 'inline' }],
+  },
+  moduleNameMapper: {
+    '^@gitpod/sdk$': '<rootDir>/src/index.ts',
+    '^@gitpod/sdk/(.*)$': '<rootDir>/src/$1',
+  },
+  modulePathIgnorePatterns: [
+    '<rootDir>/ecosystem-tests/',
+    '<rootDir>/dist/',
+    '<rootDir>/deno/',
+    '<rootDir>/deno_tests/',
+  ],
+  testPathIgnorePatterns: ['scripts'],
+};
+
+export default config;
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..a1541d7
--- /dev/null
+++ b/package.json
@@ -0,0 +1,76 @@
+{
+  "name": "@gitpod/sdk",
+  "version": "0.5.0",
+  "description": "The official TypeScript library for the Gitpod API",
+  "author": "Gitpod <dev-feedback@gitpod.com>",
+  "types": "dist/index.d.ts",
+  "main": "dist/index.js",
+  "type": "commonjs",
+  "repository": "github:gitpod-io/gitpod-sdk-typescript",
+  "license": "Apache-2.0",
+  "packageManager": "yarn@1.22.22",
+  "files": [
+    "**/*"
+  ],
+  "private": false,
+  "scripts": {
+    "test": "./scripts/test",
+    "build": "./scripts/build",
+    "prepublishOnly": "echo 'to publish, run yarn build && (cd dist; yarn publish)' && exit 1",
+    "format": "./scripts/format",
+    "prepare": "if ./scripts/utils/check-is-in-git-install.sh; then ./scripts/build && ./scripts/utils/git-swap.sh; fi",
+    "tsn": "ts-node -r tsconfig-paths/register",
+    "lint": "./scripts/lint",
+    "fix": "./scripts/format"
+  },
+  "dependencies": {},
+  "devDependencies": {
+    "@arethetypeswrong/cli": "^0.17.0",
+    "@swc/core": "^1.3.102",
+    "@swc/jest": "^0.2.29",
+    "@types/jest": "^29.4.0",
+    "@types/node": "^20.17.6",
+    "typescript-eslint": "8.31.1",
+    "@typescript-eslint/eslint-plugin": "8.31.1",
+    "@typescript-eslint/parser": "8.31.1",
+    "eslint": "^9.20.1",
+    "eslint-plugin-prettier": "^5.2.3",
+    "eslint-plugin-unused-imports": "^4.1.4",
+    "iconv-lite": "^0.6.3",
+    "jest": "^29.4.0",
+    "prettier": "^3.0.0",
+    "publint": "^0.2.12",
+    "ts-jest": "^29.1.0",
+    "ts-node": "^10.5.0",
+    "tsc-multi": "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.4/tsc-multi-1.1.4.tgz",
+    "tsconfig-paths": "^4.0.0",
+    "typescript": "5.8.3"
+  },
+  "resolutions": {
+    "synckit": "0.8.8"
+  },
+  "browser": {
+    "./internal/shims/getBuiltinModule.mjs": "./internal/shims/nullGetBuiltinModule.mjs",
+    "./internal/shims/getBuiltinModule.js": "./internal/shims/nullGetBuiltinModule.js"
+  },
+  "imports": {
+    "@gitpod/sdk": ".",
+    "@gitpod/sdk/*": "./src/*"
+  },
+  "exports": {
+    ".": {
+      "import": "./dist/index.mjs",
+      "require": "./dist/index.js"
+    },
+    "./*.mjs": {
+      "default": "./dist/*.mjs"
+    },
+    "./*.js": {
+      "default": "./dist/*.js"
+    },
+    "./*": {
+      "import": "./dist/*.mjs",
+      "require": "./dist/*.js"
+    }
+  }
+}
diff --git a/release-please-config.json b/release-please-config.json
new file mode 100644
index 0000000..1ebd0bd
--- /dev/null
+++ b/release-please-config.json
@@ -0,0 +1,64 @@
+{
+  "packages": {
+    ".": {}
+  },
+  "$schema": "https://raw.githubusercontent.com/stainless-api/release-please/main/schemas/config.json",
+  "include-v-in-tag": true,
+  "include-component-in-tag": false,
+  "versioning": "prerelease",
+  "prerelease": true,
+  "bump-minor-pre-major": true,
+  "bump-patch-for-minor-pre-major": false,
+  "pull-request-header": "Automated Release PR",
+  "pull-request-title-pattern": "release: ${version}",
+  "changelog-sections": [
+    {
+      "type": "feat",
+      "section": "Features"
+    },
+    {
+      "type": "fix",
+      "section": "Bug Fixes"
+    },
+    {
+      "type": "perf",
+      "section": "Performance Improvements"
+    },
+    {
+      "type": "revert",
+      "section": "Reverts"
+    },
+    {
+      "type": "chore",
+      "section": "Chores"
+    },
+    {
+      "type": "docs",
+      "section": "Documentation"
+    },
+    {
+      "type": "style",
+      "section": "Styles"
+    },
+    {
+      "type": "refactor",
+      "section": "Refactors"
+    },
+    {
+      "type": "test",
+      "section": "Tests",
+      "hidden": true
+    },
+    {
+      "type": "build",
+      "section": "Build System"
+    },
+    {
+      "type": "ci",
+      "section": "Continuous Integration",
+      "hidden": true
+    }
+  ],
+  "release-type": "node",
+  "extra-files": ["src/version.ts", "README.md"]
+}
diff --git a/scripts/bootstrap b/scripts/bootstrap
new file mode 100755
index 0000000..0af58e2
--- /dev/null
+++ b/scripts/bootstrap
@@ -0,0 +1,18 @@
+#!/usr/bin/env bash
+
+set -e
+
+cd "$(dirname "$0")/.."
+
+if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ]; then
+  brew bundle check >/dev/null 2>&1 || {
+    echo "==> Installing Homebrew dependencies…"
+    brew bundle
+  }
+fi
+
+echo "==> Installing Node dependencies…"
+
+PACKAGE_MANAGER=$(command -v yarn >/dev/null 2>&1 && echo "yarn" || echo "npm")
+
+$PACKAGE_MANAGER install
diff --git a/scripts/build b/scripts/build
new file mode 100755
index 0000000..3323742
--- /dev/null
+++ b/scripts/build
@@ -0,0 +1,53 @@
+#!/usr/bin/env bash
+
+set -exuo pipefail
+
+cd "$(dirname "$0")/.."
+
+node scripts/utils/check-version.cjs
+
+# Build into dist and will publish the package from there,
+# so that src/resources/foo.ts becomes <package root>/resources/foo.js
+# This way importing from `"@gitpod/sdk/resources/foo"` works
+# even with `"moduleResolution": "node"`
+
+rm -rf dist; mkdir dist
+# Copy src to dist/src and build from dist/src into dist, so that
+# the source map for index.js.map will refer to ./src/index.ts etc
+cp -rp src README.md dist
+for file in LICENSE CHANGELOG.md; do
+  if [ -e "${file}" ]; then cp "${file}" dist; fi
+done
+if [ -e "bin/cli" ]; then
+  mkdir -p dist/bin
+  cp -p "bin/cli" dist/bin/;
+fi
+if [ -e "bin/migration-config.json" ]; then
+  mkdir -p dist/bin
+  cp -p "bin/migration-config.json" dist/bin/;
+fi
+# this converts the export map paths for the dist directory
+# and does a few other minor things
+node scripts/utils/make-dist-package-json.cjs > dist/package.json
+
+# build to .js/.mjs/.d.ts files
+npm exec tsc-multi
+# we need to patch index.js so that `new module.exports()` works for cjs backwards
+# compat. No way to get that from index.ts because it would cause compile errors
+# when building .mjs
+node scripts/utils/fix-index-exports.cjs
+cp tsconfig.dist-src.json dist/src/tsconfig.json
+cp src/internal/shim-types.d.ts dist/internal/shim-types.d.ts
+cp src/internal/shim-types.d.ts dist/internal/shim-types.d.mts
+
+node scripts/utils/postprocess-files.cjs
+
+# make sure that nothing crashes when we require the output CJS or
+# import the output ESM
+(cd dist && node -e 'require("@gitpod/sdk")')
+(cd dist && node -e 'import("@gitpod/sdk")' --input-type=module)
+
+if [ -e ./scripts/build-deno ]
+then
+  ./scripts/build-deno
+fi
diff --git a/scripts/format b/scripts/format
new file mode 100755
index 0000000..7a75640
--- /dev/null
+++ b/scripts/format
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+
+set -e
+
+cd "$(dirname "$0")/.."
+
+echo "==> Running eslint --fix"
+./node_modules/.bin/eslint --fix .
+
+echo "==> Running prettier --write"
+# format things eslint didn't
+./node_modules/.bin/prettier --write --cache --cache-strategy metadata . '!**/dist' '!**/*.ts' '!**/*.mts' '!**/*.cts' '!**/*.js' '!**/*.mjs' '!**/*.cjs'
diff --git a/scripts/lint b/scripts/lint
new file mode 100755
index 0000000..3ffb78a
--- /dev/null
+++ b/scripts/lint
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+
+set -e
+
+cd "$(dirname "$0")/.."
+
+echo "==> Running eslint"
+./node_modules/.bin/eslint .
+
+echo "==> Building"
+./scripts/build
+
+echo "==> Checking types"
+./node_modules/typescript/bin/tsc
+
+echo "==> Running Are The Types Wrong?"
+./node_modules/.bin/attw --pack dist -f json >.attw.json || true
+node scripts/utils/attw-report.cjs
+
+echo "==> Running publint"
+./node_modules/.bin/publint dist
diff --git a/scripts/mock b/scripts/mock
new file mode 100755
index 0000000..d2814ae
--- /dev/null
+++ b/scripts/mock
@@ -0,0 +1,41 @@
+#!/usr/bin/env bash
+
+set -e
+
+cd "$(dirname "$0")/.."
+
+if [[ -n "$1" && "$1" != '--'* ]]; then
+  URL="$1"
+  shift
+else
+  URL="$(grep 'openapi_spec_url' .stats.yml | cut -d' ' -f2)"
+fi
+
+# Check if the URL is empty
+if [ -z "$URL" ]; then
+  echo "Error: No OpenAPI spec path/url provided or found in .stats.yml"
+  exit 1
+fi
+
+echo "==> Starting mock server with URL ${URL}"
+
+# Run prism mock on the given spec
+if [ "$1" == "--daemon" ]; then
+  npm exec --package=@stainless-api/prism-cli@5.8.5 -- prism mock "$URL" &> .prism.log &
+
+  # Wait for server to come online
+  echo -n "Waiting for server"
+  while ! grep -q "✖  fatal\|Prism is listening" ".prism.log" ; do
+    echo -n "."
+    sleep 0.1
+  done
+
+  if grep -q "✖  fatal" ".prism.log"; then
+    cat .prism.log
+    exit 1
+  fi
+
+  echo
+else
+  npm exec --package=@stainless-api/prism-cli@5.8.5 -- prism mock "$URL"
+fi
diff --git a/scripts/test b/scripts/test
new file mode 100755
index 0000000..2049e31
--- /dev/null
+++ b/scripts/test
@@ -0,0 +1,56 @@
+#!/usr/bin/env bash
+
+set -e
+
+cd "$(dirname "$0")/.."
+
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[0;33m'
+NC='\033[0m' # No Color
+
+function prism_is_running() {
+  curl --silent "http://localhost:4010" >/dev/null 2>&1
+}
+
+kill_server_on_port() {
+  pids=$(lsof -t -i tcp:"$1" || echo "")
+  if [ "$pids" != "" ]; then
+    kill "$pids"
+    echo "Stopped $pids."
+  fi
+}
+
+function is_overriding_api_base_url() {
+  [ -n "$TEST_API_BASE_URL" ]
+}
+
+if ! is_overriding_api_base_url && ! prism_is_running ; then
+  # When we exit this script, make sure to kill the background mock server process
+  trap 'kill_server_on_port 4010' EXIT
+
+  # Start the dev server
+  ./scripts/mock --daemon
+fi
+
+if is_overriding_api_base_url ; then
+  echo -e "${GREEN}✔ Running tests against ${TEST_API_BASE_URL}${NC}"
+  echo
+elif ! prism_is_running ; then
+  echo -e "${RED}ERROR:${NC} The test suite will not run without a mock Prism server"
+  echo -e "running against your OpenAPI spec."
+  echo
+  echo -e "To run the server, pass in the path or url of your OpenAPI"
+  echo -e "spec to the prism command:"
+  echo
+  echo -e "  \$ ${YELLOW}npm exec --package=@stoplight/prism-cli@~5.3.2 -- prism mock path/to/your.openapi.yml${NC}"
+  echo
+
+  exit 1
+else
+  echo -e "${GREEN}✔ Mock prism server is running with your OpenAPI spec${NC}"
+  echo
+fi
+
+echo "==> Running tests"
+./node_modules/.bin/jest "$@"
diff --git a/scripts/utils/attw-report.cjs b/scripts/utils/attw-report.cjs
new file mode 100644
index 0000000..b3477c0
--- /dev/null
+++ b/scripts/utils/attw-report.cjs
@@ -0,0 +1,24 @@
+const fs = require('fs');
+const problems = Object.values(JSON.parse(fs.readFileSync('.attw.json', 'utf-8')).problems)
+  .flat()
+  .filter(
+    (problem) =>
+      !(
+        // This is intentional, if the user specifies .mjs they get ESM.
+        (
+          (problem.kind === 'CJSResolvesToESM' && problem.entrypoint.endsWith('.mjs')) ||
+          // This is intentional for backwards compat reasons.
+          (problem.kind === 'MissingExportEquals' && problem.implementationFileName.endsWith('/index.js')) ||
+          // this is intentional, we deliberately attempt to import types that may not exist from parent node_modules
+          // folders to better support various runtimes without triggering automatic type acquisition.
+          (problem.kind === 'InternalResolutionError' && problem.moduleSpecifier.includes('node_modules'))
+        )
+      ),
+  );
+fs.unlinkSync('.attw.json');
+if (problems.length) {
+  process.stdout.write('The types are wrong!\n' + JSON.stringify(problems, null, 2) + '\n');
+  process.exitCode = 1;
+} else {
+  process.stdout.write('Types ok!\n');
+}
diff --git a/scripts/utils/check-is-in-git-install.sh b/scripts/utils/check-is-in-git-install.sh
new file mode 100755
index 0000000..1354eb4
--- /dev/null
+++ b/scripts/utils/check-is-in-git-install.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+# Check if you happen to call prepare for a repository that's already in node_modules.
+[ "$(basename "$(dirname "$PWD")")" = 'node_modules' ] ||
+# The name of the containing directory that 'npm` uses, which looks like
+# $HOME/.npm/_cacache/git-cloneXXXXXX
+[ "$(basename "$(dirname "$PWD")")" = 'tmp' ] ||
+# The name of the containing directory that 'yarn` uses, which looks like
+# $(yarn cache dir)/.tmp/XXXXX
+[ "$(basename "$(dirname "$PWD")")" = '.tmp' ]
diff --git a/scripts/utils/check-version.cjs b/scripts/utils/check-version.cjs
new file mode 100644
index 0000000..86c56df
--- /dev/null
+++ b/scripts/utils/check-version.cjs
@@ -0,0 +1,20 @@
+const fs = require('fs');
+const path = require('path');
+
+const main = () => {
+  const pkg = require('../../package.json');
+  const version = pkg['version'];
+  if (!version) throw 'The version property is not set in the package.json file';
+  if (typeof version !== 'string') {
+    throw `Unexpected type for the package.json version field; got ${typeof version}, expected string`;
+  }
+
+  const versionFile = path.resolve(__dirname, '..', '..', 'src', 'version.ts');
+  const contents = fs.readFileSync(versionFile, 'utf8');
+  const output = contents.replace(/(export const VERSION = ')(.*)(')/g, `$1${version}$3`);
+  fs.writeFileSync(versionFile, output);
+};
+
+if (require.main === module) {
+  main();
+}
diff --git a/scripts/utils/fix-index-exports.cjs b/scripts/utils/fix-index-exports.cjs
new file mode 100644
index 0000000..e5e10b3
--- /dev/null
+++ b/scripts/utils/fix-index-exports.cjs
@@ -0,0 +1,17 @@
+const fs = require('fs');
+const path = require('path');
+
+const indexJs =
+  process.env['DIST_PATH'] ?
+    path.resolve(process.env['DIST_PATH'], 'index.js')
+  : path.resolve(__dirname, '..', '..', 'dist', 'index.js');
+
+let before = fs.readFileSync(indexJs, 'utf8');
+let after = before.replace(
+  /^(\s*Object\.defineProperty\s*\(exports,\s*["']__esModule["'].+)$/m,
+  `exports = module.exports = function (...args) {
+    return new exports.default(...args)
+  }
+  $1`.replace(/^  /gm, ''),
+);
+fs.writeFileSync(indexJs, after, 'utf8');
diff --git a/scripts/utils/git-swap.sh b/scripts/utils/git-swap.sh
new file mode 100755
index 0000000..79d1888
--- /dev/null
+++ b/scripts/utils/git-swap.sh
@@ -0,0 +1,13 @@
+#!/usr/bin/env bash
+set -exuo pipefail
+# the package is published to NPM from ./dist
+# we want the final file structure for git installs to match the npm installs, so we
+
+# delete everything except ./dist and ./node_modules
+find . -maxdepth 1 -mindepth 1 ! -name 'dist' ! -name 'node_modules' -exec rm -rf '{}' +
+
+# move everything from ./dist to .
+mv dist/* .
+
+# delete the now-empty ./dist
+rmdir dist
diff --git a/scripts/utils/make-dist-package-json.cjs b/scripts/utils/make-dist-package-json.cjs
new file mode 100644
index 0000000..7c24f56
--- /dev/null
+++ b/scripts/utils/make-dist-package-json.cjs
@@ -0,0 +1,21 @@
+const pkgJson = require(process.env['PKG_JSON_PATH'] || '../../package.json');
+
+function processExportMap(m) {
+  for (const key in m) {
+    const value = m[key];
+    if (typeof value === 'string') m[key] = value.replace(/^\.\/dist\//, './');
+    else processExportMap(value);
+  }
+}
+processExportMap(pkgJson.exports);
+
+for (const key of ['types', 'main', 'module']) {
+  if (typeof pkgJson[key] === 'string') pkgJson[key] = pkgJson[key].replace(/^(\.\/)?dist\//, './');
+}
+
+delete pkgJson.devDependencies;
+delete pkgJson.scripts.prepack;
+delete pkgJson.scripts.prepublishOnly;
+delete pkgJson.scripts.prepare;
+
+console.log(JSON.stringify(pkgJson, null, 2));
diff --git a/scripts/utils/postprocess-files.cjs b/scripts/utils/postprocess-files.cjs
new file mode 100644
index 0000000..deae575
--- /dev/null
+++ b/scripts/utils/postprocess-files.cjs
@@ -0,0 +1,94 @@
+// @ts-check
+const fs = require('fs');
+const path = require('path');
+
+const distDir =
+  process.env['DIST_PATH'] ?
+    path.resolve(process.env['DIST_PATH'])
+  : path.resolve(__dirname, '..', '..', 'dist');
+
+async function* walk(dir) {
+  for await (const d of await fs.promises.opendir(dir)) {
+    const entry = path.join(dir, d.name);
+    if (d.isDirectory()) yield* walk(entry);
+    else if (d.isFile()) yield entry;
+  }
+}
+
+async function postprocess() {
+  for await (const file of walk(distDir)) {
+    if (!/(\.d)?[cm]?ts$/.test(file)) continue;
+
+    const code = await fs.promises.readFile(file, 'utf8');
+
+    // strip out lib="dom", types="node", and types="react" references; these
+    // are needed at build time, but would pollute the user's TS environment
+    const transformed = code.replace(
+      /^ *\/\/\/ *<reference +(lib="dom"|types="(node|react)").*?\n/gm,
+      // replace with same number of characters to avoid breaking source maps
+      (match) => ' '.repeat(match.length - 1) + '\n',
+    );
+
+    if (transformed !== code) {
+      console.error(`wrote ${path.relative(process.cwd(), file)}`);
+      await fs.promises.writeFile(file, transformed, 'utf8');
+    }
+  }
+
+  const newExports = {
+    '.': {
+      require: {
+        types: './index.d.ts',
+        default: './index.js',
+      },
+      types: './index.d.mts',
+      default: './index.mjs',
+    },
+  };
+
+  for (const entry of await fs.promises.readdir(distDir, { withFileTypes: true })) {
+    if (entry.isDirectory() && entry.name !== 'src' && entry.name !== 'internal' && entry.name !== 'bin') {
+      const subpath = './' + entry.name;
+      newExports[subpath + '/*.mjs'] = {
+        default: subpath + '/*.mjs',
+      };
+      newExports[subpath + '/*.js'] = {
+        default: subpath + '/*.js',
+      };
+      newExports[subpath + '/*'] = {
+        import: subpath + '/*.mjs',
+        require: subpath + '/*.js',
+      };
+    } else if (entry.isFile() && /\.[cm]?js$/.test(entry.name)) {
+      const { name, ext } = path.parse(entry.name);
+      const subpathWithoutExt = './' + name;
+      const subpath = './' + entry.name;
+      newExports[subpathWithoutExt] ||= { import: undefined, require: undefined };
+      const isModule = ext[1] === 'm';
+      if (isModule) {
+        newExports[subpathWithoutExt].import = subpath;
+      } else {
+        newExports[subpathWithoutExt].require = subpath;
+      }
+      newExports[subpath] = {
+        default: subpath,
+      };
+    }
+  }
+  await fs.promises.writeFile(
+    'dist/package.json',
+    JSON.stringify(
+      Object.assign(
+        /** @type {Record<String, unknown>} */ (
+          JSON.parse(await fs.promises.readFile('dist/package.json', 'utf-8'))
+        ),
+        {
+          exports: newExports,
+        },
+      ),
+      null,
+      2,
+    ),
+  );
+}
+postprocess();
diff --git a/scripts/utils/upload-artifact.sh b/scripts/utils/upload-artifact.sh
new file mode 100755
index 0000000..864a4c8
--- /dev/null
+++ b/scripts/utils/upload-artifact.sh
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+set -exuo pipefail
+
+RESPONSE=$(curl -X POST "$URL" \
+  -H "Authorization: Bearer $AUTH" \
+  -H "Content-Type: application/json")
+
+SIGNED_URL=$(echo "$RESPONSE" | jq -r '.url')
+
+if [[ "$SIGNED_URL" == "null" ]]; then
+  echo -e "\033[31mFailed to get signed URL.\033[0m"
+  exit 1
+fi
+
+UPLOAD_RESPONSE=$(tar -cz dist | curl -v -X PUT \
+  -H "Content-Type: application/gzip" \
+  --data-binary @- "$SIGNED_URL" 2>&1)
+
+if echo "$UPLOAD_RESPONSE" | grep -q "HTTP/[0-9.]* 200"; then
+  echo -e "\033[32mUploaded build to Stainless storage.\033[0m"
+  echo -e "\033[32mInstallation: npm install 'https://pkg.stainless.com/s/gitpod-typescript/$SHA'\033[0m"
+else
+  echo -e "\033[31mFailed to upload artifact.\033[0m"
+  exit 1
+fi
diff --git a/src/api-promise.ts b/src/api-promise.ts
new file mode 100644
index 0000000..8c775ee
--- /dev/null
+++ b/src/api-promise.ts
@@ -0,0 +1,2 @@
+/** @deprecated Import from ./core/api-promise instead */
+export * from './core/api-promise';
diff --git a/src/client.ts b/src/client.ts
new file mode 100644
index 0000000..d7e3523
--- /dev/null
+++ b/src/client.ts
@@ -0,0 +1,1243 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import type { RequestInit, RequestInfo, BodyInit } from './internal/builtin-types';
+import type { HTTPMethod, PromiseOrValue, MergedRequestInit, FinalizedRequestInit } from './internal/types';
+import { uuid4 } from './internal/utils/uuid';
+import { validatePositiveInteger, isAbsoluteURL, safeJSON } from './internal/utils/values';
+import { sleep } from './internal/utils/sleep';
+import { type Logger, type LogLevel, parseLogLevel } from './internal/utils/log';
+export type { Logger, LogLevel } from './internal/utils/log';
+import { castToError, isAbortError } from './internal/errors';
+import type { APIResponseProps } from './internal/parse';
+import { getPlatformHeaders } from './internal/detect-platform';
+import * as Shims from './internal/shims';
+import * as Opts from './internal/request-options';
+import { VERSION } from './version';
+import * as Errors from './core/error';
+import * as Pagination from './core/pagination';
+import {
+  AbstractPage,
+  type DomainVerificationsPageParams,
+  DomainVerificationsPageResponse,
+  type EditorsPageParams,
+  EditorsPageResponse,
+  type EntriesPageParams,
+  EntriesPageResponse,
+  type EnvironmentClassesPageParams,
+  EnvironmentClassesPageResponse,
+  type EnvironmentsPageParams,
+  EnvironmentsPageResponse,
+  type GroupsPageParams,
+  GroupsPageResponse,
+  type IntegrationsPageParams,
+  IntegrationsPageResponse,
+  type LoginProvidersPageParams,
+  LoginProvidersPageResponse,
+  type MembersPageParams,
+  MembersPageResponse,
+  type PersonalAccessTokensPageParams,
+  PersonalAccessTokensPageResponse,
+  type PoliciesPageParams,
+  PoliciesPageResponse,
+  type ProjectsPageParams,
+  ProjectsPageResponse,
+  type RunnersPageParams,
+  RunnersPageResponse,
+  type SSOConfigurationsPageParams,
+  SSOConfigurationsPageResponse,
+  type SecretsPageParams,
+  SecretsPageResponse,
+  type ServicesPageParams,
+  ServicesPageResponse,
+  type SessionsPageParams,
+  SessionsPageResponse,
+  type TaskExecutionsPageParams,
+  TaskExecutionsPageResponse,
+  type TasksPageParams,
+  TasksPageResponse,
+  type TokensPageParams,
+  TokensPageResponse,
+} from './core/pagination';
+import * as Uploads from './core/uploads';
+import * as API from './resources/index';
+import { APIPromise } from './core/api-promise';
+import { type Fetch } from './internal/builtin-types';
+import { HeadersLike, NullableHeaders, buildHeaders } from './internal/headers';
+import { FinalRequestOptions, RequestOptions } from './internal/request-options';
+import {
+  Account,
+  AccountDeleteParams,
+  AccountDeleteResponse,
+  AccountGetSSOLoginURLParams,
+  AccountGetSSOLoginURLResponse,
+  AccountListLoginProvidersParams,
+  AccountMembership,
+  AccountRetrieveParams,
+  AccountRetrieveResponse,
+  Accounts,
+  JoinableOrganization,
+  LoginProvider,
+  LoginProvidersLoginProvidersPage,
+} from './resources/accounts';
+import {
+  Editor,
+  EditorListParams,
+  EditorResolveURLParams,
+  EditorResolveURLResponse,
+  EditorRetrieveParams,
+  EditorRetrieveResponse,
+  Editors,
+  EditorsEditorsPage,
+} from './resources/editors';
+import {
+  EventListParams,
+  EventListResponse,
+  EventListResponsesEntriesPage,
+  EventWatchParams,
+  EventWatchResponse,
+  Events,
+  ResourceOperation,
+  ResourceType,
+} from './resources/events';
+import { Group, GroupListParams, Groups, GroupsGroupsPage } from './resources/groups';
+import {
+  IDTokenVersion,
+  Identity,
+  IdentityExchangeTokenParams,
+  IdentityExchangeTokenResponse,
+  IdentityGetAuthenticatedIdentityParams,
+  IdentityGetAuthenticatedIdentityResponse,
+  IdentityGetIDTokenParams,
+  IdentityGetIDTokenResponse,
+} from './resources/identity';
+import {
+  Secret,
+  SecretCreateParams,
+  SecretCreateResponse,
+  SecretDeleteParams,
+  SecretDeleteResponse,
+  SecretGetValueParams,
+  SecretGetValueResponse,
+  SecretListParams,
+  SecretScope,
+  SecretUpdateValueParams,
+  SecretUpdateValueResponse,
+  Secrets,
+  SecretsSecretsPage,
+} from './resources/secrets';
+import {
+  EnvironmentSession,
+  EnvironmentSessionsSessionsPage,
+  Usage,
+  UsageListEnvironmentSessionsParams,
+} from './resources/usage';
+import { readEnv } from './internal/utils/env';
+import { formatRequestDetails, loggerFor } from './internal/utils/log';
+import { isEmptyObj } from './internal/utils/values';
+import {
+  AdmissionLevel,
+  Environment,
+  EnvironmentActivitySignal,
+  EnvironmentCreateEnvironmentTokenParams,
+  EnvironmentCreateEnvironmentTokenResponse,
+  EnvironmentCreateFromProjectParams,
+  EnvironmentCreateFromProjectResponse,
+  EnvironmentCreateLogsTokenParams,
+  EnvironmentCreateLogsTokenResponse,
+  EnvironmentCreateParams,
+  EnvironmentCreateResponse,
+  EnvironmentDeleteParams,
+  EnvironmentDeleteResponse,
+  EnvironmentListParams,
+  EnvironmentMarkActiveParams,
+  EnvironmentMarkActiveResponse,
+  EnvironmentMetadata,
+  EnvironmentPhase,
+  EnvironmentRetrieveParams,
+  EnvironmentRetrieveResponse,
+  EnvironmentSpec,
+  EnvironmentStartParams,
+  EnvironmentStartResponse,
+  EnvironmentStatus,
+  EnvironmentStopParams,
+  EnvironmentStopResponse,
+  EnvironmentUpdateParams,
+  EnvironmentUpdateResponse,
+  Environments,
+  EnvironmentsEnvironmentsPage,
+} from './resources/environments/environments';
+import {
+  InviteDomains,
+  Organization,
+  OrganizationCreateParams,
+  OrganizationCreateResponse,
+  OrganizationDeleteParams,
+  OrganizationDeleteResponse,
+  OrganizationJoinParams,
+  OrganizationJoinResponse,
+  OrganizationLeaveParams,
+  OrganizationLeaveResponse,
+  OrganizationListMembersParams,
+  OrganizationMember,
+  OrganizationMembersMembersPage,
+  OrganizationRetrieveParams,
+  OrganizationRetrieveResponse,
+  OrganizationSetRoleParams,
+  OrganizationSetRoleResponse,
+  OrganizationTier,
+  OrganizationUpdateParams,
+  OrganizationUpdateResponse,
+  Organizations,
+} from './resources/organizations/organizations';
+import {
+  EnvironmentInitializer,
+  Project,
+  ProjectCreateFromEnvironmentParams,
+  ProjectCreateFromEnvironmentResponse,
+  ProjectCreateParams,
+  ProjectCreateResponse,
+  ProjectDeleteParams,
+  ProjectDeleteResponse,
+  ProjectEnvironmentClass,
+  ProjectListParams,
+  ProjectMetadata,
+  ProjectRetrieveParams,
+  ProjectRetrieveResponse,
+  ProjectUpdateParams,
+  ProjectUpdateResponse,
+  Projects,
+  ProjectsProjectsPage,
+} from './resources/projects/projects';
+import {
+  LogLevel,
+  MetricsConfiguration,
+  Runner,
+  RunnerCapability,
+  RunnerCheckAuthenticationForHostParams,
+  RunnerCheckAuthenticationForHostResponse,
+  RunnerConfiguration,
+  RunnerCreateParams,
+  RunnerCreateResponse,
+  RunnerCreateRunnerTokenParams,
+  RunnerCreateRunnerTokenResponse,
+  RunnerDeleteParams,
+  RunnerDeleteResponse,
+  RunnerKind,
+  RunnerListParams,
+  RunnerParseContextURLParams,
+  RunnerParseContextURLResponse,
+  RunnerPhase,
+  RunnerProvider,
+  RunnerReleaseChannel,
+  RunnerRetrieveParams,
+  RunnerRetrieveResponse,
+  RunnerSpec,
+  RunnerStatus,
+  RunnerUpdateParams,
+  RunnerUpdateResponse,
+  Runners,
+  RunnersRunnersPage,
+} from './resources/runners/runners';
+import {
+  User,
+  UserGetAuthenticatedUserParams,
+  UserGetAuthenticatedUserResponse,
+  UserSetSuspendedParams,
+  UserSetSuspendedResponse,
+  Users,
+} from './resources/users/users';
+
+export interface ClientOptions {
+  /**
+   * Defaults to process.env['GITPOD_API_KEY'].
+   */
+  bearerToken?: string | undefined;
+
+  /**
+   * Override the default base URL for the API, e.g., "https://api.example.com/v2/"
+   *
+   * Defaults to process.env['GITPOD_BASE_URL'].
+   */
+  baseURL?: string | null | undefined;
+
+  /**
+   * The maximum amount of time (in milliseconds) that the client should wait for a response
+   * from the server before timing out a single request.
+   *
+   * Note that request timeouts are retried by default, so in a worst-case scenario you may wait
+   * much longer than this timeout before the promise succeeds or fails.
+   */
+  timeout?: number | undefined;
+  /**
+   * Additional `RequestInit` options to be passed to `fetch` calls.
+   * Properties will be overridden by per-request `fetchOptions`.
+   */
+  fetchOptions?: MergedRequestInit | undefined;
+
+  /**
+   * Specify a custom `fetch` function implementation.
+   *
+   * If not provided, we expect that `fetch` is defined globally.
+   */
+  fetch?: Fetch | undefined;
+
+  /**
+   * The maximum number of times that the client will retry a request in case of a
+   * temporary failure, like a network error or a 5XX error from the server.
+   *
+   * @default 2
+   */
+  maxRetries?: number | undefined;
+
+  /**
+   * Default headers to include with every request to the API.
+   *
+   * These can be removed in individual requests by explicitly setting the
+   * header to `null` in request options.
+   */
+  defaultHeaders?: HeadersLike | undefined;
+
+  /**
+   * Default query parameters to include with every request to the API.
+   *
+   * These can be removed in individual requests by explicitly setting the
+   * param to `undefined` in request options.
+   */
+  defaultQuery?: Record<string, string | undefined> | undefined;
+
+  /**
+   * Set the log level.
+   *
+   * Defaults to process.env['GITPOD_LOG'] or 'warn' if it isn't set.
+   */
+  logLevel?: LogLevel | undefined;
+
+  /**
+   * Set the logger.
+   *
+   * Defaults to globalThis.console.
+   */
+  logger?: Logger | undefined;
+}
+
+/**
+ * API Client for interfacing with the Gitpod API.
+ */
+export class Gitpod {
+  bearerToken: string;
+
+  baseURL: string;
+  maxRetries: number;
+  timeout: number;
+  logger: Logger | undefined;
+  logLevel: LogLevel | undefined;
+  fetchOptions: MergedRequestInit | undefined;
+
+  private fetch: Fetch;
+  #encoder: Opts.RequestEncoder;
+  protected idempotencyHeader?: string;
+  private _options: ClientOptions;
+
+  /**
+   * API Client for interfacing with the Gitpod API.
+   *
+   * @param {string | undefined} [opts.bearerToken=process.env['GITPOD_API_KEY'] ?? undefined]
+   * @param {string} [opts.baseURL=process.env['GITPOD_BASE_URL'] ?? https://app.gitpod.io/api] - Override the default base URL for the API.
+   * @param {number} [opts.timeout=1 minute] - The maximum amount of time (in milliseconds) the client will wait for a response before timing out.
+   * @param {MergedRequestInit} [opts.fetchOptions] - Additional `RequestInit` options to be passed to `fetch` calls.
+   * @param {Fetch} [opts.fetch] - Specify a custom `fetch` function implementation.
+   * @param {number} [opts.maxRetries=2] - The maximum number of times the client will retry a request.
+   * @param {HeadersLike} opts.defaultHeaders - Default headers to include with every request to the API.
+   * @param {Record<string, string | undefined>} opts.defaultQuery - Default query parameters to include with every request to the API.
+   */
+  constructor({
+    baseURL = readEnv('GITPOD_BASE_URL'),
+    bearerToken = readEnv('GITPOD_API_KEY'),
+    ...opts
+  }: ClientOptions = {}) {
+    if (bearerToken === undefined) {
+      throw new Errors.GitpodError(
+        "The GITPOD_API_KEY environment variable is missing or empty; either provide it, or instantiate the Gitpod client with an bearerToken option, like new Gitpod({ bearerToken: 'My Bearer Token' }).",
+      );
+    }
+
+    const options: ClientOptions = {
+      bearerToken,
+      ...opts,
+      baseURL: baseURL || `https://app.gitpod.io/api`,
+    };
+
+    this.baseURL = options.baseURL!;
+    this.timeout = options.timeout ?? Gitpod.DEFAULT_TIMEOUT /* 1 minute */;
+    this.logger = options.logger ?? console;
+    const defaultLogLevel = 'warn';
+    // Set default logLevel early so that we can log a warning in parseLogLevel.
+    this.logLevel = defaultLogLevel;
+    this.logLevel =
+      parseLogLevel(options.logLevel, 'ClientOptions.logLevel', this) ??
+      parseLogLevel(readEnv('GITPOD_LOG'), "process.env['GITPOD_LOG']", this) ??
+      defaultLogLevel;
+    this.fetchOptions = options.fetchOptions;
+    this.maxRetries = options.maxRetries ?? 2;
+    this.fetch = options.fetch ?? Shims.getDefaultFetch();
+    this.#encoder = Opts.FallbackEncoder;
+
+    this._options = options;
+
+    this.bearerToken = bearerToken;
+  }
+
+  protected defaultQuery(): Record<string, string | undefined> | undefined {
+    return this._options.defaultQuery;
+  }
+
+  protected validateHeaders({ values, nulls }: NullableHeaders) {
+    return;
+  }
+
+  protected authHeaders(opts: FinalRequestOptions): NullableHeaders | undefined {
+    return buildHeaders([{ Authorization: `Bearer ${this.bearerToken}` }]);
+  }
+
+  /**
+   * Basic re-implementation of `qs.stringify` for primitive types.
+   */
+  protected stringifyQuery(query: Record<string, unknown>): string {
+    return Object.entries(query)
+      .filter(([_, value]) => typeof value !== 'undefined')
+      .map(([key, value]) => {
+        if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
+          return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
+        }
+        if (value === null) {
+          return `${encodeURIComponent(key)}=`;
+        }
+        throw new Errors.GitpodError(
+          `Cannot stringify type ${typeof value}; Expected string, number, boolean, or null. If you need to pass nested query parameters, you can manually encode them, e.g. { query: { 'foo[key1]': value1, 'foo[key2]': value2 } }, and please open a GitHub issue requesting better support for your use case.`,
+        );
+      })
+      .join('&');
+  }
+
+  private getUserAgent(): string {
+    return `${this.constructor.name}/JS ${VERSION}`;
+  }
+
+  protected defaultIdempotencyKey(): string {
+    return `stainless-node-retry-${uuid4()}`;
+  }
+
+  protected makeStatusError(
+    status: number,
+    error: Object,
+    message: string | undefined,
+    headers: Headers,
+  ): Errors.APIError {
+    return Errors.APIError.generate(status, error, message, headers);
+  }
+
+  buildURL(path: string, query: Record<string, unknown> | null | undefined): string {
+    const url =
+      isAbsoluteURL(path) ?
+        new URL(path)
+      : new URL(this.baseURL + (this.baseURL.endsWith('/') && path.startsWith('/') ? path.slice(1) : path));
+
+    const defaultQuery = this.defaultQuery();
+    if (!isEmptyObj(defaultQuery)) {
+      query = { ...defaultQuery, ...query };
+    }
+
+    if (typeof query === 'object' && query && !Array.isArray(query)) {
+      url.search = this.stringifyQuery(query as Record<string, unknown>);
+    }
+
+    return url.toString();
+  }
+
+  /**
+   * Used as a callback for mutating the given `FinalRequestOptions` object.
+   */
+  protected async prepareOptions(options: FinalRequestOptions): Promise<void> {}
+
+  /**
+   * Used as a callback for mutating the given `RequestInit` object.
+   *
+   * This is useful for cases where you want to add certain headers based off of
+   * the request properties, e.g. `method` or `url`.
+   */
+  protected async prepareRequest(
+    request: RequestInit,
+    { url, options }: { url: string; options: FinalRequestOptions },
+  ): Promise<void> {}
+
+  get<Rsp>(path: string, opts?: PromiseOrValue<RequestOptions>): APIPromise<Rsp> {
+    return this.methodRequest('get', path, opts);
+  }
+
+  post<Rsp>(path: string, opts?: PromiseOrValue<RequestOptions>): APIPromise<Rsp> {
+    return this.methodRequest('post', path, opts);
+  }
+
+  patch<Rsp>(path: string, opts?: PromiseOrValue<RequestOptions>): APIPromise<Rsp> {
+    return this.methodRequest('patch', path, opts);
+  }
+
+  put<Rsp>(path: string, opts?: PromiseOrValue<RequestOptions>): APIPromise<Rsp> {
+    return this.methodRequest('put', path, opts);
+  }
+
+  delete<Rsp>(path: string, opts?: PromiseOrValue<RequestOptions>): APIPromise<Rsp> {
+    return this.methodRequest('delete', path, opts);
+  }
+
+  private methodRequest<Rsp>(
+    method: HTTPMethod,
+    path: string,
+    opts?: PromiseOrValue<RequestOptions>,
+  ): APIPromise<Rsp> {
+    return this.request(
+      Promise.resolve(opts).then((opts) => {
+        return { method, path, ...opts };
+      }),
+    );
+  }
+
+  request<Rsp>(
+    options: PromiseOrValue<FinalRequestOptions>,
+    remainingRetries: number | null = null,
+  ): APIPromise<Rsp> {
+    return new APIPromise(this, this.makeRequest(options, remainingRetries, undefined));
+  }
+
+  private async makeRequest(
+    optionsInput: PromiseOrValue<FinalRequestOptions>,
+    retriesRemaining: number | null,
+    retryOfRequestLogID: string | undefined,
+  ): Promise<APIResponseProps> {
+    const options = await optionsInput;
+    const maxRetries = options.maxRetries ?? this.maxRetries;
+    if (retriesRemaining == null) {
+      retriesRemaining = maxRetries;
+    }
+
+    await this.prepareOptions(options);
+
+    const { req, url, timeout } = this.buildRequest(options, { retryCount: maxRetries - retriesRemaining });
+
+    await this.prepareRequest(req, { url, options });
+
+    /** Not an API request ID, just for correlating local log entries. */
+    const requestLogID = 'log_' + ((Math.random() * (1 << 24)) | 0).toString(16).padStart(6, '0');
+    const retryLogStr = retryOfRequestLogID === undefined ? '' : `, retryOf: ${retryOfRequestLogID}`;
+    const startTime = Date.now();
+
+    loggerFor(this).debug(
+      `[${requestLogID}] sending request`,
+      formatRequestDetails({
+        retryOfRequestLogID,
+        method: options.method,
+        url,
+        options,
+        headers: req.headers,
+      }),
+    );
+
+    if (options.signal?.aborted) {
+      throw new Errors.APIUserAbortError();
+    }
+
+    const controller = new AbortController();
+    const response = await this.fetchWithTimeout(url, req, timeout, controller).catch(castToError);
+    const headersTime = Date.now();
+
+    if (response instanceof Error) {
+      const retryMessage = `retrying, ${retriesRemaining} attempts remaining`;
+      if (options.signal?.aborted) {
+        throw new Errors.APIUserAbortError();
+      }
+      // detect native connection timeout errors
+      // deno throws "TypeError: error sending request for url (https://example/): client error (Connect): tcp connect error: Operation timed out (os error 60): Operation timed out (os error 60)"
+      // undici throws "TypeError: fetch failed" with cause "ConnectTimeoutError: Connect Timeout Error (attempted address: example:443, timeout: 1ms)"
+      // others do not provide enough information to distinguish timeouts from other connection errors
+      const isTimeout =
+        isAbortError(response) ||
+        /timed? ?out/i.test(String(response) + ('cause' in response ? String(response.cause) : ''));
+      if (retriesRemaining) {
+        loggerFor(this).info(
+          `[${requestLogID}] connection ${isTimeout ? 'timed out' : 'failed'} - ${retryMessage}`,
+        );
+        loggerFor(this).debug(
+          `[${requestLogID}] connection ${isTimeout ? 'timed out' : 'failed'} (${retryMessage})`,
+          formatRequestDetails({
+            retryOfRequestLogID,
+            url,
+            durationMs: headersTime - startTime,
+            message: response.message,
+          }),
+        );
+        return this.retryRequest(options, retriesRemaining, retryOfRequestLogID ?? requestLogID);
+      }
+      loggerFor(this).info(
+        `[${requestLogID}] connection ${isTimeout ? 'timed out' : 'failed'} - error; no more retries left`,
+      );
+      loggerFor(this).debug(
+        `[${requestLogID}] connection ${isTimeout ? 'timed out' : 'failed'} (error; no more retries left)`,
+        formatRequestDetails({
+          retryOfRequestLogID,
+          url,
+          durationMs: headersTime - startTime,
+          message: response.message,
+        }),
+      );
+      if (isTimeout) {
+        throw new Errors.APIConnectionTimeoutError();
+      }
+      throw new Errors.APIConnectionError({ cause: response });
+    }
+
+    const responseInfo = `[${requestLogID}${retryLogStr}] ${req.method} ${url} ${
+      response.ok ? 'succeeded' : 'failed'
+    } with status ${response.status} in ${headersTime - startTime}ms`;
+
+    if (!response.ok) {
+      const shouldRetry = this.shouldRetry(response);
+      if (retriesRemaining && shouldRetry) {
+        const retryMessage = `retrying, ${retriesRemaining} attempts remaining`;
+
+        // We don't need the body of this response.
+        await Shims.CancelReadableStream(response.body);
+        loggerFor(this).info(`${responseInfo} - ${retryMessage}`);
+        loggerFor(this).debug(
+          `[${requestLogID}] response error (${retryMessage})`,
+          formatRequestDetails({
+            retryOfRequestLogID,
+            url: response.url,
+            status: response.status,
+            headers: response.headers,
+            durationMs: headersTime - startTime,
+          }),
+        );
+        return this.retryRequest(
+          options,
+          retriesRemaining,
+          retryOfRequestLogID ?? requestLogID,
+          response.headers,
+        );
+      }
+
+      const retryMessage = shouldRetry ? `error; no more retries left` : `error; not retryable`;
+
+      loggerFor(this).info(`${responseInfo} - ${retryMessage}`);
+
+      const errText = await response.text().catch((err: any) => castToError(err).message);
+      const errJSON = safeJSON(errText);
+      const errMessage = errJSON ? undefined : errText;
+
+      loggerFor(this).debug(
+        `[${requestLogID}] response error (${retryMessage})`,
+        formatRequestDetails({
+          retryOfRequestLogID,
+          url: response.url,
+          status: response.status,
+          headers: response.headers,
+          message: errMessage,
+          durationMs: Date.now() - startTime,
+        }),
+      );
+
+      const err = this.makeStatusError(response.status, errJSON, errMessage, response.headers);
+      throw err;
+    }
+
+    loggerFor(this).info(responseInfo);
+    loggerFor(this).debug(
+      `[${requestLogID}] response start`,
+      formatRequestDetails({
+        retryOfRequestLogID,
+        url: response.url,
+        status: response.status,
+        headers: response.headers,
+        durationMs: headersTime - startTime,
+      }),
+    );
+
+    return { response, options, controller, requestLogID, retryOfRequestLogID, startTime };
+  }
+
+  getAPIList<Item, PageClass extends Pagination.AbstractPage<Item> = Pagination.AbstractPage<Item>>(
+    path: string,
+    Page: new (...args: any[]) => PageClass,
+    opts?: RequestOptions,
+  ): Pagination.PagePromise<PageClass, Item> {
+    return this.requestAPIList(Page, { method: 'get', path, ...opts });
+  }
+
+  requestAPIList<
+    Item = unknown,
+    PageClass extends Pagination.AbstractPage<Item> = Pagination.AbstractPage<Item>,
+  >(
+    Page: new (...args: ConstructorParameters<typeof Pagination.AbstractPage>) => PageClass,
+    options: FinalRequestOptions,
+  ): Pagination.PagePromise<PageClass, Item> {
+    const request = this.makeRequest(options, null, undefined);
+    return new Pagination.PagePromise<PageClass, Item>(this as any as Gitpod, request, Page);
+  }
+
+  async fetchWithTimeout(
+    url: RequestInfo,
+    init: RequestInit | undefined,
+    ms: number,
+    controller: AbortController,
+  ): Promise<Response> {
+    const { signal, method, ...options } = init || {};
+    if (signal) signal.addEventListener('abort', () => controller.abort());
+
+    const timeout = setTimeout(() => controller.abort(), ms);
+
+    const isReadableBody =
+      ((globalThis as any).ReadableStream && options.body instanceof (globalThis as any).ReadableStream) ||
+      (typeof options.body === 'object' && options.body !== null && Symbol.asyncIterator in options.body);
+
+    const fetchOptions: RequestInit = {
+      signal: controller.signal as any,
+      ...(isReadableBody ? { duplex: 'half' } : {}),
+      method: 'GET',
+      ...options,
+    };
+    if (method) {
+      // Custom methods like 'patch' need to be uppercased
+      // See https://github.com/nodejs/undici/issues/2294
+      fetchOptions.method = method.toUpperCase();
+    }
+
+    try {
+      // use undefined this binding; fetch errors if bound to something else in browser/cloudflare
+      return await this.fetch.call(undefined, url, fetchOptions);
+    } finally {
+      clearTimeout(timeout);
+    }
+  }
+
+  private shouldRetry(response: Response): boolean {
+    // Note this is not a standard header.
+    const shouldRetryHeader = response.headers.get('x-should-retry');
+
+    // If the server explicitly says whether or not to retry, obey.
+    if (shouldRetryHeader === 'true') return true;
+    if (shouldRetryHeader === 'false') return false;
+
+    // Retry on request timeouts.
+    if (response.status === 408) return true;
+
+    // Retry on lock timeouts.
+    if (response.status === 409) return true;
+
+    // Retry on rate limits.
+    if (response.status === 429) return true;
+
+    // Retry internal errors.
+    if (response.status >= 500) return true;
+
+    return false;
+  }
+
+  private async retryRequest(
+    options: FinalRequestOptions,
+    retriesRemaining: number,
+    requestLogID: string,
+    responseHeaders?: Headers | undefined,
+  ): Promise<APIResponseProps> {
+    let timeoutMillis: number | undefined;
+
+    // Note the `retry-after-ms` header may not be standard, but is a good idea and we'd like proactive support for it.
+    const retryAfterMillisHeader = responseHeaders?.get('retry-after-ms');
+    if (retryAfterMillisHeader) {
+      const timeoutMs = parseFloat(retryAfterMillisHeader);
+      if (!Number.isNaN(timeoutMs)) {
+        timeoutMillis = timeoutMs;
+      }
+    }
+
+    // About the Retry-After header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After
+    const retryAfterHeader = responseHeaders?.get('retry-after');
+    if (retryAfterHeader && !timeoutMillis) {
+      const timeoutSeconds = parseFloat(retryAfterHeader);
+      if (!Number.isNaN(timeoutSeconds)) {
+        timeoutMillis = timeoutSeconds * 1000;
+      } else {
+        timeoutMillis = Date.parse(retryAfterHeader) - Date.now();
+      }
+    }
+
+    // If the API asks us to wait a certain amount of time (and it's a reasonable amount),
+    // just do what it says, but otherwise calculate a default
+    if (!(timeoutMillis && 0 <= timeoutMillis && timeoutMillis < 60 * 1000)) {
+      const maxRetries = options.maxRetries ?? this.maxRetries;
+      timeoutMillis = this.calculateDefaultRetryTimeoutMillis(retriesRemaining, maxRetries);
+    }
+    await sleep(timeoutMillis);
+
+    return this.makeRequest(options, retriesRemaining - 1, requestLogID);
+  }
+
+  private calculateDefaultRetryTimeoutMillis(retriesRemaining: number, maxRetries: number): number {
+    const initialRetryDelay = 0.5;
+    const maxRetryDelay = 8.0;
+
+    const numRetries = maxRetries - retriesRemaining;
+
+    // Apply exponential backoff, but not more than the max.
+    const sleepSeconds = Math.min(initialRetryDelay * Math.pow(2, numRetries), maxRetryDelay);
+
+    // Apply some jitter, take up to at most 25 percent of the retry time.
+    const jitter = 1 - Math.random() * 0.25;
+
+    return sleepSeconds * jitter * 1000;
+  }
+
+  buildRequest(
+    inputOptions: FinalRequestOptions,
+    { retryCount = 0 }: { retryCount?: number } = {},
+  ): { req: FinalizedRequestInit; url: string; timeout: number } {
+    const options = { ...inputOptions };
+    const { method, path, query } = options;
+
+    const url = this.buildURL(path!, query as Record<string, unknown>);
+    if ('timeout' in options) validatePositiveInteger('timeout', options.timeout);
+    options.timeout = options.timeout ?? this.timeout;
+    const { bodyHeaders, body } = this.buildBody({ options });
+    const reqHeaders = this.buildHeaders({ options: inputOptions, method, bodyHeaders, retryCount });
+
+    const req: FinalizedRequestInit = {
+      method,
+      headers: reqHeaders,
+      ...(options.signal && { signal: options.signal }),
+      ...((globalThis as any).ReadableStream &&
+        body instanceof (globalThis as any).ReadableStream && { duplex: 'half' }),
+      ...(body && { body }),
+      ...((this.fetchOptions as any) ?? {}),
+      ...((options.fetchOptions as any) ?? {}),
+    };
+
+    return { req, url, timeout: options.timeout };
+  }
+
+  private buildHeaders({
+    options,
+    method,
+    bodyHeaders,
+    retryCount,
+  }: {
+    options: FinalRequestOptions;
+    method: HTTPMethod;
+    bodyHeaders: HeadersLike;
+    retryCount: number;
+  }): Headers {
+    let idempotencyHeaders: HeadersLike = {};
+    if (this.idempotencyHeader && method !== 'get') {
+      if (!options.idempotencyKey) options.idempotencyKey = this.defaultIdempotencyKey();
+      idempotencyHeaders[this.idempotencyHeader] = options.idempotencyKey;
+    }
+
+    const headers = buildHeaders([
+      idempotencyHeaders,
+      {
+        Accept: 'application/json',
+        'User-Agent': this.getUserAgent(),
+        'X-Stainless-Retry-Count': String(retryCount),
+        ...(options.timeout ? { 'X-Stainless-Timeout': String(Math.trunc(options.timeout / 1000)) } : {}),
+        ...getPlatformHeaders(),
+      },
+      this.authHeaders(options),
+      this._options.defaultHeaders,
+      bodyHeaders,
+      options.headers,
+    ]);
+
+    this.validateHeaders(headers);
+
+    return headers.values;
+  }
+
+  private buildBody({ options: { body, headers: rawHeaders } }: { options: FinalRequestOptions }): {
+    bodyHeaders: HeadersLike;
+    body: BodyInit | undefined;
+  } {
+    if (!body) {
+      return { bodyHeaders: undefined, body: undefined };
+    }
+    const headers = buildHeaders([rawHeaders]);
+    if (
+      // Pass raw type verbatim
+      ArrayBuffer.isView(body) ||
+      body instanceof ArrayBuffer ||
+      body instanceof DataView ||
+      (typeof body === 'string' &&
+        // Preserve legacy string encoding behavior for now
+        headers.values.has('content-type')) ||
+      // `Blob` is superset of `File`
+      body instanceof Blob ||
+      // `FormData` -> `multipart/form-data`
+      body instanceof FormData ||
+      // `URLSearchParams` -> `application/x-www-form-urlencoded`
+      body instanceof URLSearchParams ||
+      // Send chunked stream (each chunk has own `length`)
+      ((globalThis as any).ReadableStream && body instanceof (globalThis as any).ReadableStream)
+    ) {
+      return { bodyHeaders: undefined, body: body as BodyInit };
+    } else if (
+      typeof body === 'object' &&
+      (Symbol.asyncIterator in body ||
+        (Symbol.iterator in body && 'next' in body && typeof body.next === 'function'))
+    ) {
+      return { bodyHeaders: undefined, body: Shims.ReadableStreamFrom(body as AsyncIterable<Uint8Array>) };
+    } else {
+      return this.#encoder({ body, headers });
+    }
+  }
+
+  static Gitpod = this;
+  static DEFAULT_TIMEOUT = 60000; // 1 minute
+
+  static GitpodError = Errors.GitpodError;
+  static APIError = Errors.APIError;
+  static APIConnectionError = Errors.APIConnectionError;
+  static APIConnectionTimeoutError = Errors.APIConnectionTimeoutError;
+  static APIUserAbortError = Errors.APIUserAbortError;
+  static NotFoundError = Errors.NotFoundError;
+  static ConflictError = Errors.ConflictError;
+  static RateLimitError = Errors.RateLimitError;
+  static BadRequestError = Errors.BadRequestError;
+  static AuthenticationError = Errors.AuthenticationError;
+  static InternalServerError = Errors.InternalServerError;
+  static PermissionDeniedError = Errors.PermissionDeniedError;
+  static UnprocessableEntityError = Errors.UnprocessableEntityError;
+
+  static toFile = Uploads.toFile;
+
+  accounts: API.Accounts = new API.Accounts(this);
+  editors: API.Editors = new API.Editors(this);
+  environments: API.Environments = new API.Environments(this);
+  events: API.Events = new API.Events(this);
+  groups: API.Groups = new API.Groups(this);
+  identity: API.Identity = new API.Identity(this);
+  organizations: API.Organizations = new API.Organizations(this);
+  projects: API.Projects = new API.Projects(this);
+  runners: API.Runners = new API.Runners(this);
+  secrets: API.Secrets = new API.Secrets(this);
+  usage: API.Usage = new API.Usage(this);
+  users: API.Users = new API.Users(this);
+}
+Gitpod.Accounts = Accounts;
+Gitpod.Editors = Editors;
+Gitpod.Environments = Environments;
+Gitpod.Events = Events;
+Gitpod.Groups = Groups;
+Gitpod.Identity = Identity;
+Gitpod.Organizations = Organizations;
+Gitpod.Projects = Projects;
+Gitpod.Runners = Runners;
+Gitpod.Secrets = Secrets;
+Gitpod.Usage = Usage;
+Gitpod.Users = Users;
+export declare namespace Gitpod {
+  export type RequestOptions = Opts.RequestOptions;
+
+  export import DomainVerificationsPage = Pagination.DomainVerificationsPage;
+  export {
+    type DomainVerificationsPageParams as DomainVerificationsPageParams,
+    type DomainVerificationsPageResponse as DomainVerificationsPageResponse,
+  };
+
+  export import EditorsPage = Pagination.EditorsPage;
+  export { type EditorsPageParams as EditorsPageParams, type EditorsPageResponse as EditorsPageResponse };
+
+  export import EntriesPage = Pagination.EntriesPage;
+  export { type EntriesPageParams as EntriesPageParams, type EntriesPageResponse as EntriesPageResponse };
+
+  export import EnvironmentClassesPage = Pagination.EnvironmentClassesPage;
+  export {
+    type EnvironmentClassesPageParams as EnvironmentClassesPageParams,
+    type EnvironmentClassesPageResponse as EnvironmentClassesPageResponse,
+  };
+
+  export import EnvironmentsPage = Pagination.EnvironmentsPage;
+  export {
+    type EnvironmentsPageParams as EnvironmentsPageParams,
+    type EnvironmentsPageResponse as EnvironmentsPageResponse,
+  };
+
+  export import GroupsPage = Pagination.GroupsPage;
+  export { type GroupsPageParams as GroupsPageParams, type GroupsPageResponse as GroupsPageResponse };
+
+  export import IntegrationsPage = Pagination.IntegrationsPage;
+  export {
+    type IntegrationsPageParams as IntegrationsPageParams,
+    type IntegrationsPageResponse as IntegrationsPageResponse,
+  };
+
+  export import LoginProvidersPage = Pagination.LoginProvidersPage;
+  export {
+    type LoginProvidersPageParams as LoginProvidersPageParams,
+    type LoginProvidersPageResponse as LoginProvidersPageResponse,
+  };
+
+  export import MembersPage = Pagination.MembersPage;
+  export { type MembersPageParams as MembersPageParams, type MembersPageResponse as MembersPageResponse };
+
+  export import PersonalAccessTokensPage = Pagination.PersonalAccessTokensPage;
+  export {
+    type PersonalAccessTokensPageParams as PersonalAccessTokensPageParams,
+    type PersonalAccessTokensPageResponse as PersonalAccessTokensPageResponse,
+  };
+
+  export import PoliciesPage = Pagination.PoliciesPage;
+  export { type PoliciesPageParams as PoliciesPageParams, type PoliciesPageResponse as PoliciesPageResponse };
+
+  export import ProjectsPage = Pagination.ProjectsPage;
+  export { type ProjectsPageParams as ProjectsPageParams, type ProjectsPageResponse as ProjectsPageResponse };
+
+  export import RunnersPage = Pagination.RunnersPage;
+  export { type RunnersPageParams as RunnersPageParams, type RunnersPageResponse as RunnersPageResponse };
+
+  export import SecretsPage = Pagination.SecretsPage;
+  export { type SecretsPageParams as SecretsPageParams, type SecretsPageResponse as SecretsPageResponse };
+
+  export import ServicesPage = Pagination.ServicesPage;
+  export { type ServicesPageParams as ServicesPageParams, type ServicesPageResponse as ServicesPageResponse };
+
+  export import SessionsPage = Pagination.SessionsPage;
+  export { type SessionsPageParams as SessionsPageParams, type SessionsPageResponse as SessionsPageResponse };
+
+  export import SSOConfigurationsPage = Pagination.SSOConfigurationsPage;
+  export {
+    type SSOConfigurationsPageParams as SSOConfigurationsPageParams,
+    type SSOConfigurationsPageResponse as SSOConfigurationsPageResponse,
+  };
+
+  export import TaskExecutionsPage = Pagination.TaskExecutionsPage;
+  export {
+    type TaskExecutionsPageParams as TaskExecutionsPageParams,
+    type TaskExecutionsPageResponse as TaskExecutionsPageResponse,
+  };
+
+  export import TasksPage = Pagination.TasksPage;
+  export { type TasksPageParams as TasksPageParams, type TasksPageResponse as TasksPageResponse };
+
+  export import TokensPage = Pagination.TokensPage;
+  export { type TokensPageParams as TokensPageParams, type TokensPageResponse as TokensPageResponse };
+
+  export {
+    Accounts as Accounts,
+    type Account as Account,
+    type AccountMembership as AccountMembership,
+    type JoinableOrganization as JoinableOrganization,
+    type LoginProvider as LoginProvider,
+    type AccountRetrieveResponse as AccountRetrieveResponse,
+    type AccountDeleteResponse as AccountDeleteResponse,
+    type AccountGetSSOLoginURLResponse as AccountGetSSOLoginURLResponse,
+    type LoginProvidersLoginProvidersPage as LoginProvidersLoginProvidersPage,
+    type AccountRetrieveParams as AccountRetrieveParams,
+    type AccountDeleteParams as AccountDeleteParams,
+    type AccountGetSSOLoginURLParams as AccountGetSSOLoginURLParams,
+    type AccountListLoginProvidersParams as AccountListLoginProvidersParams,
+  };
+
+  export {
+    Editors as Editors,
+    type Editor as Editor,
+    type EditorRetrieveResponse as EditorRetrieveResponse,
+    type EditorResolveURLResponse as EditorResolveURLResponse,
+    type EditorsEditorsPage as EditorsEditorsPage,
+    type EditorRetrieveParams as EditorRetrieveParams,
+    type EditorListParams as EditorListParams,
+    type EditorResolveURLParams as EditorResolveURLParams,
+  };
+
+  export {
+    Environments as Environments,
+    type AdmissionLevel as AdmissionLevel,
+    type Environment as Environment,
+    type EnvironmentActivitySignal as EnvironmentActivitySignal,
+    type EnvironmentMetadata as EnvironmentMetadata,
+    type EnvironmentPhase as EnvironmentPhase,
+    type EnvironmentSpec as EnvironmentSpec,
+    type EnvironmentStatus as EnvironmentStatus,
+    type EnvironmentCreateResponse as EnvironmentCreateResponse,
+    type EnvironmentRetrieveResponse as EnvironmentRetrieveResponse,
+    type EnvironmentUpdateResponse as EnvironmentUpdateResponse,
+    type EnvironmentDeleteResponse as EnvironmentDeleteResponse,
+    type EnvironmentCreateEnvironmentTokenResponse as EnvironmentCreateEnvironmentTokenResponse,
+    type EnvironmentCreateFromProjectResponse as EnvironmentCreateFromProjectResponse,
+    type EnvironmentCreateLogsTokenResponse as EnvironmentCreateLogsTokenResponse,
+    type EnvironmentMarkActiveResponse as EnvironmentMarkActiveResponse,
+    type EnvironmentStartResponse as EnvironmentStartResponse,
+    type EnvironmentStopResponse as EnvironmentStopResponse,
+    type EnvironmentsEnvironmentsPage as EnvironmentsEnvironmentsPage,
+    type EnvironmentCreateParams as EnvironmentCreateParams,
+    type EnvironmentRetrieveParams as EnvironmentRetrieveParams,
+    type EnvironmentUpdateParams as EnvironmentUpdateParams,
+    type EnvironmentListParams as EnvironmentListParams,
+    type EnvironmentDeleteParams as EnvironmentDeleteParams,
+    type EnvironmentCreateEnvironmentTokenParams as EnvironmentCreateEnvironmentTokenParams,
+    type EnvironmentCreateFromProjectParams as EnvironmentCreateFromProjectParams,
+    type EnvironmentCreateLogsTokenParams as EnvironmentCreateLogsTokenParams,
+    type EnvironmentMarkActiveParams as EnvironmentMarkActiveParams,
+    type EnvironmentStartParams as EnvironmentStartParams,
+    type EnvironmentStopParams as EnvironmentStopParams,
+  };
+
+  export {
+    Events as Events,
+    type ResourceOperation as ResourceOperation,
+    type ResourceType as ResourceType,
+    type EventListResponse as EventListResponse,
+    type EventWatchResponse as EventWatchResponse,
+    type EventListResponsesEntriesPage as EventListResponsesEntriesPage,
+    type EventListParams as EventListParams,
+    type EventWatchParams as EventWatchParams,
+  };
+
+  export {
+    Groups as Groups,
+    type Group as Group,
+    type GroupsGroupsPage as GroupsGroupsPage,
+    type GroupListParams as GroupListParams,
+  };
+
+  export {
+    Identity as Identity,
+    type IDTokenVersion as IDTokenVersion,
+    type IdentityExchangeTokenResponse as IdentityExchangeTokenResponse,
+    type IdentityGetAuthenticatedIdentityResponse as IdentityGetAuthenticatedIdentityResponse,
+    type IdentityGetIDTokenResponse as IdentityGetIDTokenResponse,
+    type IdentityExchangeTokenParams as IdentityExchangeTokenParams,
+    type IdentityGetAuthenticatedIdentityParams as IdentityGetAuthenticatedIdentityParams,
+    type IdentityGetIDTokenParams as IdentityGetIDTokenParams,
+  };
+
+  export {
+    Organizations as Organizations,
+    type InviteDomains as InviteDomains,
+    type Organization as Organization,
+    type OrganizationMember as OrganizationMember,
+    type OrganizationTier as OrganizationTier,
+    type OrganizationCreateResponse as OrganizationCreateResponse,
+    type OrganizationRetrieveResponse as OrganizationRetrieveResponse,
+    type OrganizationUpdateResponse as OrganizationUpdateResponse,
+    type OrganizationDeleteResponse as OrganizationDeleteResponse,
+    type OrganizationJoinResponse as OrganizationJoinResponse,
+    type OrganizationLeaveResponse as OrganizationLeaveResponse,
+    type OrganizationSetRoleResponse as OrganizationSetRoleResponse,
+    type OrganizationMembersMembersPage as OrganizationMembersMembersPage,
+    type OrganizationCreateParams as OrganizationCreateParams,
+    type OrganizationRetrieveParams as OrganizationRetrieveParams,
+    type OrganizationUpdateParams as OrganizationUpdateParams,
+    type OrganizationDeleteParams as OrganizationDeleteParams,
+    type OrganizationJoinParams as OrganizationJoinParams,
+    type OrganizationLeaveParams as OrganizationLeaveParams,
+    type OrganizationListMembersParams as OrganizationListMembersParams,
+    type OrganizationSetRoleParams as OrganizationSetRoleParams,
+  };
+
+  export {
+    Projects as Projects,
+    type EnvironmentInitializer as EnvironmentInitializer,
+    type Project as Project,
+    type ProjectEnvironmentClass as ProjectEnvironmentClass,
+    type ProjectMetadata as ProjectMetadata,
+    type ProjectCreateResponse as ProjectCreateResponse,
+    type ProjectRetrieveResponse as ProjectRetrieveResponse,
+    type ProjectUpdateResponse as ProjectUpdateResponse,
+    type ProjectDeleteResponse as ProjectDeleteResponse,
+    type ProjectCreateFromEnvironmentResponse as ProjectCreateFromEnvironmentResponse,
+    type ProjectsProjectsPage as ProjectsProjectsPage,
+    type ProjectCreateParams as ProjectCreateParams,
+    type ProjectRetrieveParams as ProjectRetrieveParams,
+    type ProjectUpdateParams as ProjectUpdateParams,
+    type ProjectListParams as ProjectListParams,
+    type ProjectDeleteParams as ProjectDeleteParams,
+    type ProjectCreateFromEnvironmentParams as ProjectCreateFromEnvironmentParams,
+  };
+
+  export {
+    Runners as Runners,
+    type LogLevel as LogLevel,
+    type MetricsConfiguration as MetricsConfiguration,
+    type Runner as Runner,
+    type RunnerCapability as RunnerCapability,
+    type RunnerConfiguration as RunnerConfiguration,
+    type RunnerKind as RunnerKind,
+    type RunnerPhase as RunnerPhase,
+    type RunnerProvider as RunnerProvider,
+    type RunnerReleaseChannel as RunnerReleaseChannel,
+    type RunnerSpec as RunnerSpec,
+    type RunnerStatus as RunnerStatus,
+    type RunnerCreateResponse as RunnerCreateResponse,
+    type RunnerRetrieveResponse as RunnerRetrieveResponse,
+    type RunnerUpdateResponse as RunnerUpdateResponse,
+    type RunnerDeleteResponse as RunnerDeleteResponse,
+    type RunnerCheckAuthenticationForHostResponse as RunnerCheckAuthenticationForHostResponse,
+    type RunnerCreateRunnerTokenResponse as RunnerCreateRunnerTokenResponse,
+    type RunnerParseContextURLResponse as RunnerParseContextURLResponse,
+    type RunnersRunnersPage as RunnersRunnersPage,
+    type RunnerCreateParams as RunnerCreateParams,
+    type RunnerRetrieveParams as RunnerRetrieveParams,
+    type RunnerUpdateParams as RunnerUpdateParams,
+    type RunnerListParams as RunnerListParams,
+    type RunnerDeleteParams as RunnerDeleteParams,
+    type RunnerCheckAuthenticationForHostParams as RunnerCheckAuthenticationForHostParams,
+    type RunnerCreateRunnerTokenParams as RunnerCreateRunnerTokenParams,
+    type RunnerParseContextURLParams as RunnerParseContextURLParams,
+  };
+
+  export {
+    Secrets as Secrets,
+    type Secret as Secret,
+    type SecretScope as SecretScope,
+    type SecretCreateResponse as SecretCreateResponse,
+    type SecretDeleteResponse as SecretDeleteResponse,
+    type SecretGetValueResponse as SecretGetValueResponse,
+    type SecretUpdateValueResponse as SecretUpdateValueResponse,
+    type SecretsSecretsPage as SecretsSecretsPage,
+    type SecretCreateParams as SecretCreateParams,
+    type SecretListParams as SecretListParams,
+    type SecretDeleteParams as SecretDeleteParams,
+    type SecretGetValueParams as SecretGetValueParams,
+    type SecretUpdateValueParams as SecretUpdateValueParams,
+  };
+
+  export {
+    Usage as Usage,
+    type EnvironmentSession as EnvironmentSession,
+    type EnvironmentSessionsSessionsPage as EnvironmentSessionsSessionsPage,
+    type UsageListEnvironmentSessionsParams as UsageListEnvironmentSessionsParams,
+  };
+
+  export {
+    Users as Users,
+    type User as User,
+    type UserGetAuthenticatedUserResponse as UserGetAuthenticatedUserResponse,
+    type UserSetSuspendedResponse as UserSetSuspendedResponse,
+    type UserGetAuthenticatedUserParams as UserGetAuthenticatedUserParams,
+    type UserSetSuspendedParams as UserSetSuspendedParams,
+  };
+
+  export type AutomationTrigger = API.AutomationTrigger;
+  export type EnvironmentClass = API.EnvironmentClass;
+  export type ErrorCode = API.ErrorCode;
+  export type FieldValue = API.FieldValue;
+  export type OrganizationRole = API.OrganizationRole;
+  export type Principal = API.Principal;
+  export type RunsOn = API.RunsOn;
+  export type Subject = API.Subject;
+  export type Task = API.Task;
+  export type TaskExecution = API.TaskExecution;
+  export type TaskExecutionMetadata = API.TaskExecutionMetadata;
+  export type TaskExecutionPhase = API.TaskExecutionPhase;
+  export type TaskExecutionSpec = API.TaskExecutionSpec;
+  export type TaskExecutionStatus = API.TaskExecutionStatus;
+  export type TaskMetadata = API.TaskMetadata;
+  export type TaskSpec = API.TaskSpec;
+  export type UserStatus = API.UserStatus;
+}
diff --git a/src/core/README.md b/src/core/README.md
new file mode 100644
index 0000000..485fce8
--- /dev/null
+++ b/src/core/README.md
@@ -0,0 +1,3 @@
+# `core`
+
+This directory holds public modules implementing non-resource-specific SDK functionality.
diff --git a/src/core/api-promise.ts b/src/core/api-promise.ts
new file mode 100644
index 0000000..639a545
--- /dev/null
+++ b/src/core/api-promise.ts
@@ -0,0 +1,92 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { type Gitpod } from '../client';
+
+import { type PromiseOrValue } from '../internal/types';
+import { APIResponseProps, defaultParseResponse } from '../internal/parse';
+
+/**
+ * A subclass of `Promise` providing additional helper methods
+ * for interacting with the SDK.
+ */
+export class APIPromise<T> extends Promise<T> {
+  private parsedPromise: Promise<T> | undefined;
+  #client: Gitpod;
+
+  constructor(
+    client: Gitpod,
+    private responsePromise: Promise<APIResponseProps>,
+    private parseResponse: (
+      client: Gitpod,
+      props: APIResponseProps,
+    ) => PromiseOrValue<T> = defaultParseResponse,
+  ) {
+    super((resolve) => {
+      // this is maybe a bit weird but this has to be a no-op to not implicitly
+      // parse the response body; instead .then, .catch, .finally are overridden
+      // to parse the response
+      resolve(null as any);
+    });
+    this.#client = client;
+  }
+
+  _thenUnwrap<U>(transform: (data: T, props: APIResponseProps) => U): APIPromise<U> {
+    return new APIPromise(this.#client, this.responsePromise, async (client, props) =>
+      transform(await this.parseResponse(client, props), props),
+    );
+  }
+
+  /**
+   * Gets the raw `Response` instance instead of parsing the response
+   * data.
+   *
+   * If you want to parse the response body but still get the `Response`
+   * instance, you can use {@link withResponse()}.
+   *
+   * 👋 Getting the wrong TypeScript type for `Response`?
+   * Try setting `"moduleResolution": "NodeNext"` or add `"lib": ["DOM"]`
+   * to your `tsconfig.json`.
+   */
+  asResponse(): Promise<Response> {
+    return this.responsePromise.then((p) => p.response);
+  }
+
+  /**
+   * Gets the parsed response data and the raw `Response` instance.
+   *
+   * If you just want to get the raw `Response` instance without parsing it,
+   * you can use {@link asResponse()}.
+   *
+   * 👋 Getting the wrong TypeScript type for `Response`?
+   * Try setting `"moduleResolution": "NodeNext"` or add `"lib": ["DOM"]`
+   * to your `tsconfig.json`.
+   */
+  async withResponse(): Promise<{ data: T; response: Response }> {
+    const [data, response] = await Promise.all([this.parse(), this.asResponse()]);
+    return { data, response };
+  }
+
+  private parse(): Promise<T> {
+    if (!this.parsedPromise) {
+      this.parsedPromise = this.responsePromise.then((data) => this.parseResponse(this.#client, data));
+    }
+    return this.parsedPromise;
+  }
+
+  override then<TResult1 = T, TResult2 = never>(
+    onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
+    onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null,
+  ): Promise<TResult1 | TResult2> {
+    return this.parse().then(onfulfilled, onrejected);
+  }
+
+  override catch<TResult = never>(
+    onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | undefined | null,
+  ): Promise<T | TResult> {
+    return this.parse().catch(onrejected);
+  }
+
+  override finally(onfinally?: (() => void) | undefined | null): Promise<T> {
+    return this.parse().finally(onfinally);
+  }
+}
diff --git a/src/core/error.ts b/src/core/error.ts
new file mode 100644
index 0000000..1b22334
--- /dev/null
+++ b/src/core/error.ts
@@ -0,0 +1,140 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { castToError } from '../internal/errors';
+import * as Shared from '../resources/shared';
+
+export class GitpodError extends Error {}
+
+export class APIError<
+  TStatus extends number | undefined = number | undefined,
+  THeaders extends Headers | undefined = Headers | undefined,
+  TError extends Object | undefined = Object | undefined,
+> extends GitpodError {
+  /** HTTP status for the response that caused the error */
+  readonly status: TStatus;
+  /** HTTP headers for the response that caused the error */
+  readonly headers: THeaders;
+  /** JSON body of the response that caused the error */
+  readonly error: TError;
+
+  /**
+   * The status code, which should be an enum value of
+   * [google.rpc.Code][google.rpc.Code].
+   */
+  readonly code?: Shared.ErrorCode | undefined;
+
+  constructor(status: TStatus, error: TError, message: string | undefined, headers: THeaders) {
+    super(`${APIError.makeMessage(status, error, message)}`);
+    this.status = status;
+    this.headers = headers;
+    this.error = error;
+
+    const data = error as Record<string, any>;
+    this.code = data?.['code'];
+  }
+
+  private static makeMessage(status: number | undefined, error: any, message: string | undefined) {
+    const msg =
+      error?.message ?
+        typeof error.message === 'string' ?
+          error.message
+        : JSON.stringify(error.message)
+      : error ? JSON.stringify(error)
+      : message;
+
+    if (status && msg) {
+      return `${status} ${msg}`;
+    }
+    if (status) {
+      return `${status} status code (no body)`;
+    }
+    if (msg) {
+      return msg;
+    }
+    return '(no status code or body)';
+  }
+
+  static generate(
+    status: number | undefined,
+    errorResponse: Object | undefined,
+    message: string | undefined,
+    headers: Headers | undefined,
+  ): APIError {
+    if (!status || !headers) {
+      return new APIConnectionError({ message, cause: castToError(errorResponse) });
+    }
+
+    const error = errorResponse as Record<string, any>;
+
+    if (status === 400) {
+      return new BadRequestError(status, error, message, headers);
+    }
+
+    if (status === 401) {
+      return new AuthenticationError(status, error, message, headers);
+    }
+
+    if (status === 403) {
+      return new PermissionDeniedError(status, error, message, headers);
+    }
+
+    if (status === 404) {
+      return new NotFoundError(status, error, message, headers);
+    }
+
+    if (status === 409) {
+      return new ConflictError(status, error, message, headers);
+    }
+
+    if (status === 422) {
+      return new UnprocessableEntityError(status, error, message, headers);
+    }
+
+    if (status === 429) {
+      return new RateLimitError(status, error, message, headers);
+    }
+
+    if (status >= 500) {
+      return new InternalServerError(status, error, message, headers);
+    }
+
+    return new APIError(status, error, message, headers);
+  }
+}
+
+export class APIUserAbortError extends APIError<undefined, undefined, undefined> {
+  constructor({ message }: { message?: string } = {}) {
+    super(undefined, undefined, message || 'Request was aborted.', undefined);
+  }
+}
+
+export class APIConnectionError extends APIError<undefined, undefined, undefined> {
+  constructor({ message, cause }: { message?: string | undefined; cause?: Error | undefined }) {
+    super(undefined, undefined, message || 'Connection error.', undefined);
+    // in some environments the 'cause' property is already declared
+    // @ts-ignore
+    if (cause) this.cause = cause;
+  }
+}
+
+export class APIConnectionTimeoutError extends APIConnectionError {
+  constructor({ message }: { message?: string } = {}) {
+    super({ message: message ?? 'Request timed out.' });
+  }
+}
+
+export class BadRequestError extends APIError<400, Headers> {}
+
+export class AuthenticationError extends APIError<401, Headers> {}
+
+export class PermissionDeniedError extends APIError<403, Headers> {}
+
+export class NotFoundError extends APIError<404, Headers> {}
+
+export class ConflictError extends APIError<409, Headers> {}
+
+export class UnprocessableEntityError extends APIError<422, Headers> {}
+
+export class RateLimitError extends APIError<429, Headers> {}
+
+export class InternalServerError extends APIError<number, Headers> {}
diff --git a/src/core/pagination.ts b/src/core/pagination.ts
new file mode 100644
index 0000000..f72f6ff
--- /dev/null
+++ b/src/core/pagination.ts
@@ -0,0 +1,1220 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { GitpodError } from './error';
+import { FinalRequestOptions } from '../internal/request-options';
+import { defaultParseResponse } from '../internal/parse';
+import { type Gitpod } from '../client';
+import { APIPromise } from './api-promise';
+import { type APIResponseProps } from '../internal/parse';
+import { maybeObj } from '../internal/utils/values';
+
+export type PageRequestOptions = Pick<FinalRequestOptions, 'query' | 'headers' | 'body' | 'path' | 'method'>;
+
+export abstract class AbstractPage<Item> implements AsyncIterable<Item> {
+  #client: Gitpod;
+  protected options: FinalRequestOptions;
+
+  protected response: Response;
+  protected body: unknown;
+
+  constructor(client: Gitpod, response: Response, body: unknown, options: FinalRequestOptions) {
+    this.#client = client;
+    this.options = options;
+    this.response = response;
+    this.body = body;
+  }
+
+  abstract nextPageRequestOptions(): PageRequestOptions | null;
+
+  abstract getPaginatedItems(): Item[];
+
+  hasNextPage(): boolean {
+    const items = this.getPaginatedItems();
+    if (!items.length) return false;
+    return this.nextPageRequestOptions() != null;
+  }
+
+  async getNextPage(): Promise<this> {
+    const nextOptions = this.nextPageRequestOptions();
+    if (!nextOptions) {
+      throw new GitpodError(
+        'No next page expected; please check `.hasNextPage()` before calling `.getNextPage()`.',
+      );
+    }
+
+    return await this.#client.requestAPIList(this.constructor as any, nextOptions);
+  }
+
+  async *iterPages(): AsyncGenerator<this> {
+    let page: this = this;
+    yield page;
+    while (page.hasNextPage()) {
+      page = await page.getNextPage();
+      yield page;
+    }
+  }
+
+  async *[Symbol.asyncIterator](): AsyncGenerator<Item> {
+    for await (const page of this.iterPages()) {
+      for (const item of page.getPaginatedItems()) {
+        yield item;
+      }
+    }
+  }
+}
+
+/**
+ * This subclass of Promise will resolve to an instantiated Page once the request completes.
+ *
+ * It also implements AsyncIterable to allow auto-paginating iteration on an unawaited list call, eg:
+ *
+ *    for await (const item of client.items.list()) {
+ *      console.log(item)
+ *    }
+ */
+export class PagePromise<
+    PageClass extends AbstractPage<Item>,
+    Item = ReturnType<PageClass['getPaginatedItems']>[number],
+  >
+  extends APIPromise<PageClass>
+  implements AsyncIterable<Item>
+{
+  constructor(
+    client: Gitpod,
+    request: Promise<APIResponseProps>,
+    Page: new (...args: ConstructorParameters<typeof AbstractPage>) => PageClass,
+  ) {
+    super(
+      client,
+      request,
+      async (client, props) =>
+        new Page(client, props.response, await defaultParseResponse(client, props), props.options),
+    );
+  }
+
+  /**
+   * Allow auto-paginating iteration on an unawaited list call, eg:
+   *
+   *    for await (const item of client.items.list()) {
+   *      console.log(item)
+   *    }
+   */
+  async *[Symbol.asyncIterator]() {
+    const page = await this;
+    for await (const item of page) {
+      yield item;
+    }
+  }
+}
+
+export interface DomainVerificationsPageResponse<Item> {
+  domainVerifications: Array<Item>;
+
+  pagination: DomainVerificationsPageResponse.Pagination;
+}
+
+export namespace DomainVerificationsPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface DomainVerificationsPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class DomainVerificationsPage<Item>
+  extends AbstractPage<Item>
+  implements DomainVerificationsPageResponse<Item>
+{
+  domainVerifications: Array<Item>;
+
+  pagination: DomainVerificationsPageResponse.Pagination;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: DomainVerificationsPageResponse<Item>,
+    options: FinalRequestOptions,
+  ) {
+    super(client, response, body, options);
+
+    this.domainVerifications = body.domainVerifications || [];
+    this.pagination = body.pagination || {};
+  }
+
+  getPaginatedItems(): Item[] {
+    return this.domainVerifications ?? [];
+  }
+
+  nextPageRequestOptions(): PageRequestOptions | null {
+    const cursor = this.pagination?.nextToken;
+    if (!cursor) {
+      return null;
+    }
+
+    return {
+      ...this.options,
+      query: {
+        ...maybeObj(this.options.query),
+        token: cursor,
+      },
+    };
+  }
+}
+
+export interface EditorsPageResponse<Item> {
+  editors: Array<Item>;
+
+  pagination: EditorsPageResponse.Pagination;
+}
+
+export namespace EditorsPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface EditorsPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class EditorsPage<Item> extends AbstractPage<Item> implements EditorsPageResponse<Item> {
+  editors: Array<Item>;
+
+  pagination: EditorsPageResponse.Pagination;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: EditorsPageResponse<Item>,
+    options: FinalRequestOptions,
+  ) {
+    super(client, response, body, options);
+
+    this.editors = body.editors || [];
+    this.pagination = body.pagination || {};
+  }
+
+  getPaginatedItems(): Item[] {
+    return this.editors ?? [];
+  }
+
+  nextPageRequestOptions(): PageRequestOptions | null {
+    const cursor = this.pagination?.nextToken;
+    if (!cursor) {
+      return null;
+    }
+
+    return {
+      ...this.options,
+      query: {
+        ...maybeObj(this.options.query),
+        token: cursor,
+      },
+    };
+  }
+}
+
+export interface EntriesPageResponse<Item> {
+  entries: Array<Item>;
+
+  pagination: EntriesPageResponse.Pagination;
+}
+
+export namespace EntriesPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface EntriesPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class EntriesPage<Item> extends AbstractPage<Item> implements EntriesPageResponse<Item> {
+  entries: Array<Item>;
+
+  pagination: EntriesPageResponse.Pagination;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: EntriesPageResponse<Item>,
+    options: FinalRequestOptions,
+  ) {
+    super(client, response, body, options);
+
+    this.entries = body.entries || [];
+    this.pagination = body.pagination || {};
+  }
+
+  getPaginatedItems(): Item[] {
+    return this.entries ?? [];
+  }
+
+  nextPageRequestOptions(): PageRequestOptions | null {
+    const cursor = this.pagination?.nextToken;
+    if (!cursor) {
+      return null;
+    }
+
+    return {
+      ...this.options,
+      query: {
+        ...maybeObj(this.options.query),
+        token: cursor,
+      },
+    };
+  }
+}
+
+export interface EnvironmentClassesPageResponse<Item> {
+  environmentClasses: Array<Item>;
+
+  pagination: EnvironmentClassesPageResponse.Pagination;
+}
+
+export namespace EnvironmentClassesPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface EnvironmentClassesPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class EnvironmentClassesPage<Item>
+  extends AbstractPage<Item>
+  implements EnvironmentClassesPageResponse<Item>
+{
+  environmentClasses: Array<Item>;
+
+  pagination: EnvironmentClassesPageResponse.Pagination;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: EnvironmentClassesPageResponse<Item>,
+    options: FinalRequestOptions,
+  ) {
+    super(client, response, body, options);
+
+    this.environmentClasses = body.environmentClasses || [];
+    this.pagination = body.pagination || {};
+  }
+
+  getPaginatedItems(): Item[] {
+    return this.environmentClasses ?? [];
+  }
+
+  nextPageRequestOptions(): PageRequestOptions | null {
+    const cursor = this.pagination?.nextToken;
+    if (!cursor) {
+      return null;
+    }
+
+    return {
+      ...this.options,
+      query: {
+        ...maybeObj(this.options.query),
+        token: cursor,
+      },
+    };
+  }
+}
+
+export interface EnvironmentsPageResponse<Item> {
+  environments: Array<Item>;
+
+  pagination: EnvironmentsPageResponse.Pagination;
+}
+
+export namespace EnvironmentsPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface EnvironmentsPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class EnvironmentsPage<Item> extends AbstractPage<Item> implements EnvironmentsPageResponse<Item> {
+  environments: Array<Item>;
+
+  pagination: EnvironmentsPageResponse.Pagination;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: EnvironmentsPageResponse<Item>,
+    options: FinalRequestOptions,
+  ) {
+    super(client, response, body, options);
+
+    this.environments = body.environments || [];
+    this.pagination = body.pagination || {};
+  }
+
+  getPaginatedItems(): Item[] {
+    return this.environments ?? [];
+  }
+
+  nextPageRequestOptions(): PageRequestOptions | null {
+    const cursor = this.pagination?.nextToken;
+    if (!cursor) {
+      return null;
+    }
+
+    return {
+      ...this.options,
+      query: {
+        ...maybeObj(this.options.query),
+        token: cursor,
+      },
+    };
+  }
+}
+
+export interface GroupsPageResponse<Item> {
+  groups: Array<Item>;
+
+  pagination: GroupsPageResponse.Pagination;
+}
+
+export namespace GroupsPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface GroupsPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class GroupsPage<Item> extends AbstractPage<Item> implements GroupsPageResponse<Item> {
+  groups: Array<Item>;
+
+  pagination: GroupsPageResponse.Pagination;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: GroupsPageResponse<Item>,
+    options: FinalRequestOptions,
+  ) {
+    super(client, response, body, options);
+
+    this.groups = body.groups || [];
+    this.pagination = body.pagination || {};
+  }
+
+  getPaginatedItems(): Item[] {
+    return this.groups ?? [];
+  }
+
+  nextPageRequestOptions(): PageRequestOptions | null {
+    const cursor = this.pagination?.nextToken;
+    if (!cursor) {
+      return null;
+    }
+
+    return {
+      ...this.options,
+      query: {
+        ...maybeObj(this.options.query),
+        token: cursor,
+      },
+    };
+  }
+}
+
+export interface IntegrationsPageResponse<Item> {
+  integrations: Array<Item>;
+
+  pagination: IntegrationsPageResponse.Pagination;
+}
+
+export namespace IntegrationsPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface IntegrationsPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class IntegrationsPage<Item> extends AbstractPage<Item> implements IntegrationsPageResponse<Item> {
+  integrations: Array<Item>;
+
+  pagination: IntegrationsPageResponse.Pagination;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: IntegrationsPageResponse<Item>,
+    options: FinalRequestOptions,
+  ) {
+    super(client, response, body, options);
+
+    this.integrations = body.integrations || [];
+    this.pagination = body.pagination || {};
+  }
+
+  getPaginatedItems(): Item[] {
+    return this.integrations ?? [];
+  }
+
+  nextPageRequestOptions(): PageRequestOptions | null {
+    const cursor = this.pagination?.nextToken;
+    if (!cursor) {
+      return null;
+    }
+
+    return {
+      ...this.options,
+      query: {
+        ...maybeObj(this.options.query),
+        token: cursor,
+      },
+    };
+  }
+}
+
+export interface LoginProvidersPageResponse<Item> {
+  loginProviders: Array<Item>;
+
+  pagination: LoginProvidersPageResponse.Pagination;
+}
+
+export namespace LoginProvidersPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface LoginProvidersPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class LoginProvidersPage<Item> extends AbstractPage<Item> implements LoginProvidersPageResponse<Item> {
+  loginProviders: Array<Item>;
+
+  pagination: LoginProvidersPageResponse.Pagination;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: LoginProvidersPageResponse<Item>,
+    options: FinalRequestOptions,
+  ) {
+    super(client, response, body, options);
+
+    this.loginProviders = body.loginProviders || [];
+    this.pagination = body.pagination || {};
+  }
+
+  getPaginatedItems(): Item[] {
+    return this.loginProviders ?? [];
+  }
+
+  nextPageRequestOptions(): PageRequestOptions | null {
+    const cursor = this.pagination?.nextToken;
+    if (!cursor) {
+      return null;
+    }
+
+    return {
+      ...this.options,
+      query: {
+        ...maybeObj(this.options.query),
+        token: cursor,
+      },
+    };
+  }
+}
+
+export interface MembersPageResponse<Item> {
+  members: Array<Item>;
+
+  pagination: MembersPageResponse.Pagination;
+}
+
+export namespace MembersPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface MembersPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class MembersPage<Item> extends AbstractPage<Item> implements MembersPageResponse<Item> {
+  members: Array<Item>;
+
+  pagination: MembersPageResponse.Pagination;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: MembersPageResponse<Item>,
+    options: FinalRequestOptions,
+  ) {
+    super(client, response, body, options);
+
+    this.members = body.members || [];
+    this.pagination = body.pagination || {};
+  }
+
+  getPaginatedItems(): Item[] {
+    return this.members ?? [];
+  }
+
+  nextPageRequestOptions(): PageRequestOptions | null {
+    const cursor = this.pagination?.nextToken;
+    if (!cursor) {
+      return null;
+    }
+
+    return {
+      ...this.options,
+      query: {
+        ...maybeObj(this.options.query),
+        token: cursor,
+      },
+    };
+  }
+}
+
+export interface PersonalAccessTokensPageResponse<Item> {
+  pagination: PersonalAccessTokensPageResponse.Pagination;
+
+  personalAccessTokens: Array<Item>;
+}
+
+export namespace PersonalAccessTokensPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface PersonalAccessTokensPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class PersonalAccessTokensPage<Item>
+  extends AbstractPage<Item>
+  implements PersonalAccessTokensPageResponse<Item>
+{
+  pagination: PersonalAccessTokensPageResponse.Pagination;
+
+  personalAccessTokens: Array<Item>;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: PersonalAccessTokensPageResponse<Item>,
+    options: FinalRequestOptions,
+  ) {
+    super(client, response, body, options);
+
+    this.pagination = body.pagination || {};
+    this.personalAccessTokens = body.personalAccessTokens || [];
+  }
+
+  getPaginatedItems(): Item[] {
+    return this.personalAccessTokens ?? [];
+  }
+
+  nextPageRequestOptions(): PageRequestOptions | null {
+    const cursor = this.pagination?.nextToken;
+    if (!cursor) {
+      return null;
+    }
+
+    return {
+      ...this.options,
+      query: {
+        ...maybeObj(this.options.query),
+        token: cursor,
+      },
+    };
+  }
+}
+
+export interface PoliciesPageResponse<Item> {
+  pagination: PoliciesPageResponse.Pagination;
+
+  policies: Array<Item>;
+}
+
+export namespace PoliciesPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface PoliciesPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class PoliciesPage<Item> extends AbstractPage<Item> implements PoliciesPageResponse<Item> {
+  pagination: PoliciesPageResponse.Pagination;
+
+  policies: Array<Item>;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: PoliciesPageResponse<Item>,
+    options: FinalRequestOptions,
+  ) {
+    super(client, response, body, options);
+
+    this.pagination = body.pagination || {};
+    this.policies = body.policies || [];
+  }
+
+  getPaginatedItems(): Item[] {
+    return this.policies ?? [];
+  }
+
+  nextPageRequestOptions(): PageRequestOptions | null {
+    const cursor = this.pagination?.nextToken;
+    if (!cursor) {
+      return null;
+    }
+
+    return {
+      ...this.options,
+      query: {
+        ...maybeObj(this.options.query),
+        token: cursor,
+      },
+    };
+  }
+}
+
+export interface ProjectsPageResponse<Item> {
+  pagination: ProjectsPageResponse.Pagination;
+
+  projects: Array<Item>;
+}
+
+export namespace ProjectsPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface ProjectsPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class ProjectsPage<Item> extends AbstractPage<Item> implements ProjectsPageResponse<Item> {
+  pagination: ProjectsPageResponse.Pagination;
+
+  projects: Array<Item>;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: ProjectsPageResponse<Item>,
+    options: FinalRequestOptions,
+  ) {
+    super(client, response, body, options);
+
+    this.pagination = body.pagination || {};
+    this.projects = body.projects || [];
+  }
+
+  getPaginatedItems(): Item[] {
+    return this.projects ?? [];
+  }
+
+  nextPageRequestOptions(): PageRequestOptions | null {
+    const cursor = this.pagination?.nextToken;
+    if (!cursor) {
+      return null;
+    }
+
+    return {
+      ...this.options,
+      query: {
+        ...maybeObj(this.options.query),
+        token: cursor,
+      },
+    };
+  }
+}
+
+export interface RunnersPageResponse<Item> {
+  pagination: RunnersPageResponse.Pagination;
+
+  runners: Array<Item>;
+}
+
+export namespace RunnersPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface RunnersPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class RunnersPage<Item> extends AbstractPage<Item> implements RunnersPageResponse<Item> {
+  pagination: RunnersPageResponse.Pagination;
+
+  runners: Array<Item>;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: RunnersPageResponse<Item>,
+    options: FinalRequestOptions,
+  ) {
+    super(client, response, body, options);
+
+    this.pagination = body.pagination || {};
+    this.runners = body.runners || [];
+  }
+
+  getPaginatedItems(): Item[] {
+    return this.runners ?? [];
+  }
+
+  nextPageRequestOptions(): PageRequestOptions | null {
+    const cursor = this.pagination?.nextToken;
+    if (!cursor) {
+      return null;
+    }
+
+    return {
+      ...this.options,
+      query: {
+        ...maybeObj(this.options.query),
+        token: cursor,
+      },
+    };
+  }
+}
+
+export interface SecretsPageResponse<Item> {
+  pagination: SecretsPageResponse.Pagination;
+
+  secrets: Array<Item>;
+}
+
+export namespace SecretsPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface SecretsPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class SecretsPage<Item> extends AbstractPage<Item> implements SecretsPageResponse<Item> {
+  pagination: SecretsPageResponse.Pagination;
+
+  secrets: Array<Item>;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: SecretsPageResponse<Item>,
+    options: FinalRequestOptions,
+  ) {
+    super(client, response, body, options);
+
+    this.pagination = body.pagination || {};
+    this.secrets = body.secrets || [];
+  }
+
+  getPaginatedItems(): Item[] {
+    return this.secrets ?? [];
+  }
+
+  nextPageRequestOptions(): PageRequestOptions | null {
+    const cursor = this.pagination?.nextToken;
+    if (!cursor) {
+      return null;
+    }
+
+    return {
+      ...this.options,
+      query: {
+        ...maybeObj(this.options.query),
+        token: cursor,
+      },
+    };
+  }
+}
+
+export interface ServicesPageResponse<Item> {
+  pagination: ServicesPageResponse.Pagination;
+
+  services: Array<Item>;
+}
+
+export namespace ServicesPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface ServicesPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class ServicesPage<Item> extends AbstractPage<Item> implements ServicesPageResponse<Item> {
+  pagination: ServicesPageResponse.Pagination;
+
+  services: Array<Item>;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: ServicesPageResponse<Item>,
+    options: FinalRequestOptions,
+  ) {
+    super(client, response, body, options);
+
+    this.pagination = body.pagination || {};
+    this.services = body.services || [];
+  }
+
+  getPaginatedItems(): Item[] {
+    return this.services ?? [];
+  }
+
+  nextPageRequestOptions(): PageRequestOptions | null {
+    const cursor = this.pagination?.nextToken;
+    if (!cursor) {
+      return null;
+    }
+
+    return {
+      ...this.options,
+      query: {
+        ...maybeObj(this.options.query),
+        token: cursor,
+      },
+    };
+  }
+}
+
+export interface SessionsPageResponse<Item> {
+  pagination: SessionsPageResponse.Pagination;
+
+  sessions: Array<Item>;
+}
+
+export namespace SessionsPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface SessionsPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class SessionsPage<Item> extends AbstractPage<Item> implements SessionsPageResponse<Item> {
+  pagination: SessionsPageResponse.Pagination;
+
+  sessions: Array<Item>;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: SessionsPageResponse<Item>,
+    options: FinalRequestOptions,
+  ) {
+    super(client, response, body, options);
+
+    this.pagination = body.pagination || {};
+    this.sessions = body.sessions || [];
+  }
+
+  getPaginatedItems(): Item[] {
+    return this.sessions ?? [];
+  }
+
+  nextPageRequestOptions(): PageRequestOptions | null {
+    const cursor = this.pagination?.nextToken;
+    if (!cursor) {
+      return null;
+    }
+
+    return {
+      ...this.options,
+      query: {
+        ...maybeObj(this.options.query),
+        token: cursor,
+      },
+    };
+  }
+}
+
+export interface SSOConfigurationsPageResponse<Item> {
+  pagination: SSOConfigurationsPageResponse.Pagination;
+
+  ssoConfigurations: Array<Item>;
+}
+
+export namespace SSOConfigurationsPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface SSOConfigurationsPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class SSOConfigurationsPage<Item>
+  extends AbstractPage<Item>
+  implements SSOConfigurationsPageResponse<Item>
+{
+  pagination: SSOConfigurationsPageResponse.Pagination;
+
+  ssoConfigurations: Array<Item>;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: SSOConfigurationsPageResponse<Item>,
+    options: FinalRequestOptions,
+  ) {
+    super(client, response, body, options);
+
+    this.pagination = body.pagination || {};
+    this.ssoConfigurations = body.ssoConfigurations || [];
+  }
+
+  getPaginatedItems(): Item[] {
+    return this.ssoConfigurations ?? [];
+  }
+
+  nextPageRequestOptions(): PageRequestOptions | null {
+    const cursor = this.pagination?.nextToken;
+    if (!cursor) {
+      return null;
+    }
+
+    return {
+      ...this.options,
+      query: {
+        ...maybeObj(this.options.query),
+        token: cursor,
+      },
+    };
+  }
+}
+
+export interface TaskExecutionsPageResponse<Item> {
+  pagination: TaskExecutionsPageResponse.Pagination;
+
+  taskExecutions: Array<Item>;
+}
+
+export namespace TaskExecutionsPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface TaskExecutionsPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class TaskExecutionsPage<Item> extends AbstractPage<Item> implements TaskExecutionsPageResponse<Item> {
+  pagination: TaskExecutionsPageResponse.Pagination;
+
+  taskExecutions: Array<Item>;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: TaskExecutionsPageResponse<Item>,
+    options: FinalRequestOptions,
+  ) {
+    super(client, response, body, options);
+
+    this.pagination = body.pagination || {};
+    this.taskExecutions = body.taskExecutions || [];
+  }
+
+  getPaginatedItems(): Item[] {
+    return this.taskExecutions ?? [];
+  }
+
+  nextPageRequestOptions(): PageRequestOptions | null {
+    const cursor = this.pagination?.nextToken;
+    if (!cursor) {
+      return null;
+    }
+
+    return {
+      ...this.options,
+      query: {
+        ...maybeObj(this.options.query),
+        token: cursor,
+      },
+    };
+  }
+}
+
+export interface TasksPageResponse<Item> {
+  pagination: TasksPageResponse.Pagination;
+
+  tasks: Array<Item>;
+}
+
+export namespace TasksPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface TasksPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class TasksPage<Item> extends AbstractPage<Item> implements TasksPageResponse<Item> {
+  pagination: TasksPageResponse.Pagination;
+
+  tasks: Array<Item>;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: TasksPageResponse<Item>,
+    options: FinalRequestOptions,
+  ) {
+    super(client, response, body, options);
+
+    this.pagination = body.pagination || {};
+    this.tasks = body.tasks || [];
+  }
+
+  getPaginatedItems(): Item[] {
+    return this.tasks ?? [];
+  }
+
+  nextPageRequestOptions(): PageRequestOptions | null {
+    const cursor = this.pagination?.nextToken;
+    if (!cursor) {
+      return null;
+    }
+
+    return {
+      ...this.options,
+      query: {
+        ...maybeObj(this.options.query),
+        token: cursor,
+      },
+    };
+  }
+}
+
+export interface TokensPageResponse<Item> {
+  pagination: TokensPageResponse.Pagination;
+
+  tokens: Array<Item>;
+}
+
+export namespace TokensPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface TokensPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class TokensPage<Item> extends AbstractPage<Item> implements TokensPageResponse<Item> {
+  pagination: TokensPageResponse.Pagination;
+
+  tokens: Array<Item>;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: TokensPageResponse<Item>,
+    options: FinalRequestOptions,
+  ) {
+    super(client, response, body, options);
+
+    this.pagination = body.pagination || {};
+    this.tokens = body.tokens || [];
+  }
+
+  getPaginatedItems(): Item[] {
+    return this.tokens ?? [];
+  }
+
+  nextPageRequestOptions(): PageRequestOptions | null {
+    const cursor = this.pagination?.nextToken;
+    if (!cursor) {
+      return null;
+    }
+
+    return {
+      ...this.options,
+      query: {
+        ...maybeObj(this.options.query),
+        token: cursor,
+      },
+    };
+  }
+}
diff --git a/src/core/resource.ts b/src/core/resource.ts
new file mode 100644
index 0000000..c73b82d
--- /dev/null
+++ b/src/core/resource.ts
@@ -0,0 +1,11 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import type { Gitpod } from '../client';
+
+export class APIResource {
+  protected _client: Gitpod;
+
+  constructor(client: Gitpod) {
+    this._client = client;
+  }
+}
diff --git a/src/core/uploads.ts b/src/core/uploads.ts
new file mode 100644
index 0000000..2882ca6
--- /dev/null
+++ b/src/core/uploads.ts
@@ -0,0 +1,2 @@
+export { type Uploadable } from '../internal/uploads';
+export { toFile, type ToFileInput } from '../internal/to-file';
diff --git a/src/error.ts b/src/error.ts
new file mode 100644
index 0000000..fc55f46
--- /dev/null
+++ b/src/error.ts
@@ -0,0 +1,2 @@
+/** @deprecated Import from ./core/error instead */
+export * from './core/error';
diff --git a/src/index.ts b/src/index.ts
new file mode 100644
index 0000000..0eaf4c8
--- /dev/null
+++ b/src/index.ts
@@ -0,0 +1,23 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+export { Gitpod as default } from './client';
+
+export { type Uploadable, toFile } from './core/uploads';
+export { APIPromise } from './core/api-promise';
+export { Gitpod, type ClientOptions } from './client';
+export { PagePromise } from './core/pagination';
+export {
+  GitpodError,
+  APIError,
+  APIConnectionError,
+  APIConnectionTimeoutError,
+  APIUserAbortError,
+  NotFoundError,
+  ConflictError,
+  RateLimitError,
+  BadRequestError,
+  AuthenticationError,
+  InternalServerError,
+  PermissionDeniedError,
+  UnprocessableEntityError,
+} from './core/error';
diff --git a/src/internal/README.md b/src/internal/README.md
new file mode 100644
index 0000000..3ef5a25
--- /dev/null
+++ b/src/internal/README.md
@@ -0,0 +1,3 @@
+# `internal`
+
+The modules in this directory are not importable outside this package and will change between releases.
diff --git a/src/internal/builtin-types.ts b/src/internal/builtin-types.ts
new file mode 100644
index 0000000..c23d3bd
--- /dev/null
+++ b/src/internal/builtin-types.ts
@@ -0,0 +1,93 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+export type Fetch = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
+
+/**
+ * An alias to the builtin `RequestInit` type so we can
+ * easily alias it in import statements if there are name clashes.
+ *
+ * https://developer.mozilla.org/docs/Web/API/RequestInit
+ */
+type _RequestInit = RequestInit;
+
+/**
+ * An alias to the builtin `Response` type so we can
+ * easily alias it in import statements if there are name clashes.
+ *
+ * https://developer.mozilla.org/docs/Web/API/Response
+ */
+type _Response = Response;
+
+/**
+ * The type for the first argument to `fetch`.
+ *
+ * https://developer.mozilla.org/docs/Web/API/Window/fetch#resource
+ */
+type _RequestInfo = Request | URL | string;
+
+/**
+ * The type for constructing `RequestInit` Headers.
+ *
+ * https://developer.mozilla.org/docs/Web/API/RequestInit#setting_headers
+ */
+type _HeadersInit = RequestInit['headers'];
+
+/**
+ * The type for constructing `RequestInit` body.
+ *
+ * https://developer.mozilla.org/docs/Web/API/RequestInit#body
+ */
+type _BodyInit = RequestInit['body'];
+
+/**
+ * An alias to the builtin `Array<T>` type so we can
+ * easily alias it in import statements if there are name clashes.
+ */
+type _Array<T> = Array<T>;
+
+/**
+ * An alias to the builtin `Record<K, T>` type so we can
+ * easily alias it in import statements if there are name clashes.
+ */
+type _Record<K extends keyof any, T> = Record<K, T>;
+
+export type {
+  _Array as Array,
+  _BodyInit as BodyInit,
+  _HeadersInit as HeadersInit,
+  _Record as Record,
+  _RequestInfo as RequestInfo,
+  _RequestInit as RequestInit,
+  _Response as Response,
+};
+
+/**
+ * A copy of the builtin `EndingType` type as it isn't fully supported in certain
+ * environments and attempting to reference the global version will error.
+ *
+ * https://github.com/microsoft/TypeScript/blob/49ad1a3917a0ea57f5ff248159256e12bb1cb705/src/lib/dom.generated.d.ts#L27941
+ */
+type EndingType = 'native' | 'transparent';
+
+/**
+ * A copy of the builtin `BlobPropertyBag` type as it isn't fully supported in certain
+ * environments and attempting to reference the global version will error.
+ *
+ * https://github.com/microsoft/TypeScript/blob/49ad1a3917a0ea57f5ff248159256e12bb1cb705/src/lib/dom.generated.d.ts#L154
+ * https://developer.mozilla.org/en-US/docs/Web/API/Blob/Blob#options
+ */
+export interface BlobPropertyBag {
+  endings?: EndingType;
+  type?: string;
+}
+
+/**
+ * A copy of the builtin `FilePropertyBag` type as it isn't fully supported in certain
+ * environments and attempting to reference the global version will error.
+ *
+ * https://github.com/microsoft/TypeScript/blob/49ad1a3917a0ea57f5ff248159256e12bb1cb705/src/lib/dom.generated.d.ts#L503
+ * https://developer.mozilla.org/en-US/docs/Web/API/File/File#options
+ */
+export interface FilePropertyBag extends BlobPropertyBag {
+  lastModified?: number;
+}
diff --git a/src/internal/decoders/jsonl.ts b/src/internal/decoders/jsonl.ts
new file mode 100644
index 0000000..79ec5f8
--- /dev/null
+++ b/src/internal/decoders/jsonl.ts
@@ -0,0 +1,48 @@
+import { GitpodError } from '../../core/error';
+import { ReadableStreamToAsyncIterable } from '../shims';
+import { LineDecoder, type Bytes } from './line';
+
+export class JSONLDecoder<T> {
+  controller: AbortController;
+
+  constructor(
+    private iterator: AsyncIterableIterator<Bytes>,
+    controller: AbortController,
+  ) {
+    this.controller = controller;
+  }
+
+  private async *decoder(): AsyncIterator<T, any, undefined> {
+    const lineDecoder = new LineDecoder();
+    for await (const chunk of this.iterator) {
+      for (const line of lineDecoder.decode(chunk)) {
+        yield JSON.parse(line);
+      }
+    }
+
+    for (const line of lineDecoder.flush()) {
+      yield JSON.parse(line);
+    }
+  }
+
+  [Symbol.asyncIterator](): AsyncIterator<T> {
+    return this.decoder();
+  }
+
+  static fromResponse<T>(response: Response, controller: AbortController): JSONLDecoder<T> {
+    if (!response.body) {
+      controller.abort();
+      if (
+        typeof (globalThis as any).navigator !== 'undefined' &&
+        (globalThis as any).navigator.product === 'ReactNative'
+      ) {
+        throw new GitpodError(
+          `The default react-native fetch implementation does not support streaming. Please use expo/fetch: https://docs.expo.dev/versions/latest/sdk/expo/#expofetch-api`,
+        );
+      }
+      throw new GitpodError(`Attempted to iterate over a response with no body`);
+    }
+
+    return new JSONLDecoder(ReadableStreamToAsyncIterable<Bytes>(response.body), controller);
+  }
+}
diff --git a/src/internal/decoders/line.ts b/src/internal/decoders/line.ts
new file mode 100644
index 0000000..b3bfa97
--- /dev/null
+++ b/src/internal/decoders/line.ts
@@ -0,0 +1,135 @@
+import { concatBytes, decodeUTF8, encodeUTF8 } from '../utils/bytes';
+
+export type Bytes = string | ArrayBuffer | Uint8Array | null | undefined;
+
+/**
+ * A re-implementation of httpx's `LineDecoder` in Python that handles incrementally
+ * reading lines from text.
+ *
+ * https://github.com/encode/httpx/blob/920333ea98118e9cf617f246905d7b202510941c/httpx/_decoders.py#L258
+ */
+export class LineDecoder {
+  // prettier-ignore
+  static NEWLINE_CHARS = new Set(['\n', '\r']);
+  static NEWLINE_REGEXP = /\r\n|[\n\r]/g;
+
+  #buffer: Uint8Array;
+  #carriageReturnIndex: number | null;
+
+  constructor() {
+    this.#buffer = new Uint8Array();
+    this.#carriageReturnIndex = null;
+  }
+
+  decode(chunk: Bytes): string[] {
+    if (chunk == null) {
+      return [];
+    }
+
+    const binaryChunk =
+      chunk instanceof ArrayBuffer ? new Uint8Array(chunk)
+      : typeof chunk === 'string' ? encodeUTF8(chunk)
+      : chunk;
+
+    this.#buffer = concatBytes([this.#buffer, binaryChunk]);
+
+    const lines: string[] = [];
+    let patternIndex;
+    while ((patternIndex = findNewlineIndex(this.#buffer, this.#carriageReturnIndex)) != null) {
+      if (patternIndex.carriage && this.#carriageReturnIndex == null) {
+        // skip until we either get a corresponding `\n`, a new `\r` or nothing
+        this.#carriageReturnIndex = patternIndex.index;
+        continue;
+      }
+
+      // we got double \r or \rtext\n
+      if (
+        this.#carriageReturnIndex != null &&
+        (patternIndex.index !== this.#carriageReturnIndex + 1 || patternIndex.carriage)
+      ) {
+        lines.push(decodeUTF8(this.#buffer.subarray(0, this.#carriageReturnIndex - 1)));
+        this.#buffer = this.#buffer.subarray(this.#carriageReturnIndex);
+        this.#carriageReturnIndex = null;
+        continue;
+      }
+
+      const endIndex =
+        this.#carriageReturnIndex !== null ? patternIndex.preceding - 1 : patternIndex.preceding;
+
+      const line = decodeUTF8(this.#buffer.subarray(0, endIndex));
+      lines.push(line);
+
+      this.#buffer = this.#buffer.subarray(patternIndex.index);
+      this.#carriageReturnIndex = null;
+    }
+
+    return lines;
+  }
+
+  flush(): string[] {
+    if (!this.#buffer.length) {
+      return [];
+    }
+    return this.decode('\n');
+  }
+}
+
+/**
+ * This function searches the buffer for the end patterns, (\r or \n)
+ * and returns an object with the index preceding the matched newline and the
+ * index after the newline char. `null` is returned if no new line is found.
+ *
+ * ```ts
+ * findNewLineIndex('abc\ndef') -> { preceding: 2, index: 3 }
+ * ```
+ */
+function findNewlineIndex(
+  buffer: Uint8Array,
+  startIndex: number | null,
+): { preceding: number; index: number; carriage: boolean } | null {
+  const newline = 0x0a; // \n
+  const carriage = 0x0d; // \r
+
+  for (let i = startIndex ?? 0; i < buffer.length; i++) {
+    if (buffer[i] === newline) {
+      return { preceding: i, index: i + 1, carriage: false };
+    }
+
+    if (buffer[i] === carriage) {
+      return { preceding: i, index: i + 1, carriage: true };
+    }
+  }
+
+  return null;
+}
+
+export function findDoubleNewlineIndex(buffer: Uint8Array): number {
+  // This function searches the buffer for the end patterns (\r\r, \n\n, \r\n\r\n)
+  // and returns the index right after the first occurrence of any pattern,
+  // or -1 if none of the patterns are found.
+  const newline = 0x0a; // \n
+  const carriage = 0x0d; // \r
+
+  for (let i = 0; i < buffer.length - 1; i++) {
+    if (buffer[i] === newline && buffer[i + 1] === newline) {
+      // \n\n
+      return i + 2;
+    }
+    if (buffer[i] === carriage && buffer[i + 1] === carriage) {
+      // \r\r
+      return i + 2;
+    }
+    if (
+      buffer[i] === carriage &&
+      buffer[i + 1] === newline &&
+      i + 3 < buffer.length &&
+      buffer[i + 2] === carriage &&
+      buffer[i + 3] === newline
+    ) {
+      // \r\n\r\n
+      return i + 4;
+    }
+  }
+
+  return -1;
+}
diff --git a/src/internal/detect-platform.ts b/src/internal/detect-platform.ts
new file mode 100644
index 0000000..c5e273b
--- /dev/null
+++ b/src/internal/detect-platform.ts
@@ -0,0 +1,196 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { VERSION } from '../version';
+
+export const isRunningInBrowser = () => {
+  return (
+    // @ts-ignore
+    typeof window !== 'undefined' &&
+    // @ts-ignore
+    typeof window.document !== 'undefined' &&
+    // @ts-ignore
+    typeof navigator !== 'undefined'
+  );
+};
+
+type DetectedPlatform = 'deno' | 'node' | 'edge' | 'unknown';
+
+/**
+ * Note this does not detect 'browser'; for that, use getBrowserInfo().
+ */
+function getDetectedPlatform(): DetectedPlatform {
+  if (typeof Deno !== 'undefined' && Deno.build != null) {
+    return 'deno';
+  }
+  if (typeof EdgeRuntime !== 'undefined') {
+    return 'edge';
+  }
+  if (
+    Object.prototype.toString.call(
+      typeof (globalThis as any).process !== 'undefined' ? (globalThis as any).process : 0,
+    ) === '[object process]'
+  ) {
+    return 'node';
+  }
+  return 'unknown';
+}
+
+declare const Deno: any;
+declare const EdgeRuntime: any;
+type Arch = 'x32' | 'x64' | 'arm' | 'arm64' | `other:${string}` | 'unknown';
+type PlatformName =
+  | 'MacOS'
+  | 'Linux'
+  | 'Windows'
+  | 'FreeBSD'
+  | 'OpenBSD'
+  | 'iOS'
+  | 'Android'
+  | `Other:${string}`
+  | 'Unknown';
+type Browser = 'ie' | 'edge' | 'chrome' | 'firefox' | 'safari';
+type PlatformProperties = {
+  'X-Stainless-Lang': 'js';
+  'X-Stainless-Package-Version': string;
+  'X-Stainless-OS': PlatformName;
+  'X-Stainless-Arch': Arch;
+  'X-Stainless-Runtime': 'node' | 'deno' | 'edge' | `browser:${Browser}` | 'unknown';
+  'X-Stainless-Runtime-Version': string;
+};
+const getPlatformProperties = (): PlatformProperties => {
+  const detectedPlatform = getDetectedPlatform();
+  if (detectedPlatform === 'deno') {
+    return {
+      'X-Stainless-Lang': 'js',
+      'X-Stainless-Package-Version': VERSION,
+      'X-Stainless-OS': normalizePlatform(Deno.build.os),
+      'X-Stainless-Arch': normalizeArch(Deno.build.arch),
+      'X-Stainless-Runtime': 'deno',
+      'X-Stainless-Runtime-Version':
+        typeof Deno.version === 'string' ? Deno.version : Deno.version?.deno ?? 'unknown',
+    };
+  }
+  if (typeof EdgeRuntime !== 'undefined') {
+    return {
+      'X-Stainless-Lang': 'js',
+      'X-Stainless-Package-Version': VERSION,
+      'X-Stainless-OS': 'Unknown',
+      'X-Stainless-Arch': `other:${EdgeRuntime}`,
+      'X-Stainless-Runtime': 'edge',
+      'X-Stainless-Runtime-Version': (globalThis as any).process.version,
+    };
+  }
+  // Check if Node.js
+  if (detectedPlatform === 'node') {
+    return {
+      'X-Stainless-Lang': 'js',
+      'X-Stainless-Package-Version': VERSION,
+      'X-Stainless-OS': normalizePlatform((globalThis as any).process.platform),
+      'X-Stainless-Arch': normalizeArch((globalThis as any).process.arch),
+      'X-Stainless-Runtime': 'node',
+      'X-Stainless-Runtime-Version': (globalThis as any).process.version,
+    };
+  }
+
+  const browserInfo = getBrowserInfo();
+  if (browserInfo) {
+    return {
+      'X-Stainless-Lang': 'js',
+      'X-Stainless-Package-Version': VERSION,
+      'X-Stainless-OS': 'Unknown',
+      'X-Stainless-Arch': 'unknown',
+      'X-Stainless-Runtime': `browser:${browserInfo.browser}`,
+      'X-Stainless-Runtime-Version': browserInfo.version,
+    };
+  }
+
+  // TODO add support for Cloudflare workers, etc.
+  return {
+    'X-Stainless-Lang': 'js',
+    'X-Stainless-Package-Version': VERSION,
+    'X-Stainless-OS': 'Unknown',
+    'X-Stainless-Arch': 'unknown',
+    'X-Stainless-Runtime': 'unknown',
+    'X-Stainless-Runtime-Version': 'unknown',
+  };
+};
+
+type BrowserInfo = {
+  browser: Browser;
+  version: string;
+};
+
+declare const navigator: { userAgent: string } | undefined;
+
+// Note: modified from https://github.com/JS-DevTools/host-environment/blob/b1ab79ecde37db5d6e163c050e54fe7d287d7c92/src/isomorphic.browser.ts
+function getBrowserInfo(): BrowserInfo | null {
+  if (typeof navigator === 'undefined' || !navigator) {
+    return null;
+  }
+
+  // NOTE: The order matters here!
+  const browserPatterns = [
+    { key: 'edge' as const, pattern: /Edge(?:\W+(\d+)\.(\d+)(?:\.(\d+))?)?/ },
+    { key: 'ie' as const, pattern: /MSIE(?:\W+(\d+)\.(\d+)(?:\.(\d+))?)?/ },
+    { key: 'ie' as const, pattern: /Trident(?:.*rv\:(\d+)\.(\d+)(?:\.(\d+))?)?/ },
+    { key: 'chrome' as const, pattern: /Chrome(?:\W+(\d+)\.(\d+)(?:\.(\d+))?)?/ },
+    { key: 'firefox' as const, pattern: /Firefox(?:\W+(\d+)\.(\d+)(?:\.(\d+))?)?/ },
+    { key: 'safari' as const, pattern: /(?:Version\W+(\d+)\.(\d+)(?:\.(\d+))?)?(?:\W+Mobile\S*)?\W+Safari/ },
+  ];
+
+  // Find the FIRST matching browser
+  for (const { key, pattern } of browserPatterns) {
+    const match = pattern.exec(navigator.userAgent);
+    if (match) {
+      const major = match[1] || 0;
+      const minor = match[2] || 0;
+      const patch = match[3] || 0;
+
+      return { browser: key, version: `${major}.${minor}.${patch}` };
+    }
+  }
+
+  return null;
+}
+
+const normalizeArch = (arch: string): Arch => {
+  // Node docs:
+  // - https://nodejs.org/api/process.html#processarch
+  // Deno docs:
+  // - https://doc.deno.land/deno/stable/~/Deno.build
+  if (arch === 'x32') return 'x32';
+  if (arch === 'x86_64' || arch === 'x64') return 'x64';
+  if (arch === 'arm') return 'arm';
+  if (arch === 'aarch64' || arch === 'arm64') return 'arm64';
+  if (arch) return `other:${arch}`;
+  return 'unknown';
+};
+
+const normalizePlatform = (platform: string): PlatformName => {
+  // Node platforms:
+  // - https://nodejs.org/api/process.html#processplatform
+  // Deno platforms:
+  // - https://doc.deno.land/deno/stable/~/Deno.build
+  // - https://github.com/denoland/deno/issues/14799
+
+  platform = platform.toLowerCase();
+
+  // NOTE: this iOS check is untested and may not work
+  // Node does not work natively on IOS, there is a fork at
+  // https://github.com/nodejs-mobile/nodejs-mobile
+  // however it is unknown at the time of writing how to detect if it is running
+  if (platform.includes('ios')) return 'iOS';
+  if (platform === 'android') return 'Android';
+  if (platform === 'darwin') return 'MacOS';
+  if (platform === 'win32') return 'Windows';
+  if (platform === 'freebsd') return 'FreeBSD';
+  if (platform === 'openbsd') return 'OpenBSD';
+  if (platform === 'linux') return 'Linux';
+  if (platform) return `Other:${platform}`;
+  return 'Unknown';
+};
+
+let _platformHeaders: PlatformProperties;
+export const getPlatformHeaders = () => {
+  return (_platformHeaders ??= getPlatformProperties());
+};
diff --git a/src/internal/errors.ts b/src/internal/errors.ts
new file mode 100644
index 0000000..82c7b14
--- /dev/null
+++ b/src/internal/errors.ts
@@ -0,0 +1,33 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+export function isAbortError(err: unknown) {
+  return (
+    typeof err === 'object' &&
+    err !== null &&
+    // Spec-compliant fetch implementations
+    (('name' in err && (err as any).name === 'AbortError') ||
+      // Expo fetch
+      ('message' in err && String((err as any).message).includes('FetchRequestCanceledException')))
+  );
+}
+
+export const castToError = (err: any): Error => {
+  if (err instanceof Error) return err;
+  if (typeof err === 'object' && err !== null) {
+    try {
+      if (Object.prototype.toString.call(err) === '[object Error]') {
+        // @ts-ignore - not all envs have native support for cause yet
+        const error = new Error(err.message, err.cause ? { cause: err.cause } : {});
+        if (err.stack) error.stack = err.stack;
+        // @ts-ignore - not all envs have native support for cause yet
+        if (err.cause && !error.cause) error.cause = err.cause;
+        if (err.name) error.name = err.name;
+        return error;
+      }
+    } catch {}
+    try {
+      return new Error(JSON.stringify(err));
+    } catch {}
+  }
+  return new Error(err);
+};
diff --git a/src/internal/headers.ts b/src/internal/headers.ts
new file mode 100644
index 0000000..8659dde
--- /dev/null
+++ b/src/internal/headers.ts
@@ -0,0 +1,97 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+type HeaderValue = string | undefined | null;
+export type HeadersLike =
+  | Headers
+  | readonly HeaderValue[][]
+  | Record<string, HeaderValue | readonly HeaderValue[]>
+  | undefined
+  | null
+  | NullableHeaders;
+
+const brand_privateNullableHeaders = Symbol('brand.privateNullableHeaders');
+
+/**
+ * @internal
+ * Users can pass explicit nulls to unset default headers. When we parse them
+ * into a standard headers type we need to preserve that information.
+ */
+export type NullableHeaders = {
+  /** Brand check, prevent users from creating a NullableHeaders. */
+  [brand_privateNullableHeaders]: true;
+  /** Parsed headers. */
+  values: Headers;
+  /** Set of lowercase header names explicitly set to null. */
+  nulls: Set<string>;
+};
+
+const isArray = Array.isArray as (val: unknown) => val is readonly unknown[];
+
+function* iterateHeaders(headers: HeadersLike): IterableIterator<readonly [string, string | null]> {
+  if (!headers) return;
+
+  if (brand_privateNullableHeaders in headers) {
+    const { values, nulls } = headers;
+    yield* values.entries();
+    for (const name of nulls) {
+      yield [name, null];
+    }
+    return;
+  }
+
+  let shouldClear = false;
+  let iter: Iterable<readonly (HeaderValue | readonly HeaderValue[])[]>;
+  if (headers instanceof Headers) {
+    iter = headers.entries();
+  } else if (isArray(headers)) {
+    iter = headers;
+  } else {
+    shouldClear = true;
+    iter = Object.entries(headers ?? {});
+  }
+  for (let row of iter) {
+    const name = row[0];
+    if (typeof name !== 'string') throw new TypeError('expected header name to be a string');
+    const values = isArray(row[1]) ? row[1] : [row[1]];
+    let didClear = false;
+    for (const value of values) {
+      if (value === undefined) continue;
+
+      // Objects keys always overwrite older headers, they never append.
+      // Yield a null to clear the header before adding the new values.
+      if (shouldClear && !didClear) {
+        didClear = true;
+        yield [name, null];
+      }
+      yield [name, value];
+    }
+  }
+}
+
+export const buildHeaders = (newHeaders: HeadersLike[]): NullableHeaders => {
+  const targetHeaders = new Headers();
+  const nullHeaders = new Set<string>();
+  const seenHeaders = new Set<string>();
+  for (const headers of newHeaders) {
+    for (const [name, value] of iterateHeaders(headers)) {
+      const lowerName = name.toLowerCase();
+      if (!seenHeaders.has(lowerName)) {
+        targetHeaders.delete(name);
+        seenHeaders.add(lowerName);
+      }
+      if (value === null) {
+        targetHeaders.delete(name);
+        nullHeaders.add(lowerName);
+      } else {
+        targetHeaders.append(name, value);
+        nullHeaders.delete(lowerName);
+      }
+    }
+  }
+  return { [brand_privateNullableHeaders]: true, values: targetHeaders, nulls: nullHeaders };
+};
+
+export const isEmptyHeaders = (headers: HeadersLike) => {
+  for (const _ of iterateHeaders(headers)) return false;
+  return true;
+};
diff --git a/src/internal/parse.ts b/src/internal/parse.ts
new file mode 100644
index 0000000..a12647d
--- /dev/null
+++ b/src/internal/parse.ts
@@ -0,0 +1,50 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import type { FinalRequestOptions } from './request-options';
+import { type Gitpod } from '../client';
+import { formatRequestDetails, loggerFor } from './utils/log';
+
+export type APIResponseProps = {
+  response: Response;
+  options: FinalRequestOptions;
+  controller: AbortController;
+  requestLogID: string;
+  retryOfRequestLogID: string | undefined;
+  startTime: number;
+};
+
+export async function defaultParseResponse<T>(client: Gitpod, props: APIResponseProps): Promise<T> {
+  const { response, requestLogID, retryOfRequestLogID, startTime } = props;
+  const body = await (async () => {
+    // fetch refuses to read the body when the status code is 204.
+    if (response.status === 204) {
+      return null as T;
+    }
+
+    if (props.options.__binaryResponse) {
+      return response as unknown as T;
+    }
+
+    const contentType = response.headers.get('content-type');
+    const mediaType = contentType?.split(';')[0]?.trim();
+    const isJSON = mediaType?.includes('application/json') || mediaType?.endsWith('+json');
+    if (isJSON) {
+      const json = await response.json();
+      return json as T;
+    }
+
+    const text = await response.text();
+    return text as unknown as T;
+  })();
+  loggerFor(client).debug(
+    `[${requestLogID}] response parsed`,
+    formatRequestDetails({
+      retryOfRequestLogID,
+      url: response.url,
+      status: response.status,
+      body,
+      durationMs: Date.now() - startTime,
+    }),
+  );
+  return body;
+}
diff --git a/src/internal/request-options.ts b/src/internal/request-options.ts
new file mode 100644
index 0000000..d2ade9e
--- /dev/null
+++ b/src/internal/request-options.ts
@@ -0,0 +1,37 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { NullableHeaders } from './headers';
+
+import type { BodyInit } from './builtin-types';
+import type { HTTPMethod, MergedRequestInit } from './types';
+import { type HeadersLike } from './headers';
+
+export type FinalRequestOptions = RequestOptions & { method: HTTPMethod; path: string };
+
+export type RequestOptions = {
+  method?: HTTPMethod;
+  path?: string;
+  query?: object | undefined | null;
+  body?: unknown;
+  headers?: HeadersLike;
+  maxRetries?: number;
+  stream?: boolean | undefined;
+  timeout?: number;
+  fetchOptions?: MergedRequestInit;
+  signal?: AbortSignal | undefined | null;
+  idempotencyKey?: string;
+
+  __binaryResponse?: boolean | undefined;
+};
+
+export type EncodedContent = { bodyHeaders: HeadersLike; body: BodyInit };
+export type RequestEncoder = (request: { headers: NullableHeaders; body: unknown }) => EncodedContent;
+
+export const FallbackEncoder: RequestEncoder = ({ headers, body }) => {
+  return {
+    bodyHeaders: {
+      'content-type': 'application/json',
+    },
+    body: JSON.stringify(body),
+  };
+};
diff --git a/src/internal/shim-types.d.ts b/src/internal/shim-types.d.ts
new file mode 100644
index 0000000..fe48144
--- /dev/null
+++ b/src/internal/shim-types.d.ts
@@ -0,0 +1,28 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+/**
+ * Shims for types that we can't always rely on being available globally.
+ *
+ * Note: these only exist at the type-level, there is no corresponding runtime
+ * version for any of these symbols.
+ */
+
+/**
+ * In order to properly access the global `NodeJS` type, if it's available, we
+ * need to make use of declaration shadowing. Without this, any checks for the
+ * presence of `NodeJS.ReadableStream` will fail.
+ */
+declare namespace NodeJS {
+  interface ReadableStream {}
+}
+
+type HasProperties<T> = keyof T extends never ? false : true;
+
+// @ts-ignore
+type _ReadableStream<R = any> =
+  // @ts-ignore
+  HasProperties<NodeJS.ReadableStream> extends true ? NodeJS.ReadableStream<R> : ReadableStream<R>;
+
+// @ts-ignore
+declare const _ReadableStream: unknown extends typeof ReadableStream ? never : typeof ReadableStream;
+export { _ReadableStream as ReadableStream };
diff --git a/src/internal/shims.ts b/src/internal/shims.ts
new file mode 100644
index 0000000..95b03fb
--- /dev/null
+++ b/src/internal/shims.ts
@@ -0,0 +1,107 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+/**
+ * This module provides internal shims and utility functions for environments where certain Node.js or global types may not be available.
+ *
+ * These are used to ensure we can provide a consistent behaviour between different JavaScript environments and good error
+ * messages in cases where an environment isn't fully supported.
+ */
+
+import { type Fetch } from './builtin-types';
+import { type ReadableStream } from './shim-types';
+
+export function getDefaultFetch(): Fetch {
+  if (typeof fetch !== 'undefined') {
+    return fetch as any;
+  }
+
+  throw new Error(
+    '`fetch` is not defined as a global; Either pass `fetch` to the client, `new Gitpod({ fetch })` or polyfill the global, `globalThis.fetch = fetch`',
+  );
+}
+
+type ReadableStreamArgs = ConstructorParameters<typeof ReadableStream>;
+
+export function makeReadableStream(...args: ReadableStreamArgs): ReadableStream {
+  const ReadableStream = (globalThis as any).ReadableStream;
+  if (typeof ReadableStream === 'undefined') {
+    // Note: All of the platforms / runtimes we officially support already define
+    // `ReadableStream` as a global, so this should only ever be hit on unsupported runtimes.
+    throw new Error(
+      '`ReadableStream` is not defined as a global; You will need to polyfill it, `globalThis.ReadableStream = ReadableStream`',
+    );
+  }
+
+  return new ReadableStream(...args);
+}
+
+export function ReadableStreamFrom<T>(iterable: Iterable<T> | AsyncIterable<T>): ReadableStream<T> {
+  let iter: AsyncIterator<T> | Iterator<T> =
+    Symbol.asyncIterator in iterable ? iterable[Symbol.asyncIterator]() : iterable[Symbol.iterator]();
+
+  return makeReadableStream({
+    start() {},
+    async pull(controller: any) {
+      const { done, value } = await iter.next();
+      if (done) {
+        controller.close();
+      } else {
+        controller.enqueue(value);
+      }
+    },
+    async cancel() {
+      await iter.return?.();
+    },
+  });
+}
+
+/**
+ * Most browsers don't yet have async iterable support for ReadableStream,
+ * and Node has a very different way of reading bytes from its "ReadableStream".
+ *
+ * This polyfill was pulled from https://github.com/MattiasBuelens/web-streams-polyfill/pull/122#issuecomment-1627354490
+ */
+export function ReadableStreamToAsyncIterable<T>(stream: any): AsyncIterableIterator<T> {
+  if (stream[Symbol.asyncIterator]) return stream;
+
+  const reader = stream.getReader();
+  return {
+    async next() {
+      try {
+        const result = await reader.read();
+        if (result?.done) reader.releaseLock(); // release lock when stream becomes closed
+        return result;
+      } catch (e) {
+        reader.releaseLock(); // release lock when stream becomes errored
+        throw e;
+      }
+    },
+    async return() {
+      const cancelPromise = reader.cancel();
+      reader.releaseLock();
+      await cancelPromise;
+      return { done: true, value: undefined };
+    },
+    [Symbol.asyncIterator]() {
+      return this;
+    },
+  };
+}
+
+/**
+ * Cancels a ReadableStream we don't need to consume.
+ * See https://undici.nodejs.org/#/?id=garbage-collection
+ */
+export async function CancelReadableStream(stream: any): Promise<void> {
+  if (stream === null || typeof stream !== 'object') return;
+
+  if (stream[Symbol.asyncIterator]) {
+    await stream[Symbol.asyncIterator]().return?.();
+    return;
+  }
+
+  const reader = stream.getReader();
+  const cancelPromise = reader.cancel();
+  reader.releaseLock();
+  await cancelPromise;
+}
diff --git a/src/internal/shims/crypto.ts b/src/internal/shims/crypto.ts
new file mode 100644
index 0000000..905f81c
--- /dev/null
+++ b/src/internal/shims/crypto.ts
@@ -0,0 +1,18 @@
+import { getBuiltinModule } from './getBuiltinModule';
+
+type Crypto = {
+  /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Crypto/getRandomValues) */
+  getRandomValues<T extends ArrayBufferView | null>(array: T): T;
+  /**
+   * Available only in secure contexts.
+   *
+   * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Crypto/randomUUID)
+   */
+  randomUUID?: () => string;
+};
+export let getCrypto: () => Crypto | undefined = function lazyGetCrypto() {
+  if (getCrypto !== lazyGetCrypto) return getCrypto();
+  const crypto: Crypto = (globalThis as any).crypto || (getBuiltinModule?.('node:crypto') as any)?.webcrypto;
+  getCrypto = () => crypto;
+  return crypto;
+};
diff --git a/src/internal/shims/file.ts b/src/internal/shims/file.ts
new file mode 100644
index 0000000..d5dc820
--- /dev/null
+++ b/src/internal/shims/file.ts
@@ -0,0 +1,32 @@
+import { getBuiltinModule } from './getBuiltinModule';
+
+export let getFile = function lazyGetFile(): FileConstructor {
+  if (getFile !== lazyGetFile) return getFile();
+  // We can drop getBuiltinModule once we no longer support Node < 20.0.0
+  const File = (globalThis as any).File ?? (getBuiltinModule?.('node:buffer') as any)?.File;
+  if (!File) throw new Error('`File` is not defined as a global, which is required for file uploads.');
+  getFile = () => File;
+  return File;
+};
+
+type FileConstructor =
+  typeof globalThis extends { File: infer fileConstructor } ? fileConstructor : typeof FallbackFile;
+export type File = InstanceType<FileConstructor>;
+
+// The infer is to make TS show it as a nice union type,
+// instead of literally `ConstructorParameters<typeof Blob>[0]`
+type FallbackBlobSource = ConstructorParameters<typeof Blob>[0] extends infer T ? T : never;
+/**
+ * A [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File) provides information about files.
+ */
+declare class FallbackFile extends Blob {
+  constructor(sources: FallbackBlobSource, fileName: string, options?: any);
+  /**
+   * The name of the `File`.
+   */
+  readonly name: string;
+  /**
+   * The last modified date of the `File`.
+   */
+  readonly lastModified: number;
+}
diff --git a/src/internal/shims/getBuiltinModule.ts b/src/internal/shims/getBuiltinModule.ts
new file mode 100644
index 0000000..64daa2c
--- /dev/null
+++ b/src/internal/shims/getBuiltinModule.ts
@@ -0,0 +1,66 @@
+/**
+ * Load a Node built-in module. ID may or may not be prefixed by `node:` and
+ * will be normalized. If we used static imports then our bundle size would be bloated by
+ * injected polyfills, and if we used dynamic require then in addition to bundlers logging warnings,
+ * our code would not work when bundled to ESM and run in Node 18.
+ * @param {string} id ID of the built-in to be loaded.
+ * @returns {object|undefined} exports of the built-in. Undefined if the built-in
+ * does not exist.
+ */
+export let getBuiltinModule: null | ((id: string) => object | undefined) = function getBuiltinModuleLazy(
+  id: string,
+): object | undefined {
+  try {
+    if (getBuiltinModule !== getBuiltinModuleLazy) return getBuiltinModule!(id);
+    if ((process as any).getBuiltinModule) {
+      getBuiltinModule = (process as any).getBuiltinModule;
+    } else {
+      /* Fallback implementation for Node 18 */
+      function createFallbackGetBuiltinModule(BuiltinModule: any) {
+        return function getBuiltinModule(id: string): object | undefined {
+          id = BuiltinModule.normalizeRequirableId(String(id));
+          if (!BuiltinModule.canBeRequiredByUsers(id)) {
+            return;
+          }
+          const mod = BuiltinModule.map.get(id);
+          mod.compileForPublicLoader();
+          return mod.exports;
+        };
+      }
+      const magicKey = Math.random() + '';
+      let module: { BuiltinModule: any } | undefined;
+      let ObjectPrototype: {} = Blob;
+      for (let next; (next = Reflect.getPrototypeOf(ObjectPrototype)); ObjectPrototype = next);
+      try {
+        const kClone = Object.getOwnPropertySymbols(Blob.prototype).find(
+          (e) => e.description?.includes('clone'),
+        )!;
+        Object.defineProperty(ObjectPrototype, magicKey, {
+          get() {
+            module = this;
+            throw null;
+          },
+          configurable: true,
+        });
+        structuredClone(
+          new (class extends Blob {
+            [kClone]() {
+              return {
+                deserializeInfo: 'internal/bootstrap/realm:' + magicKey,
+              };
+            }
+          })([]),
+        );
+      } catch {}
+      delete (ObjectPrototype as any)[magicKey];
+      if (module) {
+        getBuiltinModule = createFallbackGetBuiltinModule(module.BuiltinModule);
+      } else {
+        getBuiltinModule = () => undefined;
+      }
+    }
+    return getBuiltinModule!(id);
+  } catch {
+    return undefined;
+  }
+};
diff --git a/src/internal/shims/nullGetBuiltinModule.ts b/src/internal/shims/nullGetBuiltinModule.ts
new file mode 100644
index 0000000..8bd2280
--- /dev/null
+++ b/src/internal/shims/nullGetBuiltinModule.ts
@@ -0,0 +1 @@
+export const getBuiltinModule = null;
diff --git a/src/internal/to-file.ts b/src/internal/to-file.ts
new file mode 100644
index 0000000..e92ac69
--- /dev/null
+++ b/src/internal/to-file.ts
@@ -0,0 +1,152 @@
+import { type File, getFile } from './shims/file';
+import { BlobPart, getName, makeFile, isAsyncIterable } from './uploads';
+import type { FilePropertyBag } from './builtin-types';
+
+type BlobLikePart = string | ArrayBuffer | ArrayBufferView | BlobLike | DataView;
+
+/**
+ * Intended to match DOM Blob, node-fetch Blob, node:buffer Blob, etc.
+ * Don't add arrayBuffer here, node-fetch doesn't have it
+ */
+interface BlobLike {
+  /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/size) */
+  readonly size: number;
+  /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/type) */
+  readonly type: string;
+  /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/text) */
+  text(): Promise<string>;
+  /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Blob/slice) */
+  slice(start?: number, end?: number): BlobLike;
+}
+
+/**
+ * This check adds the arrayBuffer() method type because it is available and used at runtime
+ */
+const isBlobLike = (value: any): value is BlobLike & { arrayBuffer(): Promise<ArrayBuffer> } =>
+  value != null &&
+  typeof value === 'object' &&
+  typeof value.size === 'number' &&
+  typeof value.type === 'string' &&
+  typeof value.text === 'function' &&
+  typeof value.slice === 'function' &&
+  typeof value.arrayBuffer === 'function';
+
+/**
+ * Intended to match DOM File, node:buffer File, undici File, etc.
+ */
+interface FileLike extends BlobLike {
+  /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/File/lastModified) */
+  readonly lastModified: number;
+  /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/File/name) */
+  readonly name?: string | undefined;
+}
+
+/**
+ * This check adds the arrayBuffer() method type because it is available and used at runtime
+ */
+const isFileLike = (value: any): value is FileLike & { arrayBuffer(): Promise<ArrayBuffer> } =>
+  value != null &&
+  typeof value === 'object' &&
+  typeof value.name === 'string' &&
+  typeof value.lastModified === 'number' &&
+  isBlobLike(value);
+
+/**
+ * Intended to match DOM Response, node-fetch Response, undici Response, etc.
+ */
+export interface ResponseLike {
+  url: string;
+  blob(): Promise<BlobLike>;
+}
+
+const isResponseLike = (value: any): value is ResponseLike =>
+  value != null &&
+  typeof value === 'object' &&
+  typeof value.url === 'string' &&
+  typeof value.blob === 'function';
+
+export type ToFileInput =
+  | FileLike
+  | ResponseLike
+  | Exclude<BlobLikePart, string>
+  | AsyncIterable<BlobLikePart>;
+
+/**
+ * Helper for creating a {@link File} to pass to an SDK upload method from a variety of different data formats
+ * @param value the raw content of the file.  Can be an {@link Uploadable}, {@link BlobLikePart}, or {@link AsyncIterable} of {@link BlobLikePart}s
+ * @param {string=} name the name of the file. If omitted, toFile will try to determine a file name from bits if possible
+ * @param {Object=} options additional properties
+ * @param {string=} options.type the MIME type of the content
+ * @param {number=} options.lastModified the last modified timestamp
+ * @returns a {@link File} with the given properties
+ */
+export async function toFile(
+  value: ToFileInput | PromiseLike<ToFileInput>,
+  name?: string | null | undefined,
+  options?: FilePropertyBag | undefined,
+): Promise<File> {
+  // If it's a promise, resolve it.
+  value = await value;
+
+  // If we've been given a `File` we don't need to do anything
+  if (isFileLike(value)) {
+    if (value instanceof getFile()) {
+      return value;
+    }
+    return makeFile([await value.arrayBuffer()], value.name);
+  }
+
+  if (isResponseLike(value)) {
+    const blob = await value.blob();
+    name ||= new URL(value.url).pathname.split(/[\\/]/).pop();
+
+    return makeFile(await getBytes(blob), name, options);
+  }
+
+  const parts = await getBytes(value);
+
+  name ||= getName(value);
+
+  if (!options?.type) {
+    const type = parts.find((part) => typeof part === 'object' && 'type' in part && part.type);
+    if (typeof type === 'string') {
+      options = { ...options, type };
+    }
+  }
+
+  return makeFile(parts, name, options);
+}
+
+async function getBytes(value: BlobLikePart | AsyncIterable<BlobLikePart>): Promise<Array<BlobPart>> {
+  let parts: Array<BlobPart> = [];
+  if (
+    typeof value === 'string' ||
+    ArrayBuffer.isView(value) || // includes Uint8Array, Buffer, etc.
+    value instanceof ArrayBuffer
+  ) {
+    parts.push(value);
+  } else if (isBlobLike(value)) {
+    parts.push(value instanceof Blob ? value : await value.arrayBuffer());
+  } else if (
+    isAsyncIterable(value) // includes Readable, ReadableStream, etc.
+  ) {
+    for await (const chunk of value) {
+      parts.push(...(await getBytes(chunk as BlobLikePart))); // TODO, consider validating?
+    }
+  } else {
+    const constructor = value?.constructor?.name;
+    throw new Error(
+      `Unexpected data type: ${typeof value}${
+        constructor ? `; constructor: ${constructor}` : ''
+      }${propsForError(value)}`,
+    );
+  }
+
+  return parts;
+}
+
+function propsForError(value: unknown): string {
+  if (typeof value !== 'object' || value === null) return '';
+  const props = Object.getOwnPropertyNames(value);
+  return `; props: [${props.map((p) => `"${p}"`).join(', ')}]`;
+}
diff --git a/src/internal/types.ts b/src/internal/types.ts
new file mode 100644
index 0000000..d7928cd
--- /dev/null
+++ b/src/internal/types.ts
@@ -0,0 +1,92 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+export type PromiseOrValue<T> = T | Promise<T>;
+export type HTTPMethod = 'get' | 'post' | 'put' | 'patch' | 'delete';
+
+export type KeysEnum<T> = { [P in keyof Required<T>]: true };
+
+export type FinalizedRequestInit = RequestInit & { headers: Headers };
+
+type NotAny<T> = [unknown] extends [T] ? never : T;
+
+/**
+ * Some environments overload the global fetch function, and Parameters<T> only gets the last signature.
+ */
+type OverloadedParameters<T> =
+  T extends (
+    {
+      (...args: infer A): unknown;
+      (...args: infer B): unknown;
+      (...args: infer C): unknown;
+      (...args: infer D): unknown;
+    }
+  ) ?
+    A | B | C | D
+  : T extends (
+    {
+      (...args: infer A): unknown;
+      (...args: infer B): unknown;
+      (...args: infer C): unknown;
+    }
+  ) ?
+    A | B | C
+  : T extends (
+    {
+      (...args: infer A): unknown;
+      (...args: infer B): unknown;
+    }
+  ) ?
+    A | B
+  : T extends (...args: infer A) => unknown ? A
+  : never;
+
+/* eslint-disable */
+/**
+ * These imports attempt to get types from a parent package's dependencies.
+ * Unresolved bare specifiers can trigger [automatic type acquisition][1] in some projects, which
+ * would cause typescript to show types not present at runtime. To avoid this, we import
+ * directly from parent node_modules folders.
+ *
+ * We need to check multiple levels because we don't know what directory structure we'll be in.
+ * For example, pnpm generates directories like this:
+ * ```
+ * node_modules
+ * ├── .pnpm
+ * │   └── pkg@1.0.0
+ * │       └── node_modules
+ * │           └── pkg
+ * │               └── internal
+ * │                   └── types.d.ts
+ * ├── pkg -> .pnpm/pkg@1.0.0/node_modules/pkg
+ * └── undici
+ * ```
+ *
+ * [1]: https://www.typescriptlang.org/tsconfig/#typeAcquisition
+ */
+/** @ts-ignore For users with \@types/node */
+type UndiciTypesRequestInit = NotAny<import('../node_modules/undici-types').RequestInit> | NotAny<import('../../node_modules/undici-types').RequestInit> | NotAny<import('../../../node_modules/undici-types').RequestInit> | NotAny<import('../../../../node_modules/undici-types').RequestInit> | NotAny<import('../../../../../node_modules/undici-types').RequestInit> | NotAny<import('../../../../../../node_modules/undici-types').RequestInit> | NotAny<import('../../../../../../../node_modules/undici-types').RequestInit> | NotAny<import('../../../../../../../../node_modules/undici-types').RequestInit> | NotAny<import('../../../../../../../../../node_modules/undici-types').RequestInit> | NotAny<import('../../../../../../../../../../node_modules/undici-types').RequestInit>;
+/** @ts-ignore For users with undici */
+type UndiciRequestInit = NotAny<import('../node_modules/undici').RequestInit> | NotAny<import('../../node_modules/undici').RequestInit> | NotAny<import('../../../node_modules/undici').RequestInit> | NotAny<import('../../../../node_modules/undici').RequestInit> | NotAny<import('../../../../../node_modules/undici').RequestInit> | NotAny<import('../../../../../../node_modules/undici').RequestInit> | NotAny<import('../../../../../../../node_modules/undici').RequestInit> | NotAny<import('../../../../../../../../node_modules/undici').RequestInit> | NotAny<import('../../../../../../../../../node_modules/undici').RequestInit> | NotAny<import('../../../../../../../../../../node_modules/undici').RequestInit>;
+/** @ts-ignore For users with \@types/bun */
+type BunRequestInit = globalThis.FetchRequestInit;
+/** @ts-ignore For users with node-fetch */
+type NodeFetchRequestInit = NotAny<import('../node_modules/node-fetch').RequestInit> | NotAny<import('../../node_modules/node-fetch').RequestInit> | NotAny<import('../../../node_modules/node-fetch').RequestInit> | NotAny<import('../../../../node_modules/node-fetch').RequestInit> | NotAny<import('../../../../../node_modules/node-fetch').RequestInit> | NotAny<import('../../../../../../node_modules/node-fetch').RequestInit> | NotAny<import('../../../../../../../node_modules/node-fetch').RequestInit> | NotAny<import('../../../../../../../../node_modules/node-fetch').RequestInit> | NotAny<import('../../../../../../../../../node_modules/node-fetch').RequestInit> | NotAny<import('../../../../../../../../../../node_modules/node-fetch').RequestInit>;
+/** @ts-ignore For users who use Deno */
+type FetchRequestInit = NonNullable<OverloadedParameters<typeof fetch>[1]>;
+/* eslint-enable */
+
+type RequestInits =
+  | NotAny<UndiciTypesRequestInit>
+  | NotAny<UndiciRequestInit>
+  | NotAny<BunRequestInit>
+  | NotAny<NodeFetchRequestInit>
+  | NotAny<RequestInit>
+  | NotAny<FetchRequestInit>;
+
+/**
+ * This type contains `RequestInit` options that may be available on the current runtime,
+ * including per-platform extensions like `dispatcher`, `agent`, `client`, etc.
+ */
+export type MergedRequestInit = RequestInits &
+  /** We don't include these in the types as they'll be overridden for every request. */
+  Partial<Record<'body' | 'headers' | 'method' | 'signal', never>>;
diff --git a/src/internal/uploads.ts b/src/internal/uploads.ts
new file mode 100644
index 0000000..fa0627a
--- /dev/null
+++ b/src/internal/uploads.ts
@@ -0,0 +1,175 @@
+import { type RequestOptions } from './request-options';
+import type { FilePropertyBag, Fetch } from './builtin-types';
+import type { Gitpod } from '../client';
+import { type File, getFile } from './shims/file';
+import { ReadableStreamFrom } from './shims';
+
+export type BlobPart = string | ArrayBuffer | ArrayBufferView | Blob | DataView;
+type FsReadStream = AsyncIterable<Uint8Array> & { path: string | { toString(): string } };
+
+// https://github.com/oven-sh/bun/issues/5980
+interface BunFile extends Blob {
+  readonly name?: string | undefined;
+}
+
+/**
+ * Typically, this is a native "File" class.
+ *
+ * We provide the {@link toFile} utility to convert a variety of objects
+ * into the File class.
+ *
+ * For convenience, you can also pass a fetch Response, or in Node,
+ * the result of fs.createReadStream().
+ */
+export type Uploadable = File | Response | FsReadStream | BunFile;
+
+/**
+ * Construct a `File` instance. This is used to ensure a helpful error is thrown
+ * for environments that don't define a global `File` yet.
+ */
+export function makeFile(
+  fileBits: BlobPart[],
+  fileName: string | undefined,
+  options?: FilePropertyBag,
+): File {
+  const File = getFile();
+  return new File(fileBits as any, fileName ?? 'unknown_file', options);
+}
+
+export function getName(value: any): string | undefined {
+  return (
+    (
+      (typeof value === 'object' &&
+        value !== null &&
+        (('name' in value && value.name && String(value.name)) ||
+          ('url' in value && value.url && String(value.url)) ||
+          ('filename' in value && value.filename && String(value.filename)) ||
+          ('path' in value && value.path && String(value.path)))) ||
+      ''
+    )
+      .split(/[\\/]/)
+      .pop() || undefined
+  );
+}
+
+export const isAsyncIterable = (value: any): value is AsyncIterable<any> =>
+  value != null && typeof value === 'object' && typeof value[Symbol.asyncIterator] === 'function';
+
+/**
+ * Returns a multipart/form-data request if any part of the given request body contains a File / Blob value.
+ * Otherwise returns the request as is.
+ */
+export const maybeMultipartFormRequestOptions = async (
+  opts: RequestOptions,
+  fetch: Gitpod | Fetch,
+): Promise<RequestOptions> => {
+  if (!hasUploadableValue(opts.body)) return opts;
+
+  return { ...opts, body: await createForm(opts.body, fetch) };
+};
+
+type MultipartFormRequestOptions = Omit<RequestOptions, 'body'> & { body: unknown };
+
+export const multipartFormRequestOptions = async (
+  opts: MultipartFormRequestOptions,
+  fetch: Gitpod | Fetch,
+): Promise<RequestOptions> => {
+  return { ...opts, body: await createForm(opts.body, fetch) };
+};
+
+const supportsFormDataMap = new WeakMap<Fetch, Promise<boolean>>();
+
+/**
+ * node-fetch doesn't support the global FormData object in recent node versions. Instead of sending
+ * properly-encoded form data, it just stringifies the object, resulting in a request body of "[object FormData]".
+ * This function detects if the fetch function provided supports the global FormData object to avoid
+ * confusing error messages later on.
+ */
+function supportsFormData(fetchObject: Gitpod | Fetch): Promise<boolean> {
+  const fetch: Fetch = typeof fetchObject === 'function' ? fetchObject : (fetchObject as any).fetch;
+  const cached = supportsFormDataMap.get(fetch);
+  if (cached) return cached;
+  const promise = (async () => {
+    try {
+      const FetchResponse = (
+        'Response' in fetch ?
+          fetch.Response
+        : (await fetch('data:,')).constructor) as typeof Response;
+      const data = new FormData();
+      if (data.toString() === (await new FetchResponse(data).text())) {
+        return false;
+      }
+      return true;
+    } catch {
+      // avoid false negatives
+      return true;
+    }
+  })();
+  supportsFormDataMap.set(fetch, promise);
+  return promise;
+}
+
+export const createForm = async <T = Record<string, unknown>>(
+  body: T | undefined,
+  fetch: Gitpod | Fetch,
+): Promise<FormData> => {
+  if (!(await supportsFormData(fetch))) {
+    throw new TypeError(
+      'The provided fetch function does not support file uploads with the current global FormData class.',
+    );
+  }
+  const form = new FormData();
+  await Promise.all(Object.entries(body || {}).map(([key, value]) => addFormValue(form, key, value)));
+  return form;
+};
+
+// We check for Blob not File because Bun.File doesn't inherit from File,
+// but they both inherit from Blob and have a `name` property at runtime.
+const isNamedBlob = (value: object) =>
+  value instanceof getFile() || (value instanceof Blob && 'name' in value);
+
+const isUploadable = (value: unknown) =>
+  typeof value === 'object' &&
+  value !== null &&
+  (value instanceof Response || isAsyncIterable(value) || isNamedBlob(value));
+
+const hasUploadableValue = (value: unknown): boolean => {
+  if (isUploadable(value)) return true;
+  if (Array.isArray(value)) return value.some(hasUploadableValue);
+  if (value && typeof value === 'object') {
+    for (const k in value) {
+      if (hasUploadableValue((value as any)[k])) return true;
+    }
+  }
+  return false;
+};
+
+const addFormValue = async (form: FormData, key: string, value: unknown): Promise<void> => {
+  if (value === undefined) return;
+  if (value == null) {
+    throw new TypeError(
+      `Received null for "${key}"; to pass null in FormData, you must use the string 'null'`,
+    );
+  }
+
+  // TODO: make nested formats configurable
+  if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
+    form.append(key, String(value));
+  } else if (value instanceof Response) {
+    form.append(key, makeFile([await value.blob()], getName(value)));
+  } else if (isAsyncIterable(value)) {
+    form.append(key, makeFile([await new Response(ReadableStreamFrom(value)).blob()], getName(value)));
+  } else if (isNamedBlob(value)) {
+    form.append(key, value, getName(value));
+  } else if (Array.isArray(value)) {
+    await Promise.all(value.map((entry) => addFormValue(form, key + '[]', entry)));
+  } else if (typeof value === 'object') {
+    await Promise.all(
+      Object.entries(value).map(([name, prop]) => addFormValue(form, `${key}[${name}]`, prop)),
+    );
+  } else {
+    throw new TypeError(
+      `Invalid value given to form, expected a string, number, boolean, object, Array, File or Blob but got ${value} instead`,
+    );
+  }
+};
diff --git a/src/internal/utils.ts b/src/internal/utils.ts
new file mode 100644
index 0000000..3cbfacc
--- /dev/null
+++ b/src/internal/utils.ts
@@ -0,0 +1,8 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+export * from './utils/values';
+export * from './utils/base64';
+export * from './utils/env';
+export * from './utils/log';
+export * from './utils/uuid';
+export * from './utils/sleep';
diff --git a/src/internal/utils/base64.ts b/src/internal/utils/base64.ts
new file mode 100644
index 0000000..04d8296
--- /dev/null
+++ b/src/internal/utils/base64.ts
@@ -0,0 +1,40 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { GitpodError } from '../../core/error';
+import { encodeUTF8 } from './bytes';
+
+export const toBase64 = (data: string | Uint8Array | null | undefined): string => {
+  if (!data) return '';
+
+  if (typeof (globalThis as any).Buffer !== 'undefined') {
+    return (globalThis as any).Buffer.from(data).toString('base64');
+  }
+
+  if (typeof data === 'string') {
+    data = encodeUTF8(data);
+  }
+
+  if (typeof btoa !== 'undefined') {
+    return btoa(String.fromCharCode.apply(null, data as any));
+  }
+
+  throw new GitpodError('Cannot generate base64 string; Expected `Buffer` or `btoa` to be defined');
+};
+
+export const fromBase64 = (str: string): Uint8Array => {
+  if (typeof (globalThis as any).Buffer !== 'undefined') {
+    const buf = (globalThis as any).Buffer.from(str, 'base64');
+    return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
+  }
+
+  if (typeof atob !== 'undefined') {
+    const bstr = atob(str);
+    const buf = new Uint8Array(bstr.length);
+    for (let i = 0; i < bstr.length; i++) {
+      buf[i] = bstr.charCodeAt(i);
+    }
+    return buf;
+  }
+
+  throw new GitpodError('Cannot decode base64 string; Expected `Buffer` or `atob` to be defined');
+};
diff --git a/src/internal/utils/bytes.ts b/src/internal/utils/bytes.ts
new file mode 100644
index 0000000..8da627a
--- /dev/null
+++ b/src/internal/utils/bytes.ts
@@ -0,0 +1,32 @@
+export function concatBytes(buffers: Uint8Array[]): Uint8Array {
+  let length = 0;
+  for (const buffer of buffers) {
+    length += buffer.length;
+  }
+  const output = new Uint8Array(length);
+  let index = 0;
+  for (const buffer of buffers) {
+    output.set(buffer, index);
+    index += buffer.length;
+  }
+
+  return output;
+}
+
+let encodeUTF8_: (str: string) => Uint8Array;
+export function encodeUTF8(str: string) {
+  let encoder;
+  return (
+    encodeUTF8_ ??
+    ((encoder = new (globalThis as any).TextEncoder()), (encodeUTF8_ = encoder.encode.bind(encoder)))
+  )(str);
+}
+
+let decodeUTF8_: (bytes: Uint8Array) => string;
+export function decodeUTF8(bytes: Uint8Array) {
+  let decoder;
+  return (
+    decodeUTF8_ ??
+    ((decoder = new (globalThis as any).TextDecoder()), (decodeUTF8_ = decoder.decode.bind(decoder)))
+  )(bytes);
+}
diff --git a/src/internal/utils/env.ts b/src/internal/utils/env.ts
new file mode 100644
index 0000000..2d84800
--- /dev/null
+++ b/src/internal/utils/env.ts
@@ -0,0 +1,18 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+/**
+ * Read an environment variable.
+ *
+ * Trims beginning and trailing whitespace.
+ *
+ * Will return undefined if the environment variable doesn't exist or cannot be accessed.
+ */
+export const readEnv = (env: string): string | undefined => {
+  if (typeof (globalThis as any).process !== 'undefined') {
+    return (globalThis as any).process.env?.[env]?.trim() ?? undefined;
+  }
+  if (typeof (globalThis as any).Deno !== 'undefined') {
+    return (globalThis as any).Deno.env?.get?.(env)?.trim();
+  }
+  return undefined;
+};
diff --git a/src/internal/utils/log.ts b/src/internal/utils/log.ts
new file mode 100644
index 0000000..8fdf60d
--- /dev/null
+++ b/src/internal/utils/log.ts
@@ -0,0 +1,126 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { hasOwn } from './values';
+import { type Gitpod } from '../../client';
+import { RequestOptions } from '../request-options';
+
+type LogFn = (message: string, ...rest: unknown[]) => void;
+export type Logger = {
+  error: LogFn;
+  warn: LogFn;
+  info: LogFn;
+  debug: LogFn;
+};
+export type LogLevel = 'off' | 'error' | 'warn' | 'info' | 'debug';
+
+const levelNumbers = {
+  off: 0,
+  error: 200,
+  warn: 300,
+  info: 400,
+  debug: 500,
+};
+
+export const parseLogLevel = (
+  maybeLevel: string | undefined,
+  sourceName: string,
+  client: Gitpod,
+): LogLevel | undefined => {
+  if (!maybeLevel) {
+    return undefined;
+  }
+  if (hasOwn(levelNumbers, maybeLevel)) {
+    return maybeLevel;
+  }
+  loggerFor(client).warn(
+    `${sourceName} was set to ${JSON.stringify(maybeLevel)}, expected one of ${JSON.stringify(
+      Object.keys(levelNumbers),
+    )}`,
+  );
+  return undefined;
+};
+
+function noop() {}
+
+function makeLogFn(fnLevel: keyof Logger, logger: Logger | undefined, logLevel: LogLevel) {
+  if (!logger || levelNumbers[fnLevel] > levelNumbers[logLevel]) {
+    return noop;
+  } else {
+    // Don't wrap logger functions, we want the stacktrace intact!
+    return logger[fnLevel].bind(logger);
+  }
+}
+
+const noopLogger = {
+  error: noop,
+  warn: noop,
+  info: noop,
+  debug: noop,
+};
+
+let cachedLoggers = new WeakMap<Logger, [LogLevel, Logger]>();
+
+export function loggerFor(client: Gitpod): Logger {
+  const logger = client.logger;
+  const logLevel = client.logLevel ?? 'off';
+  if (!logger) {
+    return noopLogger;
+  }
+
+  const cachedLogger = cachedLoggers.get(logger);
+  if (cachedLogger && cachedLogger[0] === logLevel) {
+    return cachedLogger[1];
+  }
+
+  const levelLogger = {
+    error: makeLogFn('error', logger, logLevel),
+    warn: makeLogFn('warn', logger, logLevel),
+    info: makeLogFn('info', logger, logLevel),
+    debug: makeLogFn('debug', logger, logLevel),
+  };
+
+  cachedLoggers.set(logger, [logLevel, levelLogger]);
+
+  return levelLogger;
+}
+
+export const formatRequestDetails = (details: {
+  options?: RequestOptions | undefined;
+  headers?: Headers | Record<string, string> | undefined;
+  retryOfRequestLogID?: string | undefined;
+  retryOf?: string | undefined;
+  url?: string | undefined;
+  status?: number | undefined;
+  method?: string | undefined;
+  durationMs?: number | undefined;
+  message?: unknown;
+  body?: unknown;
+}) => {
+  if (details.options) {
+    details.options = { ...details.options };
+    delete details.options['headers']; // redundant + leaks internals
+  }
+  if (details.headers) {
+    details.headers = Object.fromEntries(
+      (details.headers instanceof Headers ? [...details.headers] : Object.entries(details.headers)).map(
+        ([name, value]) => [
+          name,
+          (
+            name.toLowerCase() === 'authorization' ||
+            name.toLowerCase() === 'cookie' ||
+            name.toLowerCase() === 'set-cookie'
+          ) ?
+            '***'
+          : value,
+        ],
+      ),
+    );
+  }
+  if ('retryOfRequestLogID' in details) {
+    if (details.retryOfRequestLogID) {
+      details.retryOf = details.retryOfRequestLogID;
+    }
+    delete details.retryOfRequestLogID;
+  }
+  return details;
+};
diff --git a/src/internal/utils/path.ts b/src/internal/utils/path.ts
new file mode 100644
index 0000000..56154a2
--- /dev/null
+++ b/src/internal/utils/path.ts
@@ -0,0 +1,63 @@
+import { GitpodError } from '../../core/error';
+
+/**
+ * Percent-encode everything that isn't safe to have in a path without encoding safe chars.
+ *
+ * Taken from https://datatracker.ietf.org/doc/html/rfc3986#section-3.3:
+ * > unreserved  = ALPHA / DIGIT / "-" / "." / "_" / "~"
+ * > sub-delims  = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
+ * > pchar       = unreserved / pct-encoded / sub-delims / ":" / "@"
+ */
+export function encodeURIPath(str: string) {
+  return str.replace(/[^A-Za-z0-9\-._~!$&'()*+,;=:@]+/g, encodeURIComponent);
+}
+
+export const createPathTagFunction = (pathEncoder = encodeURIPath) =>
+  function path(statics: readonly string[], ...params: readonly unknown[]): string {
+    // If there are no params, no processing is needed.
+    if (statics.length === 1) return statics[0]!;
+
+    let postPath = false;
+    const path = statics.reduce((previousValue, currentValue, index) => {
+      if (/[?#]/.test(currentValue)) {
+        postPath = true;
+      }
+      return (
+        previousValue +
+        currentValue +
+        (index === params.length ? '' : (postPath ? encodeURIComponent : pathEncoder)(String(params[index])))
+      );
+    }, '');
+
+    const pathOnly = path.split(/[?#]/, 1)[0]!;
+    const invalidSegments = [];
+    const invalidSegmentPattern = /(?<=^|\/)(?:\.|%2e){1,2}(?=\/|$)/gi;
+    let match;
+
+    // Find all invalid segments
+    while ((match = invalidSegmentPattern.exec(pathOnly)) !== null) {
+      invalidSegments.push({
+        start: match.index,
+        length: match[0].length,
+      });
+    }
+
+    if (invalidSegments.length > 0) {
+      let lastEnd = 0;
+      const underline = invalidSegments.reduce((acc, segment) => {
+        const spaces = ' '.repeat(segment.start - lastEnd);
+        const arrows = '^'.repeat(segment.length);
+        lastEnd = segment.start + segment.length;
+        return acc + spaces + arrows;
+      }, '');
+
+      throw new GitpodError(`Path parameters result in path with invalid segments:\n${path}\n${underline}`);
+    }
+
+    return path;
+  };
+
+/**
+ * URI-encodes path params and ensures no unsafe /./ or /../ path segments are introduced.
+ */
+export const path = createPathTagFunction(encodeURIPath);
diff --git a/src/internal/utils/sleep.ts b/src/internal/utils/sleep.ts
new file mode 100644
index 0000000..65e5296
--- /dev/null
+++ b/src/internal/utils/sleep.ts
@@ -0,0 +1,3 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+export const sleep = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));
diff --git a/src/internal/utils/uuid.ts b/src/internal/utils/uuid.ts
new file mode 100644
index 0000000..5a262c6
--- /dev/null
+++ b/src/internal/utils/uuid.ts
@@ -0,0 +1,19 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { getCrypto } from '../shims/crypto';
+
+/**
+ * https://stackoverflow.com/a/2117523
+ */
+export let uuid4 = function () {
+  const crypto = getCrypto();
+  if (crypto?.randomUUID) {
+    uuid4 = crypto.randomUUID.bind(crypto);
+    return crypto.randomUUID();
+  }
+  const u8 = new Uint8Array(1);
+  const randomByte = crypto ? () => crypto.getRandomValues(u8)[0]! : () => (Math.random() * 0xff) & 0xff;
+  return '10000000-1000-4000-8000-100000000000'.replace(/[018]/g, (c) =>
+    (+c ^ (randomByte() & (15 >> (+c / 4)))).toString(16),
+  );
+};
diff --git a/src/internal/utils/values.ts b/src/internal/utils/values.ts
new file mode 100644
index 0000000..d0e9c61
--- /dev/null
+++ b/src/internal/utils/values.ts
@@ -0,0 +1,102 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { GitpodError } from '../../core/error';
+
+// https://url.spec.whatwg.org/#url-scheme-string
+const startsWithSchemeRegexp = /^[a-z][a-z0-9+.-]*:/i;
+
+export const isAbsoluteURL = (url: string): boolean => {
+  return startsWithSchemeRegexp.test(url);
+};
+
+/** Returns an object if the given value isn't an object, otherwise returns as-is */
+export function maybeObj(x: unknown): object {
+  if (typeof x !== 'object') {
+    return {};
+  }
+
+  return x ?? {};
+}
+
+// https://stackoverflow.com/a/34491287
+export function isEmptyObj(obj: Object | null | undefined): boolean {
+  if (!obj) return true;
+  for (const _k in obj) return false;
+  return true;
+}
+
+// https://eslint.org/docs/latest/rules/no-prototype-builtins
+export function hasOwn<T extends object = object>(obj: T, key: PropertyKey): key is keyof T {
+  return Object.prototype.hasOwnProperty.call(obj, key);
+}
+
+export function isObj(obj: unknown): obj is Record<string, unknown> {
+  return obj != null && typeof obj === 'object' && !Array.isArray(obj);
+}
+
+export const ensurePresent = <T>(value: T | null | undefined): T => {
+  if (value == null) {
+    throw new GitpodError(`Expected a value to be given but received ${value} instead.`);
+  }
+
+  return value;
+};
+
+export const validatePositiveInteger = (name: string, n: unknown): number => {
+  if (typeof n !== 'number' || !Number.isInteger(n)) {
+    throw new GitpodError(`${name} must be an integer`);
+  }
+  if (n < 0) {
+    throw new GitpodError(`${name} must be a positive integer`);
+  }
+  return n;
+};
+
+export const coerceInteger = (value: unknown): number => {
+  if (typeof value === 'number') return Math.round(value);
+  if (typeof value === 'string') return parseInt(value, 10);
+
+  throw new GitpodError(`Could not coerce ${value} (type: ${typeof value}) into a number`);
+};
+
+export const coerceFloat = (value: unknown): number => {
+  if (typeof value === 'number') return value;
+  if (typeof value === 'string') return parseFloat(value);
+
+  throw new GitpodError(`Could not coerce ${value} (type: ${typeof value}) into a number`);
+};
+
+export const coerceBoolean = (value: unknown): boolean => {
+  if (typeof value === 'boolean') return value;
+  if (typeof value === 'string') return value === 'true';
+  return Boolean(value);
+};
+
+export const maybeCoerceInteger = (value: unknown): number | undefined => {
+  if (value === undefined) {
+    return undefined;
+  }
+  return coerceInteger(value);
+};
+
+export const maybeCoerceFloat = (value: unknown): number | undefined => {
+  if (value === undefined) {
+    return undefined;
+  }
+  return coerceFloat(value);
+};
+
+export const maybeCoerceBoolean = (value: unknown): boolean | undefined => {
+  if (value === undefined) {
+    return undefined;
+  }
+  return coerceBoolean(value);
+};
+
+export const safeJSON = (text: string) => {
+  try {
+    return JSON.parse(text);
+  } catch (err) {
+    return undefined;
+  }
+};
diff --git a/src/lib/.keep b/src/lib/.keep
new file mode 100644
index 0000000..7554f8b
--- /dev/null
+++ b/src/lib/.keep
@@ -0,0 +1,4 @@
+File generated from our OpenAPI spec by Stainless.
+
+This directory can be used to store custom files to expand the SDK.
+It is ignored by Stainless code generation and its content (other than this keep file) won't be touched.
diff --git a/src/pagination.ts b/src/pagination.ts
new file mode 100644
index 0000000..90bf015
--- /dev/null
+++ b/src/pagination.ts
@@ -0,0 +1,2 @@
+/** @deprecated Import from ./core/pagination instead */
+export * from './core/pagination';
diff --git a/src/resource.ts b/src/resource.ts
new file mode 100644
index 0000000..363e351
--- /dev/null
+++ b/src/resource.ts
@@ -0,0 +1,2 @@
+/** @deprecated Import from ./core/resource instead */
+export * from './core/resource';
diff --git a/src/resources.ts b/src/resources.ts
new file mode 100644
index 0000000..b283d57
--- /dev/null
+++ b/src/resources.ts
@@ -0,0 +1 @@
+export * from './resources/index';
diff --git a/src/resources/accounts.ts b/src/resources/accounts.ts
new file mode 100644
index 0000000..b341c47
--- /dev/null
+++ b/src/resources/accounts.ts
@@ -0,0 +1,499 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../core/resource';
+import * as Shared from './shared';
+import { APIPromise } from '../core/api-promise';
+import { LoginProvidersPage, type LoginProvidersPageParams, PagePromise } from '../core/pagination';
+import { RequestOptions } from '../internal/request-options';
+
+export class Accounts extends APIResource {
+  /**
+   * Gets information about the currently authenticated account.
+   *
+   * Use this method to:
+   *
+   * - Retrieve account profile information
+   * - Check organization memberships
+   * - View account settings
+   * - Get joinable organizations
+   *
+   * ### Examples
+   *
+   * - Get account details:
+   *
+   *   Retrieves information about the authenticated account.
+   *
+   *   ```yaml
+   *   {}
+   *   ```
+   */
+  retrieve(body: AccountRetrieveParams, options?: RequestOptions): APIPromise<AccountRetrieveResponse> {
+    return this._client.post('/gitpod.v1.AccountService/GetAccount', { body, ...options });
+  }
+
+  /**
+   * Deletes an account permanently.
+   *
+   * Use this method to:
+   *
+   * - Remove unused accounts
+   * - Clean up test accounts
+   * - Complete account deletion requests
+   *
+   * The account must not be an active member of any organization.
+   *
+   * ### Examples
+   *
+   * - Delete account:
+   *
+   *   Permanently removes an account.
+   *
+   *   ```yaml
+   *   accountId: "f53d2330-3795-4c5d-a1f3-453121af9c60"
+   *   ```
+   */
+  delete(body: AccountDeleteParams, options?: RequestOptions): APIPromise<unknown> {
+    return this._client.post('/gitpod.v1.AccountService/DeleteAccount', { body, ...options });
+  }
+
+  /**
+   * Gets the SSO login URL for a specific email domain.
+   *
+   * Use this method to:
+   *
+   * - Initiate SSO authentication
+   * - Get organization-specific login URLs
+   * - Handle SSO redirects
+   *
+   * ### Examples
+   *
+   * - Get login URL:
+   *
+   *   Retrieves SSO URL for email domain.
+   *
+   *   ```yaml
+   *   email: "user@company.com"
+   *   ```
+   *
+   * - Get URL with return path:
+   *
+   *   Gets SSO URL with specific return location.
+   *
+   *   ```yaml
+   *   email: "user@company.com"
+   *   returnTo: "https://gitpod.io/workspaces"
+   *   ```
+   */
+  getSSOLoginURL(
+    body: AccountGetSSOLoginURLParams,
+    options?: RequestOptions,
+  ): APIPromise<AccountGetSSOLoginURLResponse> {
+    return this._client.post('/gitpod.v1.AccountService/GetSSOLoginURL', { body, ...options });
+  }
+
+  /**
+   * Lists available login providers with optional filtering.
+   *
+   * Use this method to:
+   *
+   * - View supported authentication methods
+   * - Get provider-specific login URLs
+   * - Filter providers by invite
+   *
+   * ### Examples
+   *
+   * - List all providers:
+   *
+   *   Shows all available login providers.
+   *
+   *   ```yaml
+   *   pagination:
+   *     pageSize: 20
+   *   ```
+   *
+   * - List for specific invite:
+   *
+   *   Shows providers available for an invite.
+   *
+   *   ```yaml
+   *   filter:
+   *     inviteId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   pagination:
+   *     pageSize: 20
+   *   ```
+   */
+  listLoginProviders(
+    params: AccountListLoginProvidersParams,
+    options?: RequestOptions,
+  ): PagePromise<LoginProvidersLoginProvidersPage, LoginProvider> {
+    const { token, pageSize, ...body } = params;
+    return this._client.getAPIList(
+      '/gitpod.v1.AccountService/ListLoginProviders',
+      LoginProvidersPage<LoginProvider>,
+      { query: { token, pageSize }, body, method: 'post', ...options },
+    );
+  }
+}
+
+export type LoginProvidersLoginProvidersPage = LoginProvidersPage<LoginProvider>;
+
+export interface Account {
+  id: string;
+
+  /**
+   * A Timestamp represents a point in time independent of any time zone or local
+   * calendar, encoded as a count of seconds and fractions of seconds at nanosecond
+   * resolution. The count is relative to an epoch at UTC midnight on January 1,
+   * 1970, in the proleptic Gregorian calendar which extends the Gregorian calendar
+   * backwards to year one.
+   *
+   * All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
+   * second table is needed for interpretation, using a
+   * [24-hour linear smear](https://developers.google.com/time/smear).
+   *
+   * The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
+   * restricting to that range, we ensure that we can convert to and from
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
+   *
+   * # Examples
+   *
+   * Example 1: Compute Timestamp from POSIX `time()`.
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(time(NULL));
+   *      timestamp.set_nanos(0);
+   *
+   * Example 2: Compute Timestamp from POSIX `gettimeofday()`.
+   *
+   *      struct timeval tv;
+   *      gettimeofday(&tv, NULL);
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(tv.tv_sec);
+   *      timestamp.set_nanos(tv.tv_usec * 1000);
+   *
+   * Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
+   *
+   *      FILETIME ft;
+   *      GetSystemTimeAsFileTime(&ft);
+   *      UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
+   *
+   *      // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
+   *      // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
+   *      timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
+   *
+   * Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
+   *
+   *      long millis = System.currentTimeMillis();
+   *
+   *      Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
+   *          .setNanos((int) ((millis % 1000) * 1000000)).build();
+   *
+   * Example 5: Compute Timestamp from Java `Instant.now()`.
+   *
+   *      Instant now = Instant.now();
+   *
+   *      Timestamp timestamp =
+   *          Timestamp.newBuilder().setSeconds(now.getEpochSecond())
+   *              .setNanos(now.getNano()).build();
+   *
+   * Example 6: Compute Timestamp from current time in Python.
+   *
+   *      timestamp = Timestamp()
+   *      timestamp.GetCurrentTime()
+   *
+   * # JSON Mapping
+   *
+   * In JSON format, the Timestamp type is encoded as a string in the
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the format is
+   * "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" where {year} is always
+   * expressed using four digits while {month}, {day}, {hour}, {min}, and {sec} are
+   * zero-padded to two digits each. The fractional seconds, which can go up to 9
+   * digits (i.e. up to 1 nanosecond resolution), are optional. The "Z" suffix
+   * indicates the timezone ("UTC"); the timezone is required. A proto3 JSON
+   * serializer should always use UTC (as indicated by "Z") when printing the
+   * Timestamp type and a proto3 JSON parser should be able to accept both UTC and
+   * other timezones (as indicated by an offset).
+   *
+   * For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past 01:30 UTC on
+   * January 15, 2017.
+   *
+   * In JavaScript, one can convert a Date object to this format using the standard
+   * [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
+   * method. In Python, a standard `datetime.datetime` object can be converted to
+   * this format using
+   * [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with the
+   * time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use the
+   * Joda Time's
+   * [`ISODateTimeFormat.dateTime()`](<http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime()>)
+   * to obtain a formatter capable of generating timestamps in this format.
+   */
+  createdAt: string;
+
+  email: string;
+
+  name: string;
+
+  /**
+   * A Timestamp represents a point in time independent of any time zone or local
+   * calendar, encoded as a count of seconds and fractions of seconds at nanosecond
+   * resolution. The count is relative to an epoch at UTC midnight on January 1,
+   * 1970, in the proleptic Gregorian calendar which extends the Gregorian calendar
+   * backwards to year one.
+   *
+   * All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
+   * second table is needed for interpretation, using a
+   * [24-hour linear smear](https://developers.google.com/time/smear).
+   *
+   * The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
+   * restricting to that range, we ensure that we can convert to and from
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
+   *
+   * # Examples
+   *
+   * Example 1: Compute Timestamp from POSIX `time()`.
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(time(NULL));
+   *      timestamp.set_nanos(0);
+   *
+   * Example 2: Compute Timestamp from POSIX `gettimeofday()`.
+   *
+   *      struct timeval tv;
+   *      gettimeofday(&tv, NULL);
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(tv.tv_sec);
+   *      timestamp.set_nanos(tv.tv_usec * 1000);
+   *
+   * Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
+   *
+   *      FILETIME ft;
+   *      GetSystemTimeAsFileTime(&ft);
+   *      UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
+   *
+   *      // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
+   *      // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
+   *      timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
+   *
+   * Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
+   *
+   *      long millis = System.currentTimeMillis();
+   *
+   *      Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
+   *          .setNanos((int) ((millis % 1000) * 1000000)).build();
+   *
+   * Example 5: Compute Timestamp from Java `Instant.now()`.
+   *
+   *      Instant now = Instant.now();
+   *
+   *      Timestamp timestamp =
+   *          Timestamp.newBuilder().setSeconds(now.getEpochSecond())
+   *              .setNanos(now.getNano()).build();
+   *
+   * Example 6: Compute Timestamp from current time in Python.
+   *
+   *      timestamp = Timestamp()
+   *      timestamp.GetCurrentTime()
+   *
+   * # JSON Mapping
+   *
+   * In JSON format, the Timestamp type is encoded as a string in the
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the format is
+   * "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" where {year} is always
+   * expressed using four digits while {month}, {day}, {hour}, {min}, and {sec} are
+   * zero-padded to two digits each. The fractional seconds, which can go up to 9
+   * digits (i.e. up to 1 nanosecond resolution), are optional. The "Z" suffix
+   * indicates the timezone ("UTC"); the timezone is required. A proto3 JSON
+   * serializer should always use UTC (as indicated by "Z") when printing the
+   * Timestamp type and a proto3 JSON parser should be able to accept both UTC and
+   * other timezones (as indicated by an offset).
+   *
+   * For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past 01:30 UTC on
+   * January 15, 2017.
+   *
+   * In JavaScript, one can convert a Date object to this format using the standard
+   * [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
+   * method. In Python, a standard `datetime.datetime` object can be converted to
+   * this format using
+   * [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with the
+   * time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use the
+   * Joda Time's
+   * [`ISODateTimeFormat.dateTime()`](<http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime()>)
+   * to obtain a formatter capable of generating timestamps in this format.
+   */
+  updatedAt: string;
+
+  avatarUrl?: string;
+
+  joinables?: Array<JoinableOrganization>;
+
+  memberships?: Array<AccountMembership>;
+
+  /**
+   * organization_id is the ID of the organization the account is owned by if it's
+   * created through custom SSO
+   */
+  organizationId?: string | null;
+
+  /**
+   * public_email_provider is true if the email for the Account matches a known
+   * public email provider
+   */
+  publicEmailProvider?: boolean;
+}
+
+export interface AccountMembership {
+  /**
+   * organization_id is the id of the organization the user is a member of
+   */
+  organizationId: string;
+
+  /**
+   * organization_name is the name of the organization the user is a member of
+   */
+  organizationName: string;
+
+  /**
+   * user_id is the ID the user has in the organization
+   */
+  userId: string;
+
+  /**
+   * user_role is the role the user has in the organization
+   */
+  userRole: Shared.OrganizationRole;
+
+  /**
+   * organization_name is the member count of the organization the user is a member
+   * of
+   */
+  organizationMemberCount?: number;
+}
+
+export interface JoinableOrganization {
+  /**
+   * organization_id is the id of the organization the user can join
+   */
+  organizationId: string;
+
+  /**
+   * organization_name is the name of the organization the user can join
+   */
+  organizationName: string;
+
+  /**
+   * organization_member_count is the member count of the organization the user can
+   * join
+   */
+  organizationMemberCount?: number;
+}
+
+export interface LoginProvider {
+  /**
+   * provider is the provider used by this login method, e.g. "github", "google",
+   * "custom"
+   */
+  provider: string;
+
+  /**
+   * login_url is the URL to redirect the browser agent to for login, when provider
+   * is "custom"
+   */
+  loginUrl?: string;
+}
+
+export interface AccountRetrieveResponse {
+  account: Account;
+}
+
+export type AccountDeleteResponse = unknown;
+
+export interface AccountGetSSOLoginURLResponse {
+  /**
+   * login_url is the URL to redirect the user to for SSO login
+   */
+  loginUrl: string;
+}
+
+export interface AccountRetrieveParams {
+  empty?: boolean;
+}
+
+export interface AccountDeleteParams {
+  accountId: string;
+}
+
+export interface AccountGetSSOLoginURLParams {
+  /**
+   * email is the email the user wants to login with
+   */
+  email: string;
+
+  /**
+   * return_to is the URL the user will be redirected to after login
+   */
+  returnTo?: string | null;
+}
+
+export interface AccountListLoginProvidersParams extends LoginProvidersPageParams {
+  /**
+   * Body param: filter contains the filter options for listing login methods
+   */
+  filter?: AccountListLoginProvidersParams.Filter;
+
+  /**
+   * Body param: pagination contains the pagination options for listing login methods
+   */
+  pagination?: AccountListLoginProvidersParams.Pagination;
+}
+
+export namespace AccountListLoginProvidersParams {
+  /**
+   * filter contains the filter options for listing login methods
+   */
+  export interface Filter {
+    /**
+     * invite_id is the ID of the invite URL the user wants to login with
+     */
+    inviteId?: string;
+  }
+
+  /**
+   * pagination contains the pagination options for listing login methods
+   */
+  export interface Pagination {
+    /**
+     * Token for the next set of results that was returned as next_token of a
+     * PaginationResponse
+     */
+    token?: string;
+
+    /**
+     * Page size is the maximum number of results to retrieve per page. Defaults to 25.
+     * Maximum 100.
+     */
+    pageSize?: number;
+  }
+}
+
+export declare namespace Accounts {
+  export {
+    type Account as Account,
+    type AccountMembership as AccountMembership,
+    type JoinableOrganization as JoinableOrganization,
+    type LoginProvider as LoginProvider,
+    type AccountRetrieveResponse as AccountRetrieveResponse,
+    type AccountDeleteResponse as AccountDeleteResponse,
+    type AccountGetSSOLoginURLResponse as AccountGetSSOLoginURLResponse,
+    type LoginProvidersLoginProvidersPage as LoginProvidersLoginProvidersPage,
+    type AccountRetrieveParams as AccountRetrieveParams,
+    type AccountDeleteParams as AccountDeleteParams,
+    type AccountGetSSOLoginURLParams as AccountGetSSOLoginURLParams,
+    type AccountListLoginProvidersParams as AccountListLoginProvidersParams,
+  };
+}
diff --git a/src/resources/editors.ts b/src/resources/editors.ts
new file mode 100644
index 0000000..84853ac
--- /dev/null
+++ b/src/resources/editors.ts
@@ -0,0 +1,210 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../core/resource';
+import { APIPromise } from '../core/api-promise';
+import { EditorsPage, type EditorsPageParams, PagePromise } from '../core/pagination';
+import { RequestOptions } from '../internal/request-options';
+
+export class Editors extends APIResource {
+  /**
+   * Gets details about a specific editor.
+   *
+   * Use this method to:
+   *
+   * - View editor information
+   * - Get editor configuration
+   *
+   * ### Examples
+   *
+   * - Get editor details:
+   *
+   *   Retrieves information about a specific editor.
+   *
+   *   ```yaml
+   *   id: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   ```
+   */
+  retrieve(body: EditorRetrieveParams, options?: RequestOptions): APIPromise<EditorRetrieveResponse> {
+    return this._client.post('/gitpod.v1.EditorService/GetEditor', { body, ...options });
+  }
+
+  /**
+   * Lists all available code editors, optionally filtered to those allowed in an
+   * organization.
+   *
+   * Use this method to:
+   *
+   * - View supported editors
+   * - Get editor capabilities
+   * - Browse editor options
+   * - Check editor availability
+   *
+   * ### Examples
+   *
+   * - List editors:
+   *
+   *   Shows all available editors with pagination.
+   *
+   *   ```yaml
+   *   pagination:
+   *     pageSize: 20
+   *   ```
+   *
+   * - List editors available to the organization:
+   *
+   *   Shows all available editors that are allowed by the policies enforced in the
+   *   organization with pagination.
+   *
+   *   ```yaml
+   *   pagination:
+   *     pageSize: 20
+   *   filter:
+   *     allowedByPolicy: true
+   *   ```
+   */
+  list(params: EditorListParams, options?: RequestOptions): PagePromise<EditorsEditorsPage, Editor> {
+    const { token, pageSize, ...body } = params;
+    return this._client.getAPIList('/gitpod.v1.EditorService/ListEditors', EditorsPage<Editor>, {
+      query: { token, pageSize },
+      body,
+      method: 'post',
+      ...options,
+    });
+  }
+
+  /**
+   * Resolves the URL for accessing an editor in a specific environment.
+   *
+   * Use this method to:
+   *
+   * - Get editor access URLs
+   * - Launch editors for environments
+   * - Set up editor connections
+   * - Configure editor access
+   *
+   * ### Examples
+   *
+   * - Resolve editor URL:
+   *
+   *   Gets the URL for accessing an editor in an environment.
+   *
+   *   ```yaml
+   *   editorId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   environmentId: "07e03a28-65a5-4d98-b532-8ea67b188048"
+   *   organizationId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   ```
+   */
+  resolveURL(body: EditorResolveURLParams, options?: RequestOptions): APIPromise<EditorResolveURLResponse> {
+    return this._client.post('/gitpod.v1.EditorService/ResolveEditorURL', { body, ...options });
+  }
+}
+
+export type EditorsEditorsPage = EditorsPage<Editor>;
+
+export interface Editor {
+  id: string;
+
+  installationInstructions: string;
+
+  name: string;
+
+  urlTemplate: string;
+
+  alias?: string;
+
+  iconUrl?: string;
+
+  shortDescription?: string;
+}
+
+export interface EditorRetrieveResponse {
+  /**
+   * editor contains the editor
+   */
+  editor: Editor;
+}
+
+export interface EditorResolveURLResponse {
+  /**
+   * url is the resolved editor URL
+   */
+  url: string;
+}
+
+export interface EditorRetrieveParams {
+  /**
+   * id is the ID of the editor to get
+   */
+  id: string;
+}
+
+export interface EditorListParams extends EditorsPageParams {
+  /**
+   * Body param: filter contains the filter options for listing editors
+   */
+  filter?: EditorListParams.Filter;
+
+  /**
+   * Body param: pagination contains the pagination options for listing environments
+   */
+  pagination?: EditorListParams.Pagination;
+}
+
+export namespace EditorListParams {
+  /**
+   * filter contains the filter options for listing editors
+   */
+  export interface Filter {
+    /**
+     * allowed_by_policy filters the response to only editors that are allowed by the
+     * policies enforced in the organization
+     */
+    allowedByPolicy?: boolean;
+  }
+
+  /**
+   * pagination contains the pagination options for listing environments
+   */
+  export interface Pagination {
+    /**
+     * Token for the next set of results that was returned as next_token of a
+     * PaginationResponse
+     */
+    token?: string;
+
+    /**
+     * Page size is the maximum number of results to retrieve per page. Defaults to 25.
+     * Maximum 100.
+     */
+    pageSize?: number;
+  }
+}
+
+export interface EditorResolveURLParams {
+  /**
+   * editorId is the ID of the editor to resolve the URL for
+   */
+  editorId: string;
+
+  /**
+   * environmentId is the ID of the environment to resolve the URL for
+   */
+  environmentId: string;
+
+  /**
+   * organizationId is the ID of the organization to resolve the URL for
+   */
+  organizationId: string;
+}
+
+export declare namespace Editors {
+  export {
+    type Editor as Editor,
+    type EditorRetrieveResponse as EditorRetrieveResponse,
+    type EditorResolveURLResponse as EditorResolveURLResponse,
+    type EditorsEditorsPage as EditorsEditorsPage,
+    type EditorRetrieveParams as EditorRetrieveParams,
+    type EditorListParams as EditorListParams,
+    type EditorResolveURLParams as EditorResolveURLParams,
+  };
+}
diff --git a/src/resources/environments.ts b/src/resources/environments.ts
new file mode 100644
index 0000000..85fdba4
--- /dev/null
+++ b/src/resources/environments.ts
@@ -0,0 +1,3 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+export * from './environments/index';
diff --git a/src/resources/environments/automations.ts b/src/resources/environments/automations.ts
new file mode 100644
index 0000000..e13af42
--- /dev/null
+++ b/src/resources/environments/automations.ts
@@ -0,0 +1,3 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+export * from './automations/index';
diff --git a/src/resources/environments/automations/automations.ts b/src/resources/environments/automations/automations.ts
new file mode 100644
index 0000000..a369c3c
--- /dev/null
+++ b/src/resources/environments/automations/automations.ts
@@ -0,0 +1,231 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../../../core/resource';
+import * as Shared from '../../shared';
+import * as ServicesAPI from './services';
+import {
+  Service,
+  ServiceCreateParams,
+  ServiceCreateResponse,
+  ServiceDeleteParams,
+  ServiceDeleteResponse,
+  ServiceListParams,
+  ServiceMetadata,
+  ServicePhase,
+  ServiceRetrieveParams,
+  ServiceRetrieveResponse,
+  ServiceSpec,
+  ServiceStartParams,
+  ServiceStartResponse,
+  ServiceStatus,
+  ServiceStopParams,
+  ServiceStopResponse,
+  ServiceUpdateParams,
+  ServiceUpdateResponse,
+  Services as ServicesAPIServices,
+  ServicesServicesPage,
+} from './services';
+import * as TasksAPI from './tasks/tasks';
+import {
+  TaskCreateParams,
+  TaskCreateResponse,
+  TaskDeleteParams,
+  TaskDeleteResponse,
+  TaskListParams,
+  TaskRetrieveParams,
+  TaskRetrieveResponse,
+  TaskStartParams,
+  TaskStartResponse,
+  TaskUpdateParams,
+  TaskUpdateResponse,
+  Tasks as TasksAPITasks,
+} from './tasks/tasks';
+import { APIPromise } from '../../../core/api-promise';
+import { RequestOptions } from '../../../internal/request-options';
+
+export class Automations extends APIResource {
+  services: ServicesAPI.Services = new ServicesAPI.Services(this._client);
+  tasks: TasksAPI.Tasks = new TasksAPI.Tasks(this._client);
+
+  /**
+   * Upserts the automations file for the given environment.
+   *
+   * Use this method to:
+   *
+   * - Configure environment automations
+   * - Update automation settings
+   * - Manage automation files
+   *
+   * ### Examples
+   *
+   * - Update automations file:
+   *
+   *   Updates or creates the automations configuration.
+   *
+   *   ```yaml
+   *   environmentId: "07e03a28-65a5-4d98-b532-8ea67b188048"
+   *   automationsFile:
+   *     services:
+   *       web-server:
+   *         name: "Web Server"
+   *         description: "Development web server"
+   *         commands:
+   *           start: "npm run dev"
+   *           ready: "curl -s http://localhost:3000"
+   *         triggeredBy:
+   *           - postDevcontainerStart
+   *     tasks:
+   *       build:
+   *         name: "Build Project"
+   *         description: "Builds the project artifacts"
+   *         command: "npm run build"
+   *         triggeredBy:
+   *           - postEnvironmentStart
+   *   ```
+   */
+  upsert(body: AutomationUpsertParams, options?: RequestOptions): APIPromise<AutomationUpsertResponse> {
+    return this._client.post('/gitpod.v1.EnvironmentAutomationService/UpsertAutomationsFile', {
+      body,
+      ...options,
+    });
+  }
+}
+
+/**
+ * WARN: Do not remove any field here, as it will break reading automation yaml
+ * files. We error if there are any unknown fields in the yaml (to ensure the yaml
+ * is correct), but would break if we removed any fields. This includes marking a
+ * field as "reserved" in the proto file, this will also break reading the yaml.
+ */
+export interface AutomationsFile {
+  services?: Record<string, AutomationsFile.Services>;
+
+  tasks?: Record<string, AutomationsFile.Tasks>;
+}
+
+export namespace AutomationsFile {
+  export interface Services {
+    commands?: Services.Commands;
+
+    description?: string;
+
+    name?: string;
+
+    runsOn?: Shared.RunsOn;
+
+    triggeredBy?: Array<'manual' | 'postEnvironmentStart' | 'postDevcontainerStart'>;
+  }
+
+  export namespace Services {
+    export interface Commands {
+      /**
+       * ready is an optional command that is run repeatedly until it exits with a zero
+       * exit code. If set, the service will first go into a Starting phase, and then
+       * into a Running phase once the ready command exits with a zero exit code.
+       */
+      ready?: string;
+
+      /**
+       * start is the command to start and run the service. If start exits, the service
+       * will transition to the following phase:
+       *
+       * - Stopped: if the exit code is 0
+       * - Failed: if the exit code is not 0 If the stop command is not set, the start
+       *   command will receive a SIGTERM signal when the service is requested to stop.
+       *   If it does not exit within 2 minutes, it will receive a SIGKILL signal.
+       */
+      start?: string;
+
+      /**
+       * stop is an optional command that runs when the service is requested to stop. If
+       * set, instead of sending a SIGTERM signal to the start command, the stop command
+       * will be run. Once the stop command exits, the start command will receive a
+       * SIGKILL signal. If the stop command exits with a non-zero exit code, the service
+       * will transition to the Failed phase. If the stop command does not exit within 2
+       * minutes, a SIGKILL signal will be sent to both the start and stop commands.
+       */
+      stop?: string;
+    }
+  }
+
+  export interface Tasks {
+    command?: string;
+
+    dependsOn?: Array<string>;
+
+    description?: string;
+
+    name?: string;
+
+    runsOn?: Shared.RunsOn;
+
+    triggeredBy?: Array<'manual' | 'postEnvironmentStart' | 'postDevcontainerStart'>;
+  }
+}
+
+export interface AutomationUpsertResponse {
+  updatedServiceIds?: Array<string>;
+
+  updatedTaskIds?: Array<string>;
+}
+
+export interface AutomationUpsertParams {
+  /**
+   * WARN: Do not remove any field here, as it will break reading automation yaml
+   * files. We error if there are any unknown fields in the yaml (to ensure the yaml
+   * is correct), but would break if we removed any fields. This includes marking a
+   * field as "reserved" in the proto file, this will also break reading the yaml.
+   */
+  automationsFile?: AutomationsFile;
+
+  environmentId?: string;
+}
+
+Automations.Services = ServicesAPIServices;
+Automations.Tasks = TasksAPITasks;
+
+export declare namespace Automations {
+  export {
+    type AutomationsFile as AutomationsFile,
+    type AutomationUpsertResponse as AutomationUpsertResponse,
+    type AutomationUpsertParams as AutomationUpsertParams,
+  };
+
+  export {
+    ServicesAPIServices as Services,
+    type Service as Service,
+    type ServiceMetadata as ServiceMetadata,
+    type ServicePhase as ServicePhase,
+    type ServiceSpec as ServiceSpec,
+    type ServiceStatus as ServiceStatus,
+    type ServiceCreateResponse as ServiceCreateResponse,
+    type ServiceRetrieveResponse as ServiceRetrieveResponse,
+    type ServiceUpdateResponse as ServiceUpdateResponse,
+    type ServiceDeleteResponse as ServiceDeleteResponse,
+    type ServiceStartResponse as ServiceStartResponse,
+    type ServiceStopResponse as ServiceStopResponse,
+    type ServicesServicesPage as ServicesServicesPage,
+    type ServiceCreateParams as ServiceCreateParams,
+    type ServiceRetrieveParams as ServiceRetrieveParams,
+    type ServiceUpdateParams as ServiceUpdateParams,
+    type ServiceListParams as ServiceListParams,
+    type ServiceDeleteParams as ServiceDeleteParams,
+    type ServiceStartParams as ServiceStartParams,
+    type ServiceStopParams as ServiceStopParams,
+  };
+
+  export {
+    TasksAPITasks as Tasks,
+    type TaskCreateResponse as TaskCreateResponse,
+    type TaskRetrieveResponse as TaskRetrieveResponse,
+    type TaskUpdateResponse as TaskUpdateResponse,
+    type TaskDeleteResponse as TaskDeleteResponse,
+    type TaskStartResponse as TaskStartResponse,
+    type TaskCreateParams as TaskCreateParams,
+    type TaskRetrieveParams as TaskRetrieveParams,
+    type TaskUpdateParams as TaskUpdateParams,
+    type TaskListParams as TaskListParams,
+    type TaskDeleteParams as TaskDeleteParams,
+    type TaskStartParams as TaskStartParams,
+  };
+}
diff --git a/src/resources/environments/automations/index.ts b/src/resources/environments/automations/index.ts
new file mode 100644
index 0000000..052267d
--- /dev/null
+++ b/src/resources/environments/automations/index.ts
@@ -0,0 +1,44 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+export {
+  Automations,
+  type AutomationsFile,
+  type AutomationUpsertResponse,
+  type AutomationUpsertParams,
+} from './automations';
+export {
+  Services,
+  type Service,
+  type ServiceMetadata,
+  type ServicePhase,
+  type ServiceSpec,
+  type ServiceStatus,
+  type ServiceCreateResponse,
+  type ServiceRetrieveResponse,
+  type ServiceUpdateResponse,
+  type ServiceDeleteResponse,
+  type ServiceStartResponse,
+  type ServiceStopResponse,
+  type ServiceCreateParams,
+  type ServiceRetrieveParams,
+  type ServiceUpdateParams,
+  type ServiceListParams,
+  type ServiceDeleteParams,
+  type ServiceStartParams,
+  type ServiceStopParams,
+  type ServicesServicesPage,
+} from './services';
+export {
+  Tasks,
+  type TaskCreateResponse,
+  type TaskRetrieveResponse,
+  type TaskUpdateResponse,
+  type TaskDeleteResponse,
+  type TaskStartResponse,
+  type TaskCreateParams,
+  type TaskRetrieveParams,
+  type TaskUpdateParams,
+  type TaskListParams,
+  type TaskDeleteParams,
+  type TaskStartParams,
+} from './tasks/index';
diff --git a/src/resources/environments/automations/services.ts b/src/resources/environments/automations/services.ts
new file mode 100644
index 0000000..efd2c80
--- /dev/null
+++ b/src/resources/environments/automations/services.ts
@@ -0,0 +1,614 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../../../core/resource';
+import * as ServicesAPI from './services';
+import * as Shared from '../../shared';
+import { APIPromise } from '../../../core/api-promise';
+import { PagePromise, ServicesPage, type ServicesPageParams } from '../../../core/pagination';
+import { RequestOptions } from '../../../internal/request-options';
+
+export class Services extends APIResource {
+  /**
+   * Creates a new automation service for an environment.
+   *
+   * Use this method to:
+   *
+   * - Set up long-running services
+   * - Configure service triggers
+   * - Define service dependencies
+   * - Specify runtime environments
+   *
+   * ### Examples
+   *
+   * - Create basic service:
+   *
+   *   Creates a simple service with start command.
+   *
+   *   ```yaml
+   *   environmentId: "07e03a28-65a5-4d98-b532-8ea67b188048"
+   *   metadata:
+   *     reference: "web-server"
+   *     name: "Web Server"
+   *     description: "Runs the development web server"
+   *     triggeredBy:
+   *       - postDevcontainerStart: true
+   *   spec:
+   *     commands:
+   *       start: "npm run dev"
+   *       ready: "curl -s http://localhost:3000"
+   *   ```
+   *
+   * - Create Docker-based service:
+   *
+   *   Creates a service running in a specific container.
+   *
+   *   ```yaml
+   *   environmentId: "07e03a28-65a5-4d98-b532-8ea67b188048"
+   *   metadata:
+   *     reference: "redis"
+   *     name: "Redis Server"
+   *     description: "Redis cache service"
+   *   spec:
+   *     commands:
+   *       start: "redis-server"
+   *     runsOn:
+   *       docker:
+   *         image: "redis:7"
+   *   ```
+   */
+  create(body: ServiceCreateParams, options?: RequestOptions): APIPromise<ServiceCreateResponse> {
+    return this._client.post('/gitpod.v1.EnvironmentAutomationService/CreateService', { body, ...options });
+  }
+
+  /**
+   * Gets details about a specific automation service.
+   *
+   * Use this method to:
+   *
+   * - Check service status
+   * - View service configuration
+   * - Monitor service health
+   * - Retrieve service metadata
+   *
+   * ### Examples
+   *
+   * - Get service details:
+   *
+   *   Retrieves information about a specific service.
+   *
+   *   ```yaml
+   *   id: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   ```
+   */
+  retrieve(body: ServiceRetrieveParams, options?: RequestOptions): APIPromise<ServiceRetrieveResponse> {
+    return this._client.post('/gitpod.v1.EnvironmentAutomationService/GetService', { body, ...options });
+  }
+
+  /**
+   * Updates an automation service configuration.
+   *
+   * Use this method to:
+   *
+   * - Modify service commands
+   * - Update triggers
+   * - Change runtime settings
+   * - Adjust dependencies
+   *
+   * ### Examples
+   *
+   * - Update commands:
+   *
+   *   Changes service start and ready commands.
+   *
+   *   ```yaml
+   *   id: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   spec:
+   *     commands:
+   *       start: "npm run start:dev"
+   *       ready: "curl -s http://localhost:8080"
+   *   ```
+   *
+   * - Update triggers:
+   *
+   *   Modifies when the service starts.
+   *
+   *   ```yaml
+   *   id: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   metadata:
+   *     triggeredBy:
+   *       trigger:
+   *         - postDevcontainerStart: true
+   *         - manual: true
+   *   ```
+   */
+  update(body: ServiceUpdateParams, options?: RequestOptions): APIPromise<unknown> {
+    return this._client.post('/gitpod.v1.EnvironmentAutomationService/UpdateService', { body, ...options });
+  }
+
+  /**
+   * Lists automation services with optional filtering.
+   *
+   * Use this method to:
+   *
+   * - View all services in an environment
+   * - Filter services by reference
+   * - Monitor service status
+   *
+   * ### Examples
+   *
+   * - List environment services:
+   *
+   *   Shows all services for an environment.
+   *
+   *   ```yaml
+   *   filter:
+   *     environmentIds: ["07e03a28-65a5-4d98-b532-8ea67b188048"]
+   *   pagination:
+   *     pageSize: 20
+   *   ```
+   *
+   * - Filter by reference:
+   *
+   *   Lists services matching specific references.
+   *
+   *   ```yaml
+   *   filter:
+   *     references: ["web-server", "database"]
+   *   pagination:
+   *     pageSize: 20
+   *   ```
+   */
+  list(params: ServiceListParams, options?: RequestOptions): PagePromise<ServicesServicesPage, Service> {
+    const { token, pageSize, ...body } = params;
+    return this._client.getAPIList(
+      '/gitpod.v1.EnvironmentAutomationService/ListServices',
+      ServicesPage<Service>,
+      { query: { token, pageSize }, body, method: 'post', ...options },
+    );
+  }
+
+  /**
+   * Deletes an automation service. This call does not block until the service is
+   * deleted. If the service is not stopped it will be stopped before deletion.
+   *
+   * Use this method to:
+   *
+   * - Remove unused services
+   * - Clean up service configurations
+   * - Stop and delete services
+   *
+   * ### Examples
+   *
+   * - Delete service:
+   *
+   *   Removes a service after stopping it.
+   *
+   *   ```yaml
+   *   id: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   force: false
+   *   ```
+   *
+   * - Force delete:
+   *
+   *   Immediately removes a service.
+   *
+   *   ```yaml
+   *   id: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   force: true
+   *   ```
+   */
+  delete(body: ServiceDeleteParams, options?: RequestOptions): APIPromise<unknown> {
+    return this._client.post('/gitpod.v1.EnvironmentAutomationService/DeleteService', { body, ...options });
+  }
+
+  /**
+   * Starts an automation service. This call does not block until the service is
+   * started. This call will not error if the service is already running or has been
+   * started.
+   *
+   * Use this method to:
+   *
+   * - Start stopped services
+   * - Resume service operations
+   * - Trigger service initialization
+   *
+   * ### Examples
+   *
+   * - Start service:
+   *
+   *   Starts a previously stopped service.
+   *
+   *   ```yaml
+   *   id: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   ```
+   */
+  start(body: ServiceStartParams, options?: RequestOptions): APIPromise<unknown> {
+    return this._client.post('/gitpod.v1.EnvironmentAutomationService/StartService', { body, ...options });
+  }
+
+  /**
+   * Stops an automation service. This call does not block until the service is
+   * stopped. This call will not error if the service is already stopped or has been
+   * stopped.
+   *
+   * Use this method to:
+   *
+   * - Pause service operations
+   * - Gracefully stop services
+   * - Prepare for updates
+   *
+   * ### Examples
+   *
+   * - Stop service:
+   *
+   *   Gracefully stops a running service.
+   *
+   *   ```yaml
+   *   id: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   ```
+   */
+  stop(body: ServiceStopParams, options?: RequestOptions): APIPromise<unknown> {
+    return this._client.post('/gitpod.v1.EnvironmentAutomationService/StopService', { body, ...options });
+  }
+}
+
+export type ServicesServicesPage = ServicesPage<Service>;
+
+export interface Service {
+  id: string;
+
+  environmentId?: string;
+
+  metadata?: ServiceMetadata;
+
+  spec?: ServiceSpec;
+
+  status?: ServiceStatus;
+}
+
+export interface ServiceMetadata {
+  /**
+   * created_at is the time the service was created.
+   */
+  createdAt?: string;
+
+  /**
+   * creator describes the principal who created the service.
+   */
+  creator?: Shared.Subject;
+
+  /**
+   * description is a user-facing description for the service. It can be used to
+   * provide context and documentation for the service.
+   */
+  description?: string;
+
+  /**
+   * name is a user-facing name for the service. Unlike the reference, this field is
+   * not unique, and not referenced by the system. This is a short descriptive name
+   * for the service.
+   */
+  name?: string;
+
+  /**
+   * reference is a user-facing identifier for the service which must be unique on
+   * the environment. It is used to express dependencies between services, and to
+   * identify the service in user interactions (e.g. the CLI).
+   */
+  reference?: string;
+
+  /**
+   * triggered_by is a list of trigger that start the service.
+   */
+  triggeredBy?: Array<Shared.AutomationTrigger>;
+}
+
+export type ServicePhase =
+  | 'SERVICE_PHASE_UNSPECIFIED'
+  | 'SERVICE_PHASE_STARTING'
+  | 'SERVICE_PHASE_RUNNING'
+  | 'SERVICE_PHASE_STOPPING'
+  | 'SERVICE_PHASE_STOPPED'
+  | 'SERVICE_PHASE_FAILED'
+  | 'SERVICE_PHASE_DELETED';
+
+export interface ServiceSpec {
+  /**
+   * commands contains the commands to start, stop and check the readiness of the
+   * service
+   */
+  commands?: ServiceSpec.Commands;
+
+  /**
+   * desired_phase is the phase the service should be in. Used to start or stop the
+   * service.
+   */
+  desiredPhase?: ServicePhase;
+
+  /**
+   * runs_on specifies the environment the service should run on.
+   */
+  runsOn?: Shared.RunsOn;
+
+  /**
+   * session should be changed to trigger a restart of the service. If a service
+   * exits it will not be restarted until the session is changed.
+   */
+  session?: string;
+
+  /**
+   * version of the spec. The value of this field has no semantic meaning (e.g. don't
+   * interpret it as as a timestamp), but it can be used to impose a partial order.
+   * If a.spec_version < b.spec_version then a was the spec before b.
+   */
+  specVersion?: string;
+}
+
+export namespace ServiceSpec {
+  /**
+   * commands contains the commands to start, stop and check the readiness of the
+   * service
+   */
+  export interface Commands {
+    /**
+     * ready is an optional command that is run repeatedly until it exits with a zero
+     * exit code. If set, the service will first go into a Starting phase, and then
+     * into a Running phase once the ready command exits with a zero exit code.
+     */
+    ready?: string;
+
+    /**
+     * start is the command to start and run the service. If start exits, the service
+     * will transition to the following phase:
+     *
+     * - Stopped: if the exit code is 0
+     * - Failed: if the exit code is not 0 If the stop command is not set, the start
+     *   command will receive a SIGTERM signal when the service is requested to stop.
+     *   If it does not exit within 2 minutes, it will receive a SIGKILL signal.
+     */
+    start?: string;
+
+    /**
+     * stop is an optional command that runs when the service is requested to stop. If
+     * set, instead of sending a SIGTERM signal to the start command, the stop command
+     * will be run. Once the stop command exits, the start command will receive a
+     * SIGKILL signal. If the stop command exits with a non-zero exit code, the service
+     * will transition to the Failed phase. If the stop command does not exit within 2
+     * minutes, a SIGKILL signal will be sent to both the start and stop commands.
+     */
+    stop?: string;
+  }
+}
+
+export interface ServiceStatus {
+  /**
+   * failure_message summarises why the service failed to operate. If this is
+   * non-empty the service has failed to operate and will likely transition to a
+   * failed state.
+   */
+  failureMessage?: string;
+
+  /**
+   * log_url contains the URL at which the service logs can be accessed.
+   */
+  logUrl?: string;
+
+  /**
+   * output contains the output of the service. setting an output field to empty
+   * string will unset it.
+   */
+  output?: Record<string, string>;
+
+  /**
+   * phase is the current phase of the service.
+   */
+  phase?: ServicePhase;
+
+  /**
+   * session is the current session of the service.
+   */
+  session?: string;
+
+  /**
+   * version of the status update. Service instances themselves are unversioned, but
+   * their status has different versions. The value of this field has no semantic
+   * meaning (e.g. don't interpret it as as a timestamp), but it can be used to
+   * impose a partial order. If a.status_version < b.status_version then a was the
+   * status before b.
+   */
+  statusVersion?: string;
+}
+
+export interface ServiceCreateResponse {
+  service: Service;
+}
+
+export interface ServiceRetrieveResponse {
+  service: Service;
+}
+
+export type ServiceUpdateResponse = unknown;
+
+export type ServiceDeleteResponse = unknown;
+
+export type ServiceStartResponse = unknown;
+
+export type ServiceStopResponse = unknown;
+
+export interface ServiceCreateParams {
+  environmentId?: string;
+
+  metadata?: ServiceMetadata;
+
+  spec?: ServiceSpec;
+}
+
+export interface ServiceRetrieveParams {
+  id?: string;
+}
+
+export interface ServiceUpdateParams {
+  id?: string;
+
+  metadata?: ServiceUpdateParams.Metadata;
+
+  /**
+   * Changing the spec of a service is a complex operation. The spec of a service can
+   * only be updated if the service is in a stopped state. If the service is running,
+   * it must be stopped first.
+   */
+  spec?: ServiceUpdateParams.Spec;
+
+  /**
+   * Service status updates are only expected from the executing environment. As a
+   * client of this API you are not expected to provide this field. Updating this
+   * field requires the `environmentservice:update_status` permission.
+   */
+  status?: ServiceUpdateParams.Status;
+}
+
+export namespace ServiceUpdateParams {
+  export interface Metadata {
+    description?: string | null;
+
+    name?: string | null;
+
+    triggeredBy?: Metadata.TriggeredBy | null;
+  }
+
+  export namespace Metadata {
+    export interface TriggeredBy {
+      trigger?: Array<Shared.AutomationTrigger>;
+    }
+  }
+
+  /**
+   * Changing the spec of a service is a complex operation. The spec of a service can
+   * only be updated if the service is in a stopped state. If the service is running,
+   * it must be stopped first.
+   */
+  export interface Spec {
+    commands?: Spec.Commands | null;
+
+    runsOn?: Shared.RunsOn | null;
+  }
+
+  export namespace Spec {
+    export interface Commands {
+      ready?: string | null;
+
+      start?: string | null;
+
+      stop?: string | null;
+    }
+  }
+
+  /**
+   * Service status updates are only expected from the executing environment. As a
+   * client of this API you are not expected to provide this field. Updating this
+   * field requires the `environmentservice:update_status` permission.
+   */
+  export interface Status {
+    failureMessage?: string | null;
+
+    logUrl?: string | null;
+
+    /**
+     * setting an output field to empty string will unset it.
+     */
+    output?: Record<string, string>;
+
+    phase?: ServicesAPI.ServicePhase | null;
+
+    session?: string | null;
+  }
+}
+
+export interface ServiceListParams extends ServicesPageParams {
+  /**
+   * Body param: filter contains the filter options for listing services
+   */
+  filter?: ServiceListParams.Filter;
+
+  /**
+   * Body param: pagination contains the pagination options for listing services
+   */
+  pagination?: ServiceListParams.Pagination;
+}
+
+export namespace ServiceListParams {
+  /**
+   * filter contains the filter options for listing services
+   */
+  export interface Filter {
+    /**
+     * environment_ids filters the response to only services of these environments
+     */
+    environmentIds?: Array<string>;
+
+    /**
+     * references filters the response to only services with these references
+     */
+    references?: Array<string>;
+
+    /**
+     * service_ids filters the response to only services with these IDs
+     */
+    serviceIds?: Array<string>;
+  }
+
+  /**
+   * pagination contains the pagination options for listing services
+   */
+  export interface Pagination {
+    /**
+     * Token for the next set of results that was returned as next_token of a
+     * PaginationResponse
+     */
+    token?: string;
+
+    /**
+     * Page size is the maximum number of results to retrieve per page. Defaults to 25.
+     * Maximum 100.
+     */
+    pageSize?: number;
+  }
+}
+
+export interface ServiceDeleteParams {
+  id?: string;
+
+  force?: boolean;
+}
+
+export interface ServiceStartParams {
+  id?: string;
+}
+
+export interface ServiceStopParams {
+  id?: string;
+}
+
+export declare namespace Services {
+  export {
+    type Service as Service,
+    type ServiceMetadata as ServiceMetadata,
+    type ServicePhase as ServicePhase,
+    type ServiceSpec as ServiceSpec,
+    type ServiceStatus as ServiceStatus,
+    type ServiceCreateResponse as ServiceCreateResponse,
+    type ServiceRetrieveResponse as ServiceRetrieveResponse,
+    type ServiceUpdateResponse as ServiceUpdateResponse,
+    type ServiceDeleteResponse as ServiceDeleteResponse,
+    type ServiceStartResponse as ServiceStartResponse,
+    type ServiceStopResponse as ServiceStopResponse,
+    type ServicesServicesPage as ServicesServicesPage,
+    type ServiceCreateParams as ServiceCreateParams,
+    type ServiceRetrieveParams as ServiceRetrieveParams,
+    type ServiceUpdateParams as ServiceUpdateParams,
+    type ServiceListParams as ServiceListParams,
+    type ServiceDeleteParams as ServiceDeleteParams,
+    type ServiceStartParams as ServiceStartParams,
+    type ServiceStopParams as ServiceStopParams,
+  };
+}
diff --git a/src/resources/environments/automations/tasks.ts b/src/resources/environments/automations/tasks.ts
new file mode 100644
index 0000000..a93e814
--- /dev/null
+++ b/src/resources/environments/automations/tasks.ts
@@ -0,0 +1,3 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+export * from './tasks/index';
diff --git a/src/resources/environments/automations/tasks/executions.ts b/src/resources/environments/automations/tasks/executions.ts
new file mode 100644
index 0000000..47410d8
--- /dev/null
+++ b/src/resources/environments/automations/tasks/executions.ts
@@ -0,0 +1,190 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../../../../core/resource';
+import * as Shared from '../../../shared';
+import { TaskExecutionsTaskExecutionsPage } from '../../../shared';
+import { APIPromise } from '../../../../core/api-promise';
+import { PagePromise, TaskExecutionsPage, type TaskExecutionsPageParams } from '../../../../core/pagination';
+import { RequestOptions } from '../../../../internal/request-options';
+
+export class Executions extends APIResource {
+  /**
+   * Gets details about a specific task execution.
+   *
+   * Use this method to:
+   *
+   * - Monitor execution progress
+   * - View execution logs
+   * - Check execution status
+   * - Debug failed executions
+   *
+   * ### Examples
+   *
+   * - Get execution details:
+   *
+   *   Retrieves information about a specific task execution.
+   *
+   *   ```yaml
+   *   id: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   ```
+   */
+  retrieve(body: ExecutionRetrieveParams, options?: RequestOptions): APIPromise<ExecutionRetrieveResponse> {
+    return this._client.post('/gitpod.v1.EnvironmentAutomationService/GetTaskExecution', {
+      body,
+      ...options,
+    });
+  }
+
+  /**
+   * Lists executions of automation tasks.
+   *
+   * Use this method to:
+   *
+   * - View task execution history
+   * - Monitor running tasks
+   * - Track task completion status
+   *
+   * ### Examples
+   *
+   * - List all executions:
+   *
+   *   Shows execution history for all tasks.
+   *
+   *   ```yaml
+   *   filter:
+   *     environmentIds: ["07e03a28-65a5-4d98-b532-8ea67b188048"]
+   *   pagination:
+   *     pageSize: 20
+   *   ```
+   *
+   * - Filter by phase:
+   *
+   *   Lists executions in specific phases.
+   *
+   *   ```yaml
+   *   filter:
+   *     phases: ["TASK_EXECUTION_PHASE_RUNNING", "TASK_EXECUTION_PHASE_FAILED"]
+   *   pagination:
+   *     pageSize: 20
+   *   ```
+   */
+  list(
+    params: ExecutionListParams,
+    options?: RequestOptions,
+  ): PagePromise<TaskExecutionsTaskExecutionsPage, Shared.TaskExecution> {
+    const { token, pageSize, ...body } = params;
+    return this._client.getAPIList(
+      '/gitpod.v1.EnvironmentAutomationService/ListTaskExecutions',
+      TaskExecutionsPage<Shared.TaskExecution>,
+      { query: { token, pageSize }, body, method: 'post', ...options },
+    );
+  }
+
+  /**
+   * Stops a running task execution.
+   *
+   * Use this method to:
+   *
+   * - Cancel long-running tasks
+   * - Stop failed executions
+   * - Interrupt task processing
+   *
+   * ### Examples
+   *
+   * - Stop execution:
+   *
+   *   Stops a running task execution.
+   *
+   *   ```yaml
+   *   id: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   ```
+   */
+  stop(body: ExecutionStopParams, options?: RequestOptions): APIPromise<unknown> {
+    return this._client.post('/gitpod.v1.EnvironmentAutomationService/StopTaskExecution', {
+      body,
+      ...options,
+    });
+  }
+}
+
+export interface ExecutionRetrieveResponse {
+  taskExecution: Shared.TaskExecution;
+}
+
+export type ExecutionStopResponse = unknown;
+
+export interface ExecutionRetrieveParams {
+  id?: string;
+}
+
+export interface ExecutionListParams extends TaskExecutionsPageParams {
+  /**
+   * Body param: filter contains the filter options for listing task runs
+   */
+  filter?: ExecutionListParams.Filter;
+
+  /**
+   * Body param: pagination contains the pagination options for listing task runs
+   */
+  pagination?: ExecutionListParams.Pagination;
+}
+
+export namespace ExecutionListParams {
+  /**
+   * filter contains the filter options for listing task runs
+   */
+  export interface Filter {
+    /**
+     * environment_ids filters the response to only task runs of these environments
+     */
+    environmentIds?: Array<string>;
+
+    /**
+     * phases filters the response to only task runs in these phases
+     */
+    phases?: Array<Shared.TaskExecutionPhase>;
+
+    /**
+     * task_ids filters the response to only task runs of these tasks
+     */
+    taskIds?: Array<string>;
+
+    /**
+     * task_references filters the response to only task runs with this reference
+     */
+    taskReferences?: Array<string>;
+  }
+
+  /**
+   * pagination contains the pagination options for listing task runs
+   */
+  export interface Pagination {
+    /**
+     * Token for the next set of results that was returned as next_token of a
+     * PaginationResponse
+     */
+    token?: string;
+
+    /**
+     * Page size is the maximum number of results to retrieve per page. Defaults to 25.
+     * Maximum 100.
+     */
+    pageSize?: number;
+  }
+}
+
+export interface ExecutionStopParams {
+  id?: string;
+}
+
+export declare namespace Executions {
+  export {
+    type ExecutionRetrieveResponse as ExecutionRetrieveResponse,
+    type ExecutionStopResponse as ExecutionStopResponse,
+    type ExecutionRetrieveParams as ExecutionRetrieveParams,
+    type ExecutionListParams as ExecutionListParams,
+    type ExecutionStopParams as ExecutionStopParams,
+  };
+}
+
+export { type TaskExecutionsTaskExecutionsPage };
diff --git a/src/resources/environments/automations/tasks/index.ts b/src/resources/environments/automations/tasks/index.ts
new file mode 100644
index 0000000..1c21f28
--- /dev/null
+++ b/src/resources/environments/automations/tasks/index.ts
@@ -0,0 +1,24 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+export {
+  Executions,
+  type ExecutionRetrieveResponse,
+  type ExecutionStopResponse,
+  type ExecutionRetrieveParams,
+  type ExecutionListParams,
+  type ExecutionStopParams,
+} from './executions';
+export {
+  Tasks,
+  type TaskCreateResponse,
+  type TaskRetrieveResponse,
+  type TaskUpdateResponse,
+  type TaskDeleteResponse,
+  type TaskStartResponse,
+  type TaskCreateParams,
+  type TaskRetrieveParams,
+  type TaskUpdateParams,
+  type TaskListParams,
+  type TaskDeleteParams,
+  type TaskStartParams,
+} from './tasks';
diff --git a/src/resources/environments/automations/tasks/tasks.ts b/src/resources/environments/automations/tasks/tasks.ts
new file mode 100644
index 0000000..6a85c1f
--- /dev/null
+++ b/src/resources/environments/automations/tasks/tasks.ts
@@ -0,0 +1,371 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../../../../core/resource';
+import * as Shared from '../../../shared';
+import { TasksTasksPage } from '../../../shared';
+import * as ExecutionsAPI from './executions';
+import {
+  ExecutionListParams,
+  ExecutionRetrieveParams,
+  ExecutionRetrieveResponse,
+  ExecutionStopParams,
+  ExecutionStopResponse,
+  Executions,
+} from './executions';
+import { APIPromise } from '../../../../core/api-promise';
+import { PagePromise, TasksPage, type TasksPageParams } from '../../../../core/pagination';
+import { RequestOptions } from '../../../../internal/request-options';
+
+export class Tasks extends APIResource {
+  executions: ExecutionsAPI.Executions = new ExecutionsAPI.Executions(this._client);
+
+  /**
+   * Creates a new automation task.
+   *
+   * Use this method to:
+   *
+   * - Define one-off or scheduled tasks
+   * - Set up build or test automation
+   * - Configure task dependencies
+   * - Specify execution environments
+   *
+   * ### Examples
+   *
+   * - Create basic task:
+   *
+   *   Creates a simple build task.
+   *
+   *   ```yaml
+   *   environmentId: "07e03a28-65a5-4d98-b532-8ea67b188048"
+   *   metadata:
+   *     reference: "build"
+   *     name: "Build Project"
+   *     description: "Builds the project artifacts"
+   *     triggeredBy:
+   *       - postEnvironmentStart: true
+   *   spec:
+   *     command: "npm run build"
+   *   ```
+   *
+   * - Create task with dependencies:
+   *
+   *   Creates a task that depends on other services.
+   *
+   *   ```yaml
+   *   environmentId: "07e03a28-65a5-4d98-b532-8ea67b188048"
+   *   metadata:
+   *     reference: "test"
+   *     name: "Run Tests"
+   *     description: "Runs the test suite"
+   *   spec:
+   *     command: "npm test"
+   *   dependsOn: ["d2c94c27-3b76-4a42-b88c-95a85e392c68"]
+   *   ```
+   */
+  create(body: TaskCreateParams, options?: RequestOptions): APIPromise<TaskCreateResponse> {
+    return this._client.post('/gitpod.v1.EnvironmentAutomationService/CreateTask', { body, ...options });
+  }
+
+  /**
+   * Gets details about a specific automation task.
+   *
+   * Use this method to:
+   *
+   * - Check task configuration
+   * - View task dependencies
+   * - Monitor task status
+   *
+   * ### Examples
+   *
+   * - Get task details:
+   *
+   *   Retrieves information about a specific task.
+   *
+   *   ```yaml
+   *   id: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   ```
+   */
+  retrieve(body: TaskRetrieveParams, options?: RequestOptions): APIPromise<TaskRetrieveResponse> {
+    return this._client.post('/gitpod.v1.EnvironmentAutomationService/GetTask', { body, ...options });
+  }
+
+  /**
+   * Updates an automation task configuration.
+   *
+   * Use this method to:
+   *
+   * - Modify task commands
+   * - Update task triggers
+   * - Change dependencies
+   * - Adjust execution settings
+   *
+   * ### Examples
+   *
+   * - Update command:
+   *
+   *   Changes the task's command.
+   *
+   *   ```yaml
+   *   id: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   spec:
+   *     command: "npm run test:coverage"
+   *   ```
+   *
+   * - Update triggers:
+   *
+   *   Modifies when the task runs.
+   *
+   *   ```yaml
+   *   id: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   metadata:
+   *     triggeredBy:
+   *       trigger:
+   *         - postEnvironmentStart: true
+   *   ```
+   */
+  update(body: TaskUpdateParams, options?: RequestOptions): APIPromise<unknown> {
+    return this._client.post('/gitpod.v1.EnvironmentAutomationService/UpdateTask', { body, ...options });
+  }
+
+  /**
+   * Lists automation tasks with optional filtering.
+   *
+   * Use this method to:
+   *
+   * - View all tasks in an environment
+   * - Filter tasks by reference
+   * - Monitor task status
+   *
+   * ### Examples
+   *
+   * - List environment tasks:
+   *
+   *   Shows all tasks for an environment.
+   *
+   *   ```yaml
+   *   filter:
+   *     environmentIds: ["07e03a28-65a5-4d98-b532-8ea67b188048"]
+   *   pagination:
+   *     pageSize: 20
+   *   ```
+   *
+   * - Filter by reference:
+   *
+   *   Lists tasks matching specific references.
+   *
+   *   ```yaml
+   *   filter:
+   *     references: ["build", "test"]
+   *   pagination:
+   *     pageSize: 20
+   *   ```
+   */
+  list(params: TaskListParams, options?: RequestOptions): PagePromise<TasksTasksPage, Shared.Task> {
+    const { token, pageSize, ...body } = params;
+    return this._client.getAPIList(
+      '/gitpod.v1.EnvironmentAutomationService/ListTasks',
+      TasksPage<Shared.Task>,
+      { query: { token, pageSize }, body, method: 'post', ...options },
+    );
+  }
+
+  /**
+   * Deletes an automation task.
+   *
+   * Use this method to:
+   *
+   * - Remove unused tasks
+   * - Clean up task configurations
+   * - Delete obsolete automations
+   *
+   * ### Examples
+   *
+   * - Delete task:
+   *
+   *   Removes a task and its configuration.
+   *
+   *   ```yaml
+   *   id: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   ```
+   */
+  delete(body: TaskDeleteParams, options?: RequestOptions): APIPromise<unknown> {
+    return this._client.post('/gitpod.v1.EnvironmentAutomationService/DeleteTask', { body, ...options });
+  }
+
+  /**
+   * Starts a task by creating a new task execution. This call does not block until
+   * the task is started; the task will be started asynchronously.
+   *
+   * Use this method to:
+   *
+   * - Trigger task execution
+   * - Run one-off tasks
+   * - Start scheduled tasks immediately
+   *
+   * ### Examples
+   *
+   * - Start task:
+   *
+   *   Creates a new execution of a task.
+   *
+   *   ```yaml
+   *   id: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   ```
+   */
+  start(body: TaskStartParams, options?: RequestOptions): APIPromise<TaskStartResponse> {
+    return this._client.post('/gitpod.v1.EnvironmentAutomationService/StartTask', { body, ...options });
+  }
+}
+
+export interface TaskCreateResponse {
+  task: Shared.Task;
+}
+
+export interface TaskRetrieveResponse {
+  task: Shared.Task;
+}
+
+export type TaskUpdateResponse = unknown;
+
+export type TaskDeleteResponse = unknown;
+
+export interface TaskStartResponse {
+  taskExecution: Shared.TaskExecution;
+}
+
+export interface TaskCreateParams {
+  dependsOn?: Array<string>;
+
+  environmentId?: string;
+
+  metadata?: Shared.TaskMetadata;
+
+  spec?: Shared.TaskSpec;
+}
+
+export interface TaskRetrieveParams {
+  id?: string;
+}
+
+export interface TaskUpdateParams {
+  id?: string;
+
+  /**
+   * dependencies specifies the IDs of the automations this task depends on.
+   */
+  dependsOn?: Array<string>;
+
+  metadata?: TaskUpdateParams.Metadata;
+
+  spec?: TaskUpdateParams.Spec;
+}
+
+export namespace TaskUpdateParams {
+  export interface Metadata {
+    description?: string | null;
+
+    name?: string | null;
+
+    triggeredBy?: Metadata.TriggeredBy | null;
+  }
+
+  export namespace Metadata {
+    export interface TriggeredBy {
+      trigger?: Array<Shared.AutomationTrigger>;
+    }
+  }
+
+  export interface Spec {
+    command?: string | null;
+
+    runsOn?: Shared.RunsOn | null;
+  }
+}
+
+export interface TaskListParams extends TasksPageParams {
+  /**
+   * Body param: filter contains the filter options for listing tasks
+   */
+  filter?: TaskListParams.Filter;
+
+  /**
+   * Body param: pagination contains the pagination options for listing tasks
+   */
+  pagination?: TaskListParams.Pagination;
+}
+
+export namespace TaskListParams {
+  /**
+   * filter contains the filter options for listing tasks
+   */
+  export interface Filter {
+    /**
+     * environment_ids filters the response to only tasks of these environments
+     */
+    environmentIds?: Array<string>;
+
+    /**
+     * references filters the response to only services with these references
+     */
+    references?: Array<string>;
+
+    /**
+     * task_ids filters the response to only tasks with these IDs
+     */
+    taskIds?: Array<string>;
+  }
+
+  /**
+   * pagination contains the pagination options for listing tasks
+   */
+  export interface Pagination {
+    /**
+     * Token for the next set of results that was returned as next_token of a
+     * PaginationResponse
+     */
+    token?: string;
+
+    /**
+     * Page size is the maximum number of results to retrieve per page. Defaults to 25.
+     * Maximum 100.
+     */
+    pageSize?: number;
+  }
+}
+
+export interface TaskDeleteParams {
+  id?: string;
+}
+
+export interface TaskStartParams {
+  id?: string;
+}
+
+Tasks.Executions = Executions;
+
+export declare namespace Tasks {
+  export {
+    type TaskCreateResponse as TaskCreateResponse,
+    type TaskRetrieveResponse as TaskRetrieveResponse,
+    type TaskUpdateResponse as TaskUpdateResponse,
+    type TaskDeleteResponse as TaskDeleteResponse,
+    type TaskStartResponse as TaskStartResponse,
+    type TaskCreateParams as TaskCreateParams,
+    type TaskRetrieveParams as TaskRetrieveParams,
+    type TaskUpdateParams as TaskUpdateParams,
+    type TaskListParams as TaskListParams,
+    type TaskDeleteParams as TaskDeleteParams,
+    type TaskStartParams as TaskStartParams,
+  };
+
+  export {
+    Executions as Executions,
+    type ExecutionRetrieveResponse as ExecutionRetrieveResponse,
+    type ExecutionStopResponse as ExecutionStopResponse,
+    type ExecutionRetrieveParams as ExecutionRetrieveParams,
+    type ExecutionListParams as ExecutionListParams,
+    type ExecutionStopParams as ExecutionStopParams,
+  };
+}
+
+export { type TasksTasksPage };
diff --git a/src/resources/environments/classes.ts b/src/resources/environments/classes.ts
new file mode 100644
index 0000000..709e8bc
--- /dev/null
+++ b/src/resources/environments/classes.ts
@@ -0,0 +1,117 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../../core/resource';
+import * as Shared from '../shared';
+import { EnvironmentClassesEnvironmentClassesPage } from '../shared';
+import * as RunnersAPI from '../runners/runners';
+import {
+  EnvironmentClassesPage,
+  type EnvironmentClassesPageParams,
+  PagePromise,
+} from '../../core/pagination';
+import { RequestOptions } from '../../internal/request-options';
+
+export class Classes extends APIResource {
+  /**
+   * Lists available environment classes with their specifications and resource
+   * limits.
+   *
+   * Use this method to understand what types of environments you can create and
+   * their capabilities. Environment classes define the compute resources and
+   * features available to your environments.
+   *
+   * ### Examples
+   *
+   * - List all available classes:
+   *
+   *   Retrieves a list of all environment classes with their specifications.
+   *
+   *   ```yaml
+   *   {}
+   *   ```
+   *
+   *   buf:lint:ignore RPC_REQUEST_RESPONSE_UNIQUE
+   */
+  list(
+    params: ClassListParams,
+    options?: RequestOptions,
+  ): PagePromise<EnvironmentClassesEnvironmentClassesPage, Shared.EnvironmentClass> {
+    const { token, pageSize, ...body } = params;
+    return this._client.getAPIList(
+      '/gitpod.v1.EnvironmentService/ListEnvironmentClasses',
+      EnvironmentClassesPage<Shared.EnvironmentClass>,
+      { query: { token, pageSize }, body, method: 'post', ...options },
+    );
+  }
+}
+
+export interface ClassListParams extends EnvironmentClassesPageParams {
+  /**
+   * Body param:
+   */
+  filter?: ClassListParams.Filter;
+
+  /**
+   * Body param: pagination contains the pagination options for listing environment
+   * classes
+   */
+  pagination?: ClassListParams.Pagination;
+}
+
+export namespace ClassListParams {
+  export interface Filter {
+    /**
+     * can_create_environments filters the response to only environment classes that
+     * can be used to create new environments by the caller. Unlike enabled, which
+     * indicates general availability, this ensures the caller only sees environment
+     * classes they are allowed to use.
+     */
+    canCreateEnvironments?: boolean | null;
+
+    /**
+     * enabled filters the response to only enabled or disabled environment classes. If
+     * not set, all environment classes are returned.
+     */
+    enabled?: boolean | null;
+
+    /**
+     * runner_ids filters the response to only EnvironmentClasses of these Runner IDs
+     */
+    runnerIds?: Array<string>;
+
+    /**
+     * runner_kind filters the response to only environment classes from runners of
+     * these kinds.
+     */
+    runnerKinds?: Array<RunnersAPI.RunnerKind>;
+
+    /**
+     * runner_providers filters the response to only environment classes from runners
+     * of these providers.
+     */
+    runnerProviders?: Array<RunnersAPI.RunnerProvider>;
+  }
+
+  /**
+   * pagination contains the pagination options for listing environment classes
+   */
+  export interface Pagination {
+    /**
+     * Token for the next set of results that was returned as next_token of a
+     * PaginationResponse
+     */
+    token?: string;
+
+    /**
+     * Page size is the maximum number of results to retrieve per page. Defaults to 25.
+     * Maximum 100.
+     */
+    pageSize?: number;
+  }
+}
+
+export declare namespace Classes {
+  export { type ClassListParams as ClassListParams };
+}
+
+export { type EnvironmentClassesEnvironmentClassesPage };
diff --git a/src/resources/environments/environments.ts b/src/resources/environments/environments.ts
new file mode 100644
index 0000000..695ee9e
--- /dev/null
+++ b/src/resources/environments/environments.ts
@@ -0,0 +1,1641 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../../core/resource';
+import * as EnvironmentsAPI from './environments';
+import * as Shared from '../shared';
+import * as ClassesAPI from './classes';
+import { ClassListParams, Classes } from './classes';
+import * as ProjectsAPI from '../projects/projects';
+import * as RunnersAPI from '../runners/runners';
+import * as AutomationsAPI from './automations/automations';
+import {
+  AutomationUpsertParams,
+  AutomationUpsertResponse,
+  Automations,
+  AutomationsFile as AutomationsAPIAutomationsFile,
+} from './automations/automations';
+import { APIPromise } from '../../core/api-promise';
+import { EnvironmentsPage, type EnvironmentsPageParams, PagePromise } from '../../core/pagination';
+import { RequestOptions } from '../../internal/request-options';
+
+export class Environments extends APIResource {
+  automations: AutomationsAPI.Automations = new AutomationsAPI.Automations(this._client);
+  classes: ClassesAPI.Classes = new ClassesAPI.Classes(this._client);
+
+  /**
+   * Creates a development environment from a context URL (e.g. Git repository) and
+   * starts it.
+   *
+   * The `class` field must be a valid environment class ID. You can find a list of
+   * available environment classes with the `ListEnvironmentClasses` method.
+   *
+   * ### Examples
+   *
+   * - Create from context URL:
+   *
+   *   Creates an environment from a Git repository URL with default settings.
+   *
+   *   ```yaml
+   *   spec:
+   *     machine:
+   *       class: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *     content:
+   *       initializer:
+   *         specs:
+   *           - contextUrl:
+   *               url: "https://github.com/gitpod-io/gitpod"
+   *   ```
+   *
+   * - Create from Git repository:
+   *
+   *   Creates an environment from a Git repository with specific branch targeting.
+   *
+   *   ```yaml
+   *   spec:
+   *     machine:
+   *       class: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *     content:
+   *       initializer:
+   *         specs:
+   *           - git:
+   *               remoteUri: "https://github.com/gitpod-io/gitpod"
+   *               cloneTarget: "main"
+   *               targetMode: "CLONE_TARGET_MODE_REMOTE_BRANCH"
+   *   ```
+   *
+   * - Create with custom timeout and ports:
+   *
+   *   Creates an environment with custom inactivity timeout and exposed port
+   *   configuration.
+   *
+   *   ```yaml
+   *   spec:
+   *     machine:
+   *       class: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *     content:
+   *       initializer:
+   *         specs:
+   *           - contextUrl:
+   *               url: "https://github.com/gitpod-io/gitpod"
+   *     timeout:
+   *       disconnected: "7200s" # 2 hours in seconds
+   *     ports:
+   *       - port: 3000
+   *         admission: "ADMISSION_LEVEL_EVERYONE"
+   *         name: "Web App"
+   *   ```
+   */
+  create(body: EnvironmentCreateParams, options?: RequestOptions): APIPromise<EnvironmentCreateResponse> {
+    return this._client.post('/gitpod.v1.EnvironmentService/CreateEnvironment', { body, ...options });
+  }
+
+  /**
+   * Gets details about a specific environment including its status, configuration,
+   * and context URL.
+   *
+   * Use this method to:
+   *
+   * - Check if an environment is ready to use
+   * - Get connection details for IDE and exposed ports
+   * - Monitor environment health and resource usage
+   * - Debug environment setup issues
+   *
+   * ### Examples
+   *
+   * - Get environment details:
+   *
+   *   Retrieves detailed information about a specific environment using its unique
+   *   identifier.
+   *
+   *   ```yaml
+   *   environmentId: "07e03a28-65a5-4d98-b532-8ea67b188048"
+   *   ```
+   */
+  retrieve(
+    body: EnvironmentRetrieveParams,
+    options?: RequestOptions,
+  ): APIPromise<EnvironmentRetrieveResponse> {
+    return this._client.post('/gitpod.v1.EnvironmentService/GetEnvironment', { body, ...options });
+  }
+
+  /**
+   * Updates an environment's configuration while it is running.
+   *
+   * Updates are limited to:
+   *
+   * - Git credentials (username, email)
+   * - SSH public keys
+   * - Content initialization
+   * - Port configurations
+   * - Automation files
+   * - Environment timeouts
+   *
+   * ### Examples
+   *
+   * - Update Git credentials:
+   *
+   *   Updates the Git configuration for the environment.
+   *
+   *   ```yaml
+   *   environmentId: "07e03a28-65a5-4d98-b532-8ea67b188048"
+   *   spec:
+   *     content:
+   *       gitUsername: "example-user"
+   *       gitEmail: "user@example.com"
+   *   ```
+   *
+   * - Add SSH public key:
+   *
+   *   Adds a new SSH public key for authentication.
+   *
+   *   ```yaml
+   *   environmentId: "07e03a28-65a5-4d98-b532-8ea67b188048"
+   *   spec:
+   *     sshPublicKeys:
+   *       - id: "0194b7c1-c954-718d-91a4-9a742aa5fc11"
+   *         value: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI..."
+   *   ```
+   *
+   * - Update content session:
+   *
+   *   Updates the content session identifier for the environment.
+   *
+   *   ```yaml
+   *   environmentId: "07e03a28-65a5-4d98-b532-8ea67b188048"
+   *   spec:
+   *     content:
+   *       session: "0194b7c1-c954-718d-91a4-9a742aa5fc11"
+   *   ```
+   *
+   * Note: Machine class changes require stopping the environment and creating a new
+   * one.
+   */
+  update(body: EnvironmentUpdateParams, options?: RequestOptions): APIPromise<unknown> {
+    return this._client.post('/gitpod.v1.EnvironmentService/UpdateEnvironment', { body, ...options });
+  }
+
+  /**
+   * Lists all environments matching the specified criteria.
+   *
+   * Use this method to find and monitor environments across your organization.
+   * Results are ordered by creation time with newest environments first.
+   *
+   * ### Examples
+   *
+   * - List running environments for a project:
+   *
+   *   Retrieves all running environments for a specific project with pagination.
+   *
+   *   ```yaml
+   *   filter:
+   *     statusPhases: ["ENVIRONMENT_PHASE_RUNNING"]
+   *     projectIds: ["b0e12f6c-4c67-429d-a4a6-d9838b5da047"]
+   *   pagination:
+   *     pageSize: 10
+   *   ```
+   *
+   * - List all environments for a specific runner:
+   *
+   *   Filters environments by runner ID and creator ID.
+   *
+   *   ```yaml
+   *   filter:
+   *     runnerIds: ["e6aa9c54-89d3-42c1-ac31-bd8d8f1concentrate"]
+   *     creatorIds: ["f53d2330-3795-4c5d-a1f3-453121af9c60"]
+   *   ```
+   *
+   * - List stopped and deleted environments:
+   *
+   *   Retrieves all environments in stopped or deleted state.
+   *
+   *   ```yaml
+   *   filter:
+   *     statusPhases: ["ENVIRONMENT_PHASE_STOPPED", "ENVIRONMENT_PHASE_DELETED"]
+   *   ```
+   */
+  list(
+    params: EnvironmentListParams,
+    options?: RequestOptions,
+  ): PagePromise<EnvironmentsEnvironmentsPage, Environment> {
+    const { token, pageSize, ...body } = params;
+    return this._client.getAPIList(
+      '/gitpod.v1.EnvironmentService/ListEnvironments',
+      EnvironmentsPage<Environment>,
+      { query: { token, pageSize }, body, method: 'post', ...options },
+    );
+  }
+
+  /**
+   * Permanently deletes an environment.
+   *
+   * Running environments are automatically stopped before deletion. If force is
+   * true, the environment is deleted immediately without graceful shutdown.
+   *
+   * ### Examples
+   *
+   * - Delete with graceful shutdown:
+   *
+   *   Deletes an environment after gracefully stopping it.
+   *
+   *   ```yaml
+   *   environmentId: "07e03a28-65a5-4d98-b532-8ea67b188048"
+   *   force: false
+   *   ```
+   *
+   * - Force delete:
+   *
+   *   Immediately deletes an environment without waiting for graceful shutdown.
+   *
+   *   ```yaml
+   *   environmentId: "07e03a28-65a5-4d98-b532-8ea67b188048"
+   *   force: true
+   *   ```
+   */
+  delete(body: EnvironmentDeleteParams, options?: RequestOptions): APIPromise<unknown> {
+    return this._client.post('/gitpod.v1.EnvironmentService/DeleteEnvironment', { body, ...options });
+  }
+
+  /**
+   * Creates an access token for the environment.
+   *
+   * Generated tokens are valid for one hour and provide environment-specific access
+   * permissions. The token is scoped to a specific environment.
+   *
+   * ### Examples
+   *
+   * - Generate environment token:
+   *
+   *   Creates a temporary access token for accessing an environment.
+   *
+   *   ```yaml
+   *   environmentId: "07e03a28-65a5-4d98-b532-8ea67b188048"
+   *   ```
+   */
+  createEnvironmentToken(
+    body: EnvironmentCreateEnvironmentTokenParams,
+    options?: RequestOptions,
+  ): APIPromise<EnvironmentCreateEnvironmentTokenResponse> {
+    return this._client.post('/gitpod.v1.EnvironmentService/CreateEnvironmentAccessToken', {
+      body,
+      ...options,
+    });
+  }
+
+  /**
+   * Creates an environment from an existing project configuration and starts it.
+   *
+   * This method uses project settings as defaults but allows overriding specific
+   * configurations. Project settings take precedence over default configurations,
+   * while custom specifications in the request override project settings.
+   *
+   * ### Examples
+   *
+   * - Create with project defaults:
+   *
+   *   Creates an environment using all default settings from the project
+   *   configuration.
+   *
+   *   ```yaml
+   *   projectId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   ```
+   *
+   * - Create with custom compute resources:
+   *
+   *   Creates an environment from project with custom machine class and timeout
+   *   settings.
+   *
+   *   ```yaml
+   *   projectId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   spec:
+   *     machine:
+   *       class: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *     timeout:
+   *       disconnected: "14400s" # 4 hours in seconds
+   *   ```
+   */
+  createFromProject(
+    body: EnvironmentCreateFromProjectParams,
+    options?: RequestOptions,
+  ): APIPromise<EnvironmentCreateFromProjectResponse> {
+    return this._client.post('/gitpod.v1.EnvironmentService/CreateEnvironmentFromProject', {
+      body,
+      ...options,
+    });
+  }
+
+  /**
+   * Creates an access token for retrieving environment logs.
+   *
+   * Generated tokens are valid for one hour and provide read-only access to the
+   * environment's logs.
+   *
+   * ### Examples
+   *
+   * - Generate logs token:
+   *
+   *   Creates a temporary access token for retrieving environment logs.
+   *
+   *   ```yaml
+   *   environmentId: "07e03a28-65a5-4d98-b532-8ea67b188048"
+   *   ```
+   */
+  createLogsToken(
+    body: EnvironmentCreateLogsTokenParams,
+    options?: RequestOptions,
+  ): APIPromise<EnvironmentCreateLogsTokenResponse> {
+    return this._client.post('/gitpod.v1.EnvironmentService/CreateEnvironmentLogsToken', {
+      body,
+      ...options,
+    });
+  }
+
+  /**
+   * Records environment activity to prevent automatic shutdown.
+   *
+   * Activity signals should be sent every 5 minutes while the environment is
+   * actively being used. The source must be between 3-80 characters.
+   *
+   * ### Examples
+   *
+   * - Signal VS Code activity:
+   *
+   *   Records VS Code editor activity to prevent environment shutdown.
+   *
+   *   ```yaml
+   *   environmentId: "07e03a28-65a5-4d98-b532-8ea67b188048"
+   *   activitySignal:
+   *     source: "VS Code"
+   *     timestamp: "2025-02-12T14:30:00Z"
+   *   ```
+   */
+  markActive(body: EnvironmentMarkActiveParams, options?: RequestOptions): APIPromise<unknown> {
+    return this._client.post('/gitpod.v1.EnvironmentService/MarkEnvironmentActive', { body, ...options });
+  }
+
+  /**
+   * Starts a stopped environment.
+   *
+   * Use this method to resume work on a previously stopped environment. The
+   * environment retains its configuration and workspace content from when it was
+   * stopped.
+   *
+   * ### Examples
+   *
+   * - Start an environment:
+   *
+   *   Resumes a previously stopped environment with its existing configuration.
+   *
+   *   ```yaml
+   *   environmentId: "07e03a28-65a5-4d98-b532-8ea67b188048"
+   *   ```
+   */
+  start(body: EnvironmentStartParams, options?: RequestOptions): APIPromise<unknown> {
+    return this._client.post('/gitpod.v1.EnvironmentService/StartEnvironment', { body, ...options });
+  }
+
+  /**
+   * Stops a running environment.
+   *
+   * Use this method to pause work while preserving the environment's state. The
+   * environment can be resumed later using StartEnvironment.
+   *
+   * ### Examples
+   *
+   * - Stop an environment:
+   *
+   *   Gracefully stops a running environment while preserving its state.
+   *
+   *   ```yaml
+   *   environmentId: "07e03a28-65a5-4d98-b532-8ea67b188048"
+   *   ```
+   */
+  stop(body: EnvironmentStopParams, options?: RequestOptions): APIPromise<unknown> {
+    return this._client.post('/gitpod.v1.EnvironmentService/StopEnvironment', { body, ...options });
+  }
+}
+
+export type EnvironmentsEnvironmentsPage = EnvironmentsPage<Environment>;
+
+/**
+ * Admission level describes who can access an environment instance and its ports.
+ */
+export type AdmissionLevel =
+  | 'ADMISSION_LEVEL_UNSPECIFIED'
+  | 'ADMISSION_LEVEL_OWNER_ONLY'
+  | 'ADMISSION_LEVEL_EVERYONE';
+
+/**
+ * +resource get environment
+ */
+export interface Environment {
+  /**
+   * ID is a unique identifier of this environment. No other environment with the
+   * same name must be managed by this environment manager
+   */
+  id: string;
+
+  /**
+   * Metadata is data associated with this environment that's required for other
+   * parts of Gitpod to function
+   */
+  metadata?: EnvironmentMetadata;
+
+  /**
+   * Spec is the configuration of the environment that's required for the runner to
+   * start the environment
+   */
+  spec?: EnvironmentSpec;
+
+  /**
+   * Status is the current status of the environment
+   */
+  status?: EnvironmentStatus;
+}
+
+/**
+ * EnvironmentActivitySignal used to signal activity for an environment.
+ */
+export interface EnvironmentActivitySignal {
+  /**
+   * source of the activity signal, such as "VS Code", "SSH", or "Automations". It
+   * should be a human-readable string that describes the source of the activity
+   * signal.
+   */
+  source?: string;
+
+  /**
+   * timestamp of when the activity was observed by the source. Only reported every 5
+   * minutes. Zero value means no activity was observed.
+   */
+  timestamp?: string;
+}
+
+/**
+ * EnvironmentMetadata is data associated with an environment that's required for
+ * other parts of the system to function
+ */
+export interface EnvironmentMetadata {
+  /**
+   * annotations are key/value pairs that gets attached to the environment.
+   * +internal - not yet implemented
+   */
+  annotations?: Record<string, string>;
+
+  /**
+   * Time when the Environment was created.
+   */
+  createdAt?: string;
+
+  /**
+   * creator is the identity of the creator of the environment
+   */
+  creator?: Shared.Subject;
+
+  /**
+   * Time when the Environment was last started (i.e. CreateEnvironment or
+   * StartEnvironment were called).
+   */
+  lastStartedAt?: string;
+
+  /**
+   * name is the name of the environment as specified by the user
+   */
+  name?: string;
+
+  /**
+   * organization_id is the ID of the organization that contains the environment
+   */
+  organizationId?: string;
+
+  /**
+   * original_context_url is the normalized URL from which the environment was
+   * created
+   */
+  originalContextUrl?: string;
+
+  /**
+   * If the Environment was started from a project, the project_id will reference the
+   * project.
+   */
+  projectId?: string;
+
+  /**
+   * Runner is the ID of the runner that runs this environment.
+   */
+  runnerId?: string;
+}
+
+export type EnvironmentPhase =
+  | 'ENVIRONMENT_PHASE_UNSPECIFIED'
+  | 'ENVIRONMENT_PHASE_CREATING'
+  | 'ENVIRONMENT_PHASE_STARTING'
+  | 'ENVIRONMENT_PHASE_RUNNING'
+  | 'ENVIRONMENT_PHASE_UPDATING'
+  | 'ENVIRONMENT_PHASE_STOPPING'
+  | 'ENVIRONMENT_PHASE_STOPPED'
+  | 'ENVIRONMENT_PHASE_DELETING'
+  | 'ENVIRONMENT_PHASE_DELETED';
+
+/**
+ * EnvironmentSpec specifies the configuration of an environment for an environment
+ * start
+ */
+export interface EnvironmentSpec {
+  /**
+   * admission controlls who can access the environment and its ports.
+   */
+  admission?: AdmissionLevel;
+
+  /**
+   * automations_file is the automations file spec of the environment
+   */
+  automationsFile?: EnvironmentSpec.AutomationsFile;
+
+  /**
+   * content is the content spec of the environment
+   */
+  content?: EnvironmentSpec.Content;
+
+  /**
+   * Phase is the desired phase of the environment
+   */
+  desiredPhase?: EnvironmentPhase;
+
+  /**
+   * devcontainer is the devcontainer spec of the environment
+   */
+  devcontainer?: EnvironmentSpec.Devcontainer;
+
+  /**
+   * machine is the machine spec of the environment
+   */
+  machine?: EnvironmentSpec.Machine;
+
+  /**
+   * ports is the set of ports which ought to be exposed to the internet
+   */
+  ports?: Array<EnvironmentSpec.Port>;
+
+  /**
+   * secrets are confidential data that is mounted into the environment
+   */
+  secrets?: Array<EnvironmentSpec.Secret>;
+
+  /**
+   * version of the spec. The value of this field has no semantic meaning (e.g. don't
+   * interpret it as as a timestamp), but it can be used to impose a partial order.
+   * If a.spec_version < b.spec_version then a was the spec before b.
+   */
+  specVersion?: string;
+
+  /**
+   * ssh_public_keys are the public keys used to ssh into the environment
+   */
+  sshPublicKeys?: Array<EnvironmentSpec.SSHPublicKey>;
+
+  /**
+   * Timeout configures the environment timeout
+   */
+  timeout?: EnvironmentSpec.Timeout;
+}
+
+export namespace EnvironmentSpec {
+  /**
+   * automations_file is the automations file spec of the environment
+   */
+  export interface AutomationsFile {
+    /**
+     * automations_file_path is the path to the automations file that is applied in the
+     * environment, relative to the repo root. path must not be absolute (start with a
+     * /):
+     *
+     * ```
+     * this.matches('^$|^[^/].*')
+     * ```
+     */
+    automationsFilePath?: string;
+
+    session?: string;
+  }
+
+  /**
+   * content is the content spec of the environment
+   */
+  export interface Content {
+    /**
+     * The Git email address
+     */
+    gitEmail?: string;
+
+    /**
+     * The Git username
+     */
+    gitUsername?: string;
+
+    /**
+     * initializer configures how the environment is to be initialized
+     */
+    initializer?: ProjectsAPI.EnvironmentInitializer;
+
+    session?: string;
+  }
+
+  /**
+   * devcontainer is the devcontainer spec of the environment
+   */
+  export interface Devcontainer {
+    /**
+     * default_devcontainer_image is the default image that is used to start the
+     * devcontainer if no devcontainer config file is found
+     */
+    defaultDevcontainerImage?: string;
+
+    /**
+     * devcontainer_file_path is the path to the devcontainer file relative to the repo
+     * root path must not be absolute (start with a /):
+     *
+     * ```
+     * this.matches('^$|^[^/].*')
+     * ```
+     */
+    devcontainerFilePath?: string;
+
+    /**
+     * Experimental: dotfiles is the dotfiles configuration of the devcontainer
+     */
+    dotfiles?: Devcontainer.Dotfiles;
+
+    session?: string;
+  }
+
+  export namespace Devcontainer {
+    /**
+     * Experimental: dotfiles is the dotfiles configuration of the devcontainer
+     */
+    export interface Dotfiles {
+      /**
+       * URL of a dotfiles Git repository (e.g. https://github.com/owner/repository)
+       */
+      repository: string;
+    }
+  }
+
+  /**
+   * machine is the machine spec of the environment
+   */
+  export interface Machine {
+    /**
+     * Class denotes the class of the environment we ought to start
+     */
+    class?: string;
+
+    session?: string;
+  }
+
+  export interface Port {
+    /**
+     * policy of this port
+     */
+    admission?: EnvironmentsAPI.AdmissionLevel;
+
+    /**
+     * name of this port
+     */
+    name?: string;
+
+    /**
+     * port number
+     */
+    port?: number;
+  }
+
+  export interface Secret {
+    /**
+     * id is the unique identifier of the secret.
+     */
+    id?: string;
+
+    /**
+     * container_registry_basic_auth_host is the hostname of the container registry
+     * that supports basic auth
+     */
+    containerRegistryBasicAuthHost?: string;
+
+    environmentVariable?: string;
+
+    /**
+     * file_path is the path inside the devcontainer where the secret is mounted
+     */
+    filePath?: string;
+
+    gitCredentialHost?: string;
+
+    /**
+     * name is the human readable description of the secret
+     */
+    name?: string;
+
+    /**
+     * session indicated the current session of the secret. When the session does not
+     * change, secrets are not reloaded in the environment.
+     */
+    session?: string;
+
+    /**
+     * source is the source of the secret, for now control-plane or runner
+     */
+    source?: string;
+
+    /**
+     * source_ref into the source, in case of control-plane this is uuid of the secret
+     */
+    sourceRef?: string;
+  }
+
+  export interface SSHPublicKey {
+    /**
+     * id is the unique identifier of the public key
+     */
+    id?: string;
+
+    /**
+     * value is the actual public key in the public key file format
+     */
+    value?: string;
+  }
+
+  /**
+   * Timeout configures the environment timeout
+   */
+  export interface Timeout {
+    /**
+     * inacitivity is the maximum time of disconnection before the environment is
+     * stopped or paused. Minimum duration is 30 minutes. Set to 0 to disable.
+     */
+    disconnected?: string;
+  }
+}
+
+/**
+ * EnvironmentStatus describes an environment status
+ */
+export interface EnvironmentStatus {
+  /**
+   * activity_signal is the last activity signal for the environment.
+   */
+  activitySignal?: EnvironmentActivitySignal;
+
+  /**
+   * automations_file contains the status of the automations file.
+   */
+  automationsFile?: EnvironmentStatus.AutomationsFile;
+
+  /**
+   * content contains the status of the environment content.
+   */
+  content?: EnvironmentStatus.Content;
+
+  /**
+   * devcontainer contains the status of the devcontainer.
+   */
+  devcontainer?: EnvironmentStatus.Devcontainer;
+
+  /**
+   * environment_url contains the URL at which the environment can be accessed. This
+   * field is only set if the environment is running.
+   */
+  environmentUrls?: EnvironmentStatus.EnvironmentURLs;
+
+  /**
+   * failure_message summarises why the environment failed to operate. If this is
+   * non-empty the environment has failed to operate and will likely transition to a
+   * stopped state.
+   */
+  failureMessage?: Array<string>;
+
+  /**
+   * machine contains the status of the environment machine
+   */
+  machine?: EnvironmentStatus.Machine;
+
+  /**
+   * the phase of an environment is a simple, high-level summary of where the
+   * environment is in its lifecycle
+   */
+  phase?: EnvironmentPhase;
+
+  /**
+   * runner_ack contains the acknowledgement from the runner that is has received the
+   * environment spec.
+   */
+  runnerAck?: EnvironmentStatus.RunnerAck;
+
+  /**
+   * secrets contains the status of the environment secrets
+   */
+  secrets?: Array<EnvironmentStatus.Secret>;
+
+  /**
+   * ssh_public_keys contains the status of the environment ssh public keys
+   */
+  sshPublicKeys?: Array<EnvironmentStatus.SSHPublicKey>;
+
+  /**
+   * version of the status update. Environment instances themselves are unversioned,
+   * but their status has different versions. The value of this field has no semantic
+   * meaning (e.g. don't interpret it as as a timestamp), but it can be used to
+   * impose a partial order. If a.status_version < b.status_version then a was the
+   * status before b.
+   */
+  statusVersion?: string;
+
+  /**
+   * warning_message contains warnings, e.g. when the environment is present but not
+   * in the expected state.
+   */
+  warningMessage?: Array<string>;
+}
+
+export namespace EnvironmentStatus {
+  /**
+   * automations_file contains the status of the automations file.
+   */
+  export interface AutomationsFile {
+    /**
+     * automations_file_path is the path to the automations file relative to the repo
+     * root.
+     */
+    automationsFilePath?: string;
+
+    /**
+     * automations_file_presence indicates how an automations file is present in the
+     * environment.
+     */
+    automationsFilePresence?:
+      | 'PRESENCE_UNSPECIFIED'
+      | 'PRESENCE_ABSENT'
+      | 'PRESENCE_DISCOVERED'
+      | 'PRESENCE_SPECIFIED';
+
+    /**
+     * failure_message contains the reason the automations file failed to be applied.
+     * This is only set if the phase is FAILED.
+     */
+    failureMessage?: string;
+
+    /**
+     * phase is the current phase of the automations file.
+     */
+    phase?:
+      | 'CONTENT_PHASE_UNSPECIFIED'
+      | 'CONTENT_PHASE_CREATING'
+      | 'CONTENT_PHASE_INITIALIZING'
+      | 'CONTENT_PHASE_READY'
+      | 'CONTENT_PHASE_UPDATING'
+      | 'CONTENT_PHASE_FAILED';
+
+    /**
+     * session is the automations file session that is currently applied in the
+     * environment.
+     */
+    session?: string;
+
+    /**
+     * warning_message contains warnings, e.g. when no triggers are defined in the
+     * automations file.
+     */
+    warningMessage?: string;
+  }
+
+  /**
+   * content contains the status of the environment content.
+   */
+  export interface Content {
+    /**
+     * content_location_in_machine is the location of the content in the machine
+     */
+    contentLocationInMachine?: string;
+
+    /**
+     * failure_message contains the reason the content initialization failed.
+     */
+    failureMessage?: string;
+
+    /**
+     * git is the Git working copy status of the environment. Note: this is a
+     * best-effort field and more often than not will not be present. Its absence does
+     * not indicate the absence of a working copy.
+     */
+    git?: Content.Git;
+
+    /**
+     * phase is the current phase of the environment content
+     */
+    phase?:
+      | 'CONTENT_PHASE_UNSPECIFIED'
+      | 'CONTENT_PHASE_CREATING'
+      | 'CONTENT_PHASE_INITIALIZING'
+      | 'CONTENT_PHASE_READY'
+      | 'CONTENT_PHASE_UPDATING'
+      | 'CONTENT_PHASE_FAILED';
+
+    /**
+     * session is the session that is currently active in the environment.
+     */
+    session?: string;
+
+    /**
+     * warning_message contains warnings, e.g. when the content is present but not in
+     * the expected state.
+     */
+    warningMessage?: string;
+  }
+
+  export namespace Content {
+    /**
+     * git is the Git working copy status of the environment. Note: this is a
+     * best-effort field and more often than not will not be present. Its absence does
+     * not indicate the absence of a working copy.
+     */
+    export interface Git {
+      /**
+       * branch is branch we're currently on
+       */
+      branch?: string;
+
+      /**
+       * changed_files is an array of changed files in the environment, possibly
+       * truncated
+       */
+      changedFiles?: Array<Git.ChangedFile>;
+
+      /**
+       * clone_url is the repository url as you would pass it to "git clone". Only HTTPS
+       * clone URLs are supported.
+       */
+      cloneUrl?: string;
+
+      /**
+       * latest_commit is the most recent commit on the current branch
+       */
+      latestCommit?: string;
+
+      totalChangedFiles?: number;
+
+      /**
+       * the total number of unpushed changes
+       */
+      totalUnpushedCommits?: number;
+
+      /**
+       * unpushed_commits is an array of unpushed changes in the environment, possibly
+       * truncated
+       */
+      unpushedCommits?: Array<string>;
+    }
+
+    export namespace Git {
+      export interface ChangedFile {
+        /**
+         * ChangeType is the type of change that happened to the file
+         */
+        changeType?:
+          | 'CHANGE_TYPE_UNSPECIFIED'
+          | 'CHANGE_TYPE_ADDED'
+          | 'CHANGE_TYPE_MODIFIED'
+          | 'CHANGE_TYPE_DELETED'
+          | 'CHANGE_TYPE_RENAMED'
+          | 'CHANGE_TYPE_COPIED'
+          | 'CHANGE_TYPE_UPDATED_BUT_UNMERGED'
+          | 'CHANGE_TYPE_UNTRACKED';
+
+        /**
+         * path is the path of the file
+         */
+        path?: string;
+      }
+    }
+  }
+
+  /**
+   * devcontainer contains the status of the devcontainer.
+   */
+  export interface Devcontainer {
+    /**
+     * container_id is the ID of the container.
+     */
+    containerId?: string;
+
+    /**
+     * container_name is the name of the container that is used to connect to the
+     * devcontainer
+     */
+    containerName?: string;
+
+    /**
+     * devcontainerconfig_in_sync indicates if the devcontainer is up to date w.r.t.
+     * the devcontainer config file.
+     */
+    devcontainerconfigInSync?: boolean;
+
+    /**
+     * devcontainer_file_path is the path to the devcontainer file relative to the repo
+     * root
+     */
+    devcontainerFilePath?: string;
+
+    /**
+     * devcontainer_file_presence indicates how the devcontainer file is present in the
+     * repo.
+     */
+    devcontainerFilePresence?:
+      | 'PRESENCE_UNSPECIFIED'
+      | 'PRESENCE_GENERATED'
+      | 'PRESENCE_DISCOVERED'
+      | 'PRESENCE_SPECIFIED';
+
+    /**
+     * failure_message contains the reason the devcontainer failed to operate.
+     */
+    failureMessage?: string;
+
+    /**
+     * phase is the current phase of the devcontainer
+     */
+    phase?: 'PHASE_UNSPECIFIED' | 'PHASE_CREATING' | 'PHASE_RUNNING' | 'PHASE_STOPPED' | 'PHASE_FAILED';
+
+    /**
+     * remote_user is the user that is used to connect to the devcontainer
+     */
+    remoteUser?: string;
+
+    /**
+     * remote_workspace_folder is the folder that is used to connect to the
+     * devcontainer
+     */
+    remoteWorkspaceFolder?: string;
+
+    /**
+     * secrets_in_sync indicates if the secrets are up to date w.r.t. the running
+     * devcontainer.
+     */
+    secretsInSync?: boolean;
+
+    /**
+     * session is the session that is currently active in the devcontainer.
+     */
+    session?: string;
+
+    /**
+     * warning_message contains warnings, e.g. when the devcontainer is present but not
+     * in the expected state.
+     */
+    warningMessage?: string;
+  }
+
+  /**
+   * environment_url contains the URL at which the environment can be accessed. This
+   * field is only set if the environment is running.
+   */
+  export interface EnvironmentURLs {
+    /**
+     * logs is the URL at which the environment logs can be accessed.
+     */
+    logs?: string;
+
+    ports?: Array<EnvironmentURLs.Port>;
+
+    /**
+     * SSH is the URL at which the environment can be accessed via SSH.
+     */
+    ssh?: EnvironmentURLs.SSH;
+  }
+
+  export namespace EnvironmentURLs {
+    export interface Port {
+      /**
+       * port is the port number of the environment port
+       */
+      port?: number;
+
+      /**
+       * url is the URL at which the environment port can be accessed
+       */
+      url?: string;
+    }
+
+    /**
+     * SSH is the URL at which the environment can be accessed via SSH.
+     */
+    export interface SSH {
+      url?: string;
+    }
+  }
+
+  /**
+   * machine contains the status of the environment machine
+   */
+  export interface Machine {
+    /**
+     * failure_message contains the reason the machine failed to operate.
+     */
+    failureMessage?: string;
+
+    /**
+     * phase is the current phase of the environment machine
+     */
+    phase?:
+      | 'PHASE_UNSPECIFIED'
+      | 'PHASE_CREATING'
+      | 'PHASE_STARTING'
+      | 'PHASE_RUNNING'
+      | 'PHASE_STOPPING'
+      | 'PHASE_STOPPED'
+      | 'PHASE_DELETING'
+      | 'PHASE_DELETED';
+
+    /**
+     * session is the session that is currently active in the machine.
+     */
+    session?: string;
+
+    /**
+     * timeout contains the reason the environment has timed out. If this field is
+     * empty, the environment has not timed out.
+     */
+    timeout?: string;
+
+    /**
+     * versions contains the versions of components in the machine.
+     */
+    versions?: Machine.Versions;
+
+    /**
+     * warning_message contains warnings, e.g. when the machine is present but not in
+     * the expected state.
+     */
+    warningMessage?: string;
+  }
+
+  export namespace Machine {
+    /**
+     * versions contains the versions of components in the machine.
+     */
+    export interface Versions {
+      supervisorCommit?: string;
+
+      supervisorVersion?: string;
+    }
+  }
+
+  /**
+   * runner_ack contains the acknowledgement from the runner that is has received the
+   * environment spec.
+   */
+  export interface RunnerAck {
+    message?: string;
+
+    specVersion?: string;
+
+    statusCode?:
+      | 'STATUS_CODE_UNSPECIFIED'
+      | 'STATUS_CODE_OK'
+      | 'STATUS_CODE_INVALID_RESOURCE'
+      | 'STATUS_CODE_FAILED_PRECONDITION';
+  }
+
+  export interface Secret {
+    /**
+     * id is the unique identifier of the secret.
+     */
+    id?: string;
+
+    /**
+     * failure_message contains the reason the secret failed to be materialize.
+     */
+    failureMessage?: string;
+
+    phase?:
+      | 'CONTENT_PHASE_UNSPECIFIED'
+      | 'CONTENT_PHASE_CREATING'
+      | 'CONTENT_PHASE_INITIALIZING'
+      | 'CONTENT_PHASE_READY'
+      | 'CONTENT_PHASE_UPDATING'
+      | 'CONTENT_PHASE_FAILED';
+
+    secretName?: string;
+
+    /**
+     * session is the session that is currently active in the environment.
+     */
+    session?: string;
+
+    /**
+     * warning_message contains warnings, e.g. when the secret is present but not in
+     * the expected state.
+     */
+    warningMessage?: string;
+  }
+
+  export interface SSHPublicKey {
+    /**
+     * id is the unique identifier of the public key
+     */
+    id?: string;
+
+    /**
+     * phase is the current phase of the public key
+     */
+    phase?:
+      | 'CONTENT_PHASE_UNSPECIFIED'
+      | 'CONTENT_PHASE_CREATING'
+      | 'CONTENT_PHASE_INITIALIZING'
+      | 'CONTENT_PHASE_READY'
+      | 'CONTENT_PHASE_UPDATING'
+      | 'CONTENT_PHASE_FAILED';
+  }
+}
+
+export interface EnvironmentCreateResponse {
+  /**
+   * +resource get environment
+   */
+  environment: Environment;
+}
+
+export interface EnvironmentRetrieveResponse {
+  /**
+   * +resource get environment
+   */
+  environment: Environment;
+}
+
+export type EnvironmentUpdateResponse = unknown;
+
+export type EnvironmentDeleteResponse = unknown;
+
+export interface EnvironmentCreateEnvironmentTokenResponse {
+  /**
+   * access_token is the token that can be used for environment authentication
+   */
+  accessToken: string;
+}
+
+export interface EnvironmentCreateFromProjectResponse {
+  /**
+   * +resource get environment
+   */
+  environment: Environment;
+}
+
+export interface EnvironmentCreateLogsTokenResponse {
+  /**
+   * access_token is the token that can be used to access the logs of the environment
+   */
+  accessToken: string;
+}
+
+export type EnvironmentMarkActiveResponse = unknown;
+
+export type EnvironmentStartResponse = unknown;
+
+export type EnvironmentStopResponse = unknown;
+
+export interface EnvironmentCreateParams {
+  /**
+   * spec is the configuration of the environment that's required for the to start
+   * the environment
+   */
+  spec?: EnvironmentSpec;
+}
+
+export interface EnvironmentRetrieveParams {
+  /**
+   * environment_id specifies the environment to get
+   */
+  environmentId: string;
+}
+
+export interface EnvironmentUpdateParams {
+  /**
+   * environment_id specifies which environment should be updated.
+   *
+   * +required
+   */
+  environmentId?: string;
+
+  metadata?: unknown | null;
+
+  spec?: EnvironmentUpdateParams.Spec | null;
+}
+
+export namespace EnvironmentUpdateParams {
+  export interface Spec {
+    /**
+     * automations_file is the automations file spec of the environment
+     */
+    automationsFile?: Spec.AutomationsFile | null;
+
+    content?: Spec.Content | null;
+
+    devcontainer?: Spec.Devcontainer | null;
+
+    /**
+     * ports controls port sharing
+     */
+    ports?: Array<Spec.Port>;
+
+    /**
+     * ssh_public_keys are the public keys to update empty array means nothing to
+     * update
+     */
+    sshPublicKeys?: Array<Spec.SSHPublicKey>;
+
+    /**
+     * Timeout configures the environment timeout
+     */
+    timeout?: Spec.Timeout | null;
+  }
+
+  export namespace Spec {
+    /**
+     * automations_file is the automations file spec of the environment
+     */
+    export interface AutomationsFile {
+      /**
+       * automations_file_path is the path to the automations file that is applied in the
+       * environment, relative to the repo root. path must not be absolute (start with a
+       * /):
+       *
+       * ```
+       * this.matches('^$|^[^/].*')
+       * ```
+       */
+      automationsFilePath?: string | null;
+
+      session?: string | null;
+    }
+
+    export interface Content {
+      /**
+       * The Git email address
+       */
+      gitEmail?: string | null;
+
+      /**
+       * The Git username
+       */
+      gitUsername?: string | null;
+
+      /**
+       * initializer configures how the environment is to be initialized
+       */
+      initializer?: ProjectsAPI.EnvironmentInitializer | null;
+
+      /**
+       * session should be changed to trigger a content reinitialization
+       */
+      session?: string | null;
+    }
+
+    export interface Devcontainer {
+      /**
+       * devcontainer_file_path is the path to the devcontainer file relative to the repo
+       * root path must not be absolute (start with a /):
+       *
+       * ```
+       * this.matches('^$|^[^/].*')
+       * ```
+       */
+      devcontainerFilePath?: string | null;
+
+      /**
+       * session should be changed to trigger a devcontainer rebuild
+       */
+      session?: string | null;
+    }
+
+    export interface Port {
+      /**
+       * policy of this port
+       */
+      admission?: EnvironmentsAPI.AdmissionLevel;
+
+      /**
+       * name of this port
+       */
+      name?: string;
+
+      /**
+       * port number
+       */
+      port?: number;
+    }
+
+    export interface SSHPublicKey {
+      /**
+       * id is the unique identifier of the public key
+       */
+      id?: string;
+
+      /**
+       * value is the actual public key in the public key file format if not provided,
+       * the public key will be removed
+       */
+      value?: string | null;
+    }
+
+    /**
+     * Timeout configures the environment timeout
+     */
+    export interface Timeout {
+      /**
+       * inacitivity is the maximum time of disconnection before the environment is
+       * stopped or paused. Minimum duration is 30 minutes. Set to 0 to disable.
+       */
+      disconnected?: string | null;
+    }
+  }
+}
+
+export interface EnvironmentListParams extends EnvironmentsPageParams {
+  /**
+   * Body param:
+   */
+  filter?: EnvironmentListParams.Filter;
+
+  /**
+   * Body param: pagination contains the pagination options for listing environments
+   */
+  pagination?: EnvironmentListParams.Pagination;
+}
+
+export namespace EnvironmentListParams {
+  export interface Filter {
+    /**
+     * creator_ids filters the response to only Environments created by specified
+     * members
+     */
+    creatorIds?: Array<string>;
+
+    /**
+     * project_ids filters the response to only Environments associated with the
+     * specified projects
+     */
+    projectIds?: Array<string>;
+
+    /**
+     * runner_ids filters the response to only Environments running on these Runner IDs
+     */
+    runnerIds?: Array<string>;
+
+    /**
+     * runner_kinds filters the response to only Environments running on these Runner
+     * Kinds
+     */
+    runnerKinds?: Array<RunnersAPI.RunnerKind>;
+
+    /**
+     * actual_phases is a list of phases the environment must be in for it to be
+     * returned in the API call
+     */
+    statusPhases?: Array<EnvironmentsAPI.EnvironmentPhase>;
+  }
+
+  /**
+   * pagination contains the pagination options for listing environments
+   */
+  export interface Pagination {
+    /**
+     * Token for the next set of results that was returned as next_token of a
+     * PaginationResponse
+     */
+    token?: string;
+
+    /**
+     * Page size is the maximum number of results to retrieve per page. Defaults to 25.
+     * Maximum 100.
+     */
+    pageSize?: number;
+  }
+}
+
+export interface EnvironmentDeleteParams {
+  /**
+   * environment_id specifies the environment that is going to delete.
+   *
+   * +required
+   */
+  environmentId?: string;
+
+  /**
+   * force indicates whether the environment should be deleted forcefully When force
+   * deleting an Environment, the Environment is removed immediately and environment
+   * lifecycle is not respected. Force deleting can result in data loss on the
+   * environment.
+   */
+  force?: boolean;
+}
+
+export interface EnvironmentCreateEnvironmentTokenParams {
+  /**
+   * environment_id specifies the environment for which the access token should be
+   * created.
+   */
+  environmentId: string;
+}
+
+export interface EnvironmentCreateFromProjectParams {
+  projectId?: string;
+
+  /**
+   * Spec is the configuration of the environment that's required for the runner to
+   * start the environment Configuration already defined in the Project will override
+   * parts of the spec, if set
+   */
+  spec?: EnvironmentSpec;
+}
+
+export interface EnvironmentCreateLogsTokenParams {
+  /**
+   * environment_id specifies the environment for which the logs token should be
+   * created.
+   *
+   * +required
+   */
+  environmentId?: string;
+}
+
+export interface EnvironmentMarkActiveParams {
+  /**
+   * activity_signal specifies the activity.
+   */
+  activitySignal?: EnvironmentActivitySignal;
+
+  /**
+   * The ID of the environment to update activity for.
+   */
+  environmentId?: string;
+}
+
+export interface EnvironmentStartParams {
+  /**
+   * environment_id specifies which environment should be started.
+   */
+  environmentId?: string;
+}
+
+export interface EnvironmentStopParams {
+  /**
+   * environment_id specifies which environment should be stopped.
+   *
+   * +required
+   */
+  environmentId?: string;
+}
+
+Environments.Automations = Automations;
+Environments.Classes = Classes;
+
+export declare namespace Environments {
+  export {
+    type AdmissionLevel as AdmissionLevel,
+    type Environment as Environment,
+    type EnvironmentActivitySignal as EnvironmentActivitySignal,
+    type EnvironmentMetadata as EnvironmentMetadata,
+    type EnvironmentPhase as EnvironmentPhase,
+    type EnvironmentSpec as EnvironmentSpec,
+    type EnvironmentStatus as EnvironmentStatus,
+    type EnvironmentCreateResponse as EnvironmentCreateResponse,
+    type EnvironmentRetrieveResponse as EnvironmentRetrieveResponse,
+    type EnvironmentUpdateResponse as EnvironmentUpdateResponse,
+    type EnvironmentDeleteResponse as EnvironmentDeleteResponse,
+    type EnvironmentCreateEnvironmentTokenResponse as EnvironmentCreateEnvironmentTokenResponse,
+    type EnvironmentCreateFromProjectResponse as EnvironmentCreateFromProjectResponse,
+    type EnvironmentCreateLogsTokenResponse as EnvironmentCreateLogsTokenResponse,
+    type EnvironmentMarkActiveResponse as EnvironmentMarkActiveResponse,
+    type EnvironmentStartResponse as EnvironmentStartResponse,
+    type EnvironmentStopResponse as EnvironmentStopResponse,
+    type EnvironmentsEnvironmentsPage as EnvironmentsEnvironmentsPage,
+    type EnvironmentCreateParams as EnvironmentCreateParams,
+    type EnvironmentRetrieveParams as EnvironmentRetrieveParams,
+    type EnvironmentUpdateParams as EnvironmentUpdateParams,
+    type EnvironmentListParams as EnvironmentListParams,
+    type EnvironmentDeleteParams as EnvironmentDeleteParams,
+    type EnvironmentCreateEnvironmentTokenParams as EnvironmentCreateEnvironmentTokenParams,
+    type EnvironmentCreateFromProjectParams as EnvironmentCreateFromProjectParams,
+    type EnvironmentCreateLogsTokenParams as EnvironmentCreateLogsTokenParams,
+    type EnvironmentMarkActiveParams as EnvironmentMarkActiveParams,
+    type EnvironmentStartParams as EnvironmentStartParams,
+    type EnvironmentStopParams as EnvironmentStopParams,
+  };
+
+  export {
+    Automations as Automations,
+    type AutomationsAPIAutomationsFile as AutomationsFile,
+    type AutomationUpsertResponse as AutomationUpsertResponse,
+    type AutomationUpsertParams as AutomationUpsertParams,
+  };
+
+  export { Classes as Classes, type ClassListParams as ClassListParams };
+}
diff --git a/src/resources/environments/index.ts b/src/resources/environments/index.ts
new file mode 100644
index 0000000..4378540
--- /dev/null
+++ b/src/resources/environments/index.ts
@@ -0,0 +1,41 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+export {
+  Automations,
+  type AutomationsFile,
+  type AutomationUpsertResponse,
+  type AutomationUpsertParams,
+} from './automations/index';
+export { Classes, type ClassListParams } from './classes';
+export {
+  Environments,
+  type AdmissionLevel,
+  type Environment,
+  type EnvironmentActivitySignal,
+  type EnvironmentMetadata,
+  type EnvironmentPhase,
+  type EnvironmentSpec,
+  type EnvironmentStatus,
+  type EnvironmentCreateResponse,
+  type EnvironmentRetrieveResponse,
+  type EnvironmentUpdateResponse,
+  type EnvironmentDeleteResponse,
+  type EnvironmentCreateEnvironmentTokenResponse,
+  type EnvironmentCreateFromProjectResponse,
+  type EnvironmentCreateLogsTokenResponse,
+  type EnvironmentMarkActiveResponse,
+  type EnvironmentStartResponse,
+  type EnvironmentStopResponse,
+  type EnvironmentCreateParams,
+  type EnvironmentRetrieveParams,
+  type EnvironmentUpdateParams,
+  type EnvironmentListParams,
+  type EnvironmentDeleteParams,
+  type EnvironmentCreateEnvironmentTokenParams,
+  type EnvironmentCreateFromProjectParams,
+  type EnvironmentCreateLogsTokenParams,
+  type EnvironmentMarkActiveParams,
+  type EnvironmentStartParams,
+  type EnvironmentStopParams,
+  type EnvironmentsEnvironmentsPage,
+} from './environments';
diff --git a/src/resources/events.ts b/src/resources/events.ts
new file mode 100644
index 0000000..109d1aa
--- /dev/null
+++ b/src/resources/events.ts
@@ -0,0 +1,307 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../core/resource';
+import * as EventsAPI from './events';
+import * as Shared from './shared';
+import { APIPromise } from '../core/api-promise';
+import { EntriesPage, type EntriesPageParams, PagePromise } from '../core/pagination';
+import { buildHeaders } from '../internal/headers';
+import { RequestOptions } from '../internal/request-options';
+import { JSONLDecoder } from '../internal/decoders/jsonl';
+
+export class Events extends APIResource {
+  /**
+   * Lists audit logs with filtering and pagination options.
+   *
+   * Use this method to:
+   *
+   * - View audit history
+   * - Track user actions
+   * - Monitor system changes
+   *
+   * ### Examples
+   *
+   * - List all logs:
+   *
+   *   ```yaml
+   *   pagination:
+   *     pageSize: 20
+   *   ```
+   *
+   * - Filter by actor:
+   *
+   *   ```yaml
+   *   filter:
+   *     actorIds: ["d2c94c27-3b76-4a42-b88c-95a85e392c68"]
+   *     actorPrincipals: ["PRINCIPAL_USER"]
+   *   pagination:
+   *     pageSize: 20
+   *   ```
+   */
+  list(
+    params: EventListParams,
+    options?: RequestOptions,
+  ): PagePromise<EventListResponsesEntriesPage, EventListResponse> {
+    const { token, pageSize, ...body } = params;
+    return this._client.getAPIList('/gitpod.v1.EventService/ListAuditLogs', EntriesPage<EventListResponse>, {
+      query: { token, pageSize },
+      body,
+      method: 'post',
+      ...options,
+    });
+  }
+
+  /**
+   * Streams events for all projects, runners, environments, tasks, and services
+   * based on the specified scope.
+   *
+   * Use this method to:
+   *
+   * - Monitor resource changes in real-time
+   * - Track system events
+   * - Receive notifications
+   *
+   * The scope parameter determines which events to watch:
+   *
+   * - Organization scope (default): Watch all organization-wide events including
+   *   projects, runners and environments. Task and service events are not included.
+   *   Use by setting organization=true or omitting the scope.
+   * - Environment scope: Watch events for a specific environment, including its
+   *   tasks, task executions, and services. Use by setting environment_id to the
+   *   UUID of the environment to watch.
+   */
+  watch(body: EventWatchParams, options?: RequestOptions): APIPromise<JSONLDecoder<EventWatchResponse>> {
+    return this._client
+      .post('/gitpod.v1.EventService/WatchEvents', {
+        body,
+        ...options,
+        headers: buildHeaders([
+          { 'Content-Type': 'application/jsonl', Accept: 'application/jsonl' },
+          options?.headers,
+        ]),
+        stream: true,
+        __binaryResponse: true,
+      })
+      ._thenUnwrap((_, props) => JSONLDecoder.fromResponse(props.response, props.controller)) as APIPromise<
+      JSONLDecoder<EventWatchResponse>
+    >;
+  }
+}
+
+export type EventListResponsesEntriesPage = EntriesPage<EventListResponse>;
+
+export type ResourceOperation =
+  | 'RESOURCE_OPERATION_UNSPECIFIED'
+  | 'RESOURCE_OPERATION_CREATE'
+  | 'RESOURCE_OPERATION_UPDATE'
+  | 'RESOURCE_OPERATION_DELETE'
+  | 'RESOURCE_OPERATION_UPDATE_STATUS';
+
+export type ResourceType =
+  | 'RESOURCE_TYPE_UNSPECIFIED'
+  | 'RESOURCE_TYPE_ENVIRONMENT'
+  | 'RESOURCE_TYPE_RUNNER'
+  | 'RESOURCE_TYPE_PROJECT'
+  | 'RESOURCE_TYPE_TASK'
+  | 'RESOURCE_TYPE_TASK_EXECUTION'
+  | 'RESOURCE_TYPE_SERVICE'
+  | 'RESOURCE_TYPE_ORGANIZATION'
+  | 'RESOURCE_TYPE_USER'
+  | 'RESOURCE_TYPE_ENVIRONMENT_CLASS'
+  | 'RESOURCE_TYPE_RUNNER_SCM_INTEGRATION'
+  | 'RESOURCE_TYPE_HOST_AUTHENTICATION_TOKEN'
+  | 'RESOURCE_TYPE_GROUP'
+  | 'RESOURCE_TYPE_PERSONAL_ACCESS_TOKEN'
+  | 'RESOURCE_TYPE_USER_PREFERENCE'
+  | 'RESOURCE_TYPE_SERVICE_ACCOUNT'
+  | 'RESOURCE_TYPE_SECRET'
+  | 'RESOURCE_TYPE_SSO_CONFIG'
+  | 'RESOURCE_TYPE_DOMAIN_VERIFICATION'
+  | 'RESOURCE_TYPE_AGENT_EXECUTION'
+  | 'RESOURCE_TYPE_RUNNER_LLM_INTEGRATION'
+  | 'RESOURCE_TYPE_AGENT'
+  | 'RESOURCE_TYPE_ENVIRONMENT_SESSION'
+  | 'RESOURCE_TYPE_USER_SECRET'
+  | 'RESOURCE_TYPE_ORGANIZATION_POLICY';
+
+export interface EventListResponse {
+  id?: string;
+
+  action?: string;
+
+  actorId?: string;
+
+  actorPrincipal?: Shared.Principal;
+
+  /**
+   * A Timestamp represents a point in time independent of any time zone or local
+   * calendar, encoded as a count of seconds and fractions of seconds at nanosecond
+   * resolution. The count is relative to an epoch at UTC midnight on January 1,
+   * 1970, in the proleptic Gregorian calendar which extends the Gregorian calendar
+   * backwards to year one.
+   *
+   * All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
+   * second table is needed for interpretation, using a
+   * [24-hour linear smear](https://developers.google.com/time/smear).
+   *
+   * The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
+   * restricting to that range, we ensure that we can convert to and from
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
+   *
+   * # Examples
+   *
+   * Example 1: Compute Timestamp from POSIX `time()`.
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(time(NULL));
+   *      timestamp.set_nanos(0);
+   *
+   * Example 2: Compute Timestamp from POSIX `gettimeofday()`.
+   *
+   *      struct timeval tv;
+   *      gettimeofday(&tv, NULL);
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(tv.tv_sec);
+   *      timestamp.set_nanos(tv.tv_usec * 1000);
+   *
+   * Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
+   *
+   *      FILETIME ft;
+   *      GetSystemTimeAsFileTime(&ft);
+   *      UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
+   *
+   *      // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
+   *      // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
+   *      timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
+   *
+   * Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
+   *
+   *      long millis = System.currentTimeMillis();
+   *
+   *      Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
+   *          .setNanos((int) ((millis % 1000) * 1000000)).build();
+   *
+   * Example 5: Compute Timestamp from Java `Instant.now()`.
+   *
+   *      Instant now = Instant.now();
+   *
+   *      Timestamp timestamp =
+   *          Timestamp.newBuilder().setSeconds(now.getEpochSecond())
+   *              .setNanos(now.getNano()).build();
+   *
+   * Example 6: Compute Timestamp from current time in Python.
+   *
+   *      timestamp = Timestamp()
+   *      timestamp.GetCurrentTime()
+   *
+   * # JSON Mapping
+   *
+   * In JSON format, the Timestamp type is encoded as a string in the
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the format is
+   * "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" where {year} is always
+   * expressed using four digits while {month}, {day}, {hour}, {min}, and {sec} are
+   * zero-padded to two digits each. The fractional seconds, which can go up to 9
+   * digits (i.e. up to 1 nanosecond resolution), are optional. The "Z" suffix
+   * indicates the timezone ("UTC"); the timezone is required. A proto3 JSON
+   * serializer should always use UTC (as indicated by "Z") when printing the
+   * Timestamp type and a proto3 JSON parser should be able to accept both UTC and
+   * other timezones (as indicated by an offset).
+   *
+   * For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past 01:30 UTC on
+   * January 15, 2017.
+   *
+   * In JavaScript, one can convert a Date object to this format using the standard
+   * [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
+   * method. In Python, a standard `datetime.datetime` object can be converted to
+   * this format using
+   * [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with the
+   * time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use the
+   * Joda Time's
+   * [`ISODateTimeFormat.dateTime()`](<http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime()>)
+   * to obtain a formatter capable of generating timestamps in this format.
+   */
+  createdAt?: string;
+
+  subjectId?: string;
+
+  subjectType?: ResourceType;
+}
+
+export interface EventWatchResponse {
+  operation?: ResourceOperation;
+
+  resourceId?: string;
+
+  resourceType?: ResourceType;
+}
+
+export interface EventListParams extends EntriesPageParams {
+  /**
+   * Body param:
+   */
+  filter?: EventListParams.Filter;
+
+  /**
+   * Body param: pagination contains the pagination options for listing environments
+   */
+  pagination?: EventListParams.Pagination;
+}
+
+export namespace EventListParams {
+  export interface Filter {
+    actorIds?: Array<string>;
+
+    actorPrincipals?: Array<Shared.Principal>;
+
+    subjectIds?: Array<string>;
+
+    subjectTypes?: Array<EventsAPI.ResourceType>;
+  }
+
+  /**
+   * pagination contains the pagination options for listing environments
+   */
+  export interface Pagination {
+    /**
+     * Token for the next set of results that was returned as next_token of a
+     * PaginationResponse
+     */
+    token?: string;
+
+    /**
+     * Page size is the maximum number of results to retrieve per page. Defaults to 25.
+     * Maximum 100.
+     */
+    pageSize?: number;
+  }
+}
+
+export interface EventWatchParams {
+  /**
+   * Environment scope produces events for the environment itself, all tasks, task
+   * executions, and services associated with that environment.
+   */
+  environmentId?: string;
+
+  /**
+   * Organization scope produces events for all projects, runners and environments
+   * the caller can see within their organization. No task, task execution or service
+   * events are produed.
+   */
+  organization?: boolean;
+}
+
+export declare namespace Events {
+  export {
+    type ResourceOperation as ResourceOperation,
+    type ResourceType as ResourceType,
+    type EventListResponse as EventListResponse,
+    type EventWatchResponse as EventWatchResponse,
+    type EventListResponsesEntriesPage as EventListResponsesEntriesPage,
+    type EventListParams as EventListParams,
+    type EventWatchParams as EventWatchParams,
+  };
+}
diff --git a/src/resources/groups.ts b/src/resources/groups.ts
new file mode 100644
index 0000000..56384a4
--- /dev/null
+++ b/src/resources/groups.ts
@@ -0,0 +1,281 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../core/resource';
+import { GroupsPage, type GroupsPageParams, PagePromise } from '../core/pagination';
+import { RequestOptions } from '../internal/request-options';
+
+export class Groups extends APIResource {
+  /**
+   * Lists groups with optional pagination.
+   *
+   * Use this method to:
+   *
+   * - View all groups
+   * - Check group memberships
+   * - Monitor group configurations
+   * - Audit group access
+   *
+   * ### Examples
+   *
+   * - List all groups:
+   *
+   *   Shows all groups with pagination.
+   *
+   *   ```yaml
+   *   pagination:
+   *     pageSize: 20
+   *   ```
+   *
+   * - List with custom page size:
+   *
+   *   Shows groups with specified page size.
+   *
+   *   ```yaml
+   *   pagination:
+   *     pageSize: 50
+   *     token: "next-page-token-from-previous-response"
+   *   ```
+   */
+  list(params: GroupListParams, options?: RequestOptions): PagePromise<GroupsGroupsPage, Group> {
+    const { token, pageSize, ...body } = params;
+    return this._client.getAPIList('/gitpod.v1.GroupService/ListGroups', GroupsPage<Group>, {
+      query: { token, pageSize },
+      body,
+      method: 'post',
+      ...options,
+    });
+  }
+}
+
+export type GroupsGroupsPage = GroupsPage<Group>;
+
+export interface Group {
+  id?: string;
+
+  /**
+   * A Timestamp represents a point in time independent of any time zone or local
+   * calendar, encoded as a count of seconds and fractions of seconds at nanosecond
+   * resolution. The count is relative to an epoch at UTC midnight on January 1,
+   * 1970, in the proleptic Gregorian calendar which extends the Gregorian calendar
+   * backwards to year one.
+   *
+   * All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
+   * second table is needed for interpretation, using a
+   * [24-hour linear smear](https://developers.google.com/time/smear).
+   *
+   * The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
+   * restricting to that range, we ensure that we can convert to and from
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
+   *
+   * # Examples
+   *
+   * Example 1: Compute Timestamp from POSIX `time()`.
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(time(NULL));
+   *      timestamp.set_nanos(0);
+   *
+   * Example 2: Compute Timestamp from POSIX `gettimeofday()`.
+   *
+   *      struct timeval tv;
+   *      gettimeofday(&tv, NULL);
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(tv.tv_sec);
+   *      timestamp.set_nanos(tv.tv_usec * 1000);
+   *
+   * Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
+   *
+   *      FILETIME ft;
+   *      GetSystemTimeAsFileTime(&ft);
+   *      UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
+   *
+   *      // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
+   *      // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
+   *      timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
+   *
+   * Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
+   *
+   *      long millis = System.currentTimeMillis();
+   *
+   *      Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
+   *          .setNanos((int) ((millis % 1000) * 1000000)).build();
+   *
+   * Example 5: Compute Timestamp from Java `Instant.now()`.
+   *
+   *      Instant now = Instant.now();
+   *
+   *      Timestamp timestamp =
+   *          Timestamp.newBuilder().setSeconds(now.getEpochSecond())
+   *              .setNanos(now.getNano()).build();
+   *
+   * Example 6: Compute Timestamp from current time in Python.
+   *
+   *      timestamp = Timestamp()
+   *      timestamp.GetCurrentTime()
+   *
+   * # JSON Mapping
+   *
+   * In JSON format, the Timestamp type is encoded as a string in the
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the format is
+   * "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" where {year} is always
+   * expressed using four digits while {month}, {day}, {hour}, {min}, and {sec} are
+   * zero-padded to two digits each. The fractional seconds, which can go up to 9
+   * digits (i.e. up to 1 nanosecond resolution), are optional. The "Z" suffix
+   * indicates the timezone ("UTC"); the timezone is required. A proto3 JSON
+   * serializer should always use UTC (as indicated by "Z") when printing the
+   * Timestamp type and a proto3 JSON parser should be able to accept both UTC and
+   * other timezones (as indicated by an offset).
+   *
+   * For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past 01:30 UTC on
+   * January 15, 2017.
+   *
+   * In JavaScript, one can convert a Date object to this format using the standard
+   * [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
+   * method. In Python, a standard `datetime.datetime` object can be converted to
+   * this format using
+   * [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with the
+   * time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use the
+   * Joda Time's
+   * [`ISODateTimeFormat.dateTime()`](<http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime()>)
+   * to obtain a formatter capable of generating timestamps in this format.
+   */
+  createdAt?: string;
+
+  name?: string;
+
+  organizationId?: string;
+
+  /**
+   * system_managed indicates that this group is created by the system automatically
+   */
+  systemManaged?: boolean;
+
+  /**
+   * A Timestamp represents a point in time independent of any time zone or local
+   * calendar, encoded as a count of seconds and fractions of seconds at nanosecond
+   * resolution. The count is relative to an epoch at UTC midnight on January 1,
+   * 1970, in the proleptic Gregorian calendar which extends the Gregorian calendar
+   * backwards to year one.
+   *
+   * All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
+   * second table is needed for interpretation, using a
+   * [24-hour linear smear](https://developers.google.com/time/smear).
+   *
+   * The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
+   * restricting to that range, we ensure that we can convert to and from
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
+   *
+   * # Examples
+   *
+   * Example 1: Compute Timestamp from POSIX `time()`.
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(time(NULL));
+   *      timestamp.set_nanos(0);
+   *
+   * Example 2: Compute Timestamp from POSIX `gettimeofday()`.
+   *
+   *      struct timeval tv;
+   *      gettimeofday(&tv, NULL);
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(tv.tv_sec);
+   *      timestamp.set_nanos(tv.tv_usec * 1000);
+   *
+   * Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
+   *
+   *      FILETIME ft;
+   *      GetSystemTimeAsFileTime(&ft);
+   *      UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
+   *
+   *      // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
+   *      // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
+   *      timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
+   *
+   * Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
+   *
+   *      long millis = System.currentTimeMillis();
+   *
+   *      Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
+   *          .setNanos((int) ((millis % 1000) * 1000000)).build();
+   *
+   * Example 5: Compute Timestamp from Java `Instant.now()`.
+   *
+   *      Instant now = Instant.now();
+   *
+   *      Timestamp timestamp =
+   *          Timestamp.newBuilder().setSeconds(now.getEpochSecond())
+   *              .setNanos(now.getNano()).build();
+   *
+   * Example 6: Compute Timestamp from current time in Python.
+   *
+   *      timestamp = Timestamp()
+   *      timestamp.GetCurrentTime()
+   *
+   * # JSON Mapping
+   *
+   * In JSON format, the Timestamp type is encoded as a string in the
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the format is
+   * "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" where {year} is always
+   * expressed using four digits while {month}, {day}, {hour}, {min}, and {sec} are
+   * zero-padded to two digits each. The fractional seconds, which can go up to 9
+   * digits (i.e. up to 1 nanosecond resolution), are optional. The "Z" suffix
+   * indicates the timezone ("UTC"); the timezone is required. A proto3 JSON
+   * serializer should always use UTC (as indicated by "Z") when printing the
+   * Timestamp type and a proto3 JSON parser should be able to accept both UTC and
+   * other timezones (as indicated by an offset).
+   *
+   * For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past 01:30 UTC on
+   * January 15, 2017.
+   *
+   * In JavaScript, one can convert a Date object to this format using the standard
+   * [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
+   * method. In Python, a standard `datetime.datetime` object can be converted to
+   * this format using
+   * [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with the
+   * time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use the
+   * Joda Time's
+   * [`ISODateTimeFormat.dateTime()`](<http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime()>)
+   * to obtain a formatter capable of generating timestamps in this format.
+   */
+  updatedAt?: string;
+}
+
+export interface GroupListParams extends GroupsPageParams {
+  /**
+   * Body param: pagination contains the pagination options for listing groups
+   */
+  pagination?: GroupListParams.Pagination;
+}
+
+export namespace GroupListParams {
+  /**
+   * pagination contains the pagination options for listing groups
+   */
+  export interface Pagination {
+    /**
+     * Token for the next set of results that was returned as next_token of a
+     * PaginationResponse
+     */
+    token?: string;
+
+    /**
+     * Page size is the maximum number of results to retrieve per page. Defaults to 25.
+     * Maximum 100.
+     */
+    pageSize?: number;
+  }
+}
+
+export declare namespace Groups {
+  export {
+    type Group as Group,
+    type GroupsGroupsPage as GroupsGroupsPage,
+    type GroupListParams as GroupListParams,
+  };
+}
diff --git a/src/resources/identity.ts b/src/resources/identity.ts
new file mode 100644
index 0000000..d1fa01f
--- /dev/null
+++ b/src/resources/identity.ts
@@ -0,0 +1,152 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../core/resource';
+import * as Shared from './shared';
+import { APIPromise } from '../core/api-promise';
+import { RequestOptions } from '../internal/request-options';
+
+export class Identity extends APIResource {
+  /**
+   * Exchanges an exchange token for a new access token.
+   *
+   * Use this method to:
+   *
+   * - Convert exchange tokens to access tokens
+   * - Obtain new access credentials
+   * - Complete token exchange flows
+   *
+   * ### Examples
+   *
+   * - Exchange token:
+   *
+   *   Trades an exchange token for an access token.
+   *
+   *   ```yaml
+   *   exchangeToken: "exchange-token-value"
+   *   ```
+   */
+  exchangeToken(
+    body: IdentityExchangeTokenParams,
+    options?: RequestOptions,
+  ): APIPromise<IdentityExchangeTokenResponse> {
+    return this._client.post('/gitpod.v1.IdentityService/ExchangeToken', { body, ...options });
+  }
+
+  /**
+   * Retrieves information about the currently authenticated identity.
+   *
+   * Use this method to:
+   *
+   * - Get current user information
+   * - Check authentication status
+   * - Retrieve organization context
+   * - Validate authentication principal
+   *
+   * ### Examples
+   *
+   * - Get current identity:
+   *
+   *   Retrieves details about the authenticated user.
+   *
+   *   ```yaml
+   *   {}
+   *   ```
+   */
+  getAuthenticatedIdentity(
+    body: IdentityGetAuthenticatedIdentityParams,
+    options?: RequestOptions,
+  ): APIPromise<IdentityGetAuthenticatedIdentityResponse> {
+    return this._client.post('/gitpod.v1.IdentityService/GetAuthenticatedIdentity', { body, ...options });
+  }
+
+  /**
+   * Gets an ID token for authenticating with other services.
+   *
+   * Use this method to:
+   *
+   * - Obtain authentication tokens for service-to-service calls
+   * - Access protected resources
+   * - Generate scoped access tokens
+   *
+   * ### Examples
+   *
+   * - Get token for single service:
+   *
+   *   Retrieves a token for authenticating with one service.
+   *
+   *   ```yaml
+   *   audience:
+   *     - "https://api.gitpod.io"
+   *   ```
+   *
+   * - Get token for multiple services:
+   *
+   *   Retrieves a token valid for multiple services.
+   *
+   *   ```yaml
+   *   audience:
+   *     - "https://api.gitpod.io"
+   *     - "https://ws.gitpod.io"
+   *   ```
+   */
+  getIDToken(
+    body: IdentityGetIDTokenParams,
+    options?: RequestOptions,
+  ): APIPromise<IdentityGetIDTokenResponse> {
+    return this._client.post('/gitpod.v1.IdentityService/GetIDToken', { body, ...options });
+  }
+}
+
+export type IDTokenVersion = 'ID_TOKEN_VERSION_UNSPECIFIED' | 'ID_TOKEN_VERSION_V1' | 'ID_TOKEN_VERSION_V2';
+
+export interface IdentityExchangeTokenResponse {
+  /**
+   * access_token is the new access token
+   */
+  accessToken?: string;
+}
+
+export interface IdentityGetAuthenticatedIdentityResponse {
+  organizationId?: string;
+
+  /**
+   * subject is the identity of the current user
+   */
+  subject?: Shared.Subject;
+}
+
+export interface IdentityGetIDTokenResponse {
+  token?: string;
+}
+
+export interface IdentityExchangeTokenParams {
+  /**
+   * exchange_token is the token to exchange
+   */
+  exchangeToken?: string;
+}
+
+export interface IdentityGetAuthenticatedIdentityParams {
+  empty?: boolean;
+}
+
+export interface IdentityGetIDTokenParams {
+  audience?: Array<string>;
+
+  /**
+   * version is the version of the ID token.
+   */
+  version?: IDTokenVersion;
+}
+
+export declare namespace Identity {
+  export {
+    type IDTokenVersion as IDTokenVersion,
+    type IdentityExchangeTokenResponse as IdentityExchangeTokenResponse,
+    type IdentityGetAuthenticatedIdentityResponse as IdentityGetAuthenticatedIdentityResponse,
+    type IdentityGetIDTokenResponse as IdentityGetIDTokenResponse,
+    type IdentityExchangeTokenParams as IdentityExchangeTokenParams,
+    type IdentityGetAuthenticatedIdentityParams as IdentityGetAuthenticatedIdentityParams,
+    type IdentityGetIDTokenParams as IdentityGetIDTokenParams,
+  };
+}
diff --git a/src/resources/index.ts b/src/resources/index.ts
new file mode 100644
index 0000000..664e230
--- /dev/null
+++ b/src/resources/index.ts
@@ -0,0 +1,182 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+export * from './shared';
+export {
+  Accounts,
+  type Account,
+  type AccountMembership,
+  type JoinableOrganization,
+  type LoginProvider,
+  type AccountRetrieveResponse,
+  type AccountDeleteResponse,
+  type AccountGetSSOLoginURLResponse,
+  type AccountRetrieveParams,
+  type AccountDeleteParams,
+  type AccountGetSSOLoginURLParams,
+  type AccountListLoginProvidersParams,
+  type LoginProvidersLoginProvidersPage,
+} from './accounts';
+export {
+  Editors,
+  type Editor,
+  type EditorRetrieveResponse,
+  type EditorResolveURLResponse,
+  type EditorRetrieveParams,
+  type EditorListParams,
+  type EditorResolveURLParams,
+  type EditorsEditorsPage,
+} from './editors';
+export {
+  Environments,
+  type AdmissionLevel,
+  type Environment,
+  type EnvironmentActivitySignal,
+  type EnvironmentMetadata,
+  type EnvironmentPhase,
+  type EnvironmentSpec,
+  type EnvironmentStatus,
+  type EnvironmentCreateResponse,
+  type EnvironmentRetrieveResponse,
+  type EnvironmentUpdateResponse,
+  type EnvironmentDeleteResponse,
+  type EnvironmentCreateEnvironmentTokenResponse,
+  type EnvironmentCreateFromProjectResponse,
+  type EnvironmentCreateLogsTokenResponse,
+  type EnvironmentMarkActiveResponse,
+  type EnvironmentStartResponse,
+  type EnvironmentStopResponse,
+  type EnvironmentCreateParams,
+  type EnvironmentRetrieveParams,
+  type EnvironmentUpdateParams,
+  type EnvironmentListParams,
+  type EnvironmentDeleteParams,
+  type EnvironmentCreateEnvironmentTokenParams,
+  type EnvironmentCreateFromProjectParams,
+  type EnvironmentCreateLogsTokenParams,
+  type EnvironmentMarkActiveParams,
+  type EnvironmentStartParams,
+  type EnvironmentStopParams,
+  type EnvironmentsEnvironmentsPage,
+} from './environments/environments';
+export {
+  Events,
+  type ResourceOperation,
+  type ResourceType,
+  type EventListResponse,
+  type EventWatchResponse,
+  type EventListParams,
+  type EventWatchParams,
+  type EventListResponsesEntriesPage,
+} from './events';
+export { Groups, type Group, type GroupListParams, type GroupsGroupsPage } from './groups';
+export {
+  Identity,
+  type IDTokenVersion,
+  type IdentityExchangeTokenResponse,
+  type IdentityGetAuthenticatedIdentityResponse,
+  type IdentityGetIDTokenResponse,
+  type IdentityExchangeTokenParams,
+  type IdentityGetAuthenticatedIdentityParams,
+  type IdentityGetIDTokenParams,
+} from './identity';
+export {
+  Organizations,
+  type InviteDomains,
+  type Organization,
+  type OrganizationMember,
+  type OrganizationTier,
+  type OrganizationCreateResponse,
+  type OrganizationRetrieveResponse,
+  type OrganizationUpdateResponse,
+  type OrganizationDeleteResponse,
+  type OrganizationJoinResponse,
+  type OrganizationLeaveResponse,
+  type OrganizationSetRoleResponse,
+  type OrganizationCreateParams,
+  type OrganizationRetrieveParams,
+  type OrganizationUpdateParams,
+  type OrganizationDeleteParams,
+  type OrganizationJoinParams,
+  type OrganizationLeaveParams,
+  type OrganizationListMembersParams,
+  type OrganizationSetRoleParams,
+  type OrganizationMembersMembersPage,
+} from './organizations/organizations';
+export {
+  Projects,
+  type EnvironmentInitializer,
+  type Project,
+  type ProjectEnvironmentClass,
+  type ProjectMetadata,
+  type ProjectCreateResponse,
+  type ProjectRetrieveResponse,
+  type ProjectUpdateResponse,
+  type ProjectDeleteResponse,
+  type ProjectCreateFromEnvironmentResponse,
+  type ProjectCreateParams,
+  type ProjectRetrieveParams,
+  type ProjectUpdateParams,
+  type ProjectListParams,
+  type ProjectDeleteParams,
+  type ProjectCreateFromEnvironmentParams,
+  type ProjectsProjectsPage,
+} from './projects/projects';
+export {
+  Runners,
+  type LogLevel,
+  type MetricsConfiguration,
+  type Runner,
+  type RunnerCapability,
+  type RunnerConfiguration,
+  type RunnerKind,
+  type RunnerPhase,
+  type RunnerProvider,
+  type RunnerReleaseChannel,
+  type RunnerSpec,
+  type RunnerStatus,
+  type RunnerCreateResponse,
+  type RunnerRetrieveResponse,
+  type RunnerUpdateResponse,
+  type RunnerDeleteResponse,
+  type RunnerCheckAuthenticationForHostResponse,
+  type RunnerCreateRunnerTokenResponse,
+  type RunnerParseContextURLResponse,
+  type RunnerCreateParams,
+  type RunnerRetrieveParams,
+  type RunnerUpdateParams,
+  type RunnerListParams,
+  type RunnerDeleteParams,
+  type RunnerCheckAuthenticationForHostParams,
+  type RunnerCreateRunnerTokenParams,
+  type RunnerParseContextURLParams,
+  type RunnersRunnersPage,
+} from './runners/runners';
+export {
+  Secrets,
+  type Secret,
+  type SecretScope,
+  type SecretCreateResponse,
+  type SecretDeleteResponse,
+  type SecretGetValueResponse,
+  type SecretUpdateValueResponse,
+  type SecretCreateParams,
+  type SecretListParams,
+  type SecretDeleteParams,
+  type SecretGetValueParams,
+  type SecretUpdateValueParams,
+  type SecretsSecretsPage,
+} from './secrets';
+export {
+  Usage,
+  type EnvironmentSession,
+  type UsageListEnvironmentSessionsParams,
+  type EnvironmentSessionsSessionsPage,
+} from './usage';
+export {
+  Users,
+  type User,
+  type UserGetAuthenticatedUserResponse,
+  type UserSetSuspendedResponse,
+  type UserGetAuthenticatedUserParams,
+  type UserSetSuspendedParams,
+} from './users/users';
diff --git a/src/resources/organizations.ts b/src/resources/organizations.ts
new file mode 100644
index 0000000..61ddaf1
--- /dev/null
+++ b/src/resources/organizations.ts
@@ -0,0 +1,3 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+export * from './organizations/index';
diff --git a/src/resources/organizations/domain-verifications.ts b/src/resources/organizations/domain-verifications.ts
new file mode 100644
index 0000000..51e3a9e
--- /dev/null
+++ b/src/resources/organizations/domain-verifications.ts
@@ -0,0 +1,449 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../../core/resource';
+import { APIPromise } from '../../core/api-promise';
+import {
+  DomainVerificationsPage,
+  type DomainVerificationsPageParams,
+  PagePromise,
+} from '../../core/pagination';
+import { RequestOptions } from '../../internal/request-options';
+
+export class DomainVerifications extends APIResource {
+  /**
+   * Initiates domain verification process to enable organization features.
+   *
+   * Use this method to:
+   *
+   * - Start domain ownership verification
+   * - Enable automatic team joining
+   * - Set up SSO restrictions
+   * - Configure email-based policies
+   *
+   * ### Examples
+   *
+   * - Verify primary domain:
+   *
+   *   Starts verification for main company domain.
+   *
+   *   ```yaml
+   *   organizationId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   domain: "acme-corp.com"
+   *   ```
+   *
+   * - Verify subsidiary domain:
+   *
+   *   Adds verification for additional company domain.
+   *
+   *   ```yaml
+   *   organizationId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   domain: "acme-subsidiary.com"
+   *   ```
+   */
+  create(
+    body: DomainVerificationCreateParams,
+    options?: RequestOptions,
+  ): APIPromise<DomainVerificationCreateResponse> {
+    return this._client.post('/gitpod.v1.OrganizationService/CreateDomainVerification', { body, ...options });
+  }
+
+  /**
+   * Retrieves the status of a domain verification request.
+   *
+   * Use this method to:
+   *
+   * - Check verification progress
+   * - View verification requirements
+   * - Monitor domain status
+   *
+   * ### Examples
+   *
+   * - Get verification status:
+   *
+   *   Checks the current state of a domain verification.
+   *
+   *   ```yaml
+   *   domainVerificationId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   ```
+   */
+  retrieve(
+    body: DomainVerificationRetrieveParams,
+    options?: RequestOptions,
+  ): APIPromise<DomainVerificationRetrieveResponse> {
+    return this._client.post('/gitpod.v1.OrganizationService/GetDomainVerification', { body, ...options });
+  }
+
+  /**
+   * Lists and monitors domain verification status across an organization.
+   *
+   * Use this method to:
+   *
+   * - Track verification progress
+   * - View all verified domains
+   * - Monitor pending verifications
+   * - Audit domain settings
+   *
+   * ### Examples
+   *
+   * - List all verifications:
+   *
+   *   Shows all domain verifications regardless of status.
+   *
+   *   ```yaml
+   *   organizationId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   pagination:
+   *     pageSize: 20
+   *   ```
+   *
+   * - List with pagination:
+   *
+   *   Retrieves next page of verifications.
+   *
+   *   ```yaml
+   *   organizationId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   pagination:
+   *     pageSize: 20
+   *     token: "next-page-token-from-previous-response"
+   *   ```
+   */
+  list(
+    params: DomainVerificationListParams,
+    options?: RequestOptions,
+  ): PagePromise<DomainVerificationsDomainVerificationsPage, DomainVerification> {
+    const { token, pageSize, ...body } = params;
+    return this._client.getAPIList(
+      '/gitpod.v1.OrganizationService/ListDomainVerifications',
+      DomainVerificationsPage<DomainVerification>,
+      { query: { token, pageSize }, body, method: 'post', ...options },
+    );
+  }
+
+  /**
+   * Removes a domain verification request.
+   *
+   * Use this method to:
+   *
+   * - Cancel pending verifications
+   * - Remove verified domains
+   * - Clean up unused domain records
+   *
+   * ### Examples
+   *
+   * - Delete verification:
+   *
+   *   Removes a domain verification request.
+   *
+   *   ```yaml
+   *   domainVerificationId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   ```
+   */
+  delete(body: DomainVerificationDeleteParams, options?: RequestOptions): APIPromise<unknown> {
+    return this._client.post('/gitpod.v1.OrganizationService/DeleteDomainVerification', { body, ...options });
+  }
+
+  /**
+   * Verifies domain ownership for an organization.
+   *
+   * Use this method to:
+   *
+   * - Complete domain verification process
+   * - Enable domain-based features
+   * - Validate DNS configuration
+   *
+   * ### Examples
+   *
+   * - Verify domain ownership:
+   *
+   *   Verifies ownership after DNS records are configured.
+   *
+   *   ```yaml
+   *   domainVerificationId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   ```
+   */
+  verify(
+    body: DomainVerificationVerifyParams,
+    options?: RequestOptions,
+  ): APIPromise<DomainVerificationVerifyResponse> {
+    return this._client.post('/gitpod.v1.OrganizationService/VerifyDomain', { body, ...options });
+  }
+}
+
+export type DomainVerificationsDomainVerificationsPage = DomainVerificationsPage<DomainVerification>;
+
+export interface DomainVerification {
+  id: string;
+
+  domain: string;
+
+  organizationId: string;
+
+  state: DomainVerificationState;
+
+  /**
+   * A Timestamp represents a point in time independent of any time zone or local
+   * calendar, encoded as a count of seconds and fractions of seconds at nanosecond
+   * resolution. The count is relative to an epoch at UTC midnight on January 1,
+   * 1970, in the proleptic Gregorian calendar which extends the Gregorian calendar
+   * backwards to year one.
+   *
+   * All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
+   * second table is needed for interpretation, using a
+   * [24-hour linear smear](https://developers.google.com/time/smear).
+   *
+   * The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
+   * restricting to that range, we ensure that we can convert to and from
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
+   *
+   * # Examples
+   *
+   * Example 1: Compute Timestamp from POSIX `time()`.
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(time(NULL));
+   *      timestamp.set_nanos(0);
+   *
+   * Example 2: Compute Timestamp from POSIX `gettimeofday()`.
+   *
+   *      struct timeval tv;
+   *      gettimeofday(&tv, NULL);
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(tv.tv_sec);
+   *      timestamp.set_nanos(tv.tv_usec * 1000);
+   *
+   * Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
+   *
+   *      FILETIME ft;
+   *      GetSystemTimeAsFileTime(&ft);
+   *      UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
+   *
+   *      // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
+   *      // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
+   *      timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
+   *
+   * Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
+   *
+   *      long millis = System.currentTimeMillis();
+   *
+   *      Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
+   *          .setNanos((int) ((millis % 1000) * 1000000)).build();
+   *
+   * Example 5: Compute Timestamp from Java `Instant.now()`.
+   *
+   *      Instant now = Instant.now();
+   *
+   *      Timestamp timestamp =
+   *          Timestamp.newBuilder().setSeconds(now.getEpochSecond())
+   *              .setNanos(now.getNano()).build();
+   *
+   * Example 6: Compute Timestamp from current time in Python.
+   *
+   *      timestamp = Timestamp()
+   *      timestamp.GetCurrentTime()
+   *
+   * # JSON Mapping
+   *
+   * In JSON format, the Timestamp type is encoded as a string in the
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the format is
+   * "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" where {year} is always
+   * expressed using four digits while {month}, {day}, {hour}, {min}, and {sec} are
+   * zero-padded to two digits each. The fractional seconds, which can go up to 9
+   * digits (i.e. up to 1 nanosecond resolution), are optional. The "Z" suffix
+   * indicates the timezone ("UTC"); the timezone is required. A proto3 JSON
+   * serializer should always use UTC (as indicated by "Z") when printing the
+   * Timestamp type and a proto3 JSON parser should be able to accept both UTC and
+   * other timezones (as indicated by an offset).
+   *
+   * For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past 01:30 UTC on
+   * January 15, 2017.
+   *
+   * In JavaScript, one can convert a Date object to this format using the standard
+   * [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
+   * method. In Python, a standard `datetime.datetime` object can be converted to
+   * this format using
+   * [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with the
+   * time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use the
+   * Joda Time's
+   * [`ISODateTimeFormat.dateTime()`](<http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime()>)
+   * to obtain a formatter capable of generating timestamps in this format.
+   */
+  createdAt?: string;
+
+  verificationToken?: string;
+
+  /**
+   * A Timestamp represents a point in time independent of any time zone or local
+   * calendar, encoded as a count of seconds and fractions of seconds at nanosecond
+   * resolution. The count is relative to an epoch at UTC midnight on January 1,
+   * 1970, in the proleptic Gregorian calendar which extends the Gregorian calendar
+   * backwards to year one.
+   *
+   * All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
+   * second table is needed for interpretation, using a
+   * [24-hour linear smear](https://developers.google.com/time/smear).
+   *
+   * The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
+   * restricting to that range, we ensure that we can convert to and from
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
+   *
+   * # Examples
+   *
+   * Example 1: Compute Timestamp from POSIX `time()`.
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(time(NULL));
+   *      timestamp.set_nanos(0);
+   *
+   * Example 2: Compute Timestamp from POSIX `gettimeofday()`.
+   *
+   *      struct timeval tv;
+   *      gettimeofday(&tv, NULL);
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(tv.tv_sec);
+   *      timestamp.set_nanos(tv.tv_usec * 1000);
+   *
+   * Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
+   *
+   *      FILETIME ft;
+   *      GetSystemTimeAsFileTime(&ft);
+   *      UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
+   *
+   *      // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
+   *      // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
+   *      timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
+   *
+   * Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
+   *
+   *      long millis = System.currentTimeMillis();
+   *
+   *      Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
+   *          .setNanos((int) ((millis % 1000) * 1000000)).build();
+   *
+   * Example 5: Compute Timestamp from Java `Instant.now()`.
+   *
+   *      Instant now = Instant.now();
+   *
+   *      Timestamp timestamp =
+   *          Timestamp.newBuilder().setSeconds(now.getEpochSecond())
+   *              .setNanos(now.getNano()).build();
+   *
+   * Example 6: Compute Timestamp from current time in Python.
+   *
+   *      timestamp = Timestamp()
+   *      timestamp.GetCurrentTime()
+   *
+   * # JSON Mapping
+   *
+   * In JSON format, the Timestamp type is encoded as a string in the
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the format is
+   * "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" where {year} is always
+   * expressed using four digits while {month}, {day}, {hour}, {min}, and {sec} are
+   * zero-padded to two digits each. The fractional seconds, which can go up to 9
+   * digits (i.e. up to 1 nanosecond resolution), are optional. The "Z" suffix
+   * indicates the timezone ("UTC"); the timezone is required. A proto3 JSON
+   * serializer should always use UTC (as indicated by "Z") when printing the
+   * Timestamp type and a proto3 JSON parser should be able to accept both UTC and
+   * other timezones (as indicated by an offset).
+   *
+   * For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past 01:30 UTC on
+   * January 15, 2017.
+   *
+   * In JavaScript, one can convert a Date object to this format using the standard
+   * [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
+   * method. In Python, a standard `datetime.datetime` object can be converted to
+   * this format using
+   * [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with the
+   * time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use the
+   * Joda Time's
+   * [`ISODateTimeFormat.dateTime()`](<http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime()>)
+   * to obtain a formatter capable of generating timestamps in this format.
+   */
+  verifiedAt?: string;
+}
+
+export type DomainVerificationState =
+  | 'DOMAIN_VERIFICATION_STATE_UNSPECIFIED'
+  | 'DOMAIN_VERIFICATION_STATE_PENDING'
+  | 'DOMAIN_VERIFICATION_STATE_VERIFIED';
+
+export interface DomainVerificationCreateResponse {
+  domainVerification: DomainVerification;
+}
+
+export interface DomainVerificationRetrieveResponse {
+  domainVerification: DomainVerification;
+}
+
+export type DomainVerificationDeleteResponse = unknown;
+
+export interface DomainVerificationVerifyResponse {
+  domainVerification: DomainVerification;
+}
+
+export interface DomainVerificationCreateParams {
+  domain: string;
+
+  organizationId: string;
+}
+
+export interface DomainVerificationRetrieveParams {
+  domainVerificationId: string;
+}
+
+export interface DomainVerificationListParams extends DomainVerificationsPageParams {
+  /**
+   * Body param:
+   */
+  organizationId: string;
+
+  /**
+   * Body param:
+   */
+  pagination?: DomainVerificationListParams.Pagination;
+}
+
+export namespace DomainVerificationListParams {
+  export interface Pagination {
+    /**
+     * Token for the next set of results that was returned as next_token of a
+     * PaginationResponse
+     */
+    token?: string;
+
+    /**
+     * Page size is the maximum number of results to retrieve per page. Defaults to 25.
+     * Maximum 100.
+     */
+    pageSize?: number;
+  }
+}
+
+export interface DomainVerificationDeleteParams {
+  domainVerificationId: string;
+}
+
+export interface DomainVerificationVerifyParams {
+  domainVerificationId: string;
+}
+
+export declare namespace DomainVerifications {
+  export {
+    type DomainVerification as DomainVerification,
+    type DomainVerificationState as DomainVerificationState,
+    type DomainVerificationCreateResponse as DomainVerificationCreateResponse,
+    type DomainVerificationRetrieveResponse as DomainVerificationRetrieveResponse,
+    type DomainVerificationDeleteResponse as DomainVerificationDeleteResponse,
+    type DomainVerificationVerifyResponse as DomainVerificationVerifyResponse,
+    type DomainVerificationsDomainVerificationsPage as DomainVerificationsDomainVerificationsPage,
+    type DomainVerificationCreateParams as DomainVerificationCreateParams,
+    type DomainVerificationRetrieveParams as DomainVerificationRetrieveParams,
+    type DomainVerificationListParams as DomainVerificationListParams,
+    type DomainVerificationDeleteParams as DomainVerificationDeleteParams,
+    type DomainVerificationVerifyParams as DomainVerificationVerifyParams,
+  };
+}
diff --git a/src/resources/organizations/index.ts b/src/resources/organizations/index.ts
new file mode 100644
index 0000000..a023d54
--- /dev/null
+++ b/src/resources/organizations/index.ts
@@ -0,0 +1,74 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+export {
+  DomainVerifications,
+  type DomainVerification,
+  type DomainVerificationState,
+  type DomainVerificationCreateResponse,
+  type DomainVerificationRetrieveResponse,
+  type DomainVerificationDeleteResponse,
+  type DomainVerificationVerifyResponse,
+  type DomainVerificationCreateParams,
+  type DomainVerificationRetrieveParams,
+  type DomainVerificationListParams,
+  type DomainVerificationDeleteParams,
+  type DomainVerificationVerifyParams,
+  type DomainVerificationsDomainVerificationsPage,
+} from './domain-verifications';
+export {
+  Invites,
+  type OrganizationInvite,
+  type InviteCreateResponse,
+  type InviteRetrieveResponse,
+  type InviteGetSummaryResponse,
+  type InviteCreateParams,
+  type InviteRetrieveParams,
+  type InviteGetSummaryParams,
+} from './invites';
+export {
+  Organizations,
+  type InviteDomains,
+  type Organization,
+  type OrganizationMember,
+  type OrganizationTier,
+  type OrganizationCreateResponse,
+  type OrganizationRetrieveResponse,
+  type OrganizationUpdateResponse,
+  type OrganizationDeleteResponse,
+  type OrganizationJoinResponse,
+  type OrganizationLeaveResponse,
+  type OrganizationSetRoleResponse,
+  type OrganizationCreateParams,
+  type OrganizationRetrieveParams,
+  type OrganizationUpdateParams,
+  type OrganizationDeleteParams,
+  type OrganizationJoinParams,
+  type OrganizationLeaveParams,
+  type OrganizationListMembersParams,
+  type OrganizationSetRoleParams,
+  type OrganizationMembersMembersPage,
+} from './organizations';
+export {
+  Policies,
+  type OrganizationPolicies,
+  type PolicyRetrieveResponse,
+  type PolicyUpdateResponse,
+  type PolicyRetrieveParams,
+  type PolicyUpdateParams,
+} from './policies';
+export {
+  SSOConfigurations,
+  type ProviderType,
+  type SSOConfiguration,
+  type SSOConfigurationState,
+  type SSOConfigurationCreateResponse,
+  type SSOConfigurationRetrieveResponse,
+  type SSOConfigurationUpdateResponse,
+  type SSOConfigurationDeleteResponse,
+  type SSOConfigurationCreateParams,
+  type SSOConfigurationRetrieveParams,
+  type SSOConfigurationUpdateParams,
+  type SSOConfigurationListParams,
+  type SSOConfigurationDeleteParams,
+  type SSOConfigurationsSSOConfigurationsPage,
+} from './sso-configurations';
diff --git a/src/resources/organizations/invites.ts b/src/resources/organizations/invites.ts
new file mode 100644
index 0000000..5dded81
--- /dev/null
+++ b/src/resources/organizations/invites.ts
@@ -0,0 +1,113 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../../core/resource';
+import { APIPromise } from '../../core/api-promise';
+import { RequestOptions } from '../../internal/request-options';
+
+export class Invites extends APIResource {
+  /**
+   * Creates an invite link for joining an organization. Any existing
+   * OrganizationInvites are invalidated and can no longer be used.
+   *
+   * Use this method to:
+   *
+   * - Generate shareable invite links
+   * - Manage team growth
+   * - Control organization access
+   *
+   * ### Examples
+   *
+   * - Create organization invite:
+   *
+   *   Generates a new invite link for the organization.
+   *
+   *   ```yaml
+   *   organizationId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   ```
+   */
+  create(body: InviteCreateParams, options?: RequestOptions): APIPromise<InviteCreateResponse> {
+    return this._client.post('/gitpod.v1.OrganizationService/CreateOrganizationInvite', { body, ...options });
+  }
+
+  /**
+   * GetOrganizationInvite
+   */
+  retrieve(body: InviteRetrieveParams, options?: RequestOptions): APIPromise<InviteRetrieveResponse> {
+    return this._client.post('/gitpod.v1.OrganizationService/GetOrganizationInvite', { body, ...options });
+  }
+
+  /**
+   * Retrieves organization details and membership info based on an invite link.
+   *
+   * Use this method to:
+   *
+   * - Preview organization details before joining
+   * - Validate invite link authenticity
+   * - Check organization size and activity
+   * - View team information before accepting
+   *
+   * ### Examples
+   *
+   * - Get invite summary:
+   *
+   *   Retrieves organization information from an invite.
+   *
+   *   ```yaml
+   *   inviteId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   ```
+   */
+  getSummary(body: InviteGetSummaryParams, options?: RequestOptions): APIPromise<InviteGetSummaryResponse> {
+    return this._client.post('/gitpod.v1.OrganizationService/GetOrganizationInviteSummary', {
+      body,
+      ...options,
+    });
+  }
+}
+
+export interface OrganizationInvite {
+  /**
+   * invite_id is the unique identifier of the invite to join the organization. Use
+   * JoinOrganization with this ID to join the organization.
+   */
+  inviteId: string;
+}
+
+export interface InviteCreateResponse {
+  invite: OrganizationInvite;
+}
+
+export interface InviteRetrieveResponse {
+  invite: OrganizationInvite;
+}
+
+export interface InviteGetSummaryResponse {
+  organizationId: string;
+
+  organizationMemberCount?: number;
+
+  organizationName?: string;
+}
+
+export interface InviteCreateParams {
+  organizationId: string;
+}
+
+export interface InviteRetrieveParams {
+  organizationId: string;
+}
+
+export interface InviteGetSummaryParams {
+  inviteId: string;
+}
+
+export declare namespace Invites {
+  export {
+    type OrganizationInvite as OrganizationInvite,
+    type InviteCreateResponse as InviteCreateResponse,
+    type InviteRetrieveResponse as InviteRetrieveResponse,
+    type InviteGetSummaryResponse as InviteGetSummaryResponse,
+    type InviteCreateParams as InviteCreateParams,
+    type InviteRetrieveParams as InviteRetrieveParams,
+    type InviteGetSummaryParams as InviteGetSummaryParams,
+  };
+}
diff --git a/src/resources/organizations/organizations.ts b/src/resources/organizations/organizations.ts
new file mode 100644
index 0000000..2393f17
--- /dev/null
+++ b/src/resources/organizations/organizations.ts
@@ -0,0 +1,882 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../../core/resource';
+import * as Shared from '../shared';
+import * as DomainVerificationsAPI from './domain-verifications';
+import {
+  DomainVerification,
+  DomainVerificationCreateParams,
+  DomainVerificationCreateResponse,
+  DomainVerificationDeleteParams,
+  DomainVerificationDeleteResponse,
+  DomainVerificationListParams,
+  DomainVerificationRetrieveParams,
+  DomainVerificationRetrieveResponse,
+  DomainVerificationState,
+  DomainVerificationVerifyParams,
+  DomainVerificationVerifyResponse,
+  DomainVerifications,
+  DomainVerificationsDomainVerificationsPage,
+} from './domain-verifications';
+import * as InvitesAPI from './invites';
+import {
+  InviteCreateParams,
+  InviteCreateResponse,
+  InviteGetSummaryParams,
+  InviteGetSummaryResponse,
+  InviteRetrieveParams,
+  InviteRetrieveResponse,
+  Invites,
+  OrganizationInvite,
+} from './invites';
+import * as PoliciesAPI from './policies';
+import {
+  OrganizationPolicies,
+  Policies,
+  PolicyRetrieveParams,
+  PolicyRetrieveResponse,
+  PolicyUpdateParams,
+  PolicyUpdateResponse,
+} from './policies';
+import * as SSOConfigurationsAPI from './sso-configurations';
+import {
+  ProviderType,
+  SSOConfiguration,
+  SSOConfigurationCreateParams,
+  SSOConfigurationCreateResponse,
+  SSOConfigurationDeleteParams,
+  SSOConfigurationDeleteResponse,
+  SSOConfigurationListParams,
+  SSOConfigurationRetrieveParams,
+  SSOConfigurationRetrieveResponse,
+  SSOConfigurationState,
+  SSOConfigurationUpdateParams,
+  SSOConfigurationUpdateResponse,
+  SSOConfigurations,
+  SSOConfigurationsSSOConfigurationsPage,
+} from './sso-configurations';
+import { APIPromise } from '../../core/api-promise';
+import { MembersPage, type MembersPageParams, PagePromise } from '../../core/pagination';
+import { RequestOptions } from '../../internal/request-options';
+
+export class Organizations extends APIResource {
+  domainVerifications: DomainVerificationsAPI.DomainVerifications =
+    new DomainVerificationsAPI.DomainVerifications(this._client);
+  invites: InvitesAPI.Invites = new InvitesAPI.Invites(this._client);
+  policies: PoliciesAPI.Policies = new PoliciesAPI.Policies(this._client);
+  ssoConfigurations: SSOConfigurationsAPI.SSOConfigurations = new SSOConfigurationsAPI.SSOConfigurations(
+    this._client,
+  );
+
+  /**
+   * Creates a new organization with the specified name and settings.
+   *
+   * Use this method to:
+   *
+   * - Create a new organization for team collaboration
+   * - Set up automatic domain-based invites for team members
+   * - Join the organization immediately upon creation
+   *
+   * ### Examples
+   *
+   * - Create a basic organization:
+   *
+   *   Creates an organization with just a name.
+   *
+   *   ```yaml
+   *   name: "Acme Corp Engineering"
+   *   joinOrganization: true
+   *   ```
+   *
+   * - Create with domain-based invites:
+   *
+   *   Creates an organization that automatically invites users with matching email
+   *   domains.
+   *
+   *   ```yaml
+   *   name: "Acme Corp"
+   *   joinOrganization: true
+   *   inviteAccountsWithMatchingDomain: true
+   *   ```
+   */
+  create(body: OrganizationCreateParams, options?: RequestOptions): APIPromise<OrganizationCreateResponse> {
+    return this._client.post('/gitpod.v1.OrganizationService/CreateOrganization', { body, ...options });
+  }
+
+  /**
+   * Gets details about a specific organization.
+   *
+   * Use this method to:
+   *
+   * - Retrieve organization settings and configuration
+   * - Check organization membership status
+   * - View domain verification settings
+   *
+   * ### Examples
+   *
+   * - Get organization details:
+   *
+   *   Retrieves information about a specific organization.
+   *
+   *   ```yaml
+   *   organizationId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   ```
+   */
+  retrieve(
+    body: OrganizationRetrieveParams,
+    options?: RequestOptions,
+  ): APIPromise<OrganizationRetrieveResponse> {
+    return this._client.post('/gitpod.v1.OrganizationService/GetOrganization', { body, ...options });
+  }
+
+  /**
+   * Updates an organization's settings including name, invite domains, and member
+   * policies.
+   *
+   * Use this method to:
+   *
+   * - Modify organization display name
+   * - Configure email domain restrictions
+   * - Update organization-wide settings
+   * - Manage member access policies
+   *
+   * ### Examples
+   *
+   * - Update basic settings:
+   *
+   *   Changes organization name and invite domains.
+   *
+   *   ```yaml
+   *   organizationId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   name: "New Company Name"
+   *   inviteDomains:
+   *     domains:
+   *       - "company.com"
+   *       - "subsidiary.com"
+   *   ```
+   *
+   * - Remove domain restrictions:
+   *
+   *   Clears all domain-based invite restrictions.
+   *
+   *   ```yaml
+   *   organizationId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   inviteDomains:
+   *     domains: []
+   *   ```
+   */
+  update(body: OrganizationUpdateParams, options?: RequestOptions): APIPromise<OrganizationUpdateResponse> {
+    return this._client.post('/gitpod.v1.OrganizationService/UpdateOrganization', { body, ...options });
+  }
+
+  /**
+   * Permanently deletes an organization.
+   *
+   * Use this method to:
+   *
+   * - Remove unused organizations
+   * - Clean up test organizations
+   * - Complete organization migration
+   *
+   * ### Examples
+   *
+   * - Delete organization:
+   *
+   *   Permanently removes an organization and all its data.
+   *
+   *   ```yaml
+   *   organizationId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   ```
+   */
+  delete(body: OrganizationDeleteParams, options?: RequestOptions): APIPromise<unknown> {
+    return this._client.post('/gitpod.v1.OrganizationService/DeleteOrganization', { body, ...options });
+  }
+
+  /**
+   * Allows users to join an organization through direct ID, invite link, or
+   * domain-based auto-join.
+   *
+   * Use this method to:
+   *
+   * - Join an organization via direct ID or invite
+   * - Join automatically based on email domain
+   * - Accept organization invitations
+   *
+   * ### Examples
+   *
+   * - Join via organization ID:
+   *
+   *   Joins an organization directly when you have the ID.
+   *
+   *   ```yaml
+   *   organizationId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   ```
+   *
+   * - Join via invite:
+   *
+   *   Accepts an organization invitation link.
+   *
+   *   ```yaml
+   *   inviteId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   ```
+   */
+  join(body: OrganizationJoinParams, options?: RequestOptions): APIPromise<OrganizationJoinResponse> {
+    return this._client.post('/gitpod.v1.OrganizationService/JoinOrganization', { body, ...options });
+  }
+
+  /**
+   * Removes a user from an organization while preserving organization data.
+   *
+   * Use this method to:
+   *
+   * - Remove yourself from an organization
+   * - Clean up inactive memberships
+   * - Transfer project ownership before leaving
+   * - Manage team transitions
+   *
+   * ### Examples
+   *
+   * - Leave organization:
+   *
+   *   Removes user from organization membership.
+   *
+   *   ```yaml
+   *   userId: "f53d2330-3795-4c5d-a1f3-453121af9c60"
+   *   ```
+   *
+   * Note: Ensure all projects and resources are transferred before leaving.
+   */
+  leave(body: OrganizationLeaveParams, options?: RequestOptions): APIPromise<unknown> {
+    return this._client.post('/gitpod.v1.OrganizationService/LeaveOrganization', { body, ...options });
+  }
+
+  /**
+   * Lists and filters organization members with optional pagination.
+   *
+   * Use this method to:
+   *
+   * - View all organization members
+   * - Monitor member activity
+   * - Manage team membership
+   *
+   * ### Examples
+   *
+   * - List active members:
+   *
+   *   Retrieves active members with pagination.
+   *
+   *   ```yaml
+   *   organizationId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   pagination:
+   *     pageSize: 20
+   *   ```
+   *
+   * - List with pagination:
+   *
+   *   Retrieves next page of members.
+   *
+   *   ```yaml
+   *   organizationId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   pagination:
+   *     pageSize: 50
+   *     token: "next-page-token-from-previous-response"
+   *   ```
+   */
+  listMembers(
+    params: OrganizationListMembersParams,
+    options?: RequestOptions,
+  ): PagePromise<OrganizationMembersMembersPage, OrganizationMember> {
+    const { token, pageSize, ...body } = params;
+    return this._client.getAPIList(
+      '/gitpod.v1.OrganizationService/ListMembers',
+      MembersPage<OrganizationMember>,
+      { query: { token, pageSize }, body, method: 'post', ...options },
+    );
+  }
+
+  /**
+   * Manages organization membership and roles by setting a user's role within the
+   * organization.
+   *
+   * Use this method to:
+   *
+   * - Promote members to admin role
+   * - Change member permissions
+   * - Demote admins to regular members
+   *
+   * ### Examples
+   *
+   * - Promote to admin:
+   *
+   *   Makes a user an organization administrator.
+   *
+   *   ```yaml
+   *   organizationId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   userId: "f53d2330-3795-4c5d-a1f3-453121af9c60"
+   *   role: ORGANIZATION_ROLE_ADMIN
+   *   ```
+   *
+   * - Change to member:
+   *
+   *   Changes a user's role to regular member.
+   *
+   *   ```yaml
+   *   organizationId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   userId: "f53d2330-3795-4c5d-a1f3-453121af9c60"
+   *   role: ORGANIZATION_ROLE_MEMBER
+   *   ```
+   */
+  setRole(body: OrganizationSetRoleParams, options?: RequestOptions): APIPromise<unknown> {
+    return this._client.post('/gitpod.v1.OrganizationService/SetRole', { body, ...options });
+  }
+}
+
+export type OrganizationMembersMembersPage = MembersPage<OrganizationMember>;
+
+export interface InviteDomains {
+  /**
+   * domains is the list of domains that are allowed to join the organization
+   */
+  domains?: Array<string>;
+}
+
+export interface Organization {
+  id: string;
+
+  /**
+   * A Timestamp represents a point in time independent of any time zone or local
+   * calendar, encoded as a count of seconds and fractions of seconds at nanosecond
+   * resolution. The count is relative to an epoch at UTC midnight on January 1,
+   * 1970, in the proleptic Gregorian calendar which extends the Gregorian calendar
+   * backwards to year one.
+   *
+   * All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
+   * second table is needed for interpretation, using a
+   * [24-hour linear smear](https://developers.google.com/time/smear).
+   *
+   * The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
+   * restricting to that range, we ensure that we can convert to and from
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
+   *
+   * # Examples
+   *
+   * Example 1: Compute Timestamp from POSIX `time()`.
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(time(NULL));
+   *      timestamp.set_nanos(0);
+   *
+   * Example 2: Compute Timestamp from POSIX `gettimeofday()`.
+   *
+   *      struct timeval tv;
+   *      gettimeofday(&tv, NULL);
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(tv.tv_sec);
+   *      timestamp.set_nanos(tv.tv_usec * 1000);
+   *
+   * Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
+   *
+   *      FILETIME ft;
+   *      GetSystemTimeAsFileTime(&ft);
+   *      UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
+   *
+   *      // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
+   *      // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
+   *      timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
+   *
+   * Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
+   *
+   *      long millis = System.currentTimeMillis();
+   *
+   *      Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
+   *          .setNanos((int) ((millis % 1000) * 1000000)).build();
+   *
+   * Example 5: Compute Timestamp from Java `Instant.now()`.
+   *
+   *      Instant now = Instant.now();
+   *
+   *      Timestamp timestamp =
+   *          Timestamp.newBuilder().setSeconds(now.getEpochSecond())
+   *              .setNanos(now.getNano()).build();
+   *
+   * Example 6: Compute Timestamp from current time in Python.
+   *
+   *      timestamp = Timestamp()
+   *      timestamp.GetCurrentTime()
+   *
+   * # JSON Mapping
+   *
+   * In JSON format, the Timestamp type is encoded as a string in the
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the format is
+   * "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" where {year} is always
+   * expressed using four digits while {month}, {day}, {hour}, {min}, and {sec} are
+   * zero-padded to two digits each. The fractional seconds, which can go up to 9
+   * digits (i.e. up to 1 nanosecond resolution), are optional. The "Z" suffix
+   * indicates the timezone ("UTC"); the timezone is required. A proto3 JSON
+   * serializer should always use UTC (as indicated by "Z") when printing the
+   * Timestamp type and a proto3 JSON parser should be able to accept both UTC and
+   * other timezones (as indicated by an offset).
+   *
+   * For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past 01:30 UTC on
+   * January 15, 2017.
+   *
+   * In JavaScript, one can convert a Date object to this format using the standard
+   * [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
+   * method. In Python, a standard `datetime.datetime` object can be converted to
+   * this format using
+   * [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with the
+   * time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use the
+   * Joda Time's
+   * [`ISODateTimeFormat.dateTime()`](<http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime()>)
+   * to obtain a formatter capable of generating timestamps in this format.
+   */
+  createdAt: string;
+
+  name: string;
+
+  /**
+   * The tier of the organization - free or enterprise
+   */
+  tier: OrganizationTier;
+
+  /**
+   * A Timestamp represents a point in time independent of any time zone or local
+   * calendar, encoded as a count of seconds and fractions of seconds at nanosecond
+   * resolution. The count is relative to an epoch at UTC midnight on January 1,
+   * 1970, in the proleptic Gregorian calendar which extends the Gregorian calendar
+   * backwards to year one.
+   *
+   * All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
+   * second table is needed for interpretation, using a
+   * [24-hour linear smear](https://developers.google.com/time/smear).
+   *
+   * The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
+   * restricting to that range, we ensure that we can convert to and from
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
+   *
+   * # Examples
+   *
+   * Example 1: Compute Timestamp from POSIX `time()`.
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(time(NULL));
+   *      timestamp.set_nanos(0);
+   *
+   * Example 2: Compute Timestamp from POSIX `gettimeofday()`.
+   *
+   *      struct timeval tv;
+   *      gettimeofday(&tv, NULL);
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(tv.tv_sec);
+   *      timestamp.set_nanos(tv.tv_usec * 1000);
+   *
+   * Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
+   *
+   *      FILETIME ft;
+   *      GetSystemTimeAsFileTime(&ft);
+   *      UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
+   *
+   *      // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
+   *      // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
+   *      timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
+   *
+   * Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
+   *
+   *      long millis = System.currentTimeMillis();
+   *
+   *      Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
+   *          .setNanos((int) ((millis % 1000) * 1000000)).build();
+   *
+   * Example 5: Compute Timestamp from Java `Instant.now()`.
+   *
+   *      Instant now = Instant.now();
+   *
+   *      Timestamp timestamp =
+   *          Timestamp.newBuilder().setSeconds(now.getEpochSecond())
+   *              .setNanos(now.getNano()).build();
+   *
+   * Example 6: Compute Timestamp from current time in Python.
+   *
+   *      timestamp = Timestamp()
+   *      timestamp.GetCurrentTime()
+   *
+   * # JSON Mapping
+   *
+   * In JSON format, the Timestamp type is encoded as a string in the
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the format is
+   * "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" where {year} is always
+   * expressed using four digits while {month}, {day}, {hour}, {min}, and {sec} are
+   * zero-padded to two digits each. The fractional seconds, which can go up to 9
+   * digits (i.e. up to 1 nanosecond resolution), are optional. The "Z" suffix
+   * indicates the timezone ("UTC"); the timezone is required. A proto3 JSON
+   * serializer should always use UTC (as indicated by "Z") when printing the
+   * Timestamp type and a proto3 JSON parser should be able to accept both UTC and
+   * other timezones (as indicated by an offset).
+   *
+   * For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past 01:30 UTC on
+   * January 15, 2017.
+   *
+   * In JavaScript, one can convert a Date object to this format using the standard
+   * [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
+   * method. In Python, a standard `datetime.datetime` object can be converted to
+   * this format using
+   * [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with the
+   * time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use the
+   * Joda Time's
+   * [`ISODateTimeFormat.dateTime()`](<http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime()>)
+   * to obtain a formatter capable of generating timestamps in this format.
+   */
+  updatedAt: string;
+
+  inviteDomains?: InviteDomains;
+}
+
+export interface OrganizationMember {
+  email: string;
+
+  fullName: string;
+
+  /**
+   * login_provider is the login provider the user uses to sign in
+   */
+  loginProvider: string;
+
+  /**
+   * A Timestamp represents a point in time independent of any time zone or local
+   * calendar, encoded as a count of seconds and fractions of seconds at nanosecond
+   * resolution. The count is relative to an epoch at UTC midnight on January 1,
+   * 1970, in the proleptic Gregorian calendar which extends the Gregorian calendar
+   * backwards to year one.
+   *
+   * All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
+   * second table is needed for interpretation, using a
+   * [24-hour linear smear](https://developers.google.com/time/smear).
+   *
+   * The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
+   * restricting to that range, we ensure that we can convert to and from
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
+   *
+   * # Examples
+   *
+   * Example 1: Compute Timestamp from POSIX `time()`.
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(time(NULL));
+   *      timestamp.set_nanos(0);
+   *
+   * Example 2: Compute Timestamp from POSIX `gettimeofday()`.
+   *
+   *      struct timeval tv;
+   *      gettimeofday(&tv, NULL);
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(tv.tv_sec);
+   *      timestamp.set_nanos(tv.tv_usec * 1000);
+   *
+   * Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
+   *
+   *      FILETIME ft;
+   *      GetSystemTimeAsFileTime(&ft);
+   *      UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
+   *
+   *      // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
+   *      // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
+   *      timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
+   *
+   * Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
+   *
+   *      long millis = System.currentTimeMillis();
+   *
+   *      Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
+   *          .setNanos((int) ((millis % 1000) * 1000000)).build();
+   *
+   * Example 5: Compute Timestamp from Java `Instant.now()`.
+   *
+   *      Instant now = Instant.now();
+   *
+   *      Timestamp timestamp =
+   *          Timestamp.newBuilder().setSeconds(now.getEpochSecond())
+   *              .setNanos(now.getNano()).build();
+   *
+   * Example 6: Compute Timestamp from current time in Python.
+   *
+   *      timestamp = Timestamp()
+   *      timestamp.GetCurrentTime()
+   *
+   * # JSON Mapping
+   *
+   * In JSON format, the Timestamp type is encoded as a string in the
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the format is
+   * "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" where {year} is always
+   * expressed using four digits while {month}, {day}, {hour}, {min}, and {sec} are
+   * zero-padded to two digits each. The fractional seconds, which can go up to 9
+   * digits (i.e. up to 1 nanosecond resolution), are optional. The "Z" suffix
+   * indicates the timezone ("UTC"); the timezone is required. A proto3 JSON
+   * serializer should always use UTC (as indicated by "Z") when printing the
+   * Timestamp type and a proto3 JSON parser should be able to accept both UTC and
+   * other timezones (as indicated by an offset).
+   *
+   * For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past 01:30 UTC on
+   * January 15, 2017.
+   *
+   * In JavaScript, one can convert a Date object to this format using the standard
+   * [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
+   * method. In Python, a standard `datetime.datetime` object can be converted to
+   * this format using
+   * [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with the
+   * time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use the
+   * Joda Time's
+   * [`ISODateTimeFormat.dateTime()`](<http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime()>)
+   * to obtain a formatter capable of generating timestamps in this format.
+   */
+  memberSince: string;
+
+  role: Shared.OrganizationRole;
+
+  status: Shared.UserStatus;
+
+  userId: string;
+
+  avatarUrl?: string;
+}
+
+export type OrganizationTier =
+  | 'ORGANIZATION_TIER_UNSPECIFIED'
+  | 'ORGANIZATION_TIER_FREE'
+  | 'ORGANIZATION_TIER_ENTERPRISE';
+
+export interface OrganizationCreateResponse {
+  /**
+   * organization is the created organization
+   */
+  organization: Organization;
+
+  /**
+   * member is the member that joined the org on creation. Only set if specified
+   * "join_organization" is "true" in the request.
+   */
+  member?: OrganizationMember;
+}
+
+export interface OrganizationRetrieveResponse {
+  /**
+   * organization is the requested organization
+   */
+  organization: Organization;
+}
+
+export interface OrganizationUpdateResponse {
+  /**
+   * organization is the updated organization
+   */
+  organization: Organization;
+}
+
+export type OrganizationDeleteResponse = unknown;
+
+export interface OrganizationJoinResponse {
+  /**
+   * member is the member that was created by joining the organization.
+   */
+  member: OrganizationMember;
+}
+
+export type OrganizationLeaveResponse = unknown;
+
+export type OrganizationSetRoleResponse = unknown;
+
+export interface OrganizationCreateParams {
+  /**
+   * name is the organization name
+   */
+  name: string;
+
+  /**
+   * Should other Accounts with the same domain be automatically invited to the
+   * organization?
+   */
+  inviteAccountsWithMatchingDomain?: boolean;
+
+  /**
+   * join_organization decides whether the Identity issuing this request joins the
+   * org on creation
+   */
+  joinOrganization?: boolean;
+}
+
+export interface OrganizationRetrieveParams {
+  /**
+   * organization_id is the unique identifier of the Organization to retreive.
+   */
+  organizationId: string;
+}
+
+export interface OrganizationUpdateParams {
+  /**
+   * organization_id is the ID of the organization to update the settings for.
+   */
+  organizationId: string;
+
+  /**
+   * invite_domains is the domain allowlist of the organization
+   */
+  inviteDomains?: InviteDomains | null;
+
+  /**
+   * name is the new name of the organization
+   */
+  name?: string | null;
+}
+
+export interface OrganizationDeleteParams {
+  /**
+   * organization_id is the ID of the organization to delete
+   */
+  organizationId: string;
+}
+
+export interface OrganizationJoinParams {
+  /**
+   * invite_id is the unique identifier of the invite to join the organization.
+   */
+  inviteId?: string;
+
+  /**
+   * organization_id is the unique identifier of the Organization to join.
+   */
+  organizationId?: string;
+}
+
+export interface OrganizationLeaveParams {
+  userId: string;
+}
+
+export interface OrganizationListMembersParams extends MembersPageParams {
+  /**
+   * Body param: organization_id is the ID of the organization to list members for
+   */
+  organizationId: string;
+
+  /**
+   * Body param: pagination contains the pagination options for listing members
+   */
+  pagination?: OrganizationListMembersParams.Pagination;
+}
+
+export namespace OrganizationListMembersParams {
+  /**
+   * pagination contains the pagination options for listing members
+   */
+  export interface Pagination {
+    /**
+     * Token for the next set of results that was returned as next_token of a
+     * PaginationResponse
+     */
+    token?: string;
+
+    /**
+     * Page size is the maximum number of results to retrieve per page. Defaults to 25.
+     * Maximum 100.
+     */
+    pageSize?: number;
+  }
+}
+
+export interface OrganizationSetRoleParams {
+  organizationId: string;
+
+  userId: string;
+
+  role?: Shared.OrganizationRole;
+}
+
+Organizations.DomainVerifications = DomainVerifications;
+Organizations.Invites = Invites;
+Organizations.Policies = Policies;
+Organizations.SSOConfigurations = SSOConfigurations;
+
+export declare namespace Organizations {
+  export {
+    type InviteDomains as InviteDomains,
+    type Organization as Organization,
+    type OrganizationMember as OrganizationMember,
+    type OrganizationTier as OrganizationTier,
+    type OrganizationCreateResponse as OrganizationCreateResponse,
+    type OrganizationRetrieveResponse as OrganizationRetrieveResponse,
+    type OrganizationUpdateResponse as OrganizationUpdateResponse,
+    type OrganizationDeleteResponse as OrganizationDeleteResponse,
+    type OrganizationJoinResponse as OrganizationJoinResponse,
+    type OrganizationLeaveResponse as OrganizationLeaveResponse,
+    type OrganizationSetRoleResponse as OrganizationSetRoleResponse,
+    type OrganizationMembersMembersPage as OrganizationMembersMembersPage,
+    type OrganizationCreateParams as OrganizationCreateParams,
+    type OrganizationRetrieveParams as OrganizationRetrieveParams,
+    type OrganizationUpdateParams as OrganizationUpdateParams,
+    type OrganizationDeleteParams as OrganizationDeleteParams,
+    type OrganizationJoinParams as OrganizationJoinParams,
+    type OrganizationLeaveParams as OrganizationLeaveParams,
+    type OrganizationListMembersParams as OrganizationListMembersParams,
+    type OrganizationSetRoleParams as OrganizationSetRoleParams,
+  };
+
+  export {
+    DomainVerifications as DomainVerifications,
+    type DomainVerification as DomainVerification,
+    type DomainVerificationState as DomainVerificationState,
+    type DomainVerificationCreateResponse as DomainVerificationCreateResponse,
+    type DomainVerificationRetrieveResponse as DomainVerificationRetrieveResponse,
+    type DomainVerificationDeleteResponse as DomainVerificationDeleteResponse,
+    type DomainVerificationVerifyResponse as DomainVerificationVerifyResponse,
+    type DomainVerificationsDomainVerificationsPage as DomainVerificationsDomainVerificationsPage,
+    type DomainVerificationCreateParams as DomainVerificationCreateParams,
+    type DomainVerificationRetrieveParams as DomainVerificationRetrieveParams,
+    type DomainVerificationListParams as DomainVerificationListParams,
+    type DomainVerificationDeleteParams as DomainVerificationDeleteParams,
+    type DomainVerificationVerifyParams as DomainVerificationVerifyParams,
+  };
+
+  export {
+    Invites as Invites,
+    type OrganizationInvite as OrganizationInvite,
+    type InviteCreateResponse as InviteCreateResponse,
+    type InviteRetrieveResponse as InviteRetrieveResponse,
+    type InviteGetSummaryResponse as InviteGetSummaryResponse,
+    type InviteCreateParams as InviteCreateParams,
+    type InviteRetrieveParams as InviteRetrieveParams,
+    type InviteGetSummaryParams as InviteGetSummaryParams,
+  };
+
+  export {
+    Policies as Policies,
+    type OrganizationPolicies as OrganizationPolicies,
+    type PolicyRetrieveResponse as PolicyRetrieveResponse,
+    type PolicyUpdateResponse as PolicyUpdateResponse,
+    type PolicyRetrieveParams as PolicyRetrieveParams,
+    type PolicyUpdateParams as PolicyUpdateParams,
+  };
+
+  export {
+    SSOConfigurations as SSOConfigurations,
+    type ProviderType as ProviderType,
+    type SSOConfiguration as SSOConfiguration,
+    type SSOConfigurationState as SSOConfigurationState,
+    type SSOConfigurationCreateResponse as SSOConfigurationCreateResponse,
+    type SSOConfigurationRetrieveResponse as SSOConfigurationRetrieveResponse,
+    type SSOConfigurationUpdateResponse as SSOConfigurationUpdateResponse,
+    type SSOConfigurationDeleteResponse as SSOConfigurationDeleteResponse,
+    type SSOConfigurationsSSOConfigurationsPage as SSOConfigurationsSSOConfigurationsPage,
+    type SSOConfigurationCreateParams as SSOConfigurationCreateParams,
+    type SSOConfigurationRetrieveParams as SSOConfigurationRetrieveParams,
+    type SSOConfigurationUpdateParams as SSOConfigurationUpdateParams,
+    type SSOConfigurationListParams as SSOConfigurationListParams,
+    type SSOConfigurationDeleteParams as SSOConfigurationDeleteParams,
+  };
+}
diff --git a/src/resources/organizations/policies.ts b/src/resources/organizations/policies.ts
new file mode 100644
index 0000000..468ef07
--- /dev/null
+++ b/src/resources/organizations/policies.ts
@@ -0,0 +1,217 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../../core/resource';
+import { APIPromise } from '../../core/api-promise';
+import { RequestOptions } from '../../internal/request-options';
+
+export class Policies extends APIResource {
+  /**
+   * Gets organization policy settings by organization ID.
+   *
+   * Use this method to:
+   *
+   * - Retrieve current policy settings for an organization
+   * - View resource limits and restrictions
+   * - Check allowed editors and other configurations
+   *
+   * ### Examples
+   *
+   * - Get organization policies:
+   *
+   *   Retrieves policy settings for a specific organization.
+   *
+   *   ```yaml
+   *   organizationId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   ```
+   */
+  retrieve(body: PolicyRetrieveParams, options?: RequestOptions): APIPromise<PolicyRetrieveResponse> {
+    return this._client.post('/gitpod.v1.OrganizationService/GetOrganizationPolicies', { body, ...options });
+  }
+
+  /**
+   * Updates organization policy settings.
+   *
+   * Use this method to:
+   *
+   * - Configure editor restrictions
+   * - Set environment resource limits
+   * - Define project creation permissions
+   * - Customize default configurations
+   *
+   * ### Examples
+   *
+   * - Update editor policies:
+   *
+   *   Restricts available editors and sets a default.
+   *
+   *   ```yaml
+   *   organizationId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   allowedEditorIds:
+   *     - "vscode"
+   *     - "jetbrains"
+   *   defaultEditorId: "vscode"
+   *   ```
+   *
+   * - Set environment limits:
+   *
+   *   Configures limits for environment usage.
+   *
+   *   ```yaml
+   *   organizationId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   maximumEnvironmentTimeout: "3600s"
+   *   maximumRunningEnvironmentsPerUser: "5"
+   *   maximumEnvironmentsPerUser: "20"
+   *   ```
+   */
+  update(body: PolicyUpdateParams, options?: RequestOptions): APIPromise<unknown> {
+    return this._client.post('/gitpod.v1.OrganizationService/UpdateOrganizationPolicies', {
+      body,
+      ...options,
+    });
+  }
+}
+
+export interface OrganizationPolicies {
+  /**
+   * allowed_editor_ids is the list of editor IDs that are allowed to be used in the
+   * organization
+   */
+  allowedEditorIds: Array<string>;
+
+  /**
+   * allow_local_runners controls whether local runners are allowed to be used in the
+   * organization
+   */
+  allowLocalRunners: boolean;
+
+  /**
+   * default_editor_id is the default editor ID to be used when a user doesn't
+   * specify one
+   */
+  defaultEditorId: string;
+
+  /**
+   * default_environment_image is the default container image when none is defined in
+   * repo
+   */
+  defaultEnvironmentImage: string;
+
+  /**
+   * maximum_environments_per_user limits total environments (running or stopped) per
+   * user
+   */
+  maximumEnvironmentsPerUser: string;
+
+  /**
+   * maximum_running_environments_per_user limits simultaneously running environments
+   * per user
+   */
+  maximumRunningEnvironmentsPerUser: string;
+
+  /**
+   * members_create_projects controls whether members can create projects
+   */
+  membersCreateProjects: boolean;
+
+  /**
+   * members_require_projects controls whether environments can only be created from
+   * projects by non-admin users
+   */
+  membersRequireProjects: boolean;
+
+  /**
+   * organization_id is the ID of the organization
+   */
+  organizationId: string;
+
+  /**
+   * maximum_environment_timeout controls the maximum timeout allowed for
+   * environments in seconds. 0 means no limit (never). Minimum duration is 30
+   * minutes.
+   */
+  maximumEnvironmentTimeout?: string;
+}
+
+export interface PolicyRetrieveResponse {
+  policies: OrganizationPolicies;
+}
+
+export type PolicyUpdateResponse = unknown;
+
+export interface PolicyRetrieveParams {
+  /**
+   * organization_id is the ID of the organization to retrieve policies for
+   */
+  organizationId: string;
+}
+
+export interface PolicyUpdateParams {
+  /**
+   * organization_id is the ID of the organization to update policies for
+   */
+  organizationId: string;
+
+  /**
+   * allowed_editor_ids is the list of editor IDs that are allowed to be used in the
+   * organization
+   */
+  allowedEditorIds?: Array<string>;
+
+  /**
+   * allow_local_runners controls whether local runners are allowed to be used in the
+   * organization
+   */
+  allowLocalRunners?: boolean | null;
+
+  /**
+   * default_editor_id is the default editor ID to be used when a user doesn't
+   * specify one
+   */
+  defaultEditorId?: string | null;
+
+  /**
+   * default_environment_image is the default container image when none is defined in
+   * repo
+   */
+  defaultEnvironmentImage?: string | null;
+
+  /**
+   * maximum_environments_per_user limits total environments (running or stopped) per
+   * user
+   */
+  maximumEnvironmentsPerUser?: string | null;
+
+  /**
+   * maximum_environment_timeout controls the maximum timeout allowed for
+   * environments in seconds. 0 means no limit (never). Minimum duration is 30
+   * minutes.
+   */
+  maximumEnvironmentTimeout?: string | null;
+
+  /**
+   * maximum_running_environments_per_user limits simultaneously running environments
+   * per user
+   */
+  maximumRunningEnvironmentsPerUser?: string | null;
+
+  /**
+   * members_create_projects controls whether members can create projects
+   */
+  membersCreateProjects?: boolean | null;
+
+  /**
+   * members_require_projects controls whether environments can only be created from
+   * projects by non-admin users
+   */
+  membersRequireProjects?: boolean | null;
+}
+
+export declare namespace Policies {
+  export {
+    type OrganizationPolicies as OrganizationPolicies,
+    type PolicyRetrieveResponse as PolicyRetrieveResponse,
+    type PolicyUpdateResponse as PolicyUpdateResponse,
+    type PolicyRetrieveParams as PolicyRetrieveParams,
+    type PolicyUpdateParams as PolicyUpdateParams,
+  };
+}
diff --git a/src/resources/organizations/sso-configurations.ts b/src/resources/organizations/sso-configurations.ts
new file mode 100644
index 0000000..a447375
--- /dev/null
+++ b/src/resources/organizations/sso-configurations.ts
@@ -0,0 +1,360 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../../core/resource';
+import { APIPromise } from '../../core/api-promise';
+import { PagePromise, SSOConfigurationsPage, type SSOConfigurationsPageParams } from '../../core/pagination';
+import { RequestOptions } from '../../internal/request-options';
+
+export class SSOConfigurations extends APIResource {
+  /**
+   * Creates or updates SSO configuration for organizational authentication.
+   *
+   * Use this method to:
+   *
+   * - Configure OIDC-based SSO providers
+   * - Set up built-in providers (Google, GitHub, etc.)
+   * - Define custom identity providers
+   * - Manage authentication policies
+   *
+   * ### Examples
+   *
+   * - Configure built-in Google SSO:
+   *
+   *   Sets up SSO using Google Workspace.
+   *
+   *   ```yaml
+   *   organizationId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   clientId: "012345678-abcdefghijklmnopqrstuvwxyz.apps.googleusercontent.com"
+   *   clientSecret: "GOCSPX-abcdefghijklmnopqrstuvwxyz123456"
+   *   issuerUrl: "https://accounts.google.com"
+   *   emailDomain: "acme-corp.com"
+   *   ```
+   *
+   * - Configure custom OIDC provider:
+   *
+   *   Sets up SSO with a custom identity provider.
+   *
+   *   ```yaml
+   *   organizationId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   clientId: "acme-corp-gitpod"
+   *   clientSecret: "secret-token-value"
+   *   issuerUrl: "https://sso.acme-corp.com"
+   *   emailDomain: "acme-corp.com"
+   *   ```
+   */
+  create(
+    body: SSOConfigurationCreateParams,
+    options?: RequestOptions,
+  ): APIPromise<SSOConfigurationCreateResponse> {
+    return this._client.post('/gitpod.v1.OrganizationService/CreateSSOConfiguration', { body, ...options });
+  }
+
+  /**
+   * Retrieves a specific SSO configuration.
+   *
+   * Use this method to:
+   *
+   * - View SSO provider details
+   * - Check configuration status
+   * - Verify SSO settings
+   *
+   * ### Examples
+   *
+   * - Get SSO configuration:
+   *
+   *   Retrieves details of a specific SSO configuration.
+   *
+   *   ```yaml
+   *   ssoConfigurationId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   ```
+   */
+  retrieve(
+    body: SSOConfigurationRetrieveParams,
+    options?: RequestOptions,
+  ): APIPromise<SSOConfigurationRetrieveResponse> {
+    return this._client.post('/gitpod.v1.OrganizationService/GetSSOConfiguration', { body, ...options });
+  }
+
+  /**
+   * Updates SSO provider settings and authentication rules.
+   *
+   * Use this method to:
+   *
+   * - Rotate client credentials
+   * - Update provider endpoints
+   * - Modify claim mappings
+   * - Change authentication policies
+   * - Toggle SSO enforcement
+   *
+   * ### Examples
+   *
+   * - Update credentials:
+   *
+   *   Rotates client ID and secret.
+   *
+   *   ```yaml
+   *   ssoConfigurationId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   clientId: "new-client-id"
+   *   clientSecret: "new-client-secret"
+   *   ```
+   *
+   * - Update provider status:
+   *
+   *   Activates or deactivates SSO provider.
+   *
+   *   ```yaml
+   *   ssoConfigurationId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   state: SSO_CONFIGURATION_STATE_ACTIVE
+   *   ```
+   */
+  update(body: SSOConfigurationUpdateParams, options?: RequestOptions): APIPromise<unknown> {
+    return this._client.post('/gitpod.v1.OrganizationService/UpdateSSOConfiguration', { body, ...options });
+  }
+
+  /**
+   * Lists and filters SSO configurations for an organization.
+   *
+   * Use this method to:
+   *
+   * - View all SSO providers
+   * - Monitor authentication status
+   * - Audit security settings
+   * - Manage provider configurations
+   *
+   * ### Examples
+   *
+   * - List active configurations:
+   *
+   *   Shows all active SSO providers.
+   *
+   *   ```yaml
+   *   organizationId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   pagination:
+   *     pageSize: 20
+   *   ```
+   *
+   * - List by provider type:
+   *
+   *   Shows custom SSO configurations.
+   *
+   *   ```yaml
+   *   organizationId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   pagination:
+   *     pageSize: 20
+   *     token: "next-page-token-from-previous-response"
+   *   ```
+   */
+  list(
+    params: SSOConfigurationListParams,
+    options?: RequestOptions,
+  ): PagePromise<SSOConfigurationsSSOConfigurationsPage, SSOConfiguration> {
+    const { token, pageSize, ...body } = params;
+    return this._client.getAPIList(
+      '/gitpod.v1.OrganizationService/ListSSOConfigurations',
+      SSOConfigurationsPage<SSOConfiguration>,
+      { query: { token, pageSize }, body, method: 'post', ...options },
+    );
+  }
+
+  /**
+   * Removes an SSO configuration from an organization.
+   *
+   * Use this method to:
+   *
+   * - Disable SSO authentication
+   * - Remove outdated providers
+   * - Clean up unused configurations
+   *
+   * ### Examples
+   *
+   * - Delete SSO configuration:
+   *
+   *   Removes a specific SSO configuration.
+   *
+   *   ```yaml
+   *   ssoConfigurationId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   ```
+   */
+  delete(body: SSOConfigurationDeleteParams, options?: RequestOptions): APIPromise<unknown> {
+    return this._client.post('/gitpod.v1.OrganizationService/DeleteSSOConfiguration', { body, ...options });
+  }
+}
+
+export type SSOConfigurationsSSOConfigurationsPage = SSOConfigurationsPage<SSOConfiguration>;
+
+export type ProviderType = 'PROVIDER_TYPE_UNSPECIFIED' | 'PROVIDER_TYPE_BUILTIN' | 'PROVIDER_TYPE_CUSTOM';
+
+export interface SSOConfiguration {
+  /**
+   * id is the unique identifier of the SSO configuration
+   */
+  id: string;
+
+  /**
+   * issuer_url is the URL of the IdP issuer
+   */
+  issuerUrl: string;
+
+  organizationId: string;
+
+  /**
+   * provider_type defines the type of the SSO configuration
+   */
+  providerType: ProviderType;
+
+  /**
+   * state is the state of the SSO configuration
+   */
+  state: SSOConfigurationState;
+
+  /**
+   * claims are key/value pairs that defines a mapping of claims issued by the IdP.
+   */
+  claims?: Record<string, string>;
+
+  /**
+   * client_id is the client ID of the OIDC application set on the IdP
+   */
+  clientId?: string;
+
+  emailDomain?: string;
+}
+
+export type SSOConfigurationState =
+  | 'SSO_CONFIGURATION_STATE_UNSPECIFIED'
+  | 'SSO_CONFIGURATION_STATE_INACTIVE'
+  | 'SSO_CONFIGURATION_STATE_ACTIVE';
+
+export interface SSOConfigurationCreateResponse {
+  /**
+   * sso_configuration is the created SSO configuration
+   */
+  ssoConfiguration: SSOConfiguration;
+}
+
+export interface SSOConfigurationRetrieveResponse {
+  /**
+   * sso_configuration is the SSO configuration identified by the ID
+   */
+  ssoConfiguration: SSOConfiguration;
+}
+
+export type SSOConfigurationUpdateResponse = unknown;
+
+export type SSOConfigurationDeleteResponse = unknown;
+
+export interface SSOConfigurationCreateParams {
+  /**
+   * client_id is the client ID of the OIDC application set on the IdP
+   */
+  clientId: string;
+
+  /**
+   * client_secret is the client secret of the OIDC application set on the IdP
+   */
+  clientSecret: string;
+
+  /**
+   * email_domain is the domain that is allowed to sign in to the organization
+   */
+  emailDomain: string;
+
+  /**
+   * issuer_url is the URL of the IdP issuer
+   */
+  issuerUrl: string;
+
+  organizationId: string;
+}
+
+export interface SSOConfigurationRetrieveParams {
+  /**
+   * sso_configuration_id is the ID of the SSO configuration to get
+   */
+  ssoConfigurationId: string;
+}
+
+export interface SSOConfigurationUpdateParams {
+  /**
+   * sso_configuration_id is the ID of the SSO configuration to update
+   */
+  ssoConfigurationId: string;
+
+  /**
+   * claims are key/value pairs that defines a mapping of claims issued by the IdP.
+   */
+  claims?: Record<string, string>;
+
+  /**
+   * client_id is the client ID of the SSO provider
+   */
+  clientId?: string | null;
+
+  /**
+   * client_secret is the client secret of the SSO provider
+   */
+  clientSecret?: string | null;
+
+  emailDomain?: string | null;
+
+  /**
+   * issuer_url is the URL of the IdP issuer
+   */
+  issuerUrl?: string | null;
+
+  /**
+   * state is the state of the SSO configuration
+   */
+  state?: SSOConfigurationState | null;
+}
+
+export interface SSOConfigurationListParams extends SSOConfigurationsPageParams {
+  /**
+   * Body param: organization_id is the ID of the organization to list SSO
+   * configurations for.
+   */
+  organizationId: string;
+
+  /**
+   * Body param:
+   */
+  pagination?: SSOConfigurationListParams.Pagination;
+}
+
+export namespace SSOConfigurationListParams {
+  export interface Pagination {
+    /**
+     * Token for the next set of results that was returned as next_token of a
+     * PaginationResponse
+     */
+    token?: string;
+
+    /**
+     * Page size is the maximum number of results to retrieve per page. Defaults to 25.
+     * Maximum 100.
+     */
+    pageSize?: number;
+  }
+}
+
+export interface SSOConfigurationDeleteParams {
+  ssoConfigurationId: string;
+}
+
+export declare namespace SSOConfigurations {
+  export {
+    type ProviderType as ProviderType,
+    type SSOConfiguration as SSOConfiguration,
+    type SSOConfigurationState as SSOConfigurationState,
+    type SSOConfigurationCreateResponse as SSOConfigurationCreateResponse,
+    type SSOConfigurationRetrieveResponse as SSOConfigurationRetrieveResponse,
+    type SSOConfigurationUpdateResponse as SSOConfigurationUpdateResponse,
+    type SSOConfigurationDeleteResponse as SSOConfigurationDeleteResponse,
+    type SSOConfigurationsSSOConfigurationsPage as SSOConfigurationsSSOConfigurationsPage,
+    type SSOConfigurationCreateParams as SSOConfigurationCreateParams,
+    type SSOConfigurationRetrieveParams as SSOConfigurationRetrieveParams,
+    type SSOConfigurationUpdateParams as SSOConfigurationUpdateParams,
+    type SSOConfigurationListParams as SSOConfigurationListParams,
+    type SSOConfigurationDeleteParams as SSOConfigurationDeleteParams,
+  };
+}
diff --git a/src/resources/projects.ts b/src/resources/projects.ts
new file mode 100644
index 0000000..f9985fc
--- /dev/null
+++ b/src/resources/projects.ts
@@ -0,0 +1,3 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+export * from './projects/index';
diff --git a/src/resources/projects/index.ts b/src/resources/projects/index.ts
new file mode 100644
index 0000000..0dc0a82
--- /dev/null
+++ b/src/resources/projects/index.ts
@@ -0,0 +1,34 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+export {
+  Policies,
+  type ProjectPolicy,
+  type ProjectRole,
+  type PolicyCreateResponse,
+  type PolicyUpdateResponse,
+  type PolicyDeleteResponse,
+  type PolicyCreateParams,
+  type PolicyUpdateParams,
+  type PolicyListParams,
+  type PolicyDeleteParams,
+  type ProjectPoliciesPoliciesPage,
+} from './policies';
+export {
+  Projects,
+  type EnvironmentInitializer,
+  type Project,
+  type ProjectEnvironmentClass,
+  type ProjectMetadata,
+  type ProjectCreateResponse,
+  type ProjectRetrieveResponse,
+  type ProjectUpdateResponse,
+  type ProjectDeleteResponse,
+  type ProjectCreateFromEnvironmentResponse,
+  type ProjectCreateParams,
+  type ProjectRetrieveParams,
+  type ProjectUpdateParams,
+  type ProjectListParams,
+  type ProjectDeleteParams,
+  type ProjectCreateFromEnvironmentParams,
+  type ProjectsProjectsPage,
+} from './projects';
diff --git a/src/resources/projects/policies.ts b/src/resources/projects/policies.ts
new file mode 100644
index 0000000..ea39ec5
--- /dev/null
+++ b/src/resources/projects/policies.ts
@@ -0,0 +1,229 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../../core/resource';
+import { APIPromise } from '../../core/api-promise';
+import { PagePromise, PoliciesPage, type PoliciesPageParams } from '../../core/pagination';
+import { RequestOptions } from '../../internal/request-options';
+
+export class Policies extends APIResource {
+  /**
+   * Creates a new policy for a project.
+   *
+   * Use this method to:
+   *
+   * - Set up access controls
+   * - Define group permissions
+   * - Configure role-based access
+   *
+   * ### Examples
+   *
+   * - Create admin policy:
+   *
+   *   Grants admin access to a group.
+   *
+   *   ```yaml
+   *   projectId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   groupId: "f53d2330-3795-4c5d-a1f3-453121af9c60"
+   *   role: PROJECT_ROLE_ADMIN
+   *   ```
+   */
+  create(body: PolicyCreateParams, options?: RequestOptions): APIPromise<PolicyCreateResponse> {
+    return this._client.post('/gitpod.v1.ProjectService/CreateProjectPolicy', { body, ...options });
+  }
+
+  /**
+   * Updates an existing project policy.
+   *
+   * Use this method to:
+   *
+   * - Modify access levels
+   * - Change group roles
+   * - Update permissions
+   *
+   * ### Examples
+   *
+   * - Update policy role:
+   *
+   *   Changes a group's access level.
+   *
+   *   ```yaml
+   *   projectId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   groupId: "f53d2330-3795-4c5d-a1f3-453121af9c60"
+   *   role: PROJECT_ROLE_EDITOR
+   *   ```
+   */
+  update(body: PolicyUpdateParams, options?: RequestOptions): APIPromise<PolicyUpdateResponse> {
+    return this._client.post('/gitpod.v1.ProjectService/UpdateProjectPolicy', { body, ...options });
+  }
+
+  /**
+   * Lists policies for a project.
+   *
+   * Use this method to:
+   *
+   * - View access controls
+   * - Check policy configurations
+   * - Audit permissions
+   *
+   * ### Examples
+   *
+   * - List policies:
+   *
+   *   Shows all policies for a project.
+   *
+   *   ```yaml
+   *   projectId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   pagination:
+   *     pageSize: 20
+   *   ```
+   */
+  list(
+    params: PolicyListParams,
+    options?: RequestOptions,
+  ): PagePromise<ProjectPoliciesPoliciesPage, ProjectPolicy> {
+    const { token, pageSize, ...body } = params;
+    return this._client.getAPIList(
+      '/gitpod.v1.ProjectService/ListProjectPolicies',
+      PoliciesPage<ProjectPolicy>,
+      { query: { token, pageSize }, body, method: 'post', ...options },
+    );
+  }
+
+  /**
+   * Deletes a project policy.
+   *
+   * Use this method to:
+   *
+   * - Remove access controls
+   * - Revoke permissions
+   * - Clean up policies
+   *
+   * ### Examples
+   *
+   * - Delete policy:
+   *
+   *   Removes a group's access policy.
+   *
+   *   ```yaml
+   *   projectId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   groupId: "f53d2330-3795-4c5d-a1f3-453121af9c60"
+   *   ```
+   */
+  delete(body: PolicyDeleteParams, options?: RequestOptions): APIPromise<unknown> {
+    return this._client.post('/gitpod.v1.ProjectService/DeleteProjectPolicy', { body, ...options });
+  }
+}
+
+export type ProjectPoliciesPoliciesPage = PoliciesPage<ProjectPolicy>;
+
+export interface ProjectPolicy {
+  groupId?: string;
+
+  /**
+   * role is the role assigned to the group
+   */
+  role?: ProjectRole;
+}
+
+export type ProjectRole =
+  | 'PROJECT_ROLE_UNSPECIFIED'
+  | 'PROJECT_ROLE_ADMIN'
+  | 'PROJECT_ROLE_USER'
+  | 'PROJECT_ROLE_EDITOR';
+
+export interface PolicyCreateResponse {
+  policy?: ProjectPolicy;
+}
+
+export interface PolicyUpdateResponse {
+  policy?: ProjectPolicy;
+}
+
+export type PolicyDeleteResponse = unknown;
+
+export interface PolicyCreateParams {
+  /**
+   * group_id specifies the group_id identifier
+   */
+  groupId?: string;
+
+  /**
+   * project_id specifies the project identifier
+   */
+  projectId?: string;
+
+  role?: ProjectRole;
+}
+
+export interface PolicyUpdateParams {
+  /**
+   * group_id specifies the group_id identifier
+   */
+  groupId?: string;
+
+  /**
+   * project_id specifies the project identifier
+   */
+  projectId?: string;
+
+  role?: ProjectRole;
+}
+
+export interface PolicyListParams extends PoliciesPageParams {
+  /**
+   * Body param: pagination contains the pagination options for listing project
+   * policies
+   */
+  pagination?: PolicyListParams.Pagination;
+
+  /**
+   * Body param: project_id specifies the project identifier
+   */
+  projectId?: string;
+}
+
+export namespace PolicyListParams {
+  /**
+   * pagination contains the pagination options for listing project policies
+   */
+  export interface Pagination {
+    /**
+     * Token for the next set of results that was returned as next_token of a
+     * PaginationResponse
+     */
+    token?: string;
+
+    /**
+     * Page size is the maximum number of results to retrieve per page. Defaults to 25.
+     * Maximum 100.
+     */
+    pageSize?: number;
+  }
+}
+
+export interface PolicyDeleteParams {
+  /**
+   * group_id specifies the group_id identifier
+   */
+  groupId?: string;
+
+  /**
+   * project_id specifies the project identifier
+   */
+  projectId?: string;
+}
+
+export declare namespace Policies {
+  export {
+    type ProjectPolicy as ProjectPolicy,
+    type ProjectRole as ProjectRole,
+    type PolicyCreateResponse as PolicyCreateResponse,
+    type PolicyUpdateResponse as PolicyUpdateResponse,
+    type PolicyDeleteResponse as PolicyDeleteResponse,
+    type ProjectPoliciesPoliciesPage as ProjectPoliciesPoliciesPage,
+    type PolicyCreateParams as PolicyCreateParams,
+    type PolicyUpdateParams as PolicyUpdateParams,
+    type PolicyListParams as PolicyListParams,
+    type PolicyDeleteParams as PolicyDeleteParams,
+  };
+}
diff --git a/src/resources/projects/projects.ts b/src/resources/projects/projects.ts
new file mode 100644
index 0000000..27f10b3
--- /dev/null
+++ b/src/resources/projects/projects.ts
@@ -0,0 +1,727 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../../core/resource';
+import * as Shared from '../shared';
+import * as PoliciesAPI from './policies';
+import {
+  Policies,
+  PolicyCreateParams,
+  PolicyCreateResponse,
+  PolicyDeleteParams,
+  PolicyDeleteResponse,
+  PolicyListParams,
+  PolicyUpdateParams,
+  PolicyUpdateResponse,
+  ProjectPoliciesPoliciesPage,
+  ProjectPolicy,
+  ProjectRole,
+} from './policies';
+import { APIPromise } from '../../core/api-promise';
+import { PagePromise, ProjectsPage, type ProjectsPageParams } from '../../core/pagination';
+import { RequestOptions } from '../../internal/request-options';
+
+export class Projects extends APIResource {
+  policies: PoliciesAPI.Policies = new PoliciesAPI.Policies(this._client);
+
+  /**
+   * Creates a new project with specified configuration.
+   *
+   * Use this method to:
+   *
+   * - Set up development projects
+   * - Configure project environments
+   * - Define project settings
+   * - Initialize project content
+   *
+   * ### Examples
+   *
+   * - Create basic project:
+   *
+   *   Creates a project with minimal configuration.
+   *
+   *   ```yaml
+   *   name: "Web Application"
+   *   environmentClass:
+   *     environmentClassId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   initializer:
+   *     specs:
+   *       - git:
+   *           remoteUri: "https://github.com/org/repo"
+   *   ```
+   *
+   * - Create project with devcontainer:
+   *
+   *   Creates a project with custom development container.
+   *
+   *   ```yaml
+   *   name: "Backend Service"
+   *   environmentClass:
+   *     environmentClassId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   initializer:
+   *     specs:
+   *       - git:
+   *           remoteUri: "https://github.com/org/backend"
+   *   devcontainerFilePath: ".devcontainer/devcontainer.json"
+   *   automationsFilePath: ".gitpod/automations.yaml"
+   *   ```
+   */
+  create(body: ProjectCreateParams, options?: RequestOptions): APIPromise<ProjectCreateResponse> {
+    return this._client.post('/gitpod.v1.ProjectService/CreateProject', { body, ...options });
+  }
+
+  /**
+   * Gets details about a specific project.
+   *
+   * Use this method to:
+   *
+   * - View project configuration
+   * - Check project status
+   * - Get project metadata
+   *
+   * ### Examples
+   *
+   * - Get project details:
+   *
+   *   Retrieves information about a specific project.
+   *
+   *   ```yaml
+   *   projectId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   ```
+   */
+  retrieve(body: ProjectRetrieveParams, options?: RequestOptions): APIPromise<ProjectRetrieveResponse> {
+    return this._client.post('/gitpod.v1.ProjectService/GetProject', { body, ...options });
+  }
+
+  /**
+   * Updates a project's configuration.
+   *
+   * Use this method to:
+   *
+   * - Modify project settings
+   * - Update environment class
+   * - Change project name
+   * - Configure initializers
+   *
+   * ### Examples
+   *
+   * - Update project name:
+   *
+   *   Changes the project's display name.
+   *
+   *   ```yaml
+   *   projectId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   name: "New Project Name"
+   *   ```
+   *
+   * - Update environment class:
+   *
+   *   Changes the project's environment class.
+   *
+   *   ```yaml
+   *   projectId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   environmentClass:
+   *     environmentClassId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   ```
+   */
+  update(body: ProjectUpdateParams, options?: RequestOptions): APIPromise<ProjectUpdateResponse> {
+    return this._client.post('/gitpod.v1.ProjectService/UpdateProject', { body, ...options });
+  }
+
+  /**
+   * Lists projects with optional filtering.
+   *
+   * Use this method to:
+   *
+   * - View all accessible projects
+   * - Browse project configurations
+   * - Monitor project status
+   *
+   * ### Examples
+   *
+   * - List projects:
+   *
+   *   Shows all projects with pagination.
+   *
+   *   ```yaml
+   *   pagination:
+   *     pageSize: 20
+   *   ```
+   */
+  list(params: ProjectListParams, options?: RequestOptions): PagePromise<ProjectsProjectsPage, Project> {
+    const { token, pageSize, ...body } = params;
+    return this._client.getAPIList('/gitpod.v1.ProjectService/ListProjects', ProjectsPage<Project>, {
+      query: { token, pageSize },
+      body,
+      method: 'post',
+      ...options,
+    });
+  }
+
+  /**
+   * Deletes a project permanently.
+   *
+   * Use this method to:
+   *
+   * - Remove unused projects
+   * - Clean up test projects
+   * - Delete obsolete configurations
+   *
+   * ### Examples
+   *
+   * - Delete project:
+   *
+   *   Permanently removes a project.
+   *
+   *   ```yaml
+   *   projectId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   ```
+   */
+  delete(body: ProjectDeleteParams, options?: RequestOptions): APIPromise<unknown> {
+    return this._client.post('/gitpod.v1.ProjectService/DeleteProject', { body, ...options });
+  }
+
+  /**
+   * Creates a new project using an existing environment as a template.
+   *
+   * Use this method to:
+   *
+   * - Clone environment configurations
+   * - Create projects from templates
+   * - Share environment setups
+   *
+   * ### Examples
+   *
+   * - Create from environment:
+   *
+   *   Creates a project based on existing environment.
+   *
+   *   ```yaml
+   *   name: "Frontend Project"
+   *   environmentId: "07e03a28-65a5-4d98-b532-8ea67b188048"
+   *   ```
+   */
+  createFromEnvironment(
+    body: ProjectCreateFromEnvironmentParams,
+    options?: RequestOptions,
+  ): APIPromise<ProjectCreateFromEnvironmentResponse> {
+    return this._client.post('/gitpod.v1.ProjectService/CreateProjectFromEnvironment', { body, ...options });
+  }
+}
+
+export type ProjectsProjectsPage = ProjectsPage<Project>;
+
+/**
+ * EnvironmentInitializer specifies how an environment is to be initialized
+ */
+export interface EnvironmentInitializer {
+  specs?: Array<EnvironmentInitializer.Spec>;
+}
+
+export namespace EnvironmentInitializer {
+  export interface Spec {
+    contextUrl?: Spec.ContextURL;
+
+    git?: Spec.Git;
+  }
+
+  export namespace Spec {
+    export interface ContextURL {
+      /**
+       * url is the URL from which the environment is created
+       */
+      url?: string;
+    }
+
+    export interface Git {
+      /**
+       * a path relative to the environment root in which the code will be checked out to
+       */
+      checkoutLocation?: string;
+
+      /**
+       * the value for the clone target mode - use depends on the target mode
+       */
+      cloneTarget?: string;
+
+      /**
+       * remote_uri is the Git remote origin
+       */
+      remoteUri?: string;
+
+      /**
+       * the target mode determines what gets checked out
+       */
+      targetMode?:
+        | 'CLONE_TARGET_MODE_UNSPECIFIED'
+        | 'CLONE_TARGET_MODE_REMOTE_HEAD'
+        | 'CLONE_TARGET_MODE_REMOTE_COMMIT'
+        | 'CLONE_TARGET_MODE_REMOTE_BRANCH'
+        | 'CLONE_TARGET_MODE_LOCAL_BRANCH';
+
+      /**
+       * upstream_Remote_uri is the fork upstream of a repository
+       */
+      upstreamRemoteUri?: string;
+    }
+  }
+}
+
+export interface Project {
+  environmentClass: ProjectEnvironmentClass;
+
+  /**
+   * id is the unique identifier for the project
+   */
+  id?: string;
+
+  /**
+   * automations_file_path is the path to the automations file relative to the repo
+   * root
+   */
+  automationsFilePath?: string;
+
+  /**
+   * devcontainer_file_path is the path to the devcontainer file relative to the repo
+   * root
+   */
+  devcontainerFilePath?: string;
+
+  /**
+   * initializer is the content initializer
+   */
+  initializer?: EnvironmentInitializer;
+
+  metadata?: ProjectMetadata;
+
+  /**
+   * technical_description is a detailed technical description of the project This
+   * field is not returned by default in GetProject or ListProjects responses
+   */
+  technicalDescription?: string;
+
+  usedBy?: Project.UsedBy;
+}
+
+export namespace Project {
+  export interface UsedBy {
+    /**
+     * Subjects are the 10 most recent subjects who have used the project to create an
+     * environment
+     */
+    subjects?: Array<Shared.Subject>;
+
+    /**
+     * Total number of unique subjects who have used the project
+     */
+    totalSubjects?: number;
+  }
+}
+
+export interface ProjectEnvironmentClass {
+  /**
+   * Use a fixed environment class on a given Runner. This cannot be a local runner's
+   * environment class.
+   */
+  environmentClassId?: string;
+
+  /**
+   * Use a local runner for the user
+   */
+  localRunner?: boolean;
+}
+
+export interface ProjectMetadata {
+  /**
+   * A Timestamp represents a point in time independent of any time zone or local
+   * calendar, encoded as a count of seconds and fractions of seconds at nanosecond
+   * resolution. The count is relative to an epoch at UTC midnight on January 1,
+   * 1970, in the proleptic Gregorian calendar which extends the Gregorian calendar
+   * backwards to year one.
+   *
+   * All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
+   * second table is needed for interpretation, using a
+   * [24-hour linear smear](https://developers.google.com/time/smear).
+   *
+   * The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
+   * restricting to that range, we ensure that we can convert to and from
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
+   *
+   * # Examples
+   *
+   * Example 1: Compute Timestamp from POSIX `time()`.
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(time(NULL));
+   *      timestamp.set_nanos(0);
+   *
+   * Example 2: Compute Timestamp from POSIX `gettimeofday()`.
+   *
+   *      struct timeval tv;
+   *      gettimeofday(&tv, NULL);
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(tv.tv_sec);
+   *      timestamp.set_nanos(tv.tv_usec * 1000);
+   *
+   * Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
+   *
+   *      FILETIME ft;
+   *      GetSystemTimeAsFileTime(&ft);
+   *      UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
+   *
+   *      // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
+   *      // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
+   *      timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
+   *
+   * Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
+   *
+   *      long millis = System.currentTimeMillis();
+   *
+   *      Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
+   *          .setNanos((int) ((millis % 1000) * 1000000)).build();
+   *
+   * Example 5: Compute Timestamp from Java `Instant.now()`.
+   *
+   *      Instant now = Instant.now();
+   *
+   *      Timestamp timestamp =
+   *          Timestamp.newBuilder().setSeconds(now.getEpochSecond())
+   *              .setNanos(now.getNano()).build();
+   *
+   * Example 6: Compute Timestamp from current time in Python.
+   *
+   *      timestamp = Timestamp()
+   *      timestamp.GetCurrentTime()
+   *
+   * # JSON Mapping
+   *
+   * In JSON format, the Timestamp type is encoded as a string in the
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the format is
+   * "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" where {year} is always
+   * expressed using four digits while {month}, {day}, {hour}, {min}, and {sec} are
+   * zero-padded to two digits each. The fractional seconds, which can go up to 9
+   * digits (i.e. up to 1 nanosecond resolution), are optional. The "Z" suffix
+   * indicates the timezone ("UTC"); the timezone is required. A proto3 JSON
+   * serializer should always use UTC (as indicated by "Z") when printing the
+   * Timestamp type and a proto3 JSON parser should be able to accept both UTC and
+   * other timezones (as indicated by an offset).
+   *
+   * For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past 01:30 UTC on
+   * January 15, 2017.
+   *
+   * In JavaScript, one can convert a Date object to this format using the standard
+   * [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
+   * method. In Python, a standard `datetime.datetime` object can be converted to
+   * this format using
+   * [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with the
+   * time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use the
+   * Joda Time's
+   * [`ISODateTimeFormat.dateTime()`](<http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime()>)
+   * to obtain a formatter capable of generating timestamps in this format.
+   */
+  createdAt?: string;
+
+  /**
+   * creator is the identity of the project creator
+   */
+  creator?: Shared.Subject;
+
+  /**
+   * name is the human readable name of the project
+   */
+  name?: string;
+
+  /**
+   * organization_id is the ID of the organization that contains the environment
+   */
+  organizationId?: string;
+
+  /**
+   * A Timestamp represents a point in time independent of any time zone or local
+   * calendar, encoded as a count of seconds and fractions of seconds at nanosecond
+   * resolution. The count is relative to an epoch at UTC midnight on January 1,
+   * 1970, in the proleptic Gregorian calendar which extends the Gregorian calendar
+   * backwards to year one.
+   *
+   * All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
+   * second table is needed for interpretation, using a
+   * [24-hour linear smear](https://developers.google.com/time/smear).
+   *
+   * The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
+   * restricting to that range, we ensure that we can convert to and from
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
+   *
+   * # Examples
+   *
+   * Example 1: Compute Timestamp from POSIX `time()`.
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(time(NULL));
+   *      timestamp.set_nanos(0);
+   *
+   * Example 2: Compute Timestamp from POSIX `gettimeofday()`.
+   *
+   *      struct timeval tv;
+   *      gettimeofday(&tv, NULL);
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(tv.tv_sec);
+   *      timestamp.set_nanos(tv.tv_usec * 1000);
+   *
+   * Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
+   *
+   *      FILETIME ft;
+   *      GetSystemTimeAsFileTime(&ft);
+   *      UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
+   *
+   *      // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
+   *      // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
+   *      timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
+   *
+   * Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
+   *
+   *      long millis = System.currentTimeMillis();
+   *
+   *      Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
+   *          .setNanos((int) ((millis % 1000) * 1000000)).build();
+   *
+   * Example 5: Compute Timestamp from Java `Instant.now()`.
+   *
+   *      Instant now = Instant.now();
+   *
+   *      Timestamp timestamp =
+   *          Timestamp.newBuilder().setSeconds(now.getEpochSecond())
+   *              .setNanos(now.getNano()).build();
+   *
+   * Example 6: Compute Timestamp from current time in Python.
+   *
+   *      timestamp = Timestamp()
+   *      timestamp.GetCurrentTime()
+   *
+   * # JSON Mapping
+   *
+   * In JSON format, the Timestamp type is encoded as a string in the
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the format is
+   * "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" where {year} is always
+   * expressed using four digits while {month}, {day}, {hour}, {min}, and {sec} are
+   * zero-padded to two digits each. The fractional seconds, which can go up to 9
+   * digits (i.e. up to 1 nanosecond resolution), are optional. The "Z" suffix
+   * indicates the timezone ("UTC"); the timezone is required. A proto3 JSON
+   * serializer should always use UTC (as indicated by "Z") when printing the
+   * Timestamp type and a proto3 JSON parser should be able to accept both UTC and
+   * other timezones (as indicated by an offset).
+   *
+   * For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past 01:30 UTC on
+   * January 15, 2017.
+   *
+   * In JavaScript, one can convert a Date object to this format using the standard
+   * [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
+   * method. In Python, a standard `datetime.datetime` object can be converted to
+   * this format using
+   * [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with the
+   * time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use the
+   * Joda Time's
+   * [`ISODateTimeFormat.dateTime()`](<http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime()>)
+   * to obtain a formatter capable of generating timestamps in this format.
+   */
+  updatedAt?: string;
+}
+
+export interface ProjectCreateResponse {
+  project?: Project;
+}
+
+export interface ProjectRetrieveResponse {
+  project?: Project;
+}
+
+export interface ProjectUpdateResponse {
+  project?: Project;
+}
+
+export type ProjectDeleteResponse = unknown;
+
+export interface ProjectCreateFromEnvironmentResponse {
+  project?: Project;
+}
+
+export interface ProjectCreateParams {
+  environmentClass: ProjectEnvironmentClass;
+
+  /**
+   * initializer is the content initializer
+   */
+  initializer: EnvironmentInitializer;
+
+  /**
+   * automations_file_path is the path to the automations file relative to the repo
+   * root path must not be absolute (start with a /):
+   *
+   * ```
+   * this.matches('^$|^[^/].*')
+   * ```
+   */
+  automationsFilePath?: string;
+
+  /**
+   * devcontainer_file_path is the path to the devcontainer file relative to the repo
+   * root path must not be absolute (start with a /):
+   *
+   * ```
+   * this.matches('^$|^[^/].*')
+   * ```
+   */
+  devcontainerFilePath?: string;
+
+  name?: string;
+
+  /**
+   * technical_description is a detailed technical description of the project This
+   * field is not returned by default in GetProject or ListProjects responses 8KB max
+   */
+  technicalDescription?: string;
+}
+
+export interface ProjectRetrieveParams {
+  /**
+   * project_id specifies the project identifier
+   */
+  projectId?: string;
+}
+
+export interface ProjectUpdateParams {
+  /**
+   * automations_file_path is the path to the automations file relative to the repo
+   * root path must not be absolute (start with a /):
+   *
+   * ```
+   * this.matches('^$|^[^/].*')
+   * ```
+   */
+  automationsFilePath?: string | null;
+
+  /**
+   * devcontainer_file_path is the path to the devcontainer file relative to the repo
+   * root path must not be absolute (start with a /):
+   *
+   * ```
+   * this.matches('^$|^[^/].*')
+   * ```
+   */
+  devcontainerFilePath?: string | null;
+
+  environmentClass?: ProjectEnvironmentClass | null;
+
+  /**
+   * initializer is the content initializer
+   */
+  initializer?: EnvironmentInitializer | null;
+
+  name?: string | null;
+
+  /**
+   * project_id specifies the project identifier
+   */
+  projectId?: string;
+
+  /**
+   * technical_description is a detailed technical description of the project This
+   * field is not returned by default in GetProject or ListProjects responses 8KB max
+   */
+  technicalDescription?: string | null;
+}
+
+export interface ProjectListParams extends ProjectsPageParams {
+  /**
+   * Body param:
+   */
+  filter?: ProjectListParams.Filter;
+
+  /**
+   * Body param: pagination contains the pagination options for listing organizations
+   */
+  pagination?: ProjectListParams.Pagination;
+}
+
+export namespace ProjectListParams {
+  export interface Filter {
+    /**
+     * project_ids filters the response to only projects with these IDs
+     */
+    projectIds?: Array<string>;
+  }
+
+  /**
+   * pagination contains the pagination options for listing organizations
+   */
+  export interface Pagination {
+    /**
+     * Token for the next set of results that was returned as next_token of a
+     * PaginationResponse
+     */
+    token?: string;
+
+    /**
+     * Page size is the maximum number of results to retrieve per page. Defaults to 25.
+     * Maximum 100.
+     */
+    pageSize?: number;
+  }
+}
+
+export interface ProjectDeleteParams {
+  /**
+   * project_id specifies the project identifier
+   */
+  projectId?: string;
+}
+
+export interface ProjectCreateFromEnvironmentParams {
+  /**
+   * environment_id specifies the environment identifier
+   */
+  environmentId?: string;
+
+  name?: string;
+}
+
+Projects.Policies = Policies;
+
+export declare namespace Projects {
+  export {
+    type EnvironmentInitializer as EnvironmentInitializer,
+    type Project as Project,
+    type ProjectEnvironmentClass as ProjectEnvironmentClass,
+    type ProjectMetadata as ProjectMetadata,
+    type ProjectCreateResponse as ProjectCreateResponse,
+    type ProjectRetrieveResponse as ProjectRetrieveResponse,
+    type ProjectUpdateResponse as ProjectUpdateResponse,
+    type ProjectDeleteResponse as ProjectDeleteResponse,
+    type ProjectCreateFromEnvironmentResponse as ProjectCreateFromEnvironmentResponse,
+    type ProjectsProjectsPage as ProjectsProjectsPage,
+    type ProjectCreateParams as ProjectCreateParams,
+    type ProjectRetrieveParams as ProjectRetrieveParams,
+    type ProjectUpdateParams as ProjectUpdateParams,
+    type ProjectListParams as ProjectListParams,
+    type ProjectDeleteParams as ProjectDeleteParams,
+    type ProjectCreateFromEnvironmentParams as ProjectCreateFromEnvironmentParams,
+  };
+
+  export {
+    Policies as Policies,
+    type ProjectPolicy as ProjectPolicy,
+    type ProjectRole as ProjectRole,
+    type PolicyCreateResponse as PolicyCreateResponse,
+    type PolicyUpdateResponse as PolicyUpdateResponse,
+    type PolicyDeleteResponse as PolicyDeleteResponse,
+    type ProjectPoliciesPoliciesPage as ProjectPoliciesPoliciesPage,
+    type PolicyCreateParams as PolicyCreateParams,
+    type PolicyUpdateParams as PolicyUpdateParams,
+    type PolicyListParams as PolicyListParams,
+    type PolicyDeleteParams as PolicyDeleteParams,
+  };
+}
diff --git a/src/resources/runners.ts b/src/resources/runners.ts
new file mode 100644
index 0000000..003f18c
--- /dev/null
+++ b/src/resources/runners.ts
@@ -0,0 +1,3 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+export * from './runners/index';
diff --git a/src/resources/runners/configurations.ts b/src/resources/runners/configurations.ts
new file mode 100644
index 0000000..451b34e
--- /dev/null
+++ b/src/resources/runners/configurations.ts
@@ -0,0 +1,3 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+export * from './configurations/index';
diff --git a/src/resources/runners/configurations/configurations.ts b/src/resources/runners/configurations/configurations.ts
new file mode 100644
index 0000000..db30ece
--- /dev/null
+++ b/src/resources/runners/configurations/configurations.ts
@@ -0,0 +1,243 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../../../core/resource';
+import * as Shared from '../../shared';
+import * as EnvironmentClassesAPI from './environment-classes';
+import {
+  EnvironmentClassCreateParams,
+  EnvironmentClassCreateResponse,
+  EnvironmentClassListParams,
+  EnvironmentClassRetrieveParams,
+  EnvironmentClassRetrieveResponse,
+  EnvironmentClassUpdateParams,
+  EnvironmentClassUpdateResponse,
+  EnvironmentClasses,
+} from './environment-classes';
+import * as HostAuthenticationTokensAPI from './host-authentication-tokens';
+import {
+  HostAuthenticationToken,
+  HostAuthenticationTokenCreateParams,
+  HostAuthenticationTokenCreateResponse,
+  HostAuthenticationTokenDeleteParams,
+  HostAuthenticationTokenDeleteResponse,
+  HostAuthenticationTokenListParams,
+  HostAuthenticationTokenRetrieveParams,
+  HostAuthenticationTokenRetrieveResponse,
+  HostAuthenticationTokenSource,
+  HostAuthenticationTokenUpdateParams,
+  HostAuthenticationTokenUpdateResponse,
+  HostAuthenticationTokens,
+  HostAuthenticationTokensTokensPage,
+} from './host-authentication-tokens';
+import * as SchemaAPI from './schema';
+import { RunnerConfigurationSchema, Schema, SchemaRetrieveParams, SchemaRetrieveResponse } from './schema';
+import * as ScmIntegrationsAPI from './scm-integrations';
+import {
+  ScmIntegration as ScmIntegrationsAPIScmIntegration,
+  ScmIntegrationCreateParams,
+  ScmIntegrationCreateResponse,
+  ScmIntegrationDeleteParams,
+  ScmIntegrationDeleteResponse,
+  ScmIntegrationListParams,
+  ScmIntegrationOAuthConfig,
+  ScmIntegrationRetrieveParams,
+  ScmIntegrationRetrieveResponse,
+  ScmIntegrationUpdateParams,
+  ScmIntegrationUpdateResponse,
+  ScmIntegrations,
+  ScmIntegrationsIntegrationsPage,
+} from './scm-integrations';
+import { APIPromise } from '../../../core/api-promise';
+import { RequestOptions } from '../../../internal/request-options';
+
+export class Configurations extends APIResource {
+  environmentClasses: EnvironmentClassesAPI.EnvironmentClasses = new EnvironmentClassesAPI.EnvironmentClasses(
+    this._client,
+  );
+  hostAuthenticationTokens: HostAuthenticationTokensAPI.HostAuthenticationTokens =
+    new HostAuthenticationTokensAPI.HostAuthenticationTokens(this._client);
+  schema: SchemaAPI.Schema = new SchemaAPI.Schema(this._client);
+  scmIntegrations: ScmIntegrationsAPI.ScmIntegrations = new ScmIntegrationsAPI.ScmIntegrations(this._client);
+
+  /**
+   * Validates a runner configuration.
+   *
+   * Use this method to:
+   *
+   * - Check configuration validity
+   * - Verify integration settings
+   * - Validate environment classes
+   *
+   * ### Examples
+   *
+   * - Validate SCM integration:
+   *
+   *   Checks if an SCM integration is valid.
+   *
+   *   ```yaml
+   *   runnerId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   scmIntegration:
+   *     id: "integration-id"
+   *     scmId: "github"
+   *     host: "github.com"
+   *     oauthClientId: "client_id"
+   *     oauthPlaintextClientSecret: "client_secret"
+   *   ```
+   */
+  validate(
+    body: ConfigurationValidateParams,
+    options?: RequestOptions,
+  ): APIPromise<ConfigurationValidateResponse> {
+    return this._client.post('/gitpod.v1.RunnerConfigurationService/ValidateRunnerConfiguration', {
+      body,
+      ...options,
+    });
+  }
+}
+
+export interface EnvironmentClassValidationResult {
+  configurationErrors?: Array<FieldValidationError>;
+
+  descriptionError?: string | null;
+
+  displayNameError?: string | null;
+
+  valid?: boolean;
+}
+
+export interface FieldValidationError {
+  error?: string;
+
+  key?: string;
+}
+
+export interface ScmIntegrationValidationResult {
+  hostError?: string | null;
+
+  oauthError?: string | null;
+
+  patError?: string | null;
+
+  scmIdError?: string | null;
+
+  valid?: boolean;
+}
+
+export interface ConfigurationValidateResponse {
+  environmentClass?: EnvironmentClassValidationResult;
+
+  scmIntegration?: ScmIntegrationValidationResult;
+}
+
+export interface ConfigurationValidateParams {
+  environmentClass?: Shared.EnvironmentClass;
+
+  runnerId?: string;
+
+  scmIntegration?: ConfigurationValidateParams.ScmIntegration;
+}
+
+export namespace ConfigurationValidateParams {
+  export interface ScmIntegration {
+    /**
+     * id is the unique identifier of the SCM integration
+     */
+    id?: string;
+
+    host?: string;
+
+    /**
+     * oauth_client_id is the OAuth app's client ID, if OAuth is configured. If
+     * configured, oauth_client_secret must also be set.
+     */
+    oauthClientId?: string | null;
+
+    /**
+     * oauth_encrypted_client_secret is the OAuth app's client secret encrypted with
+     * the runner's public key, if OAuth is configured. This can be used to e.g.
+     * validate an already encrypted client secret of an existing SCM integration.
+     */
+    oauthEncryptedClientSecret?: string;
+
+    /**
+     * oauth_plaintext_client_secret is the OAuth app's client secret in clear text, if
+     * OAuth is configured. This can be set to validate any new client secret before it
+     * is encrypted and stored. This value will not be stored and get encrypted with
+     * the runner's public key before passing it to the runner.
+     */
+    oauthPlaintextClientSecret?: string;
+
+    pat?: boolean;
+
+    /**
+     * scm_id references the scm_id in the runner's configuration schema that this
+     * integration is for
+     */
+    scmId?: string;
+  }
+}
+
+Configurations.EnvironmentClasses = EnvironmentClasses;
+Configurations.HostAuthenticationTokens = HostAuthenticationTokens;
+Configurations.Schema = Schema;
+Configurations.ScmIntegrations = ScmIntegrations;
+
+export declare namespace Configurations {
+  export {
+    type EnvironmentClassValidationResult as EnvironmentClassValidationResult,
+    type FieldValidationError as FieldValidationError,
+    type ScmIntegrationValidationResult as ScmIntegrationValidationResult,
+    type ConfigurationValidateResponse as ConfigurationValidateResponse,
+    type ConfigurationValidateParams as ConfigurationValidateParams,
+  };
+
+  export {
+    EnvironmentClasses as EnvironmentClasses,
+    type EnvironmentClassCreateResponse as EnvironmentClassCreateResponse,
+    type EnvironmentClassRetrieveResponse as EnvironmentClassRetrieveResponse,
+    type EnvironmentClassUpdateResponse as EnvironmentClassUpdateResponse,
+    type EnvironmentClassCreateParams as EnvironmentClassCreateParams,
+    type EnvironmentClassRetrieveParams as EnvironmentClassRetrieveParams,
+    type EnvironmentClassUpdateParams as EnvironmentClassUpdateParams,
+    type EnvironmentClassListParams as EnvironmentClassListParams,
+  };
+
+  export {
+    HostAuthenticationTokens as HostAuthenticationTokens,
+    type HostAuthenticationToken as HostAuthenticationToken,
+    type HostAuthenticationTokenSource as HostAuthenticationTokenSource,
+    type HostAuthenticationTokenCreateResponse as HostAuthenticationTokenCreateResponse,
+    type HostAuthenticationTokenRetrieveResponse as HostAuthenticationTokenRetrieveResponse,
+    type HostAuthenticationTokenUpdateResponse as HostAuthenticationTokenUpdateResponse,
+    type HostAuthenticationTokenDeleteResponse as HostAuthenticationTokenDeleteResponse,
+    type HostAuthenticationTokensTokensPage as HostAuthenticationTokensTokensPage,
+    type HostAuthenticationTokenCreateParams as HostAuthenticationTokenCreateParams,
+    type HostAuthenticationTokenRetrieveParams as HostAuthenticationTokenRetrieveParams,
+    type HostAuthenticationTokenUpdateParams as HostAuthenticationTokenUpdateParams,
+    type HostAuthenticationTokenListParams as HostAuthenticationTokenListParams,
+    type HostAuthenticationTokenDeleteParams as HostAuthenticationTokenDeleteParams,
+  };
+
+  export {
+    Schema as Schema,
+    type RunnerConfigurationSchema as RunnerConfigurationSchema,
+    type SchemaRetrieveResponse as SchemaRetrieveResponse,
+    type SchemaRetrieveParams as SchemaRetrieveParams,
+  };
+
+  export {
+    ScmIntegrations as ScmIntegrations,
+    type ScmIntegrationsAPIScmIntegration as ScmIntegration,
+    type ScmIntegrationOAuthConfig as ScmIntegrationOAuthConfig,
+    type ScmIntegrationCreateResponse as ScmIntegrationCreateResponse,
+    type ScmIntegrationRetrieveResponse as ScmIntegrationRetrieveResponse,
+    type ScmIntegrationUpdateResponse as ScmIntegrationUpdateResponse,
+    type ScmIntegrationDeleteResponse as ScmIntegrationDeleteResponse,
+    type ScmIntegrationsIntegrationsPage as ScmIntegrationsIntegrationsPage,
+    type ScmIntegrationCreateParams as ScmIntegrationCreateParams,
+    type ScmIntegrationRetrieveParams as ScmIntegrationRetrieveParams,
+    type ScmIntegrationUpdateParams as ScmIntegrationUpdateParams,
+    type ScmIntegrationListParams as ScmIntegrationListParams,
+    type ScmIntegrationDeleteParams as ScmIntegrationDeleteParams,
+  };
+}
diff --git a/src/resources/runners/configurations/environment-classes.ts b/src/resources/runners/configurations/environment-classes.ts
new file mode 100644
index 0000000..1db7137
--- /dev/null
+++ b/src/resources/runners/configurations/environment-classes.ts
@@ -0,0 +1,267 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../../../core/resource';
+import * as Shared from '../../shared';
+import { EnvironmentClassesEnvironmentClassesPage } from '../../shared';
+import * as RunnersAPI from '../runners';
+import { APIPromise } from '../../../core/api-promise';
+import {
+  EnvironmentClassesPage,
+  type EnvironmentClassesPageParams,
+  PagePromise,
+} from '../../../core/pagination';
+import { RequestOptions } from '../../../internal/request-options';
+
+export class EnvironmentClasses extends APIResource {
+  /**
+   * Creates a new environment class for a runner.
+   *
+   * Use this method to:
+   *
+   * - Define compute resources
+   * - Configure environment settings
+   * - Set up runtime options
+   *
+   * ### Examples
+   *
+   * - Create environment class:
+   *
+   *   Creates a new environment configuration.
+   *
+   *   ```yaml
+   *   runnerId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   displayName: "Large Instance"
+   *   description: "8 CPU, 16GB RAM"
+   *   configuration:
+   *     - key: "cpu"
+   *       value: "8"
+   *     - key: "memory"
+   *       value: "16384"
+   *   ```
+   */
+  create(
+    body: EnvironmentClassCreateParams,
+    options?: RequestOptions,
+  ): APIPromise<EnvironmentClassCreateResponse> {
+    return this._client.post('/gitpod.v1.RunnerConfigurationService/CreateEnvironmentClass', {
+      body,
+      ...options,
+    });
+  }
+
+  /**
+   * Gets details about a specific environment class.
+   *
+   * Use this method to:
+   *
+   * - View class configuration
+   * - Check resource settings
+   * - Verify availability
+   *
+   * ### Examples
+   *
+   * - Get class details:
+   *
+   *   Retrieves information about a specific class.
+   *
+   *   ```yaml
+   *   environmentClassId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   ```
+   */
+  retrieve(
+    body: EnvironmentClassRetrieveParams,
+    options?: RequestOptions,
+  ): APIPromise<EnvironmentClassRetrieveResponse> {
+    return this._client.post('/gitpod.v1.RunnerConfigurationService/GetEnvironmentClass', {
+      body,
+      ...options,
+    });
+  }
+
+  /**
+   * Updates an environment class.
+   *
+   * Use this method to:
+   *
+   * - Modify class settings
+   * - Update resource limits
+   * - Change availability
+   *
+   * ### Examples
+   *
+   * - Update class:
+   *
+   *   Changes class configuration.
+   *
+   *   ```yaml
+   *   environmentClassId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   displayName: "Updated Large Instance"
+   *   description: "16 CPU, 32GB RAM"
+   *   enabled: true
+   *   ```
+   */
+  update(body: EnvironmentClassUpdateParams, options?: RequestOptions): APIPromise<unknown> {
+    return this._client.post('/gitpod.v1.RunnerConfigurationService/UpdateEnvironmentClass', {
+      body,
+      ...options,
+    });
+  }
+
+  /**
+   * Lists environment classes with optional filtering.
+   *
+   * Use this method to:
+   *
+   * - View available classes
+   * - Filter by capability
+   * - Check enabled status
+   *
+   * ### Examples
+   *
+   * - List all classes:
+   *
+   *   Shows all environment classes.
+   *
+   *   ```yaml
+   *   pagination:
+   *     pageSize: 20
+   *   ```
+   *
+   * - Filter enabled classes:
+   *
+   *   Lists only enabled environment classes.
+   *
+   *   ```yaml
+   *   filter:
+   *     enabled: true
+   *   pagination:
+   *     pageSize: 20
+   *   ```
+   *
+   *   buf:lint:ignore RPC_REQUEST_RESPONSE_UNIQUE
+   */
+  list(
+    params: EnvironmentClassListParams,
+    options?: RequestOptions,
+  ): PagePromise<EnvironmentClassesEnvironmentClassesPage, Shared.EnvironmentClass> {
+    const { token, pageSize, ...body } = params;
+    return this._client.getAPIList(
+      '/gitpod.v1.RunnerConfigurationService/ListEnvironmentClasses',
+      EnvironmentClassesPage<Shared.EnvironmentClass>,
+      { query: { token, pageSize }, body, method: 'post', ...options },
+    );
+  }
+}
+
+export interface EnvironmentClassCreateResponse {
+  id?: string;
+}
+
+export interface EnvironmentClassRetrieveResponse {
+  environmentClass?: Shared.EnvironmentClass;
+}
+
+export type EnvironmentClassUpdateResponse = unknown;
+
+export interface EnvironmentClassCreateParams {
+  configuration?: Array<Shared.FieldValue>;
+
+  description?: string;
+
+  displayName?: string;
+
+  runnerId?: string;
+}
+
+export interface EnvironmentClassRetrieveParams {
+  environmentClassId?: string;
+}
+
+export interface EnvironmentClassUpdateParams {
+  description?: string | null;
+
+  displayName?: string | null;
+
+  enabled?: boolean | null;
+
+  environmentClassId?: string;
+}
+
+export interface EnvironmentClassListParams extends EnvironmentClassesPageParams {
+  /**
+   * Body param:
+   */
+  filter?: EnvironmentClassListParams.Filter;
+
+  /**
+   * Body param: pagination contains the pagination options for listing environment
+   * classes
+   */
+  pagination?: EnvironmentClassListParams.Pagination;
+}
+
+export namespace EnvironmentClassListParams {
+  export interface Filter {
+    /**
+     * can_create_environments filters the response to only environment classes that
+     * can be used to create new environments by the caller. Unlike enabled, which
+     * indicates general availability, this ensures the caller only sees environment
+     * classes they are allowed to use.
+     */
+    canCreateEnvironments?: boolean | null;
+
+    /**
+     * enabled filters the response to only enabled or disabled environment classes. If
+     * not set, all environment classes are returned.
+     */
+    enabled?: boolean | null;
+
+    /**
+     * runner_ids filters the response to only EnvironmentClasses of these Runner IDs
+     */
+    runnerIds?: Array<string>;
+
+    /**
+     * runner_kind filters the response to only environment classes from runners of
+     * these kinds.
+     */
+    runnerKinds?: Array<RunnersAPI.RunnerKind>;
+
+    /**
+     * runner_providers filters the response to only environment classes from runners
+     * of these providers.
+     */
+    runnerProviders?: Array<RunnersAPI.RunnerProvider>;
+  }
+
+  /**
+   * pagination contains the pagination options for listing environment classes
+   */
+  export interface Pagination {
+    /**
+     * Token for the next set of results that was returned as next_token of a
+     * PaginationResponse
+     */
+    token?: string;
+
+    /**
+     * Page size is the maximum number of results to retrieve per page. Defaults to 25.
+     * Maximum 100.
+     */
+    pageSize?: number;
+  }
+}
+
+export declare namespace EnvironmentClasses {
+  export {
+    type EnvironmentClassCreateResponse as EnvironmentClassCreateResponse,
+    type EnvironmentClassRetrieveResponse as EnvironmentClassRetrieveResponse,
+    type EnvironmentClassUpdateResponse as EnvironmentClassUpdateResponse,
+    type EnvironmentClassCreateParams as EnvironmentClassCreateParams,
+    type EnvironmentClassRetrieveParams as EnvironmentClassRetrieveParams,
+    type EnvironmentClassUpdateParams as EnvironmentClassUpdateParams,
+    type EnvironmentClassListParams as EnvironmentClassListParams,
+  };
+}
+
+export { type EnvironmentClassesEnvironmentClassesPage };
diff --git a/src/resources/runners/configurations/host-authentication-tokens.ts b/src/resources/runners/configurations/host-authentication-tokens.ts
new file mode 100644
index 0000000..8196628
--- /dev/null
+++ b/src/resources/runners/configurations/host-authentication-tokens.ts
@@ -0,0 +1,558 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../../../core/resource';
+import { APIPromise } from '../../../core/api-promise';
+import { PagePromise, TokensPage, type TokensPageParams } from '../../../core/pagination';
+import { RequestOptions } from '../../../internal/request-options';
+
+export class HostAuthenticationTokens extends APIResource {
+  /**
+   * Creates a new authentication token for accessing remote hosts.
+   *
+   * Use this method to:
+   *
+   * - Set up SCM authentication
+   * - Configure OAuth credentials
+   * - Manage PAT tokens
+   *
+   * ### Examples
+   *
+   * - Create OAuth token:
+   *
+   *   Creates a new OAuth-based authentication token.
+   *
+   *   ```yaml
+   *   runnerId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   userId: "f53d2330-3795-4c5d-a1f3-453121af9c60"
+   *   host: "github.com"
+   *   token: "gho_xxxxxxxxxxxx"
+   *   source: HOST_AUTHENTICATION_TOKEN_SOURCE_OAUTH
+   *   expiresAt: "2024-12-31T23:59:59Z"
+   *   refreshToken: "ghr_xxxxxxxxxxxx"
+   *   ```
+   */
+  create(
+    body: HostAuthenticationTokenCreateParams,
+    options?: RequestOptions,
+  ): APIPromise<HostAuthenticationTokenCreateResponse> {
+    return this._client.post('/gitpod.v1.RunnerConfigurationService/CreateHostAuthenticationToken', {
+      body,
+      ...options,
+    });
+  }
+
+  /**
+   * Gets details about a specific host authentication token.
+   *
+   * Use this method to:
+   *
+   * - View token information
+   * - Check token expiration
+   * - Verify token validity
+   *
+   * ### Examples
+   *
+   * - Get token details:
+   *
+   *   Retrieves information about a specific token.
+   *
+   *   ```yaml
+   *   id: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   ```
+   */
+  retrieve(
+    body: HostAuthenticationTokenRetrieveParams,
+    options?: RequestOptions,
+  ): APIPromise<HostAuthenticationTokenRetrieveResponse> {
+    return this._client.post('/gitpod.v1.RunnerConfigurationService/GetHostAuthenticationToken', {
+      body,
+      ...options,
+    });
+  }
+
+  /**
+   * Updates an existing host authentication token.
+   *
+   * Use this method to:
+   *
+   * - Refresh token values
+   * - Update expiration
+   * - Modify token settings
+   *
+   * ### Examples
+   *
+   * - Update token:
+   *
+   *   Updates token value and expiration.
+   *
+   *   ```yaml
+   *   id: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   token: "gho_xxxxxxxxxxxx"
+   *   expiresAt: "2024-12-31T23:59:59Z"
+   *   refreshToken: "ghr_xxxxxxxxxxxx"
+   *   ```
+   */
+  update(body: HostAuthenticationTokenUpdateParams, options?: RequestOptions): APIPromise<unknown> {
+    return this._client.post('/gitpod.v1.RunnerConfigurationService/UpdateHostAuthenticationToken', {
+      body,
+      ...options,
+    });
+  }
+
+  /**
+   * Lists host authentication tokens with optional filtering.
+   *
+   * Use this method to:
+   *
+   * - View all tokens
+   * - Filter by runner or user
+   * - Monitor token status
+   *
+   * ### Examples
+   *
+   * - List all tokens:
+   *
+   *   Shows all tokens with pagination.
+   *
+   *   ```yaml
+   *   pagination:
+   *     pageSize: 20
+   *   ```
+   *
+   * - Filter by runner:
+   *
+   *   Lists tokens for a specific runner.
+   *
+   *   ```yaml
+   *   filter:
+   *     runnerId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   pagination:
+   *     pageSize: 20
+   *   ```
+   */
+  list(
+    params: HostAuthenticationTokenListParams,
+    options?: RequestOptions,
+  ): PagePromise<HostAuthenticationTokensTokensPage, HostAuthenticationToken> {
+    const { token, pageSize, ...body } = params;
+    return this._client.getAPIList(
+      '/gitpod.v1.RunnerConfigurationService/ListHostAuthenticationTokens',
+      TokensPage<HostAuthenticationToken>,
+      { query: { token, pageSize }, body, method: 'post', ...options },
+    );
+  }
+
+  /**
+   * Deletes a host authentication token.
+   *
+   * Use this method to:
+   *
+   * - Remove unused tokens
+   * - Revoke access
+   * - Clean up expired tokens
+   *
+   * ### Examples
+   *
+   * - Delete token:
+   *
+   *   Permanently removes a token.
+   *
+   *   ```yaml
+   *   id: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   ```
+   */
+  delete(body: HostAuthenticationTokenDeleteParams, options?: RequestOptions): APIPromise<unknown> {
+    return this._client.post('/gitpod.v1.RunnerConfigurationService/DeleteHostAuthenticationToken', {
+      body,
+      ...options,
+    });
+  }
+}
+
+export type HostAuthenticationTokensTokensPage = TokensPage<HostAuthenticationToken>;
+
+export interface HostAuthenticationToken {
+  id: string;
+
+  /**
+   * A Timestamp represents a point in time independent of any time zone or local
+   * calendar, encoded as a count of seconds and fractions of seconds at nanosecond
+   * resolution. The count is relative to an epoch at UTC midnight on January 1,
+   * 1970, in the proleptic Gregorian calendar which extends the Gregorian calendar
+   * backwards to year one.
+   *
+   * All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
+   * second table is needed for interpretation, using a
+   * [24-hour linear smear](https://developers.google.com/time/smear).
+   *
+   * The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
+   * restricting to that range, we ensure that we can convert to and from
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
+   *
+   * # Examples
+   *
+   * Example 1: Compute Timestamp from POSIX `time()`.
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(time(NULL));
+   *      timestamp.set_nanos(0);
+   *
+   * Example 2: Compute Timestamp from POSIX `gettimeofday()`.
+   *
+   *      struct timeval tv;
+   *      gettimeofday(&tv, NULL);
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(tv.tv_sec);
+   *      timestamp.set_nanos(tv.tv_usec * 1000);
+   *
+   * Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
+   *
+   *      FILETIME ft;
+   *      GetSystemTimeAsFileTime(&ft);
+   *      UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
+   *
+   *      // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
+   *      // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
+   *      timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
+   *
+   * Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
+   *
+   *      long millis = System.currentTimeMillis();
+   *
+   *      Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
+   *          .setNanos((int) ((millis % 1000) * 1000000)).build();
+   *
+   * Example 5: Compute Timestamp from Java `Instant.now()`.
+   *
+   *      Instant now = Instant.now();
+   *
+   *      Timestamp timestamp =
+   *          Timestamp.newBuilder().setSeconds(now.getEpochSecond())
+   *              .setNanos(now.getNano()).build();
+   *
+   * Example 6: Compute Timestamp from current time in Python.
+   *
+   *      timestamp = Timestamp()
+   *      timestamp.GetCurrentTime()
+   *
+   * # JSON Mapping
+   *
+   * In JSON format, the Timestamp type is encoded as a string in the
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the format is
+   * "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" where {year} is always
+   * expressed using four digits while {month}, {day}, {hour}, {min}, and {sec} are
+   * zero-padded to two digits each. The fractional seconds, which can go up to 9
+   * digits (i.e. up to 1 nanosecond resolution), are optional. The "Z" suffix
+   * indicates the timezone ("UTC"); the timezone is required. A proto3 JSON
+   * serializer should always use UTC (as indicated by "Z") when printing the
+   * Timestamp type and a proto3 JSON parser should be able to accept both UTC and
+   * other timezones (as indicated by an offset).
+   *
+   * For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past 01:30 UTC on
+   * January 15, 2017.
+   *
+   * In JavaScript, one can convert a Date object to this format using the standard
+   * [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
+   * method. In Python, a standard `datetime.datetime` object can be converted to
+   * this format using
+   * [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with the
+   * time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use the
+   * Joda Time's
+   * [`ISODateTimeFormat.dateTime()`](<http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime()>)
+   * to obtain a formatter capable of generating timestamps in this format.
+   */
+  expiresAt?: string;
+
+  host?: string;
+
+  runnerId?: string;
+
+  source?: HostAuthenticationTokenSource;
+
+  userId?: string;
+}
+
+export type HostAuthenticationTokenSource =
+  | 'HOST_AUTHENTICATION_TOKEN_SOURCE_UNSPECIFIED'
+  | 'HOST_AUTHENTICATION_TOKEN_SOURCE_OAUTH'
+  | 'HOST_AUTHENTICATION_TOKEN_SOURCE_PAT';
+
+export interface HostAuthenticationTokenCreateResponse {
+  token: HostAuthenticationToken;
+}
+
+export interface HostAuthenticationTokenRetrieveResponse {
+  token: HostAuthenticationToken;
+}
+
+export type HostAuthenticationTokenUpdateResponse = unknown;
+
+export type HostAuthenticationTokenDeleteResponse = unknown;
+
+export interface HostAuthenticationTokenCreateParams {
+  token?: string;
+
+  /**
+   * A Timestamp represents a point in time independent of any time zone or local
+   * calendar, encoded as a count of seconds and fractions of seconds at nanosecond
+   * resolution. The count is relative to an epoch at UTC midnight on January 1,
+   * 1970, in the proleptic Gregorian calendar which extends the Gregorian calendar
+   * backwards to year one.
+   *
+   * All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
+   * second table is needed for interpretation, using a
+   * [24-hour linear smear](https://developers.google.com/time/smear).
+   *
+   * The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
+   * restricting to that range, we ensure that we can convert to and from
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
+   *
+   * # Examples
+   *
+   * Example 1: Compute Timestamp from POSIX `time()`.
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(time(NULL));
+   *      timestamp.set_nanos(0);
+   *
+   * Example 2: Compute Timestamp from POSIX `gettimeofday()`.
+   *
+   *      struct timeval tv;
+   *      gettimeofday(&tv, NULL);
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(tv.tv_sec);
+   *      timestamp.set_nanos(tv.tv_usec * 1000);
+   *
+   * Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
+   *
+   *      FILETIME ft;
+   *      GetSystemTimeAsFileTime(&ft);
+   *      UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
+   *
+   *      // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
+   *      // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
+   *      timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
+   *
+   * Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
+   *
+   *      long millis = System.currentTimeMillis();
+   *
+   *      Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
+   *          .setNanos((int) ((millis % 1000) * 1000000)).build();
+   *
+   * Example 5: Compute Timestamp from Java `Instant.now()`.
+   *
+   *      Instant now = Instant.now();
+   *
+   *      Timestamp timestamp =
+   *          Timestamp.newBuilder().setSeconds(now.getEpochSecond())
+   *              .setNanos(now.getNano()).build();
+   *
+   * Example 6: Compute Timestamp from current time in Python.
+   *
+   *      timestamp = Timestamp()
+   *      timestamp.GetCurrentTime()
+   *
+   * # JSON Mapping
+   *
+   * In JSON format, the Timestamp type is encoded as a string in the
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the format is
+   * "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" where {year} is always
+   * expressed using four digits while {month}, {day}, {hour}, {min}, and {sec} are
+   * zero-padded to two digits each. The fractional seconds, which can go up to 9
+   * digits (i.e. up to 1 nanosecond resolution), are optional. The "Z" suffix
+   * indicates the timezone ("UTC"); the timezone is required. A proto3 JSON
+   * serializer should always use UTC (as indicated by "Z") when printing the
+   * Timestamp type and a proto3 JSON parser should be able to accept both UTC and
+   * other timezones (as indicated by an offset).
+   *
+   * For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past 01:30 UTC on
+   * January 15, 2017.
+   *
+   * In JavaScript, one can convert a Date object to this format using the standard
+   * [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
+   * method. In Python, a standard `datetime.datetime` object can be converted to
+   * this format using
+   * [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with the
+   * time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use the
+   * Joda Time's
+   * [`ISODateTimeFormat.dateTime()`](<http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime()>)
+   * to obtain a formatter capable of generating timestamps in this format.
+   */
+  expiresAt?: string;
+
+  host?: string;
+
+  refreshToken?: string;
+
+  runnerId?: string;
+
+  source?: HostAuthenticationTokenSource;
+
+  userId?: string;
+}
+
+export interface HostAuthenticationTokenRetrieveParams {
+  id?: string;
+}
+
+export interface HostAuthenticationTokenUpdateParams {
+  id?: string;
+
+  token?: string | null;
+
+  /**
+   * A Timestamp represents a point in time independent of any time zone or local
+   * calendar, encoded as a count of seconds and fractions of seconds at nanosecond
+   * resolution. The count is relative to an epoch at UTC midnight on January 1,
+   * 1970, in the proleptic Gregorian calendar which extends the Gregorian calendar
+   * backwards to year one.
+   *
+   * All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
+   * second table is needed for interpretation, using a
+   * [24-hour linear smear](https://developers.google.com/time/smear).
+   *
+   * The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
+   * restricting to that range, we ensure that we can convert to and from
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
+   *
+   * # Examples
+   *
+   * Example 1: Compute Timestamp from POSIX `time()`.
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(time(NULL));
+   *      timestamp.set_nanos(0);
+   *
+   * Example 2: Compute Timestamp from POSIX `gettimeofday()`.
+   *
+   *      struct timeval tv;
+   *      gettimeofday(&tv, NULL);
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(tv.tv_sec);
+   *      timestamp.set_nanos(tv.tv_usec * 1000);
+   *
+   * Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
+   *
+   *      FILETIME ft;
+   *      GetSystemTimeAsFileTime(&ft);
+   *      UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
+   *
+   *      // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
+   *      // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
+   *      timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
+   *
+   * Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
+   *
+   *      long millis = System.currentTimeMillis();
+   *
+   *      Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
+   *          .setNanos((int) ((millis % 1000) * 1000000)).build();
+   *
+   * Example 5: Compute Timestamp from Java `Instant.now()`.
+   *
+   *      Instant now = Instant.now();
+   *
+   *      Timestamp timestamp =
+   *          Timestamp.newBuilder().setSeconds(now.getEpochSecond())
+   *              .setNanos(now.getNano()).build();
+   *
+   * Example 6: Compute Timestamp from current time in Python.
+   *
+   *      timestamp = Timestamp()
+   *      timestamp.GetCurrentTime()
+   *
+   * # JSON Mapping
+   *
+   * In JSON format, the Timestamp type is encoded as a string in the
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the format is
+   * "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" where {year} is always
+   * expressed using four digits while {month}, {day}, {hour}, {min}, and {sec} are
+   * zero-padded to two digits each. The fractional seconds, which can go up to 9
+   * digits (i.e. up to 1 nanosecond resolution), are optional. The "Z" suffix
+   * indicates the timezone ("UTC"); the timezone is required. A proto3 JSON
+   * serializer should always use UTC (as indicated by "Z") when printing the
+   * Timestamp type and a proto3 JSON parser should be able to accept both UTC and
+   * other timezones (as indicated by an offset).
+   *
+   * For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past 01:30 UTC on
+   * January 15, 2017.
+   *
+   * In JavaScript, one can convert a Date object to this format using the standard
+   * [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
+   * method. In Python, a standard `datetime.datetime` object can be converted to
+   * this format using
+   * [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with the
+   * time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use the
+   * Joda Time's
+   * [`ISODateTimeFormat.dateTime()`](<http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime()>)
+   * to obtain a formatter capable of generating timestamps in this format.
+   */
+  expiresAt?: string | null;
+
+  refreshToken?: string | null;
+}
+
+export interface HostAuthenticationTokenListParams extends TokensPageParams {
+  /**
+   * Body param:
+   */
+  filter?: HostAuthenticationTokenListParams.Filter;
+
+  /**
+   * Body param:
+   */
+  pagination?: HostAuthenticationTokenListParams.Pagination;
+}
+
+export namespace HostAuthenticationTokenListParams {
+  export interface Filter {
+    runnerId?: string | null;
+
+    userId?: string | null;
+  }
+
+  export interface Pagination {
+    /**
+     * Token for the next set of results that was returned as next_token of a
+     * PaginationResponse
+     */
+    token?: string;
+
+    /**
+     * Page size is the maximum number of results to retrieve per page. Defaults to 25.
+     * Maximum 100.
+     */
+    pageSize?: number;
+  }
+}
+
+export interface HostAuthenticationTokenDeleteParams {
+  id?: string;
+}
+
+export declare namespace HostAuthenticationTokens {
+  export {
+    type HostAuthenticationToken as HostAuthenticationToken,
+    type HostAuthenticationTokenSource as HostAuthenticationTokenSource,
+    type HostAuthenticationTokenCreateResponse as HostAuthenticationTokenCreateResponse,
+    type HostAuthenticationTokenRetrieveResponse as HostAuthenticationTokenRetrieveResponse,
+    type HostAuthenticationTokenUpdateResponse as HostAuthenticationTokenUpdateResponse,
+    type HostAuthenticationTokenDeleteResponse as HostAuthenticationTokenDeleteResponse,
+    type HostAuthenticationTokensTokensPage as HostAuthenticationTokensTokensPage,
+    type HostAuthenticationTokenCreateParams as HostAuthenticationTokenCreateParams,
+    type HostAuthenticationTokenRetrieveParams as HostAuthenticationTokenRetrieveParams,
+    type HostAuthenticationTokenUpdateParams as HostAuthenticationTokenUpdateParams,
+    type HostAuthenticationTokenListParams as HostAuthenticationTokenListParams,
+    type HostAuthenticationTokenDeleteParams as HostAuthenticationTokenDeleteParams,
+  };
+}
diff --git a/src/resources/runners/configurations/index.ts b/src/resources/runners/configurations/index.ts
new file mode 100644
index 0000000..4a0a213
--- /dev/null
+++ b/src/resources/runners/configurations/index.ts
@@ -0,0 +1,56 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+export {
+  Configurations,
+  type EnvironmentClassValidationResult,
+  type FieldValidationError,
+  type ScmIntegrationValidationResult,
+  type ConfigurationValidateResponse,
+  type ConfigurationValidateParams,
+} from './configurations';
+export {
+  EnvironmentClasses,
+  type EnvironmentClassCreateResponse,
+  type EnvironmentClassRetrieveResponse,
+  type EnvironmentClassUpdateResponse,
+  type EnvironmentClassCreateParams,
+  type EnvironmentClassRetrieveParams,
+  type EnvironmentClassUpdateParams,
+  type EnvironmentClassListParams,
+} from './environment-classes';
+export {
+  HostAuthenticationTokens,
+  type HostAuthenticationToken,
+  type HostAuthenticationTokenSource,
+  type HostAuthenticationTokenCreateResponse,
+  type HostAuthenticationTokenRetrieveResponse,
+  type HostAuthenticationTokenUpdateResponse,
+  type HostAuthenticationTokenDeleteResponse,
+  type HostAuthenticationTokenCreateParams,
+  type HostAuthenticationTokenRetrieveParams,
+  type HostAuthenticationTokenUpdateParams,
+  type HostAuthenticationTokenListParams,
+  type HostAuthenticationTokenDeleteParams,
+  type HostAuthenticationTokensTokensPage,
+} from './host-authentication-tokens';
+export {
+  Schema,
+  type RunnerConfigurationSchema,
+  type SchemaRetrieveResponse,
+  type SchemaRetrieveParams,
+} from './schema';
+export {
+  ScmIntegrations,
+  type ScmIntegration,
+  type ScmIntegrationOAuthConfig,
+  type ScmIntegrationCreateResponse,
+  type ScmIntegrationRetrieveResponse,
+  type ScmIntegrationUpdateResponse,
+  type ScmIntegrationDeleteResponse,
+  type ScmIntegrationCreateParams,
+  type ScmIntegrationRetrieveParams,
+  type ScmIntegrationUpdateParams,
+  type ScmIntegrationListParams,
+  type ScmIntegrationDeleteParams,
+  type ScmIntegrationsIntegrationsPage,
+} from './scm-integrations';
diff --git a/src/resources/runners/configurations/schema.ts b/src/resources/runners/configurations/schema.ts
new file mode 100644
index 0000000..cd1222c
--- /dev/null
+++ b/src/resources/runners/configurations/schema.ts
@@ -0,0 +1,259 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../../../core/resource';
+import { APIPromise } from '../../../core/api-promise';
+import { RequestOptions } from '../../../internal/request-options';
+
+export class Schema extends APIResource {
+  /**
+   * Gets the latest runner configuration schema.
+   *
+   * Use this method to:
+   *
+   * - View available settings
+   * - Check configuration options
+   * - Validate configurations
+   *
+   * ### Examples
+   *
+   * - Get schema:
+   *
+   *   Retrieves configuration schema for a runner.
+   *
+   *   ```yaml
+   *   runnerId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   ```
+   */
+  retrieve(body: SchemaRetrieveParams, options?: RequestOptions): APIPromise<SchemaRetrieveResponse> {
+    return this._client.post('/gitpod.v1.RunnerConfigurationService/GetRunnerConfigurationSchema', {
+      body,
+      ...options,
+    });
+  }
+}
+
+export interface RunnerConfigurationSchema {
+  environmentClasses?: Array<RunnerConfigurationSchema.EnvironmentClass>;
+
+  runnerConfig?: Array<RunnerConfigurationSchema.RunnerConfig>;
+
+  scm?: Array<RunnerConfigurationSchema.Scm>;
+
+  /**
+   * The schema version
+   */
+  version?: string;
+}
+
+export namespace RunnerConfigurationSchema {
+  export interface EnvironmentClass {
+    id?: string;
+
+    bool?: EnvironmentClass.Bool;
+
+    description?: string;
+
+    display?: EnvironmentClass.Display;
+
+    enum?: EnvironmentClass.Enum;
+
+    int?: EnvironmentClass.Int;
+
+    name?: string;
+
+    required?: boolean;
+
+    secret?: boolean;
+
+    string?: EnvironmentClass.String;
+  }
+
+  export namespace EnvironmentClass {
+    export interface Bool {
+      default?: boolean;
+    }
+
+    export interface Display {
+      default?: string;
+    }
+
+    export interface Enum {
+      /**
+       * @deprecated deprecated, will be removed, use default_value instead
+       */
+      default?: string;
+
+      defaultValue?: Enum.DefaultValue;
+
+      possibleValues?: Array<Enum.PossibleValue>;
+
+      /**
+       * @deprecated deprecated, will be removed, use possible_values instead
+       */
+      values?: Array<string>;
+    }
+
+    export namespace Enum {
+      export interface DefaultValue {
+        detail?: string;
+
+        subtitle?: string;
+
+        title?: string;
+      }
+
+      export interface PossibleValue {
+        detail?: string;
+
+        subtitle?: string;
+
+        title?: string;
+      }
+    }
+
+    export interface Int {
+      default?: number;
+
+      max?: number;
+
+      min?: number;
+    }
+
+    export interface String {
+      default?: string;
+
+      pattern?: string;
+    }
+  }
+
+  export interface RunnerConfig {
+    id?: string;
+
+    bool?: RunnerConfig.Bool;
+
+    description?: string;
+
+    display?: RunnerConfig.Display;
+
+    enum?: RunnerConfig.Enum;
+
+    int?: RunnerConfig.Int;
+
+    name?: string;
+
+    required?: boolean;
+
+    secret?: boolean;
+
+    string?: RunnerConfig.String;
+  }
+
+  export namespace RunnerConfig {
+    export interface Bool {
+      default?: boolean;
+    }
+
+    export interface Display {
+      default?: string;
+    }
+
+    export interface Enum {
+      /**
+       * @deprecated deprecated, will be removed, use default_value instead
+       */
+      default?: string;
+
+      defaultValue?: Enum.DefaultValue;
+
+      possibleValues?: Array<Enum.PossibleValue>;
+
+      /**
+       * @deprecated deprecated, will be removed, use possible_values instead
+       */
+      values?: Array<string>;
+    }
+
+    export namespace Enum {
+      export interface DefaultValue {
+        detail?: string;
+
+        subtitle?: string;
+
+        title?: string;
+      }
+
+      export interface PossibleValue {
+        detail?: string;
+
+        subtitle?: string;
+
+        title?: string;
+      }
+    }
+
+    export interface Int {
+      default?: number;
+
+      max?: number;
+
+      min?: number;
+    }
+
+    export interface String {
+      default?: string;
+
+      pattern?: string;
+    }
+  }
+
+  export interface Scm {
+    defaultHosts?: Array<string>;
+
+    name?: string;
+
+    oauth?: Scm.OAuth;
+
+    pat?: Scm.Pat;
+
+    scmId?: string;
+  }
+
+  export namespace Scm {
+    export interface OAuth {
+      /**
+       * callback_url is the URL the OAuth app will redirect to after the user has
+       * authenticated.
+       */
+      callbackUrl?: string;
+    }
+
+    export interface Pat {
+      /**
+       * description is a human-readable description of the PAT.
+       */
+      description?: string;
+
+      /**
+       * docs_link is a link to the documentation on how to create a PAT for this SCM
+       * system.
+       */
+      docsLink?: string;
+    }
+  }
+}
+
+export interface SchemaRetrieveResponse {
+  schema?: RunnerConfigurationSchema;
+}
+
+export interface SchemaRetrieveParams {
+  runnerId?: string;
+}
+
+export declare namespace Schema {
+  export {
+    type RunnerConfigurationSchema as RunnerConfigurationSchema,
+    type SchemaRetrieveResponse as SchemaRetrieveResponse,
+    type SchemaRetrieveParams as SchemaRetrieveParams,
+  };
+}
diff --git a/src/resources/runners/configurations/scm-integrations.ts b/src/resources/runners/configurations/scm-integrations.ts
new file mode 100644
index 0000000..ebdc52b
--- /dev/null
+++ b/src/resources/runners/configurations/scm-integrations.ts
@@ -0,0 +1,326 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../../../core/resource';
+import { APIPromise } from '../../../core/api-promise';
+import { IntegrationsPage, type IntegrationsPageParams, PagePromise } from '../../../core/pagination';
+import { RequestOptions } from '../../../internal/request-options';
+
+export class ScmIntegrations extends APIResource {
+  /**
+   * Creates a new SCM integration for a runner.
+   *
+   * Use this method to:
+   *
+   * - Configure source control access
+   * - Set up repository integrations
+   * - Enable code synchronization
+   *
+   * ### Examples
+   *
+   * - Create GitHub integration:
+   *
+   *   Sets up GitHub SCM integration.
+   *
+   *   ```yaml
+   *   runnerId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   scmId: "github"
+   *   host: "github.com"
+   *   oauthClientId: "client_id"
+   *   oauthPlaintextClientSecret: "client_secret"
+   *   ```
+   */
+  create(
+    body: ScmIntegrationCreateParams,
+    options?: RequestOptions,
+  ): APIPromise<ScmIntegrationCreateResponse> {
+    return this._client.post('/gitpod.v1.RunnerConfigurationService/CreateSCMIntegration', {
+      body,
+      ...options,
+    });
+  }
+
+  /**
+   * Gets details about a specific SCM integration.
+   *
+   * Use this method to:
+   *
+   * - View integration settings
+   * - Check integration status
+   * - Verify configuration
+   *
+   * ### Examples
+   *
+   * - Get integration details:
+   *
+   *   Retrieves information about a specific integration.
+   *
+   *   ```yaml
+   *   id: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   ```
+   */
+  retrieve(
+    body: ScmIntegrationRetrieveParams,
+    options?: RequestOptions,
+  ): APIPromise<ScmIntegrationRetrieveResponse> {
+    return this._client.post('/gitpod.v1.RunnerConfigurationService/GetSCMIntegration', { body, ...options });
+  }
+
+  /**
+   * Updates an existing SCM integration.
+   *
+   * Use this method to:
+   *
+   * - Modify integration settings
+   * - Update credentials
+   * - Change configuration
+   *
+   * ### Examples
+   *
+   * - Update integration:
+   *
+   *   Updates OAuth credentials.
+   *
+   *   ```yaml
+   *   id: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   oauthClientId: "new_client_id"
+   *   oauthPlaintextClientSecret: "new_client_secret"
+   *   ```
+   */
+  update(body: ScmIntegrationUpdateParams, options?: RequestOptions): APIPromise<unknown> {
+    return this._client.post('/gitpod.v1.RunnerConfigurationService/UpdateSCMIntegration', {
+      body,
+      ...options,
+    });
+  }
+
+  /**
+   * Lists SCM integrations for a runner.
+   *
+   * Use this method to:
+   *
+   * - View all integrations
+   * - Monitor integration status
+   * - Check available SCMs
+   *
+   * ### Examples
+   *
+   * - List integrations:
+   *
+   *   Shows all SCM integrations.
+   *
+   *   ```yaml
+   *   filter:
+   *     runnerIds: ["d2c94c27-3b76-4a42-b88c-95a85e392c68"]
+   *   pagination:
+   *     pageSize: 20
+   *   ```
+   */
+  list(
+    params: ScmIntegrationListParams,
+    options?: RequestOptions,
+  ): PagePromise<ScmIntegrationsIntegrationsPage, ScmIntegration> {
+    const { token, pageSize, ...body } = params;
+    return this._client.getAPIList(
+      '/gitpod.v1.RunnerConfigurationService/ListSCMIntegrations',
+      IntegrationsPage<ScmIntegration>,
+      { query: { token, pageSize }, body, method: 'post', ...options },
+    );
+  }
+
+  /**
+   * Deletes an SCM integration.
+   *
+   * Use this method to:
+   *
+   * - Remove unused integrations
+   * - Clean up configurations
+   * - Revoke SCM access
+   *
+   * ### Examples
+   *
+   * - Delete integration:
+   *
+   *   Removes an SCM integration.
+   *
+   *   ```yaml
+   *   id: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   ```
+   */
+  delete(body: ScmIntegrationDeleteParams, options?: RequestOptions): APIPromise<unknown> {
+    return this._client.post('/gitpod.v1.RunnerConfigurationService/DeleteSCMIntegration', {
+      body,
+      ...options,
+    });
+  }
+}
+
+export type ScmIntegrationsIntegrationsPage = IntegrationsPage<ScmIntegration>;
+
+export interface ScmIntegration {
+  /**
+   * id is the unique identifier of the SCM integration
+   */
+  id?: string;
+
+  host?: string;
+
+  oauth?: ScmIntegrationOAuthConfig | null;
+
+  pat?: boolean;
+
+  runnerId?: string;
+
+  /**
+   * scm_id references the scm_id in the runner's configuration schema that this
+   * integration is for
+   */
+  scmId?: string;
+}
+
+export interface ScmIntegrationOAuthConfig {
+  /**
+   * client_id is the OAuth app's client ID in clear text.
+   */
+  clientId?: string;
+
+  /**
+   * encrypted_client_secret is the OAuth app's secret encrypted with the runner's
+   * public key.
+   */
+  encryptedClientSecret?: string;
+}
+
+export interface ScmIntegrationCreateResponse {
+  /**
+   * id is a uniquely generated identifier for the SCM integration
+   */
+  id?: string;
+}
+
+export interface ScmIntegrationRetrieveResponse {
+  integration?: ScmIntegration;
+}
+
+export type ScmIntegrationUpdateResponse = unknown;
+
+export type ScmIntegrationDeleteResponse = unknown;
+
+export interface ScmIntegrationCreateParams {
+  host?: string;
+
+  /**
+   * oauth_client_id is the OAuth app's client ID, if OAuth is configured. If
+   * configured, oauth_plaintext_client_secret must also be set.
+   */
+  oauthClientId?: string | null;
+
+  /**
+   * oauth_plaintext_client_secret is the OAuth app's client secret in clear text.
+   * This will first be encrypted with the runner's public key before being stored.
+   */
+  oauthPlaintextClientSecret?: string | null;
+
+  pat?: boolean;
+
+  runnerId?: string;
+
+  /**
+   * scm_id references the scm_id in the runner's configuration schema that this
+   * integration is for
+   */
+  scmId?: string;
+}
+
+export interface ScmIntegrationRetrieveParams {
+  id?: string;
+}
+
+export interface ScmIntegrationUpdateParams {
+  id?: string;
+
+  /**
+   * oauth_client_id can be set to update the OAuth app's client ID. If an empty
+   * string is set, the OAuth configuration will be removed (regardless of whether a
+   * client secret is set), and any existing Host Authentication Tokens for the SCM
+   * integration's runner and host that were created using the OAuth app will be
+   * deleted. This might lead to users being unable to access their repositories
+   * until they re-authenticate.
+   */
+  oauthClientId?: string | null;
+
+  /**
+   * oauth_plaintext_client_secret can be set to update the OAuth app's client
+   * secret. The cleartext secret will be encrypted with the runner's public key
+   * before being stored.
+   */
+  oauthPlaintextClientSecret?: string | null;
+
+  /**
+   * pat can be set to enable or disable Personal Access Tokens support. When
+   * disabling PATs, any existing Host Authentication Tokens for the SCM
+   * integration's runner and host that were created using a PAT will be deleted.
+   * This might lead to users being unable to access their repositories until they
+   * re-authenticate.
+   */
+  pat?: boolean | null;
+}
+
+export interface ScmIntegrationListParams extends IntegrationsPageParams {
+  /**
+   * Body param:
+   */
+  filter?: ScmIntegrationListParams.Filter;
+
+  /**
+   * Body param: pagination contains the pagination options for listing scm
+   * integrations
+   */
+  pagination?: ScmIntegrationListParams.Pagination;
+}
+
+export namespace ScmIntegrationListParams {
+  export interface Filter {
+    /**
+     * runner_ids filters the response to only SCM integrations of these Runner IDs
+     */
+    runnerIds?: Array<string>;
+  }
+
+  /**
+   * pagination contains the pagination options for listing scm integrations
+   */
+  export interface Pagination {
+    /**
+     * Token for the next set of results that was returned as next_token of a
+     * PaginationResponse
+     */
+    token?: string;
+
+    /**
+     * Page size is the maximum number of results to retrieve per page. Defaults to 25.
+     * Maximum 100.
+     */
+    pageSize?: number;
+  }
+}
+
+export interface ScmIntegrationDeleteParams {
+  id?: string;
+}
+
+export declare namespace ScmIntegrations {
+  export {
+    type ScmIntegration as ScmIntegration,
+    type ScmIntegrationOAuthConfig as ScmIntegrationOAuthConfig,
+    type ScmIntegrationCreateResponse as ScmIntegrationCreateResponse,
+    type ScmIntegrationRetrieveResponse as ScmIntegrationRetrieveResponse,
+    type ScmIntegrationUpdateResponse as ScmIntegrationUpdateResponse,
+    type ScmIntegrationDeleteResponse as ScmIntegrationDeleteResponse,
+    type ScmIntegrationsIntegrationsPage as ScmIntegrationsIntegrationsPage,
+    type ScmIntegrationCreateParams as ScmIntegrationCreateParams,
+    type ScmIntegrationRetrieveParams as ScmIntegrationRetrieveParams,
+    type ScmIntegrationUpdateParams as ScmIntegrationUpdateParams,
+    type ScmIntegrationListParams as ScmIntegrationListParams,
+    type ScmIntegrationDeleteParams as ScmIntegrationDeleteParams,
+  };
+}
diff --git a/src/resources/runners/index.ts b/src/resources/runners/index.ts
new file mode 100644
index 0000000..0f5a99a
--- /dev/null
+++ b/src/resources/runners/index.ts
@@ -0,0 +1,53 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+export {
+  Configurations,
+  type EnvironmentClassValidationResult,
+  type FieldValidationError,
+  type ScmIntegrationValidationResult,
+  type ConfigurationValidateResponse,
+  type ConfigurationValidateParams,
+} from './configurations/index';
+export {
+  Policies,
+  type RunnerPolicy,
+  type RunnerRole,
+  type PolicyCreateResponse,
+  type PolicyUpdateResponse,
+  type PolicyDeleteResponse,
+  type PolicyCreateParams,
+  type PolicyUpdateParams,
+  type PolicyListParams,
+  type PolicyDeleteParams,
+  type RunnerPoliciesPoliciesPage,
+} from './policies';
+export {
+  Runners,
+  type LogLevel,
+  type MetricsConfiguration,
+  type Runner,
+  type RunnerCapability,
+  type RunnerConfiguration,
+  type RunnerKind,
+  type RunnerPhase,
+  type RunnerProvider,
+  type RunnerReleaseChannel,
+  type RunnerSpec,
+  type RunnerStatus,
+  type RunnerCreateResponse,
+  type RunnerRetrieveResponse,
+  type RunnerUpdateResponse,
+  type RunnerDeleteResponse,
+  type RunnerCheckAuthenticationForHostResponse,
+  type RunnerCreateRunnerTokenResponse,
+  type RunnerParseContextURLResponse,
+  type RunnerCreateParams,
+  type RunnerRetrieveParams,
+  type RunnerUpdateParams,
+  type RunnerListParams,
+  type RunnerDeleteParams,
+  type RunnerCheckAuthenticationForHostParams,
+  type RunnerCreateRunnerTokenParams,
+  type RunnerParseContextURLParams,
+  type RunnersRunnersPage,
+} from './runners';
diff --git a/src/resources/runners/policies.ts b/src/resources/runners/policies.ts
new file mode 100644
index 0000000..0cdd4c1
--- /dev/null
+++ b/src/resources/runners/policies.ts
@@ -0,0 +1,225 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../../core/resource';
+import { APIPromise } from '../../core/api-promise';
+import { PagePromise, PoliciesPage, type PoliciesPageParams } from '../../core/pagination';
+import { RequestOptions } from '../../internal/request-options';
+
+export class Policies extends APIResource {
+  /**
+   * Creates a new policy for a runner.
+   *
+   * Use this method to:
+   *
+   * - Set up access controls
+   * - Define group permissions
+   * - Configure role-based access
+   *
+   * ### Examples
+   *
+   * - Create admin policy:
+   *
+   *   Grants admin access to a group.
+   *
+   *   ```yaml
+   *   runnerId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   groupId: "f53d2330-3795-4c5d-a1f3-453121af9c60"
+   *   role: RUNNER_ROLE_ADMIN
+   *   ```
+   */
+  create(body: PolicyCreateParams, options?: RequestOptions): APIPromise<PolicyCreateResponse> {
+    return this._client.post('/gitpod.v1.RunnerService/CreateRunnerPolicy', { body, ...options });
+  }
+
+  /**
+   * Updates an existing runner policy.
+   *
+   * Use this method to:
+   *
+   * - Modify access levels
+   * - Change group roles
+   * - Update permissions
+   *
+   * ### Examples
+   *
+   * - Update policy role:
+   *
+   *   Changes a group's access level.
+   *
+   *   ```yaml
+   *   runnerId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   groupId: "f53d2330-3795-4c5d-a1f3-453121af9c60"
+   *   role: RUNNER_ROLE_USER
+   *   ```
+   */
+  update(body: PolicyUpdateParams, options?: RequestOptions): APIPromise<PolicyUpdateResponse> {
+    return this._client.post('/gitpod.v1.RunnerService/UpdateRunnerPolicy', { body, ...options });
+  }
+
+  /**
+   * Lists policies for a runner.
+   *
+   * Use this method to:
+   *
+   * - View access controls
+   * - Check policy configurations
+   * - Audit permissions
+   *
+   * ### Examples
+   *
+   * - List policies:
+   *
+   *   Shows all policies for a runner.
+   *
+   *   ```yaml
+   *   runnerId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   pagination:
+   *     pageSize: 20
+   *   ```
+   */
+  list(
+    params: PolicyListParams,
+    options?: RequestOptions,
+  ): PagePromise<RunnerPoliciesPoliciesPage, RunnerPolicy> {
+    const { token, pageSize, ...body } = params;
+    return this._client.getAPIList(
+      '/gitpod.v1.RunnerService/ListRunnerPolicies',
+      PoliciesPage<RunnerPolicy>,
+      { query: { token, pageSize }, body, method: 'post', ...options },
+    );
+  }
+
+  /**
+   * Deletes a runner policy.
+   *
+   * Use this method to:
+   *
+   * - Remove access controls
+   * - Revoke permissions
+   * - Clean up policies
+   *
+   * ### Examples
+   *
+   * - Delete policy:
+   *
+   *   Removes a group's access policy.
+   *
+   *   ```yaml
+   *   runnerId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   groupId: "f53d2330-3795-4c5d-a1f3-453121af9c60"
+   *   ```
+   */
+  delete(body: PolicyDeleteParams, options?: RequestOptions): APIPromise<unknown> {
+    return this._client.post('/gitpod.v1.RunnerService/DeleteRunnerPolicy', { body, ...options });
+  }
+}
+
+export type RunnerPoliciesPoliciesPage = PoliciesPage<RunnerPolicy>;
+
+export interface RunnerPolicy {
+  groupId?: string;
+
+  /**
+   * role is the role assigned to the group
+   */
+  role?: RunnerRole;
+}
+
+export type RunnerRole = 'RUNNER_ROLE_UNSPECIFIED' | 'RUNNER_ROLE_ADMIN' | 'RUNNER_ROLE_USER';
+
+export interface PolicyCreateResponse {
+  policy: RunnerPolicy;
+}
+
+export interface PolicyUpdateResponse {
+  policy: RunnerPolicy;
+}
+
+export type PolicyDeleteResponse = unknown;
+
+export interface PolicyCreateParams {
+  /**
+   * group_id specifies the group_id identifier
+   */
+  groupId?: string;
+
+  role?: RunnerRole;
+
+  /**
+   * runner_id specifies the project identifier
+   */
+  runnerId?: string;
+}
+
+export interface PolicyUpdateParams {
+  /**
+   * group_id specifies the group_id identifier
+   */
+  groupId?: string;
+
+  role?: RunnerRole;
+
+  /**
+   * runner_id specifies the project identifier
+   */
+  runnerId?: string;
+}
+
+export interface PolicyListParams extends PoliciesPageParams {
+  /**
+   * Body param: pagination contains the pagination options for listing project
+   * policies
+   */
+  pagination?: PolicyListParams.Pagination;
+
+  /**
+   * Body param: runner_id specifies the project identifier
+   */
+  runnerId?: string;
+}
+
+export namespace PolicyListParams {
+  /**
+   * pagination contains the pagination options for listing project policies
+   */
+  export interface Pagination {
+    /**
+     * Token for the next set of results that was returned as next_token of a
+     * PaginationResponse
+     */
+    token?: string;
+
+    /**
+     * Page size is the maximum number of results to retrieve per page. Defaults to 25.
+     * Maximum 100.
+     */
+    pageSize?: number;
+  }
+}
+
+export interface PolicyDeleteParams {
+  /**
+   * group_id specifies the group_id identifier
+   */
+  groupId?: string;
+
+  /**
+   * runner_id specifies the project identifier
+   */
+  runnerId?: string;
+}
+
+export declare namespace Policies {
+  export {
+    type RunnerPolicy as RunnerPolicy,
+    type RunnerRole as RunnerRole,
+    type PolicyCreateResponse as PolicyCreateResponse,
+    type PolicyUpdateResponse as PolicyUpdateResponse,
+    type PolicyDeleteResponse as PolicyDeleteResponse,
+    type RunnerPoliciesPoliciesPage as RunnerPoliciesPoliciesPage,
+    type PolicyCreateParams as PolicyCreateParams,
+    type PolicyUpdateParams as PolicyUpdateParams,
+    type PolicyListParams as PolicyListParams,
+    type PolicyDeleteParams as PolicyDeleteParams,
+  };
+}
diff --git a/src/resources/runners/runners.ts b/src/resources/runners/runners.ts
new file mode 100644
index 0000000..5abb6dd
--- /dev/null
+++ b/src/resources/runners/runners.ts
@@ -0,0 +1,898 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../../core/resource';
+import * as RunnersAPI from './runners';
+import * as Shared from '../shared';
+import * as PoliciesAPI from './policies';
+import {
+  Policies,
+  PolicyCreateParams,
+  PolicyCreateResponse,
+  PolicyDeleteParams,
+  PolicyDeleteResponse,
+  PolicyListParams,
+  PolicyUpdateParams,
+  PolicyUpdateResponse,
+  RunnerPoliciesPoliciesPage,
+  RunnerPolicy,
+  RunnerRole,
+} from './policies';
+import * as ConfigurationsAPI from './configurations/configurations';
+import {
+  ConfigurationValidateParams,
+  ConfigurationValidateResponse,
+  Configurations,
+  EnvironmentClassValidationResult,
+  FieldValidationError,
+  ScmIntegrationValidationResult,
+} from './configurations/configurations';
+import { APIPromise } from '../../core/api-promise';
+import { PagePromise, RunnersPage, type RunnersPageParams } from '../../core/pagination';
+import { RequestOptions } from '../../internal/request-options';
+
+export class Runners extends APIResource {
+  configurations: ConfigurationsAPI.Configurations = new ConfigurationsAPI.Configurations(this._client);
+  policies: PoliciesAPI.Policies = new PoliciesAPI.Policies(this._client);
+
+  /**
+   * Creates a new runner registration with the server. Registrations are very
+   * short-lived and must be renewed every 30 seconds.
+   *
+   * Use this method to:
+   *
+   * - Register organization runners
+   * - Set up runner configurations
+   * - Initialize runner credentials
+   * - Configure auto-updates
+   *
+   * ### Examples
+   *
+   * - Create cloud runner:
+   *
+   *   Creates a new runner in AWS EC2.
+   *
+   *   ```yaml
+   *   name: "Production Runner"
+   *   provider: RUNNER_PROVIDER_AWS_EC2
+   *   spec:
+   *     desiredPhase: RUNNER_PHASE_ACTIVE
+   *     configuration:
+   *       region: "us-west"
+   *       releaseChannel: RUNNER_RELEASE_CHANNEL_STABLE
+   *       autoUpdate: true
+   *   ```
+   *
+   * - Create local runner:
+   *
+   *   Creates a new local runner on Linux.
+   *
+   *   ```yaml
+   *   name: "Local Development Runner"
+   *   provider: RUNNER_PROVIDER_LINUX_HOST
+   *   spec:
+   *     desiredPhase: RUNNER_PHASE_ACTIVE
+   *     configuration:
+   *       releaseChannel: RUNNER_RELEASE_CHANNEL_LATEST
+   *       autoUpdate: true
+   *   ```
+   */
+  create(body: RunnerCreateParams, options?: RequestOptions): APIPromise<RunnerCreateResponse> {
+    return this._client.post('/gitpod.v1.RunnerService/CreateRunner', { body, ...options });
+  }
+
+  /**
+   * Gets details about a specific runner.
+   *
+   * Use this method to:
+   *
+   * - Check runner status
+   * - View runner configuration
+   * - Monitor runner health
+   * - Verify runner capabilities
+   *
+   * ### Examples
+   *
+   * - Get runner details:
+   *
+   *   Retrieves information about a specific runner.
+   *
+   *   ```yaml
+   *   runnerId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   ```
+   */
+  retrieve(body: RunnerRetrieveParams, options?: RequestOptions): APIPromise<RunnerRetrieveResponse> {
+    return this._client.post('/gitpod.v1.RunnerService/GetRunner', { body, ...options });
+  }
+
+  /**
+   * Updates a runner's configuration.
+   *
+   * Use this method to:
+   *
+   * - Modify runner settings
+   * - Update release channels
+   * - Change runner status
+   * - Configure auto-update settings
+   *
+   * ### Examples
+   *
+   * - Update configuration:
+   *
+   *   Changes runner settings.
+   *
+   *   ```yaml
+   *   runnerId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   name: "Updated Runner Name"
+   *   spec:
+   *     configuration:
+   *       releaseChannel: RUNNER_RELEASE_CHANNEL_LATEST
+   *       autoUpdate: true
+   *   ```
+   */
+  update(body: RunnerUpdateParams, options?: RequestOptions): APIPromise<unknown> {
+    return this._client.post('/gitpod.v1.RunnerService/UpdateRunner', { body, ...options });
+  }
+
+  /**
+   * Lists all registered runners with optional filtering.
+   *
+   * Use this method to:
+   *
+   * - View all available runners
+   * - Filter by runner type
+   * - Monitor runner status
+   * - Check runner availability
+   *
+   * ### Examples
+   *
+   * - List all runners:
+   *
+   *   Shows all runners with pagination.
+   *
+   *   ```yaml
+   *   pagination:
+   *     pageSize: 20
+   *   ```
+   *
+   * - Filter by provider:
+   *
+   *   Lists only AWS EC2 runners.
+   *
+   *   ```yaml
+   *   filter:
+   *     providers: ["RUNNER_PROVIDER_AWS_EC2"]
+   *   pagination:
+   *     pageSize: 20
+   *   ```
+   */
+  list(params: RunnerListParams, options?: RequestOptions): PagePromise<RunnersRunnersPage, Runner> {
+    const { token, pageSize, ...body } = params;
+    return this._client.getAPIList('/gitpod.v1.RunnerService/ListRunners', RunnersPage<Runner>, {
+      query: { token, pageSize },
+      body,
+      method: 'post',
+      ...options,
+    });
+  }
+
+  /**
+   * Deletes a runner permanently.
+   *
+   * Use this method to:
+   *
+   * - Remove unused runners
+   * - Clean up runner registrations
+   * - Delete obsolete runners
+   *
+   * ### Examples
+   *
+   * - Delete runner:
+   *
+   *   Permanently removes a runner.
+   *
+   *   ```yaml
+   *   runnerId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   ```
+   */
+  delete(body: RunnerDeleteParams, options?: RequestOptions): APIPromise<unknown> {
+    return this._client.post('/gitpod.v1.RunnerService/DeleteRunner', { body, ...options });
+  }
+
+  /**
+   * Checks if a user is authenticated for a specific host.
+   *
+   * Use this method to:
+   *
+   * - Verify authentication status
+   * - Get authentication URLs
+   * - Check PAT support
+   *
+   * ### Examples
+   *
+   * - Check authentication:
+   *
+   *   Verifies authentication for a host.
+   *
+   *   ```yaml
+   *   host: "github.com"
+   *   ```
+   */
+  checkAuthenticationForHost(
+    body: RunnerCheckAuthenticationForHostParams,
+    options?: RequestOptions,
+  ): APIPromise<RunnerCheckAuthenticationForHostResponse> {
+    return this._client.post('/gitpod.v1.RunnerService/CheckAuthenticationForHost', { body, ...options });
+  }
+
+  /**
+   * Creates a new authentication token for a runner.
+   *
+   * Use this method to:
+   *
+   * - Generate runner credentials
+   * - Renew expired tokens
+   * - Set up runner authentication
+   *
+   * Note: This does not expire previously issued tokens.
+   *
+   * ### Examples
+   *
+   * - Create token:
+   *
+   *   Creates a new token for runner authentication.
+   *
+   *   ```yaml
+   *   runnerId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   ```
+   */
+  createRunnerToken(
+    body: RunnerCreateRunnerTokenParams,
+    options?: RequestOptions,
+  ): APIPromise<RunnerCreateRunnerTokenResponse> {
+    return this._client.post('/gitpod.v1.RunnerService/CreateRunnerToken', { body, ...options });
+  }
+
+  /**
+   * Parses a context URL and returns the parsed result.
+   *
+   * Use this method to:
+   *
+   * - Validate context URLs
+   * - Check repository access
+   * - Verify branch existence
+   *
+   * Returns:
+   *
+   * - FAILED_PRECONDITION if authentication is required
+   * - PERMISSION_DENIED if access is not allowed
+   * - INVALID_ARGUMENT if URL is invalid
+   * - NOT_FOUND if repository/branch doesn't exist
+   *
+   * ### Examples
+   *
+   * - Parse URL:
+   *
+   *   Parses and validates a context URL.
+   *
+   *   ```yaml
+   *   contextUrl: "https://github.com/org/repo/tree/main"
+   *   ```
+   */
+  parseContextURL(
+    body: RunnerParseContextURLParams,
+    options?: RequestOptions,
+  ): APIPromise<RunnerParseContextURLResponse> {
+    return this._client.post('/gitpod.v1.RunnerService/ParseContextURL', { body, ...options });
+  }
+}
+
+export type RunnersRunnersPage = RunnersPage<Runner>;
+
+export type LogLevel =
+  | 'LOG_LEVEL_UNSPECIFIED'
+  | 'LOG_LEVEL_DEBUG'
+  | 'LOG_LEVEL_INFO'
+  | 'LOG_LEVEL_WARN'
+  | 'LOG_LEVEL_ERROR';
+
+export interface MetricsConfiguration {
+  /**
+   * enabled indicates whether the runner should collect metrics
+   */
+  enabled?: boolean;
+
+  /**
+   * password is the password to use for the metrics collector
+   */
+  password?: string;
+
+  /**
+   * url is the URL of the metrics collector
+   */
+  url?: string;
+
+  /**
+   * username is the username to use for the metrics collector
+   */
+  username?: string;
+}
+
+export interface Runner {
+  /**
+   * Time when the Runner was created.
+   */
+  createdAt?: string;
+
+  /**
+   * creator is the identity of the creator of the environment
+   */
+  creator?: Shared.Subject;
+
+  /**
+   * The runner's kind
+   */
+  kind?: RunnerKind;
+
+  /**
+   * The runner's name which is shown to users
+   */
+  name?: string;
+
+  /**
+   * The runner's provider
+   */
+  provider?: RunnerProvider;
+
+  runnerId?: string;
+
+  /**
+   * The runner's specification
+   */
+  spec?: RunnerSpec;
+
+  /**
+   * The runner's status
+   */
+  status?: RunnerStatus;
+
+  /**
+   * Time when the Runner was last udpated.
+   */
+  updatedAt?: string;
+}
+
+export type RunnerCapability =
+  | 'RUNNER_CAPABILITY_UNSPECIFIED'
+  | 'RUNNER_CAPABILITY_FETCH_LOCAL_SCM_INTEGRATIONS'
+  | 'RUNNER_CAPABILITY_SECRET_CONTAINER_REGISTRY'
+  | 'RUNNER_CAPABILITY_AGENT_EXECUTION'
+  | 'RUNNER_CAPABILITY_ALLOW_ENV_TOKEN_POPULATION'
+  | 'RUNNER_CAPABILITY_DEFAULT_DEV_CONTAINER_IMAGE';
+
+export interface RunnerConfiguration {
+  /**
+   * auto_update indicates whether the runner should automatically update itself.
+   */
+  autoUpdate?: boolean;
+
+  /**
+   * devcontainer_image_cache_enabled controls whether the devcontainer build cache
+   * is enabled for this runner. Only takes effect on supported runners, currently
+   * only AWS EC2 runners.
+   */
+  devcontainerImageCacheEnabled?: boolean;
+
+  /**
+   * log_level is the log level for the runner
+   */
+  logLevel?: LogLevel;
+
+  /**
+   * metrics contains configuration for the runner's metrics collection
+   */
+  metrics?: MetricsConfiguration;
+
+  /**
+   * Region to deploy the runner in, if applicable. This is mainly used for remote
+   * runners, and is only a hint. The runner may be deployed in a different region.
+   * See the runner's status for the actual region.
+   */
+  region?: string;
+
+  /**
+   * The release channel the runner is on
+   */
+  releaseChannel?: RunnerReleaseChannel;
+}
+
+/**
+ * RunnerKind represents the kind of a runner
+ */
+export type RunnerKind =
+  | 'RUNNER_KIND_UNSPECIFIED'
+  | 'RUNNER_KIND_LOCAL'
+  | 'RUNNER_KIND_REMOTE'
+  | 'RUNNER_KIND_LOCAL_CONFIGURATION';
+
+/**
+ * RunnerPhase represents the phase a runner is in
+ */
+export type RunnerPhase =
+  | 'RUNNER_PHASE_UNSPECIFIED'
+  | 'RUNNER_PHASE_CREATED'
+  | 'RUNNER_PHASE_INACTIVE'
+  | 'RUNNER_PHASE_ACTIVE'
+  | 'RUNNER_PHASE_DELETING'
+  | 'RUNNER_PHASE_DELETED'
+  | 'RUNNER_PHASE_DEGRADED';
+
+/**
+ * RunnerProvider identifies the specific implementation type of a runner. Each
+ * provider maps to a specific kind of runner (local or remote), as specified below
+ * for each provider.
+ */
+export type RunnerProvider =
+  | 'RUNNER_PROVIDER_UNSPECIFIED'
+  | 'RUNNER_PROVIDER_AWS_EC2'
+  | 'RUNNER_PROVIDER_LINUX_HOST'
+  | 'RUNNER_PROVIDER_DESKTOP_MAC';
+
+export type RunnerReleaseChannel =
+  | 'RUNNER_RELEASE_CHANNEL_UNSPECIFIED'
+  | 'RUNNER_RELEASE_CHANNEL_STABLE'
+  | 'RUNNER_RELEASE_CHANNEL_LATEST';
+
+export interface RunnerSpec {
+  /**
+   * The runner's configuration
+   */
+  configuration?: RunnerConfiguration;
+
+  /**
+   * RunnerPhase represents the phase a runner is in
+   */
+  desiredPhase?: RunnerPhase;
+}
+
+/**
+ * RunnerStatus represents the status of a runner
+ */
+export interface RunnerStatus {
+  /**
+   * additional_info contains additional information about the runner, e.g. a
+   * CloudFormation stack URL.
+   */
+  additionalInfo?: Array<Shared.FieldValue>;
+
+  /**
+   * capabilities is a list of capabilities the runner supports.
+   */
+  capabilities?: Array<RunnerCapability>;
+
+  logUrl?: string;
+
+  /**
+   * The runner's reported message which is shown to users. This message adds more
+   * context to the runner's phase.
+   */
+  message?: string;
+
+  /**
+   * The runner's reported phase
+   */
+  phase?: RunnerPhase;
+
+  /**
+   * region is the region the runner is running in, if applicable.
+   */
+  region?: string;
+
+  systemDetails?: string;
+
+  /**
+   * Time when the status was last udpated.
+   */
+  updatedAt?: string;
+
+  version?: string;
+}
+
+export interface RunnerCreateResponse {
+  runner: Runner;
+
+  /**
+   * @deprecated deprecated, will be removed. Use exchange_token instead.
+   */
+  accessToken?: string;
+
+  /**
+   * exchange_token is a one-time use token that should be exchanged by the runner
+   * for an access token, using the IdentityService.ExchangeToken rpc. The token
+   * expires after 24 hours.
+   */
+  exchangeToken?: string;
+}
+
+export interface RunnerRetrieveResponse {
+  runner: Runner;
+}
+
+export type RunnerUpdateResponse = unknown;
+
+export type RunnerDeleteResponse = unknown;
+
+export interface RunnerCheckAuthenticationForHostResponse {
+  authenticated?: boolean;
+
+  /**
+   * @deprecated
+   */
+  authenticationUrl?: string;
+
+  /**
+   * @deprecated
+   */
+  patSupported?: boolean;
+
+  /**
+   * scm_id is the unique identifier of the SCM provider
+   */
+  scmId?: string;
+
+  /**
+   * scm_name is the human-readable name of the SCM provider (e.g., "GitHub",
+   * "GitLab")
+   */
+  scmName?: string;
+
+  /**
+   * supports_oauth2 indicates that the host supports OAuth2 authentication
+   */
+  supportsOauth2?: RunnerCheckAuthenticationForHostResponse.SupportsOauth2;
+
+  /**
+   * supports_pat indicates that the host supports Personal Access Token
+   * authentication
+   */
+  supportsPat?: RunnerCheckAuthenticationForHostResponse.SupportsPat;
+}
+
+export namespace RunnerCheckAuthenticationForHostResponse {
+  /**
+   * supports_oauth2 indicates that the host supports OAuth2 authentication
+   */
+  export interface SupportsOauth2 {
+    /**
+     * auth_url is the URL where users can authenticate
+     */
+    authUrl?: string;
+
+    /**
+     * docs_url is the URL to the documentation explaining this authentication method
+     */
+    docsUrl?: string;
+  }
+
+  /**
+   * supports_pat indicates that the host supports Personal Access Token
+   * authentication
+   */
+  export interface SupportsPat {
+    /**
+     * create_url is the URL where users can create a new Personal Access Token
+     */
+    createUrl?: string;
+
+    /**
+     * docs_url is the URL to the documentation explaining PAT usage for this host
+     */
+    docsUrl?: string;
+
+    /**
+     * example is an example of a Personal Access Token
+     */
+    example?: string;
+
+    /**
+     * required_scopes is the list of permissions required for the Personal Access
+     * Token
+     */
+    requiredScopes?: Array<string>;
+  }
+}
+
+export interface RunnerCreateRunnerTokenResponse {
+  /**
+   * @deprecated deprecated, will be removed. Use exchange_token instead.
+   */
+  accessToken?: string;
+
+  /**
+   * exchange_token is a one-time use token that should be exchanged by the runner
+   * for an access token, using the IdentityService.ExchangeToken rpc. The token
+   * expires after 24 hours.
+   */
+  exchangeToken?: string;
+}
+
+export interface RunnerParseContextURLResponse {
+  git?: RunnerParseContextURLResponse.Git;
+
+  originalContextUrl?: string;
+
+  /**
+   * project_ids is a list of projects to which the context URL belongs to.
+   */
+  projectIds?: Array<string>;
+}
+
+export namespace RunnerParseContextURLResponse {
+  export interface Git {
+    branch?: string;
+
+    cloneUrl?: string;
+
+    commit?: string;
+
+    host?: string;
+
+    owner?: string;
+
+    repo?: string;
+
+    upstreamRemoteUrl?: string;
+  }
+}
+
+export interface RunnerCreateParams {
+  /**
+   * The runner's kind This field is optional and here for backwards-compatibility.
+   * Use the provider field instead. If provider is set, the runner's kind will be
+   * deduced from the provider. Only one of kind and provider must be set.
+   */
+  kind?: RunnerKind;
+
+  /**
+   * The runner name for humans
+   */
+  name?: string;
+
+  /**
+   * The specific implementation type of the runner This field is optional for
+   * backwards compatibility but will be required in the future. When specified, kind
+   * must not be specified (will be deduced from provider)
+   */
+  provider?: RunnerProvider;
+
+  spec?: RunnerSpec;
+}
+
+export interface RunnerRetrieveParams {
+  runnerId?: string;
+}
+
+export interface RunnerUpdateParams {
+  /**
+   * The runner's name which is shown to users
+   */
+  name?: string | null;
+
+  /**
+   * runner_id specifies which runner to be updated.
+   *
+   * +required
+   */
+  runnerId?: string;
+
+  spec?: RunnerUpdateParams.Spec | null;
+}
+
+export namespace RunnerUpdateParams {
+  export interface Spec {
+    configuration?: Spec.Configuration | null;
+
+    /**
+     * desired_phase can currently only be updated on local-configuration runners, to
+     * toggle whether local runners are allowed for running environments in the
+     * organization. Set to:
+     *
+     * - ACTIVE to enable local runners.
+     * - INACTIVE to disable all local runners. Existing local runners and their
+     *   environments will stop, and cannot be started again until the desired_phase is
+     *   set to ACTIVE. Use this carefully, as it will affect all users in the
+     *   organization who use local runners.
+     */
+    desiredPhase?: RunnersAPI.RunnerPhase | null;
+  }
+
+  export namespace Spec {
+    export interface Configuration {
+      /**
+       * auto_update indicates whether the runner should automatically update itself.
+       */
+      autoUpdate?: boolean | null;
+
+      /**
+       * devcontainer_image_cache_enabled controls whether the shared devcontainer build
+       * cache is enabled for this runner.
+       */
+      devcontainerImageCacheEnabled?: boolean | null;
+
+      /**
+       * log_level is the log level for the runner
+       */
+      logLevel?: RunnersAPI.LogLevel | null;
+
+      /**
+       * metrics contains configuration for the runner's metrics collection
+       */
+      metrics?: Configuration.Metrics | null;
+
+      /**
+       * The release channel the runner is on
+       */
+      releaseChannel?: RunnersAPI.RunnerReleaseChannel | null;
+    }
+
+    export namespace Configuration {
+      /**
+       * metrics contains configuration for the runner's metrics collection
+       */
+      export interface Metrics {
+        /**
+         * enabled indicates whether the runner should collect metrics
+         */
+        enabled?: boolean | null;
+
+        /**
+         * password is the password to use for the metrics collector
+         */
+        password?: string | null;
+
+        /**
+         * url is the URL of the metrics collector
+         */
+        url?: string | null;
+
+        /**
+         * username is the username to use for the metrics collector
+         */
+        username?: string | null;
+      }
+    }
+  }
+}
+
+export interface RunnerListParams extends RunnersPageParams {
+  /**
+   * Body param:
+   */
+  filter?: RunnerListParams.Filter;
+
+  /**
+   * Body param: pagination contains the pagination options for listing runners
+   */
+  pagination?: RunnerListParams.Pagination;
+}
+
+export namespace RunnerListParams {
+  export interface Filter {
+    /**
+     * creator_ids filters the response to only runner created by specified users
+     */
+    creatorIds?: Array<string>;
+
+    /**
+     * kinds filters the response to only runners of the specified kinds
+     */
+    kinds?: Array<RunnersAPI.RunnerKind>;
+
+    /**
+     * providers filters the response to only runners of the specified providers
+     */
+    providers?: Array<RunnersAPI.RunnerProvider>;
+  }
+
+  /**
+   * pagination contains the pagination options for listing runners
+   */
+  export interface Pagination {
+    /**
+     * Token for the next set of results that was returned as next_token of a
+     * PaginationResponse
+     */
+    token?: string;
+
+    /**
+     * Page size is the maximum number of results to retrieve per page. Defaults to 25.
+     * Maximum 100.
+     */
+    pageSize?: number;
+  }
+}
+
+export interface RunnerDeleteParams {
+  /**
+   * force indicates whether the runner should be deleted forcefully. When force
+   * deleting a Runner, all Environments on the runner are also force deleted and
+   * regular Runner lifecycle is not respected. Force deleting can result in data
+   * loss.
+   */
+  force?: boolean;
+
+  runnerId?: string;
+}
+
+export interface RunnerCheckAuthenticationForHostParams {
+  host?: string;
+
+  runnerId?: string;
+}
+
+export interface RunnerCreateRunnerTokenParams {
+  runnerId?: string;
+}
+
+export interface RunnerParseContextURLParams {
+  contextUrl?: string;
+
+  runnerId?: string;
+}
+
+Runners.Configurations = Configurations;
+Runners.Policies = Policies;
+
+export declare namespace Runners {
+  export {
+    type LogLevel as LogLevel,
+    type MetricsConfiguration as MetricsConfiguration,
+    type Runner as Runner,
+    type RunnerCapability as RunnerCapability,
+    type RunnerConfiguration as RunnerConfiguration,
+    type RunnerKind as RunnerKind,
+    type RunnerPhase as RunnerPhase,
+    type RunnerProvider as RunnerProvider,
+    type RunnerReleaseChannel as RunnerReleaseChannel,
+    type RunnerSpec as RunnerSpec,
+    type RunnerStatus as RunnerStatus,
+    type RunnerCreateResponse as RunnerCreateResponse,
+    type RunnerRetrieveResponse as RunnerRetrieveResponse,
+    type RunnerUpdateResponse as RunnerUpdateResponse,
+    type RunnerDeleteResponse as RunnerDeleteResponse,
+    type RunnerCheckAuthenticationForHostResponse as RunnerCheckAuthenticationForHostResponse,
+    type RunnerCreateRunnerTokenResponse as RunnerCreateRunnerTokenResponse,
+    type RunnerParseContextURLResponse as RunnerParseContextURLResponse,
+    type RunnersRunnersPage as RunnersRunnersPage,
+    type RunnerCreateParams as RunnerCreateParams,
+    type RunnerRetrieveParams as RunnerRetrieveParams,
+    type RunnerUpdateParams as RunnerUpdateParams,
+    type RunnerListParams as RunnerListParams,
+    type RunnerDeleteParams as RunnerDeleteParams,
+    type RunnerCheckAuthenticationForHostParams as RunnerCheckAuthenticationForHostParams,
+    type RunnerCreateRunnerTokenParams as RunnerCreateRunnerTokenParams,
+    type RunnerParseContextURLParams as RunnerParseContextURLParams,
+  };
+
+  export {
+    Configurations as Configurations,
+    type EnvironmentClassValidationResult as EnvironmentClassValidationResult,
+    type FieldValidationError as FieldValidationError,
+    type ScmIntegrationValidationResult as ScmIntegrationValidationResult,
+    type ConfigurationValidateResponse as ConfigurationValidateResponse,
+    type ConfigurationValidateParams as ConfigurationValidateParams,
+  };
+
+  export {
+    Policies as Policies,
+    type RunnerPolicy as RunnerPolicy,
+    type RunnerRole as RunnerRole,
+    type PolicyCreateResponse as PolicyCreateResponse,
+    type PolicyUpdateResponse as PolicyUpdateResponse,
+    type PolicyDeleteResponse as PolicyDeleteResponse,
+    type RunnerPoliciesPoliciesPage as RunnerPoliciesPoliciesPage,
+    type PolicyCreateParams as PolicyCreateParams,
+    type PolicyUpdateParams as PolicyUpdateParams,
+    type PolicyListParams as PolicyListParams,
+    type PolicyDeleteParams as PolicyDeleteParams,
+  };
+}
diff --git a/src/resources/secrets.ts b/src/resources/secrets.ts
new file mode 100644
index 0000000..68c387d
--- /dev/null
+++ b/src/resources/secrets.ts
@@ -0,0 +1,539 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../core/resource';
+import * as SecretsAPI from './secrets';
+import * as Shared from './shared';
+import { APIPromise } from '../core/api-promise';
+import { PagePromise, SecretsPage, type SecretsPageParams } from '../core/pagination';
+import { RequestOptions } from '../internal/request-options';
+
+export class Secrets extends APIResource {
+  /**
+   * Creates a new secret for a project.
+   *
+   * Use this method to:
+   *
+   * - Store sensitive configuration values
+   * - Set up environment variables
+   * - Configure registry authentication
+   * - Add file-based secrets
+   *
+   * ### Examples
+   *
+   * - Create environment variable:
+   *
+   *   Creates a secret that will be available as an environment variable.
+   *
+   *   ```yaml
+   *   name: "DATABASE_URL"
+   *   projectId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   value: "postgresql://user:pass@localhost:5432/db"
+   *   environmentVariable: true
+   *   ```
+   *
+   * - Create file secret:
+   *
+   *   Creates a secret that will be mounted as a file.
+   *
+   *   ```yaml
+   *   name: "SSH_KEY"
+   *   projectId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   value: "-----BEGIN RSA PRIVATE KEY-----\n..."
+   *   filePath: "/home/gitpod/.ssh/id_rsa"
+   *   ```
+   *
+   * - Create registry auth:
+   *
+   *   Creates credentials for private container registry.
+   *
+   *   ```yaml
+   *   name: "DOCKER_AUTH"
+   *   projectId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   value: "username:password"
+   *   containerRegistryBasicAuthHost: "https://registry.example.com"
+   *   ```
+   */
+  create(body: SecretCreateParams, options?: RequestOptions): APIPromise<SecretCreateResponse> {
+    return this._client.post('/gitpod.v1.SecretService/CreateSecret', { body, ...options });
+  }
+
+  /**
+   * Lists secrets
+   *
+   * Use this method to:
+   *
+   * - View all project secrets
+   * - View all user secrets
+   *
+   * ### Examples
+   *
+   * - List project secrets:
+   *
+   *   Shows all secrets for a project.
+   *
+   *   ```yaml
+   *   filter:
+   *     scope:
+   *       projectId: "b0e12f6c-4c67-429d-a4a6-d9838b5da047"
+   *   pagination:
+   *     pageSize: 20
+   *   ```
+   *
+   * - List user secrets:
+   *
+   *   Shows all secrets for a user.
+   *
+   *   ```yaml
+   *   filter:
+   *     scope:
+   *       userId: "123e4567-e89b-12d3-a456-426614174000"
+   *   pagination:
+   *     pageSize: 20
+   *   ```
+   */
+  list(params: SecretListParams, options?: RequestOptions): PagePromise<SecretsSecretsPage, Secret> {
+    const { token, pageSize, ...body } = params;
+    return this._client.getAPIList('/gitpod.v1.SecretService/ListSecrets', SecretsPage<Secret>, {
+      query: { token, pageSize },
+      body,
+      method: 'post',
+      ...options,
+    });
+  }
+
+  /**
+   * Deletes a secret permanently.
+   *
+   * Use this method to:
+   *
+   * - Remove unused secrets
+   * - Clean up old credentials
+   *
+   * ### Examples
+   *
+   * - Delete secret:
+   *
+   *   Permanently removes a secret.
+   *
+   *   ```yaml
+   *   secretId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   ```
+   */
+  delete(body: SecretDeleteParams, options?: RequestOptions): APIPromise<unknown> {
+    return this._client.post('/gitpod.v1.SecretService/DeleteSecret', { body, ...options });
+  }
+
+  /**
+   * Gets the value of a secret. Only available to environments that are authorized
+   * to access the secret.
+   *
+   * Use this method to:
+   *
+   * - Retrieve secret values
+   * - Access credentials
+   *
+   * ### Examples
+   *
+   * - Get secret value:
+   *
+   *   Retrieves the value of a specific secret.
+   *
+   *   ```yaml
+   *   secretId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   ```
+   */
+  getValue(body: SecretGetValueParams, options?: RequestOptions): APIPromise<SecretGetValueResponse> {
+    return this._client.post('/gitpod.v1.SecretService/GetSecretValue', { body, ...options });
+  }
+
+  /**
+   * Updates the value of an existing secret.
+   *
+   * Use this method to:
+   *
+   * - Rotate secret values
+   * - Update credentials
+   *
+   * ### Examples
+   *
+   * - Update secret value:
+   *
+   *   Changes the value of an existing secret.
+   *
+   *   ```yaml
+   *   secretId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   value: "new-secret-value"
+   *   ```
+   */
+  updateValue(body: SecretUpdateValueParams, options?: RequestOptions): APIPromise<unknown> {
+    return this._client.post('/gitpod.v1.SecretService/UpdateSecretValue', { body, ...options });
+  }
+}
+
+export type SecretsSecretsPage = SecretsPage<Secret>;
+
+export interface Secret {
+  id?: string;
+
+  /**
+   * secret will be mounted as a registry secret
+   */
+  containerRegistryBasicAuthHost?: string;
+
+  /**
+   * A Timestamp represents a point in time independent of any time zone or local
+   * calendar, encoded as a count of seconds and fractions of seconds at nanosecond
+   * resolution. The count is relative to an epoch at UTC midnight on January 1,
+   * 1970, in the proleptic Gregorian calendar which extends the Gregorian calendar
+   * backwards to year one.
+   *
+   * All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
+   * second table is needed for interpretation, using a
+   * [24-hour linear smear](https://developers.google.com/time/smear).
+   *
+   * The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
+   * restricting to that range, we ensure that we can convert to and from
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
+   *
+   * # Examples
+   *
+   * Example 1: Compute Timestamp from POSIX `time()`.
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(time(NULL));
+   *      timestamp.set_nanos(0);
+   *
+   * Example 2: Compute Timestamp from POSIX `gettimeofday()`.
+   *
+   *      struct timeval tv;
+   *      gettimeofday(&tv, NULL);
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(tv.tv_sec);
+   *      timestamp.set_nanos(tv.tv_usec * 1000);
+   *
+   * Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
+   *
+   *      FILETIME ft;
+   *      GetSystemTimeAsFileTime(&ft);
+   *      UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
+   *
+   *      // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
+   *      // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
+   *      timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
+   *
+   * Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
+   *
+   *      long millis = System.currentTimeMillis();
+   *
+   *      Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
+   *          .setNanos((int) ((millis % 1000) * 1000000)).build();
+   *
+   * Example 5: Compute Timestamp from Java `Instant.now()`.
+   *
+   *      Instant now = Instant.now();
+   *
+   *      Timestamp timestamp =
+   *          Timestamp.newBuilder().setSeconds(now.getEpochSecond())
+   *              .setNanos(now.getNano()).build();
+   *
+   * Example 6: Compute Timestamp from current time in Python.
+   *
+   *      timestamp = Timestamp()
+   *      timestamp.GetCurrentTime()
+   *
+   * # JSON Mapping
+   *
+   * In JSON format, the Timestamp type is encoded as a string in the
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the format is
+   * "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" where {year} is always
+   * expressed using four digits while {month}, {day}, {hour}, {min}, and {sec} are
+   * zero-padded to two digits each. The fractional seconds, which can go up to 9
+   * digits (i.e. up to 1 nanosecond resolution), are optional. The "Z" suffix
+   * indicates the timezone ("UTC"); the timezone is required. A proto3 JSON
+   * serializer should always use UTC (as indicated by "Z") when printing the
+   * Timestamp type and a proto3 JSON parser should be able to accept both UTC and
+   * other timezones (as indicated by an offset).
+   *
+   * For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past 01:30 UTC on
+   * January 15, 2017.
+   *
+   * In JavaScript, one can convert a Date object to this format using the standard
+   * [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
+   * method. In Python, a standard `datetime.datetime` object can be converted to
+   * this format using
+   * [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with the
+   * time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use the
+   * Joda Time's
+   * [`ISODateTimeFormat.dateTime()`](<http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime()>)
+   * to obtain a formatter capable of generating timestamps in this format.
+   */
+  createdAt?: string;
+
+  /**
+   * creator is the identity of the creator of the secret
+   */
+  creator?: Shared.Subject;
+
+  /**
+   * secret will be created as an Environment Variable with the same name as the
+   * secret
+   */
+  environmentVariable?: boolean;
+
+  /**
+   * absolute path to the file where the secret is mounted
+   */
+  filePath?: string;
+
+  /**
+   * Name of the secret for humans.
+   */
+  name?: string;
+
+  /**
+   * @deprecated The Project ID this Secret belongs to Deprecated: use scope instead
+   */
+  projectId?: string;
+
+  scope?: SecretScope;
+
+  /**
+   * A Timestamp represents a point in time independent of any time zone or local
+   * calendar, encoded as a count of seconds and fractions of seconds at nanosecond
+   * resolution. The count is relative to an epoch at UTC midnight on January 1,
+   * 1970, in the proleptic Gregorian calendar which extends the Gregorian calendar
+   * backwards to year one.
+   *
+   * All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
+   * second table is needed for interpretation, using a
+   * [24-hour linear smear](https://developers.google.com/time/smear).
+   *
+   * The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
+   * restricting to that range, we ensure that we can convert to and from
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
+   *
+   * # Examples
+   *
+   * Example 1: Compute Timestamp from POSIX `time()`.
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(time(NULL));
+   *      timestamp.set_nanos(0);
+   *
+   * Example 2: Compute Timestamp from POSIX `gettimeofday()`.
+   *
+   *      struct timeval tv;
+   *      gettimeofday(&tv, NULL);
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(tv.tv_sec);
+   *      timestamp.set_nanos(tv.tv_usec * 1000);
+   *
+   * Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
+   *
+   *      FILETIME ft;
+   *      GetSystemTimeAsFileTime(&ft);
+   *      UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
+   *
+   *      // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
+   *      // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
+   *      timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
+   *
+   * Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
+   *
+   *      long millis = System.currentTimeMillis();
+   *
+   *      Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
+   *          .setNanos((int) ((millis % 1000) * 1000000)).build();
+   *
+   * Example 5: Compute Timestamp from Java `Instant.now()`.
+   *
+   *      Instant now = Instant.now();
+   *
+   *      Timestamp timestamp =
+   *          Timestamp.newBuilder().setSeconds(now.getEpochSecond())
+   *              .setNanos(now.getNano()).build();
+   *
+   * Example 6: Compute Timestamp from current time in Python.
+   *
+   *      timestamp = Timestamp()
+   *      timestamp.GetCurrentTime()
+   *
+   * # JSON Mapping
+   *
+   * In JSON format, the Timestamp type is encoded as a string in the
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the format is
+   * "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" where {year} is always
+   * expressed using four digits while {month}, {day}, {hour}, {min}, and {sec} are
+   * zero-padded to two digits each. The fractional seconds, which can go up to 9
+   * digits (i.e. up to 1 nanosecond resolution), are optional. The "Z" suffix
+   * indicates the timezone ("UTC"); the timezone is required. A proto3 JSON
+   * serializer should always use UTC (as indicated by "Z") when printing the
+   * Timestamp type and a proto3 JSON parser should be able to accept both UTC and
+   * other timezones (as indicated by an offset).
+   *
+   * For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past 01:30 UTC on
+   * January 15, 2017.
+   *
+   * In JavaScript, one can convert a Date object to this format using the standard
+   * [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
+   * method. In Python, a standard `datetime.datetime` object can be converted to
+   * this format using
+   * [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with the
+   * time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use the
+   * Joda Time's
+   * [`ISODateTimeFormat.dateTime()`](<http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime()>)
+   * to obtain a formatter capable of generating timestamps in this format.
+   */
+  updatedAt?: string;
+}
+
+export interface SecretScope {
+  /**
+   * project_id is the Project ID this Secret belongs to
+   */
+  projectId?: string;
+
+  /**
+   * user_id is the User ID this Secret belongs to
+   */
+  userId?: string;
+}
+
+export interface SecretCreateResponse {
+  secret?: Secret;
+}
+
+export type SecretDeleteResponse = unknown;
+
+export interface SecretGetValueResponse {
+  value?: string;
+}
+
+export type SecretUpdateValueResponse = unknown;
+
+export interface SecretCreateParams {
+  /**
+   * secret will be mounted as a docker config in the environment VM, mount will have
+   * the docker registry host
+   */
+  containerRegistryBasicAuthHost?: string;
+
+  /**
+   * secret will be created as an Environment Variable with the same name as the
+   * secret
+   */
+  environmentVariable?: boolean;
+
+  /**
+   * absolute path to the file where the secret is mounted value must be an absolute
+   * path (start with a /):
+   *
+   * ```
+   * this.matches('^/(?:[^/]* /)*.*$')
+   * ```
+   */
+  filePath?: string;
+
+  name?: string;
+
+  /**
+   * @deprecated project_id is the ProjectID this Secret belongs to Deprecated: use
+   * scope instead
+   */
+  projectId?: string;
+
+  /**
+   * scope is the scope of the secret
+   */
+  scope?: SecretScope;
+
+  /**
+   * value is the plaintext value of the secret
+   */
+  value?: string;
+}
+
+export interface SecretListParams extends SecretsPageParams {
+  /**
+   * Body param:
+   */
+  filter?: SecretListParams.Filter;
+
+  /**
+   * Body param: pagination contains the pagination options for listing environments
+   */
+  pagination?: SecretListParams.Pagination;
+}
+
+export namespace SecretListParams {
+  export interface Filter {
+    /**
+     * @deprecated project_ids filters the response to only Secrets used by these
+     * Project IDs Deprecated: use scope instead. Values in project_ids will be
+     * ignored.
+     */
+    projectIds?: Array<string>;
+
+    /**
+     * scope is the scope of the secrets to list
+     */
+    scope?: SecretsAPI.SecretScope;
+  }
+
+  /**
+   * pagination contains the pagination options for listing environments
+   */
+  export interface Pagination {
+    /**
+     * Token for the next set of results that was returned as next_token of a
+     * PaginationResponse
+     */
+    token?: string;
+
+    /**
+     * Page size is the maximum number of results to retrieve per page. Defaults to 25.
+     * Maximum 100.
+     */
+    pageSize?: number;
+  }
+}
+
+export interface SecretDeleteParams {
+  secretId?: string;
+}
+
+export interface SecretGetValueParams {
+  secretId?: string;
+}
+
+export interface SecretUpdateValueParams {
+  secretId?: string;
+
+  /**
+   * value is the plaintext value of the secret
+   */
+  value?: string;
+}
+
+export declare namespace Secrets {
+  export {
+    type Secret as Secret,
+    type SecretScope as SecretScope,
+    type SecretCreateResponse as SecretCreateResponse,
+    type SecretDeleteResponse as SecretDeleteResponse,
+    type SecretGetValueResponse as SecretGetValueResponse,
+    type SecretUpdateValueResponse as SecretUpdateValueResponse,
+    type SecretsSecretsPage as SecretsSecretsPage,
+    type SecretCreateParams as SecretCreateParams,
+    type SecretListParams as SecretListParams,
+    type SecretDeleteParams as SecretDeleteParams,
+    type SecretGetValueParams as SecretGetValueParams,
+    type SecretUpdateValueParams as SecretUpdateValueParams,
+  };
+}
diff --git a/src/resources/shared.ts b/src/resources/shared.ts
new file mode 100644
index 0000000..d2f7c17
--- /dev/null
+++ b/src/resources/shared.ts
@@ -0,0 +1,355 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import * as Shared from './shared';
+import { EnvironmentClassesPage, TaskExecutionsPage, TasksPage } from '../core/pagination';
+
+/**
+ * An AutomationTrigger represents a trigger for an automation action. The
+ * `post_environment_start` field indicates that the automation should be triggered
+ * after the environment has started. The `post_devcontainer_start` field indicates
+ * that the automation should be triggered after the dev container has started.
+ */
+export interface AutomationTrigger {
+  manual?: boolean;
+
+  postDevcontainerStart?: boolean;
+
+  postEnvironmentStart?: boolean;
+}
+
+export interface EnvironmentClass {
+  /**
+   * id is the unique identifier of the environment class
+   */
+  id: string;
+
+  /**
+   * runner_id is the unique identifier of the runner the environment class belongs
+   * to
+   */
+  runnerId: string;
+
+  /**
+   * configuration describes the configuration of the environment class
+   */
+  configuration?: Array<FieldValue>;
+
+  /**
+   * description is a human readable description of the environment class
+   */
+  description?: string;
+
+  /**
+   * display_name is the human readable name of the environment class
+   */
+  displayName?: string;
+
+  /**
+   * enabled indicates whether the environment class can be used to create new
+   * environments.
+   */
+  enabled?: boolean;
+}
+
+/**
+ * The status code, which should be an enum value of
+ * [google.rpc.Code][google.rpc.Code].
+ */
+export type ErrorCode =
+  | 'canceled'
+  | 'unknown'
+  | 'invalid_argument'
+  | 'deadline_exceeded'
+  | 'not_found'
+  | 'already_exists'
+  | 'permission_denied'
+  | 'resource_exhausted'
+  | 'failed_precondition'
+  | 'aborted'
+  | 'out_of_range'
+  | 'unimplemented'
+  | 'internal'
+  | 'unavailable'
+  | 'data_loss'
+  | 'unauthenticated';
+
+export interface FieldValue {
+  key?: string;
+
+  value?: string;
+}
+
+export type OrganizationRole =
+  | 'ORGANIZATION_ROLE_UNSPECIFIED'
+  | 'ORGANIZATION_ROLE_ADMIN'
+  | 'ORGANIZATION_ROLE_MEMBER';
+
+export type Principal =
+  | 'PRINCIPAL_UNSPECIFIED'
+  | 'PRINCIPAL_ACCOUNT'
+  | 'PRINCIPAL_USER'
+  | 'PRINCIPAL_RUNNER'
+  | 'PRINCIPAL_ENVIRONMENT'
+  | 'PRINCIPAL_SERVICE_ACCOUNT';
+
+export interface RunsOn {
+  docker: RunsOn.Docker;
+}
+
+export namespace RunsOn {
+  export interface Docker {
+    environment?: Array<string>;
+
+    image?: string;
+  }
+}
+
+export interface Subject {
+  /**
+   * id is the UUID of the subject
+   */
+  id?: string;
+
+  /**
+   * Principal is the principal of the subject
+   */
+  principal?: Principal;
+}
+
+export interface Task {
+  id: string;
+
+  /**
+   * dependencies specifies the IDs of the automations this task depends on.
+   */
+  dependsOn?: Array<string>;
+
+  environmentId?: string;
+
+  metadata?: TaskMetadata;
+
+  spec?: TaskSpec;
+}
+
+export interface TaskExecution {
+  id: string;
+
+  metadata?: TaskExecutionMetadata;
+
+  spec?: TaskExecutionSpec;
+
+  status?: TaskExecutionStatus;
+}
+
+export interface TaskExecutionMetadata {
+  /**
+   * completed_at is the time the task execution was done.
+   */
+  completedAt?: string;
+
+  /**
+   * created_at is the time the task was created.
+   */
+  createdAt?: string;
+
+  /**
+   * creator describes the principal who created/started the task run.
+   */
+  creator?: Subject;
+
+  /**
+   * environment_id is the ID of the environment in which the task run is executed.
+   */
+  environmentId?: string;
+
+  /**
+   * started_at is the time the task execution actually started to run.
+   */
+  startedAt?: string;
+
+  /**
+   * started_by describes the trigger that started the task execution.
+   */
+  startedBy?: string;
+
+  /**
+   * task_id is the ID of the main task being executed.
+   */
+  taskId?: string;
+}
+
+export type TaskExecutionPhase =
+  | 'TASK_EXECUTION_PHASE_UNSPECIFIED'
+  | 'TASK_EXECUTION_PHASE_PENDING'
+  | 'TASK_EXECUTION_PHASE_RUNNING'
+  | 'TASK_EXECUTION_PHASE_SUCCEEDED'
+  | 'TASK_EXECUTION_PHASE_FAILED'
+  | 'TASK_EXECUTION_PHASE_STOPPED';
+
+export interface TaskExecutionSpec {
+  /**
+   * desired_phase is the phase the task execution should be in. Used to stop a
+   * running task execution early.
+   */
+  desiredPhase?: TaskExecutionPhase;
+
+  /**
+   * plan is a list of groups of steps. The steps in a group are executed
+   * concurrently, while the groups are executed sequentially. The order of the
+   * groups is the order in which they are executed.
+   */
+  plan?: Array<TaskExecutionSpec.Plan>;
+}
+
+export namespace TaskExecutionSpec {
+  export interface Plan {
+    steps?: Array<Plan.Step>;
+  }
+
+  export namespace Plan {
+    export interface Step {
+      /**
+       * ID is the ID of the execution step
+       */
+      id?: string;
+
+      dependsOn?: Array<string>;
+
+      label?: string;
+
+      serviceId?: string;
+
+      task?: Step.Task;
+    }
+
+    export namespace Step {
+      export interface Task {
+        id?: string;
+
+        spec?: Shared.TaskSpec;
+      }
+    }
+  }
+}
+
+export interface TaskExecutionStatus {
+  /**
+   * failure_message summarises why the task execution failed to operate. If this is
+   * non-empty the task execution has failed to operate and will likely transition to
+   * a failed state.
+   */
+  failureMessage?: string;
+
+  /**
+   * log_url is the URL to the logs of the task's steps. If this is empty, the task
+   * either has no logs or has not yet started.
+   */
+  logUrl?: string;
+
+  /**
+   * the phase of a task execution represents the aggregated phase of all steps.
+   */
+  phase?: TaskExecutionPhase;
+
+  /**
+   * version of the status update. Task executions themselves are unversioned, but
+   * their status has different versions. The value of this field has no semantic
+   * meaning (e.g. don't interpret it as as a timestamp), but it can be used to
+   * impose a partial order. If a.status_version < b.status_version then a was the
+   * status before b.
+   */
+  statusVersion?: string;
+
+  /**
+   * steps provides the status for each individual step of the task execution. If a
+   * step is missing it has not yet started.
+   */
+  steps?: Array<TaskExecutionStatus.Step>;
+}
+
+export namespace TaskExecutionStatus {
+  export interface Step {
+    /**
+     * ID is the ID of the execution step
+     */
+    id?: string;
+
+    /**
+     * failure_message summarises why the step failed to operate. If this is non-empty
+     * the step has failed to operate and will likely transition to a failed state.
+     */
+    failureMessage?: string;
+
+    /**
+     * output contains the output of the task execution. setting an output field to
+     * empty string will unset it.
+     */
+    output?: Record<string, string>;
+
+    /**
+     * phase is the current phase of the execution step
+     */
+    phase?: Shared.TaskExecutionPhase;
+  }
+}
+
+export interface TaskMetadata {
+  /**
+   * created_at is the time the task was created.
+   */
+  createdAt?: string;
+
+  /**
+   * creator describes the principal who created the task.
+   */
+  creator?: Subject;
+
+  /**
+   * description is a user-facing description for the task. It can be used to provide
+   * context and documentation for the task.
+   */
+  description?: string;
+
+  /**
+   * name is a user-facing name for the task. Unlike the reference, this field is not
+   * unique, and not referenced by the system. This is a short descriptive name for
+   * the task.
+   */
+  name?: string;
+
+  /**
+   * reference is a user-facing identifier for the task which must be unique on the
+   * environment. It is used to express dependencies between tasks, and to identify
+   * the task in user interactions (e.g. the CLI).
+   */
+  reference?: string;
+
+  /**
+   * triggered_by is a list of trigger that start the task.
+   */
+  triggeredBy?: Array<AutomationTrigger>;
+}
+
+export interface TaskSpec {
+  /**
+   * command contains the command the task should execute
+   */
+  command?: string;
+
+  /**
+   * runs_on specifies the environment the task should run on.
+   */
+  runsOn?: RunsOn;
+}
+
+export type UserStatus =
+  | 'USER_STATUS_UNSPECIFIED'
+  | 'USER_STATUS_ACTIVE'
+  | 'USER_STATUS_SUSPENDED'
+  | 'USER_STATUS_LEFT';
+
+export type TasksTasksPage = TasksPage<Task>;
+
+export type TaskExecutionsTaskExecutionsPage = TaskExecutionsPage<TaskExecution>;
+
+export type EnvironmentClassesEnvironmentClassesPage = EnvironmentClassesPage<EnvironmentClass>;
diff --git a/src/resources/usage.ts b/src/resources/usage.ts
new file mode 100644
index 0000000..9417a8f
--- /dev/null
+++ b/src/resources/usage.ts
@@ -0,0 +1,159 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../core/resource';
+import { PagePromise, SessionsPage, type SessionsPageParams } from '../core/pagination';
+import { RequestOptions } from '../internal/request-options';
+
+export class Usage extends APIResource {
+  /**
+   * Lists completed environment sessions within a specified date range.
+   *
+   * Returns a list of environment sessions that were completed within the specified
+   * date range. Currently running sessions are not included.
+   *
+   * Use this method to:
+   *
+   * - View environment sessions
+   * - Filter by project
+   * - Monitor session activity
+   * - Create custom usage reports
+   *
+   * ### Example
+   *
+   * ```yaml
+   * filter:
+   *   projectId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   dateRange:
+   *     startTime: "2024-01-01T00:00:00Z"
+   *     endTime: "2024-01-02T00:00:00Z"
+   * pagination:
+   *   pageSize: 100
+   * ```
+   */
+  listEnvironmentSessions(
+    params: UsageListEnvironmentSessionsParams,
+    options?: RequestOptions,
+  ): PagePromise<EnvironmentSessionsSessionsPage, EnvironmentSession> {
+    const { token, pageSize, ...body } = params;
+    return this._client.getAPIList(
+      '/gitpod.v1.UsageService/ListEnvironmentSessions',
+      SessionsPage<EnvironmentSession>,
+      { query: { token, pageSize }, body, method: 'post', ...options },
+    );
+  }
+}
+
+export type EnvironmentSessionsSessionsPage = SessionsPage<EnvironmentSession>;
+
+export interface EnvironmentSession {
+  /**
+   * Environment session ID.
+   */
+  id?: string;
+
+  /**
+   * Time when the session was created.
+   */
+  createdAt?: string;
+
+  /**
+   * Environment class ID associated with the session.
+   */
+  environmentClassId?: string;
+
+  /**
+   * Environment ID associated with the session.
+   */
+  environmentId?: string;
+
+  /**
+   * Project ID associated with the session (if available).
+   */
+  projectId?: string;
+
+  /**
+   * Runner ID associated with the session.
+   */
+  runnerId?: string;
+
+  /**
+   * Time when the session was stopped.
+   */
+  stoppedAt?: string;
+
+  /**
+   * User ID that created the session.
+   */
+  userId?: string;
+}
+
+export interface UsageListEnvironmentSessionsParams extends SessionsPageParams {
+  /**
+   * Body param: Filter options.
+   */
+  filter?: UsageListEnvironmentSessionsParams.Filter;
+
+  /**
+   * Body param: Pagination options.
+   */
+  pagination?: UsageListEnvironmentSessionsParams.Pagination;
+}
+
+export namespace UsageListEnvironmentSessionsParams {
+  /**
+   * Filter options.
+   */
+  export interface Filter {
+    /**
+     * Date range to query sessions within.
+     */
+    dateRange: Filter.DateRange;
+
+    /**
+     * Optional project ID to filter sessions by.
+     */
+    projectId?: string;
+  }
+
+  export namespace Filter {
+    /**
+     * Date range to query sessions within.
+     */
+    export interface DateRange {
+      /**
+       * End time of the date range (exclusive).
+       */
+      endTime: string;
+
+      /**
+       * Start time of the date range (inclusive).
+       */
+      startTime: string;
+    }
+  }
+
+  /**
+   * Pagination options.
+   */
+  export interface Pagination {
+    /**
+     * Token for the next set of results that was returned as next_token of a
+     * PaginationResponse
+     */
+    token?: string;
+
+    /**
+     * Page size is the maximum number of results to retrieve per page. Defaults to 25.
+     * Maximum 100.
+     */
+    pageSize?: number;
+  }
+}
+
+export declare namespace Usage {
+  export {
+    type EnvironmentSession as EnvironmentSession,
+    type EnvironmentSessionsSessionsPage as EnvironmentSessionsSessionsPage,
+    type UsageListEnvironmentSessionsParams as UsageListEnvironmentSessionsParams,
+  };
+}
diff --git a/src/resources/users.ts b/src/resources/users.ts
new file mode 100644
index 0000000..db908c7
--- /dev/null
+++ b/src/resources/users.ts
@@ -0,0 +1,3 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+export * from './users/index';
diff --git a/src/resources/users/dotfiles.ts b/src/resources/users/dotfiles.ts
new file mode 100644
index 0000000..7a69609
--- /dev/null
+++ b/src/resources/users/dotfiles.ts
@@ -0,0 +1,89 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../../core/resource';
+import { APIPromise } from '../../core/api-promise';
+import { RequestOptions } from '../../internal/request-options';
+
+export class Dotfiles extends APIResource {
+  /**
+   * Gets the dotfiles for a user.
+   *
+   * Use this method to:
+   *
+   * - Retrieve user dotfiles
+   *
+   * ### Examples
+   *
+   * - Get dotfiles:
+   *
+   *   Retrieves the dotfiles for the current user.
+   *
+   *   ```yaml
+   *   {}
+   *   ```
+   */
+  get(body: DotfileGetParams, options?: RequestOptions): APIPromise<DotfileGetResponse> {
+    return this._client.post('/gitpod.v1.UserService/GetDotfilesConfiguration', { body, ...options });
+  }
+
+  /**
+   * Sets the dotfiles configuration for a user.
+   *
+   * Use this method to:
+   *
+   * - Configure user dotfiles
+   * - Update dotfiles settings
+   *
+   * ### Examples
+   *
+   * - Set dotfiles configuration:
+   *
+   *   Sets the dotfiles configuration for the current user.
+   *
+   *   ```yaml
+   *   { "repository": "https://github.com/gitpod-io/dotfiles" }
+   *   ```
+   *
+   * - Remove dotfiles:
+   *
+   *   Removes the dotfiles for the current user.
+   *
+   *   ```yaml
+   *   {}
+   *   ```
+   */
+  set(body: DotfileSetParams, options?: RequestOptions): APIPromise<unknown> {
+    return this._client.post('/gitpod.v1.UserService/SetDotfilesConfiguration', { body, ...options });
+  }
+}
+
+export interface DotfilesConfiguration {
+  /**
+   * The URL of a dotfiles repository.
+   */
+  repository?: string;
+}
+
+export interface DotfileGetResponse {
+  dotfilesConfiguration: DotfilesConfiguration;
+}
+
+export type DotfileSetResponse = unknown;
+
+export interface DotfileGetParams {
+  empty?: boolean;
+}
+
+export interface DotfileSetParams {
+  repository?: string;
+}
+
+export declare namespace Dotfiles {
+  export {
+    type DotfilesConfiguration as DotfilesConfiguration,
+    type DotfileGetResponse as DotfileGetResponse,
+    type DotfileSetResponse as DotfileSetResponse,
+    type DotfileGetParams as DotfileGetParams,
+    type DotfileSetParams as DotfileSetParams,
+  };
+}
diff --git a/src/resources/users/index.ts b/src/resources/users/index.ts
new file mode 100644
index 0000000..c91cbf0
--- /dev/null
+++ b/src/resources/users/index.ts
@@ -0,0 +1,28 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+export {
+  Dotfiles,
+  type DotfilesConfiguration,
+  type DotfileGetResponse,
+  type DotfileSetResponse,
+  type DotfileGetParams,
+  type DotfileSetParams,
+} from './dotfiles';
+export {
+  Pats,
+  type PersonalAccessToken,
+  type PatDeleteResponse,
+  type PatGetResponse,
+  type PatListParams,
+  type PatDeleteParams,
+  type PatGetParams,
+  type PersonalAccessTokensPersonalAccessTokensPage,
+} from './pats';
+export {
+  Users,
+  type User,
+  type UserGetAuthenticatedUserResponse,
+  type UserSetSuspendedResponse,
+  type UserGetAuthenticatedUserParams,
+  type UserSetSuspendedParams,
+} from './users';
diff --git a/src/resources/users/pats.ts b/src/resources/users/pats.ts
new file mode 100644
index 0000000..6827d68
--- /dev/null
+++ b/src/resources/users/pats.ts
@@ -0,0 +1,443 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../../core/resource';
+import * as Shared from '../shared';
+import { APIPromise } from '../../core/api-promise';
+import {
+  PagePromise,
+  PersonalAccessTokensPage,
+  type PersonalAccessTokensPageParams,
+} from '../../core/pagination';
+import { RequestOptions } from '../../internal/request-options';
+
+export class Pats extends APIResource {
+  /**
+   * Lists personal access tokens with optional filtering.
+   *
+   * Use this method to:
+   *
+   * - View all active tokens
+   * - Audit token usage
+   * - Manage token lifecycle
+   *
+   * ### Examples
+   *
+   * - List user tokens:
+   *
+   *   Shows all tokens for specific users.
+   *
+   *   ```yaml
+   *   filter:
+   *     userIds: ["f53d2330-3795-4c5d-a1f3-453121af9c60"]
+   *   pagination:
+   *     pageSize: 20
+   *   ```
+   */
+  list(
+    params: PatListParams,
+    options?: RequestOptions,
+  ): PagePromise<PersonalAccessTokensPersonalAccessTokensPage, PersonalAccessToken> {
+    const { token, pageSize, ...body } = params;
+    return this._client.getAPIList(
+      '/gitpod.v1.UserService/ListPersonalAccessTokens',
+      PersonalAccessTokensPage<PersonalAccessToken>,
+      { query: { token, pageSize }, body, method: 'post', ...options },
+    );
+  }
+
+  /**
+   * Deletes a personal access token.
+   *
+   * Use this method to:
+   *
+   * - Revoke token access
+   * - Remove unused tokens
+   * - Rotate credentials
+   *
+   * ### Examples
+   *
+   * - Delete token:
+   *
+   *   Permanently revokes a token.
+   *
+   *   ```yaml
+   *   personalAccessTokenId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   ```
+   */
+  delete(body: PatDeleteParams, options?: RequestOptions): APIPromise<unknown> {
+    return this._client.post('/gitpod.v1.UserService/DeletePersonalAccessToken', { body, ...options });
+  }
+
+  /**
+   * Gets details about a specific personal access token.
+   *
+   * Use this method to:
+   *
+   * - View token metadata
+   * - Check token expiration
+   * - Monitor token usage
+   *
+   * ### Examples
+   *
+   * - Get token details:
+   *
+   *   Retrieves information about a specific token.
+   *
+   *   ```yaml
+   *   personalAccessTokenId: "d2c94c27-3b76-4a42-b88c-95a85e392c68"
+   *   ```
+   */
+  get(body: PatGetParams, options?: RequestOptions): APIPromise<PatGetResponse> {
+    return this._client.post('/gitpod.v1.UserService/GetPersonalAccessToken', { body, ...options });
+  }
+}
+
+export type PersonalAccessTokensPersonalAccessTokensPage = PersonalAccessTokensPage<PersonalAccessToken>;
+
+export interface PersonalAccessToken {
+  id?: string;
+
+  /**
+   * A Timestamp represents a point in time independent of any time zone or local
+   * calendar, encoded as a count of seconds and fractions of seconds at nanosecond
+   * resolution. The count is relative to an epoch at UTC midnight on January 1,
+   * 1970, in the proleptic Gregorian calendar which extends the Gregorian calendar
+   * backwards to year one.
+   *
+   * All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
+   * second table is needed for interpretation, using a
+   * [24-hour linear smear](https://developers.google.com/time/smear).
+   *
+   * The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
+   * restricting to that range, we ensure that we can convert to and from
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
+   *
+   * # Examples
+   *
+   * Example 1: Compute Timestamp from POSIX `time()`.
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(time(NULL));
+   *      timestamp.set_nanos(0);
+   *
+   * Example 2: Compute Timestamp from POSIX `gettimeofday()`.
+   *
+   *      struct timeval tv;
+   *      gettimeofday(&tv, NULL);
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(tv.tv_sec);
+   *      timestamp.set_nanos(tv.tv_usec * 1000);
+   *
+   * Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
+   *
+   *      FILETIME ft;
+   *      GetSystemTimeAsFileTime(&ft);
+   *      UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
+   *
+   *      // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
+   *      // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
+   *      timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
+   *
+   * Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
+   *
+   *      long millis = System.currentTimeMillis();
+   *
+   *      Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
+   *          .setNanos((int) ((millis % 1000) * 1000000)).build();
+   *
+   * Example 5: Compute Timestamp from Java `Instant.now()`.
+   *
+   *      Instant now = Instant.now();
+   *
+   *      Timestamp timestamp =
+   *          Timestamp.newBuilder().setSeconds(now.getEpochSecond())
+   *              .setNanos(now.getNano()).build();
+   *
+   * Example 6: Compute Timestamp from current time in Python.
+   *
+   *      timestamp = Timestamp()
+   *      timestamp.GetCurrentTime()
+   *
+   * # JSON Mapping
+   *
+   * In JSON format, the Timestamp type is encoded as a string in the
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the format is
+   * "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" where {year} is always
+   * expressed using four digits while {month}, {day}, {hour}, {min}, and {sec} are
+   * zero-padded to two digits each. The fractional seconds, which can go up to 9
+   * digits (i.e. up to 1 nanosecond resolution), are optional. The "Z" suffix
+   * indicates the timezone ("UTC"); the timezone is required. A proto3 JSON
+   * serializer should always use UTC (as indicated by "Z") when printing the
+   * Timestamp type and a proto3 JSON parser should be able to accept both UTC and
+   * other timezones (as indicated by an offset).
+   *
+   * For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past 01:30 UTC on
+   * January 15, 2017.
+   *
+   * In JavaScript, one can convert a Date object to this format using the standard
+   * [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
+   * method. In Python, a standard `datetime.datetime` object can be converted to
+   * this format using
+   * [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with the
+   * time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use the
+   * Joda Time's
+   * [`ISODateTimeFormat.dateTime()`](<http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime()>)
+   * to obtain a formatter capable of generating timestamps in this format.
+   */
+  createdAt?: string;
+
+  creator?: Shared.Subject;
+
+  description?: string;
+
+  /**
+   * A Timestamp represents a point in time independent of any time zone or local
+   * calendar, encoded as a count of seconds and fractions of seconds at nanosecond
+   * resolution. The count is relative to an epoch at UTC midnight on January 1,
+   * 1970, in the proleptic Gregorian calendar which extends the Gregorian calendar
+   * backwards to year one.
+   *
+   * All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
+   * second table is needed for interpretation, using a
+   * [24-hour linear smear](https://developers.google.com/time/smear).
+   *
+   * The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
+   * restricting to that range, we ensure that we can convert to and from
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
+   *
+   * # Examples
+   *
+   * Example 1: Compute Timestamp from POSIX `time()`.
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(time(NULL));
+   *      timestamp.set_nanos(0);
+   *
+   * Example 2: Compute Timestamp from POSIX `gettimeofday()`.
+   *
+   *      struct timeval tv;
+   *      gettimeofday(&tv, NULL);
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(tv.tv_sec);
+   *      timestamp.set_nanos(tv.tv_usec * 1000);
+   *
+   * Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
+   *
+   *      FILETIME ft;
+   *      GetSystemTimeAsFileTime(&ft);
+   *      UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
+   *
+   *      // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
+   *      // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
+   *      timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
+   *
+   * Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
+   *
+   *      long millis = System.currentTimeMillis();
+   *
+   *      Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
+   *          .setNanos((int) ((millis % 1000) * 1000000)).build();
+   *
+   * Example 5: Compute Timestamp from Java `Instant.now()`.
+   *
+   *      Instant now = Instant.now();
+   *
+   *      Timestamp timestamp =
+   *          Timestamp.newBuilder().setSeconds(now.getEpochSecond())
+   *              .setNanos(now.getNano()).build();
+   *
+   * Example 6: Compute Timestamp from current time in Python.
+   *
+   *      timestamp = Timestamp()
+   *      timestamp.GetCurrentTime()
+   *
+   * # JSON Mapping
+   *
+   * In JSON format, the Timestamp type is encoded as a string in the
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the format is
+   * "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" where {year} is always
+   * expressed using four digits while {month}, {day}, {hour}, {min}, and {sec} are
+   * zero-padded to two digits each. The fractional seconds, which can go up to 9
+   * digits (i.e. up to 1 nanosecond resolution), are optional. The "Z" suffix
+   * indicates the timezone ("UTC"); the timezone is required. A proto3 JSON
+   * serializer should always use UTC (as indicated by "Z") when printing the
+   * Timestamp type and a proto3 JSON parser should be able to accept both UTC and
+   * other timezones (as indicated by an offset).
+   *
+   * For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past 01:30 UTC on
+   * January 15, 2017.
+   *
+   * In JavaScript, one can convert a Date object to this format using the standard
+   * [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
+   * method. In Python, a standard `datetime.datetime` object can be converted to
+   * this format using
+   * [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with the
+   * time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use the
+   * Joda Time's
+   * [`ISODateTimeFormat.dateTime()`](<http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime()>)
+   * to obtain a formatter capable of generating timestamps in this format.
+   */
+  expiresAt?: string;
+
+  /**
+   * A Timestamp represents a point in time independent of any time zone or local
+   * calendar, encoded as a count of seconds and fractions of seconds at nanosecond
+   * resolution. The count is relative to an epoch at UTC midnight on January 1,
+   * 1970, in the proleptic Gregorian calendar which extends the Gregorian calendar
+   * backwards to year one.
+   *
+   * All minutes are 60 seconds long. Leap seconds are "smeared" so that no leap
+   * second table is needed for interpretation, using a
+   * [24-hour linear smear](https://developers.google.com/time/smear).
+   *
+   * The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By
+   * restricting to that range, we ensure that we can convert to and from
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.
+   *
+   * # Examples
+   *
+   * Example 1: Compute Timestamp from POSIX `time()`.
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(time(NULL));
+   *      timestamp.set_nanos(0);
+   *
+   * Example 2: Compute Timestamp from POSIX `gettimeofday()`.
+   *
+   *      struct timeval tv;
+   *      gettimeofday(&tv, NULL);
+   *
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds(tv.tv_sec);
+   *      timestamp.set_nanos(tv.tv_usec * 1000);
+   *
+   * Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.
+   *
+   *      FILETIME ft;
+   *      GetSystemTimeAsFileTime(&ft);
+   *      UINT64 ticks = (((UINT64)ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
+   *
+   *      // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z
+   *      // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.
+   *      Timestamp timestamp;
+   *      timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));
+   *      timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));
+   *
+   * Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.
+   *
+   *      long millis = System.currentTimeMillis();
+   *
+   *      Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)
+   *          .setNanos((int) ((millis % 1000) * 1000000)).build();
+   *
+   * Example 5: Compute Timestamp from Java `Instant.now()`.
+   *
+   *      Instant now = Instant.now();
+   *
+   *      Timestamp timestamp =
+   *          Timestamp.newBuilder().setSeconds(now.getEpochSecond())
+   *              .setNanos(now.getNano()).build();
+   *
+   * Example 6: Compute Timestamp from current time in Python.
+   *
+   *      timestamp = Timestamp()
+   *      timestamp.GetCurrentTime()
+   *
+   * # JSON Mapping
+   *
+   * In JSON format, the Timestamp type is encoded as a string in the
+   * [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the format is
+   * "{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z" where {year} is always
+   * expressed using four digits while {month}, {day}, {hour}, {min}, and {sec} are
+   * zero-padded to two digits each. The fractional seconds, which can go up to 9
+   * digits (i.e. up to 1 nanosecond resolution), are optional. The "Z" suffix
+   * indicates the timezone ("UTC"); the timezone is required. A proto3 JSON
+   * serializer should always use UTC (as indicated by "Z") when printing the
+   * Timestamp type and a proto3 JSON parser should be able to accept both UTC and
+   * other timezones (as indicated by an offset).
+   *
+   * For example, "2017-01-15T01:30:15.01Z" encodes 15.01 seconds past 01:30 UTC on
+   * January 15, 2017.
+   *
+   * In JavaScript, one can convert a Date object to this format using the standard
+   * [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)
+   * method. In Python, a standard `datetime.datetime` object can be converted to
+   * this format using
+   * [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with the
+   * time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use the
+   * Joda Time's
+   * [`ISODateTimeFormat.dateTime()`](<http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime()>)
+   * to obtain a formatter capable of generating timestamps in this format.
+   */
+  lastUsed?: string;
+
+  userId?: string;
+}
+
+export type PatDeleteResponse = unknown;
+
+export interface PatGetResponse {
+  pat: PersonalAccessToken;
+}
+
+export interface PatListParams extends PersonalAccessTokensPageParams {
+  /**
+   * Body param:
+   */
+  filter?: PatListParams.Filter;
+
+  /**
+   * Body param:
+   */
+  pagination?: PatListParams.Pagination;
+}
+
+export namespace PatListParams {
+  export interface Filter {
+    /**
+     * creator_ids filters the response to only Environments created by specified
+     * members
+     */
+    userIds?: Array<string>;
+  }
+
+  export interface Pagination {
+    /**
+     * Token for the next set of results that was returned as next_token of a
+     * PaginationResponse
+     */
+    token?: string;
+
+    /**
+     * Page size is the maximum number of results to retrieve per page. Defaults to 25.
+     * Maximum 100.
+     */
+    pageSize?: number;
+  }
+}
+
+export interface PatDeleteParams {
+  personalAccessTokenId?: string;
+}
+
+export interface PatGetParams {
+  personalAccessTokenId?: string;
+}
+
+export declare namespace Pats {
+  export {
+    type PersonalAccessToken as PersonalAccessToken,
+    type PatDeleteResponse as PatDeleteResponse,
+    type PatGetResponse as PatGetResponse,
+    type PersonalAccessTokensPersonalAccessTokensPage as PersonalAccessTokensPersonalAccessTokensPage,
+    type PatListParams as PatListParams,
+    type PatDeleteParams as PatDeleteParams,
+    type PatGetParams as PatGetParams,
+  };
+}
diff --git a/src/resources/users/users.ts b/src/resources/users/users.ts
new file mode 100644
index 0000000..07f8eca
--- /dev/null
+++ b/src/resources/users/users.ts
@@ -0,0 +1,174 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIResource } from '../../core/resource';
+import * as Shared from '../shared';
+import * as DotfilesAPI from './dotfiles';
+import {
+  DotfileGetParams,
+  DotfileGetResponse,
+  DotfileSetParams,
+  DotfileSetResponse,
+  Dotfiles,
+  DotfilesConfiguration,
+} from './dotfiles';
+import * as PatsAPI from './pats';
+import {
+  PatDeleteParams,
+  PatDeleteResponse,
+  PatGetParams,
+  PatGetResponse,
+  PatListParams,
+  Pats,
+  PersonalAccessToken,
+  PersonalAccessTokensPersonalAccessTokensPage,
+} from './pats';
+import { APIPromise } from '../../core/api-promise';
+import { RequestOptions } from '../../internal/request-options';
+
+export class Users extends APIResource {
+  dotfiles: DotfilesAPI.Dotfiles = new DotfilesAPI.Dotfiles(this._client);
+  pats: PatsAPI.Pats = new PatsAPI.Pats(this._client);
+
+  /**
+   * Gets information about the currently authenticated user.
+   *
+   * Use this method to:
+   *
+   * - Get user profile information
+   * - Check authentication status
+   * - Retrieve user settings
+   * - Verify account details
+   *
+   * ### Examples
+   *
+   * - Get current user:
+   *
+   *   Retrieves details about the authenticated user.
+   *
+   *   ```yaml
+   *   {}
+   *   ```
+   */
+  getAuthenticatedUser(
+    body: UserGetAuthenticatedUserParams,
+    options?: RequestOptions,
+  ): APIPromise<UserGetAuthenticatedUserResponse> {
+    return this._client.post('/gitpod.v1.UserService/GetAuthenticatedUser', { body, ...options });
+  }
+
+  /**
+   * Sets whether a user account is suspended.
+   *
+   * Use this method to:
+   *
+   * - Suspend problematic users
+   * - Reactivate suspended accounts
+   * - Manage user access
+   *
+   * ### Examples
+   *
+   * - Suspend user:
+   *
+   *   Suspends a user account.
+   *
+   *   ```yaml
+   *   userId: "f53d2330-3795-4c5d-a1f3-453121af9c60"
+   *   suspended: true
+   *   ```
+   *
+   * - Reactivate user:
+   *
+   *   Removes suspension from a user account.
+   *
+   *   ```yaml
+   *   userId: "f53d2330-3795-4c5d-a1f3-453121af9c60"
+   *   suspended: false
+   *   ```
+   */
+  setSuspended(body: UserSetSuspendedParams, options?: RequestOptions): APIPromise<unknown> {
+    return this._client.post('/gitpod.v1.UserService/SetSuspended', { body, ...options });
+  }
+}
+
+export interface User {
+  /**
+   * id is a UUID of the user
+   */
+  id: string;
+
+  /**
+   * avatar_url is a link to the user avatar
+   */
+  avatarUrl?: string;
+
+  /**
+   * created_at is the creation time
+   */
+  createdAt?: string;
+
+  /**
+   * name is the full name of the user
+   */
+  name?: string;
+
+  /**
+   * organization_id is the id of the organization this account is owned by.
+   *
+   * +optional if not set, this account is owned by the installation.
+   */
+  organizationId?: string;
+
+  /**
+   * status is the status the user is in
+   */
+  status?: Shared.UserStatus;
+}
+
+export interface UserGetAuthenticatedUserResponse {
+  user: User;
+}
+
+export type UserSetSuspendedResponse = unknown;
+
+export interface UserGetAuthenticatedUserParams {
+  empty?: boolean;
+}
+
+export interface UserSetSuspendedParams {
+  suspended?: boolean;
+
+  userId?: string;
+}
+
+Users.Dotfiles = Dotfiles;
+Users.Pats = Pats;
+
+export declare namespace Users {
+  export {
+    type User as User,
+    type UserGetAuthenticatedUserResponse as UserGetAuthenticatedUserResponse,
+    type UserSetSuspendedResponse as UserSetSuspendedResponse,
+    type UserGetAuthenticatedUserParams as UserGetAuthenticatedUserParams,
+    type UserSetSuspendedParams as UserSetSuspendedParams,
+  };
+
+  export {
+    Dotfiles as Dotfiles,
+    type DotfilesConfiguration as DotfilesConfiguration,
+    type DotfileGetResponse as DotfileGetResponse,
+    type DotfileSetResponse as DotfileSetResponse,
+    type DotfileGetParams as DotfileGetParams,
+    type DotfileSetParams as DotfileSetParams,
+  };
+
+  export {
+    Pats as Pats,
+    type PersonalAccessToken as PersonalAccessToken,
+    type PatDeleteResponse as PatDeleteResponse,
+    type PatGetResponse as PatGetResponse,
+    type PersonalAccessTokensPersonalAccessTokensPage as PersonalAccessTokensPersonalAccessTokensPage,
+    type PatListParams as PatListParams,
+    type PatDeleteParams as PatDeleteParams,
+    type PatGetParams as PatGetParams,
+  };
+}
diff --git a/src/uploads.ts b/src/uploads.ts
new file mode 100644
index 0000000..b2ef647
--- /dev/null
+++ b/src/uploads.ts
@@ -0,0 +1,2 @@
+/** @deprecated Import from ./core/uploads instead */
+export * from './core/uploads';
diff --git a/src/version.ts b/src/version.ts
new file mode 100644
index 0000000..1f5d158
--- /dev/null
+++ b/src/version.ts
@@ -0,0 +1 @@
+export const VERSION = '0.5.0'; // x-release-please-version
diff --git a/tests/api-resources/accounts.test.ts b/tests/api-resources/accounts.test.ts
new file mode 100644
index 0000000..85aed19
--- /dev/null
+++ b/tests/api-resources/accounts.test.ts
@@ -0,0 +1,71 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Gitpod from '@gitpod/sdk';
+
+const client = new Gitpod({
+  bearerToken: 'My Bearer Token',
+  baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource accounts', () => {
+  // skipped: tests are disabled for the time being
+  test.skip('retrieve', async () => {
+    const responsePromise = client.accounts.retrieve({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('delete: only required params', async () => {
+    const responsePromise = client.accounts.delete({ accountId: 'f53d2330-3795-4c5d-a1f3-453121af9c60' });
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('delete: required and optional params', async () => {
+    const response = await client.accounts.delete({ accountId: 'f53d2330-3795-4c5d-a1f3-453121af9c60' });
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('getSSOLoginURL: only required params', async () => {
+    const responsePromise = client.accounts.getSSOLoginURL({ email: 'user@company.com' });
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('getSSOLoginURL: required and optional params', async () => {
+    const response = await client.accounts.getSSOLoginURL({
+      email: 'user@company.com',
+      returnTo: 'https://example.com',
+    });
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('listLoginProviders', async () => {
+    const responsePromise = client.accounts.listLoginProviders({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+});
diff --git a/tests/api-resources/editors.test.ts b/tests/api-resources/editors.test.ts
new file mode 100644
index 0000000..e3e3a7c
--- /dev/null
+++ b/tests/api-resources/editors.test.ts
@@ -0,0 +1,64 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Gitpod from '@gitpod/sdk';
+
+const client = new Gitpod({
+  bearerToken: 'My Bearer Token',
+  baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource editors', () => {
+  // skipped: tests are disabled for the time being
+  test.skip('retrieve: only required params', async () => {
+    const responsePromise = client.editors.retrieve({ id: 'd2c94c27-3b76-4a42-b88c-95a85e392c68' });
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('retrieve: required and optional params', async () => {
+    const response = await client.editors.retrieve({ id: 'd2c94c27-3b76-4a42-b88c-95a85e392c68' });
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('list', async () => {
+    const responsePromise = client.editors.list({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('resolveURL: only required params', async () => {
+    const responsePromise = client.editors.resolveURL({
+      editorId: 'd2c94c27-3b76-4a42-b88c-95a85e392c68',
+      environmentId: '07e03a28-65a5-4d98-b532-8ea67b188048',
+      organizationId: 'b0e12f6c-4c67-429d-a4a6-d9838b5da047',
+    });
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('resolveURL: required and optional params', async () => {
+    const response = await client.editors.resolveURL({
+      editorId: 'd2c94c27-3b76-4a42-b88c-95a85e392c68',
+      environmentId: '07e03a28-65a5-4d98-b532-8ea67b188048',
+      organizationId: 'b0e12f6c-4c67-429d-a4a6-d9838b5da047',
+    });
+  });
+});
diff --git a/tests/api-resources/environments/automations/automations.test.ts b/tests/api-resources/environments/automations/automations.test.ts
new file mode 100644
index 0000000..45b054d
--- /dev/null
+++ b/tests/api-resources/environments/automations/automations.test.ts
@@ -0,0 +1,22 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Gitpod from '@gitpod/sdk';
+
+const client = new Gitpod({
+  bearerToken: 'My Bearer Token',
+  baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource automations', () => {
+  // skipped: tests are disabled for the time being
+  test.skip('upsert', async () => {
+    const responsePromise = client.environments.automations.upsert({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+});
diff --git a/tests/api-resources/environments/automations/services.test.ts b/tests/api-resources/environments/automations/services.test.ts
new file mode 100644
index 0000000..9f01bea
--- /dev/null
+++ b/tests/api-resources/environments/automations/services.test.ts
@@ -0,0 +1,94 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Gitpod from '@gitpod/sdk';
+
+const client = new Gitpod({
+  bearerToken: 'My Bearer Token',
+  baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource services', () => {
+  // skipped: tests are disabled for the time being
+  test.skip('create', async () => {
+    const responsePromise = client.environments.automations.services.create({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('retrieve', async () => {
+    const responsePromise = client.environments.automations.services.retrieve({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('update', async () => {
+    const responsePromise = client.environments.automations.services.update({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('list', async () => {
+    const responsePromise = client.environments.automations.services.list({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('delete', async () => {
+    const responsePromise = client.environments.automations.services.delete({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('start', async () => {
+    const responsePromise = client.environments.automations.services.start({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('stop', async () => {
+    const responsePromise = client.environments.automations.services.stop({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+});
diff --git a/tests/api-resources/environments/automations/tasks/executions.test.ts b/tests/api-resources/environments/automations/tasks/executions.test.ts
new file mode 100644
index 0000000..7c8f4e4
--- /dev/null
+++ b/tests/api-resources/environments/automations/tasks/executions.test.ts
@@ -0,0 +1,46 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Gitpod from '@gitpod/sdk';
+
+const client = new Gitpod({
+  bearerToken: 'My Bearer Token',
+  baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource executions', () => {
+  // skipped: tests are disabled for the time being
+  test.skip('retrieve', async () => {
+    const responsePromise = client.environments.automations.tasks.executions.retrieve({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('list', async () => {
+    const responsePromise = client.environments.automations.tasks.executions.list({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('stop', async () => {
+    const responsePromise = client.environments.automations.tasks.executions.stop({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+});
diff --git a/tests/api-resources/environments/automations/tasks/tasks.test.ts b/tests/api-resources/environments/automations/tasks/tasks.test.ts
new file mode 100644
index 0000000..a928d7f
--- /dev/null
+++ b/tests/api-resources/environments/automations/tasks/tasks.test.ts
@@ -0,0 +1,82 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Gitpod from '@gitpod/sdk';
+
+const client = new Gitpod({
+  bearerToken: 'My Bearer Token',
+  baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource tasks', () => {
+  // skipped: tests are disabled for the time being
+  test.skip('create', async () => {
+    const responsePromise = client.environments.automations.tasks.create({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('retrieve', async () => {
+    const responsePromise = client.environments.automations.tasks.retrieve({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('update', async () => {
+    const responsePromise = client.environments.automations.tasks.update({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('list', async () => {
+    const responsePromise = client.environments.automations.tasks.list({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('delete', async () => {
+    const responsePromise = client.environments.automations.tasks.delete({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('start', async () => {
+    const responsePromise = client.environments.automations.tasks.start({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+});
diff --git a/tests/api-resources/environments/classes.test.ts b/tests/api-resources/environments/classes.test.ts
new file mode 100644
index 0000000..da2c482
--- /dev/null
+++ b/tests/api-resources/environments/classes.test.ts
@@ -0,0 +1,22 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Gitpod from '@gitpod/sdk';
+
+const client = new Gitpod({
+  bearerToken: 'My Bearer Token',
+  baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource classes', () => {
+  // skipped: tests are disabled for the time being
+  test.skip('list', async () => {
+    const responsePromise = client.environments.classes.list({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+});
diff --git a/tests/api-resources/environments/environments.test.ts b/tests/api-resources/environments/environments.test.ts
new file mode 100644
index 0000000..aaf1efc
--- /dev/null
+++ b/tests/api-resources/environments/environments.test.ts
@@ -0,0 +1,160 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Gitpod from '@gitpod/sdk';
+
+const client = new Gitpod({
+  bearerToken: 'My Bearer Token',
+  baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource environments', () => {
+  // skipped: tests are disabled for the time being
+  test.skip('create', async () => {
+    const responsePromise = client.environments.create({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('retrieve: only required params', async () => {
+    const responsePromise = client.environments.retrieve({
+      environmentId: '07e03a28-65a5-4d98-b532-8ea67b188048',
+    });
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('retrieve: required and optional params', async () => {
+    const response = await client.environments.retrieve({
+      environmentId: '07e03a28-65a5-4d98-b532-8ea67b188048',
+    });
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('update', async () => {
+    const responsePromise = client.environments.update({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('list', async () => {
+    const responsePromise = client.environments.list({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('delete', async () => {
+    const responsePromise = client.environments.delete({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('createEnvironmentToken: only required params', async () => {
+    const responsePromise = client.environments.createEnvironmentToken({
+      environmentId: '07e03a28-65a5-4d98-b532-8ea67b188048',
+    });
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('createEnvironmentToken: required and optional params', async () => {
+    const response = await client.environments.createEnvironmentToken({
+      environmentId: '07e03a28-65a5-4d98-b532-8ea67b188048',
+    });
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('createFromProject', async () => {
+    const responsePromise = client.environments.createFromProject({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('createLogsToken', async () => {
+    const responsePromise = client.environments.createLogsToken({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('markActive', async () => {
+    const responsePromise = client.environments.markActive({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('start', async () => {
+    const responsePromise = client.environments.start({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('stop', async () => {
+    const responsePromise = client.environments.stop({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+});
diff --git a/tests/api-resources/events.test.ts b/tests/api-resources/events.test.ts
new file mode 100644
index 0000000..d556b72
--- /dev/null
+++ b/tests/api-resources/events.test.ts
@@ -0,0 +1,34 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Gitpod from '@gitpod/sdk';
+
+const client = new Gitpod({
+  bearerToken: 'My Bearer Token',
+  baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource events', () => {
+  // skipped: tests are disabled for the time being
+  test.skip('list', async () => {
+    const responsePromise = client.events.list({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // Prism doesn't support JSONL responses yet
+  test.skip('watch', async () => {
+    const responsePromise = client.events.watch({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+});
diff --git a/tests/api-resources/groups.test.ts b/tests/api-resources/groups.test.ts
new file mode 100644
index 0000000..dcc3e5b
--- /dev/null
+++ b/tests/api-resources/groups.test.ts
@@ -0,0 +1,22 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Gitpod from '@gitpod/sdk';
+
+const client = new Gitpod({
+  bearerToken: 'My Bearer Token',
+  baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource groups', () => {
+  // skipped: tests are disabled for the time being
+  test.skip('list', async () => {
+    const responsePromise = client.groups.list({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+});
diff --git a/tests/api-resources/identity.test.ts b/tests/api-resources/identity.test.ts
new file mode 100644
index 0000000..985ccfb
--- /dev/null
+++ b/tests/api-resources/identity.test.ts
@@ -0,0 +1,46 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Gitpod from '@gitpod/sdk';
+
+const client = new Gitpod({
+  bearerToken: 'My Bearer Token',
+  baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource identity', () => {
+  // skipped: tests are disabled for the time being
+  test.skip('exchangeToken', async () => {
+    const responsePromise = client.identity.exchangeToken({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('getAuthenticatedIdentity', async () => {
+    const responsePromise = client.identity.getAuthenticatedIdentity({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('getIDToken', async () => {
+    const responsePromise = client.identity.getIDToken({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+});
diff --git a/tests/api-resources/organizations/domain-verifications.test.ts b/tests/api-resources/organizations/domain-verifications.test.ts
new file mode 100644
index 0000000..aa5b7e4
--- /dev/null
+++ b/tests/api-resources/organizations/domain-verifications.test.ts
@@ -0,0 +1,120 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Gitpod from '@gitpod/sdk';
+
+const client = new Gitpod({
+  bearerToken: 'My Bearer Token',
+  baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource domainVerifications', () => {
+  // skipped: tests are disabled for the time being
+  test.skip('create: only required params', async () => {
+    const responsePromise = client.organizations.domainVerifications.create({
+      domain: 'acme-corp.com',
+      organizationId: 'b0e12f6c-4c67-429d-a4a6-d9838b5da047',
+    });
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('create: required and optional params', async () => {
+    const response = await client.organizations.domainVerifications.create({
+      domain: 'acme-corp.com',
+      organizationId: 'b0e12f6c-4c67-429d-a4a6-d9838b5da047',
+    });
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('retrieve: only required params', async () => {
+    const responsePromise = client.organizations.domainVerifications.retrieve({
+      domainVerificationId: 'd2c94c27-3b76-4a42-b88c-95a85e392c68',
+    });
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('retrieve: required and optional params', async () => {
+    const response = await client.organizations.domainVerifications.retrieve({
+      domainVerificationId: 'd2c94c27-3b76-4a42-b88c-95a85e392c68',
+    });
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('list: only required params', async () => {
+    const responsePromise = client.organizations.domainVerifications.list({
+      organizationId: 'b0e12f6c-4c67-429d-a4a6-d9838b5da047',
+    });
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('list: required and optional params', async () => {
+    const response = await client.organizations.domainVerifications.list({
+      organizationId: 'b0e12f6c-4c67-429d-a4a6-d9838b5da047',
+      token: 'token',
+      pageSize: 0,
+      pagination: { token: 'token', pageSize: 20 },
+    });
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('delete: only required params', async () => {
+    const responsePromise = client.organizations.domainVerifications.delete({
+      domainVerificationId: 'd2c94c27-3b76-4a42-b88c-95a85e392c68',
+    });
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('delete: required and optional params', async () => {
+    const response = await client.organizations.domainVerifications.delete({
+      domainVerificationId: 'd2c94c27-3b76-4a42-b88c-95a85e392c68',
+    });
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('verify: only required params', async () => {
+    const responsePromise = client.organizations.domainVerifications.verify({
+      domainVerificationId: 'd2c94c27-3b76-4a42-b88c-95a85e392c68',
+    });
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('verify: required and optional params', async () => {
+    const response = await client.organizations.domainVerifications.verify({
+      domainVerificationId: 'd2c94c27-3b76-4a42-b88c-95a85e392c68',
+    });
+  });
+});
diff --git a/tests/api-resources/organizations/invites.test.ts b/tests/api-resources/organizations/invites.test.ts
new file mode 100644
index 0000000..8d2dbb3
--- /dev/null
+++ b/tests/api-resources/organizations/invites.test.ts
@@ -0,0 +1,73 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Gitpod from '@gitpod/sdk';
+
+const client = new Gitpod({
+  bearerToken: 'My Bearer Token',
+  baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource invites', () => {
+  // skipped: tests are disabled for the time being
+  test.skip('create: only required params', async () => {
+    const responsePromise = client.organizations.invites.create({
+      organizationId: 'b0e12f6c-4c67-429d-a4a6-d9838b5da047',
+    });
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('create: required and optional params', async () => {
+    const response = await client.organizations.invites.create({
+      organizationId: 'b0e12f6c-4c67-429d-a4a6-d9838b5da047',
+    });
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('retrieve: only required params', async () => {
+    const responsePromise = client.organizations.invites.retrieve({
+      organizationId: '182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e',
+    });
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('retrieve: required and optional params', async () => {
+    const response = await client.organizations.invites.retrieve({
+      organizationId: '182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e',
+    });
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('getSummary: only required params', async () => {
+    const responsePromise = client.organizations.invites.getSummary({
+      inviteId: 'd2c94c27-3b76-4a42-b88c-95a85e392c68',
+    });
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('getSummary: required and optional params', async () => {
+    const response = await client.organizations.invites.getSummary({
+      inviteId: 'd2c94c27-3b76-4a42-b88c-95a85e392c68',
+    });
+  });
+});
diff --git a/tests/api-resources/organizations/organizations.test.ts b/tests/api-resources/organizations/organizations.test.ts
new file mode 100644
index 0000000..09fc8b7
--- /dev/null
+++ b/tests/api-resources/organizations/organizations.test.ts
@@ -0,0 +1,173 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Gitpod from '@gitpod/sdk';
+
+const client = new Gitpod({
+  bearerToken: 'My Bearer Token',
+  baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource organizations', () => {
+  // skipped: tests are disabled for the time being
+  test.skip('create: only required params', async () => {
+    const responsePromise = client.organizations.create({ name: 'Acme Corp Engineering' });
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('create: required and optional params', async () => {
+    const response = await client.organizations.create({
+      name: 'Acme Corp Engineering',
+      inviteAccountsWithMatchingDomain: true,
+      joinOrganization: true,
+    });
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('retrieve: only required params', async () => {
+    const responsePromise = client.organizations.retrieve({
+      organizationId: 'b0e12f6c-4c67-429d-a4a6-d9838b5da047',
+    });
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('retrieve: required and optional params', async () => {
+    const response = await client.organizations.retrieve({
+      organizationId: 'b0e12f6c-4c67-429d-a4a6-d9838b5da047',
+    });
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('update: only required params', async () => {
+    const responsePromise = client.organizations.update({
+      organizationId: 'b0e12f6c-4c67-429d-a4a6-d9838b5da047',
+    });
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('update: required and optional params', async () => {
+    const response = await client.organizations.update({
+      organizationId: 'b0e12f6c-4c67-429d-a4a6-d9838b5da047',
+      inviteDomains: { domains: ['sfN2.l.iJR-BU.u9JV9.a.m.o2D-4b-Jd.0Z-kX.L.n.S.f.UKbxB'] },
+      name: 'name',
+    });
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('delete: only required params', async () => {
+    const responsePromise = client.organizations.delete({
+      organizationId: 'b0e12f6c-4c67-429d-a4a6-d9838b5da047',
+    });
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('delete: required and optional params', async () => {
+    const response = await client.organizations.delete({
+      organizationId: 'b0e12f6c-4c67-429d-a4a6-d9838b5da047',
+    });
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('join', async () => {
+    const responsePromise = client.organizations.join({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('leave: only required params', async () => {
+    const responsePromise = client.organizations.leave({ userId: 'f53d2330-3795-4c5d-a1f3-453121af9c60' });
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('leave: required and optional params', async () => {
+    const response = await client.organizations.leave({ userId: 'f53d2330-3795-4c5d-a1f3-453121af9c60' });
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('listMembers: only required params', async () => {
+    const responsePromise = client.organizations.listMembers({
+      organizationId: 'b0e12f6c-4c67-429d-a4a6-d9838b5da047',
+    });
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('listMembers: required and optional params', async () => {
+    const response = await client.organizations.listMembers({
+      organizationId: 'b0e12f6c-4c67-429d-a4a6-d9838b5da047',
+      token: 'token',
+      pageSize: 0,
+      pagination: { token: 'token', pageSize: 20 },
+    });
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('setRole: only required params', async () => {
+    const responsePromise = client.organizations.setRole({
+      organizationId: 'b0e12f6c-4c67-429d-a4a6-d9838b5da047',
+      userId: 'f53d2330-3795-4c5d-a1f3-453121af9c60',
+    });
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('setRole: required and optional params', async () => {
+    const response = await client.organizations.setRole({
+      organizationId: 'b0e12f6c-4c67-429d-a4a6-d9838b5da047',
+      userId: 'f53d2330-3795-4c5d-a1f3-453121af9c60',
+      role: 'ORGANIZATION_ROLE_MEMBER',
+    });
+  });
+});
diff --git a/tests/api-resources/organizations/policies.test.ts b/tests/api-resources/organizations/policies.test.ts
new file mode 100644
index 0000000..a0aba22
--- /dev/null
+++ b/tests/api-resources/organizations/policies.test.ts
@@ -0,0 +1,61 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Gitpod from '@gitpod/sdk';
+
+const client = new Gitpod({
+  bearerToken: 'My Bearer Token',
+  baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource policies', () => {
+  // skipped: tests are disabled for the time being
+  test.skip('retrieve: only required params', async () => {
+    const responsePromise = client.organizations.policies.retrieve({
+      organizationId: 'b0e12f6c-4c67-429d-a4a6-d9838b5da047',
+    });
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('retrieve: required and optional params', async () => {
+    const response = await client.organizations.policies.retrieve({
+      organizationId: 'b0e12f6c-4c67-429d-a4a6-d9838b5da047',
+    });
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('update: only required params', async () => {
+    const responsePromise = client.organizations.policies.update({
+      organizationId: 'b0e12f6c-4c67-429d-a4a6-d9838b5da047',
+    });
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('update: required and optional params', async () => {
+    const response = await client.organizations.policies.update({
+      organizationId: 'b0e12f6c-4c67-429d-a4a6-d9838b5da047',
+      allowedEditorIds: ['string'],
+      allowLocalRunners: true,
+      defaultEditorId: 'defaultEditorId',
+      defaultEnvironmentImage: 'defaultEnvironmentImage',
+      maximumEnvironmentsPerUser: '20',
+      maximumEnvironmentTimeout: '3600s',
+      maximumRunningEnvironmentsPerUser: '5',
+      membersCreateProjects: true,
+      membersRequireProjects: true,
+    });
+  });
+});
diff --git a/tests/api-resources/organizations/sso-configurations.test.ts b/tests/api-resources/organizations/sso-configurations.test.ts
new file mode 100644
index 0000000..06c24ee
--- /dev/null
+++ b/tests/api-resources/organizations/sso-configurations.test.ts
@@ -0,0 +1,132 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Gitpod from '@gitpod/sdk';
+
+const client = new Gitpod({
+  bearerToken: 'My Bearer Token',
+  baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource ssoConfigurations', () => {
+  // skipped: tests are disabled for the time being
+  test.skip('create: only required params', async () => {
+    const responsePromise = client.organizations.ssoConfigurations.create({
+      clientId: '012345678-abcdefghijklmnopqrstuvwxyz.apps.googleusercontent.com',
+      clientSecret: 'GOCSPX-abcdefghijklmnopqrstuvwxyz123456',
+      emailDomain: 'acme-corp.com',
+      issuerUrl: 'https://accounts.google.com',
+      organizationId: 'b0e12f6c-4c67-429d-a4a6-d9838b5da047',
+    });
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('create: required and optional params', async () => {
+    const response = await client.organizations.ssoConfigurations.create({
+      clientId: '012345678-abcdefghijklmnopqrstuvwxyz.apps.googleusercontent.com',
+      clientSecret: 'GOCSPX-abcdefghijklmnopqrstuvwxyz123456',
+      emailDomain: 'acme-corp.com',
+      issuerUrl: 'https://accounts.google.com',
+      organizationId: 'b0e12f6c-4c67-429d-a4a6-d9838b5da047',
+    });
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('retrieve: only required params', async () => {
+    const responsePromise = client.organizations.ssoConfigurations.retrieve({
+      ssoConfigurationId: 'd2c94c27-3b76-4a42-b88c-95a85e392c68',
+    });
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('retrieve: required and optional params', async () => {
+    const response = await client.organizations.ssoConfigurations.retrieve({
+      ssoConfigurationId: 'd2c94c27-3b76-4a42-b88c-95a85e392c68',
+    });
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('update: only required params', async () => {
+    const responsePromise = client.organizations.ssoConfigurations.update({
+      ssoConfigurationId: 'd2c94c27-3b76-4a42-b88c-95a85e392c68',
+    });
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('update: required and optional params', async () => {
+    const response = await client.organizations.ssoConfigurations.update({
+      ssoConfigurationId: 'd2c94c27-3b76-4a42-b88c-95a85e392c68',
+      claims: { foo: 'string' },
+      clientId: 'new-client-id',
+      clientSecret: 'new-client-secret',
+      emailDomain: 'xxxx',
+      issuerUrl: 'https://example.com',
+      state: 'SSO_CONFIGURATION_STATE_UNSPECIFIED',
+    });
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('list: only required params', async () => {
+    const responsePromise = client.organizations.ssoConfigurations.list({
+      organizationId: 'b0e12f6c-4c67-429d-a4a6-d9838b5da047',
+    });
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('list: required and optional params', async () => {
+    const response = await client.organizations.ssoConfigurations.list({
+      organizationId: 'b0e12f6c-4c67-429d-a4a6-d9838b5da047',
+      token: 'token',
+      pageSize: 0,
+      pagination: { token: 'token', pageSize: 20 },
+    });
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('delete: only required params', async () => {
+    const responsePromise = client.organizations.ssoConfigurations.delete({
+      ssoConfigurationId: 'd2c94c27-3b76-4a42-b88c-95a85e392c68',
+    });
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('delete: required and optional params', async () => {
+    const response = await client.organizations.ssoConfigurations.delete({
+      ssoConfigurationId: 'd2c94c27-3b76-4a42-b88c-95a85e392c68',
+    });
+  });
+});
diff --git a/tests/api-resources/projects/policies.test.ts b/tests/api-resources/projects/policies.test.ts
new file mode 100644
index 0000000..2b99235
--- /dev/null
+++ b/tests/api-resources/projects/policies.test.ts
@@ -0,0 +1,58 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Gitpod from '@gitpod/sdk';
+
+const client = new Gitpod({
+  bearerToken: 'My Bearer Token',
+  baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource policies', () => {
+  // skipped: tests are disabled for the time being
+  test.skip('create', async () => {
+    const responsePromise = client.projects.policies.create({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('update', async () => {
+    const responsePromise = client.projects.policies.update({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('list', async () => {
+    const responsePromise = client.projects.policies.list({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('delete', async () => {
+    const responsePromise = client.projects.policies.delete({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+});
diff --git a/tests/api-resources/projects/projects.test.ts b/tests/api-resources/projects/projects.test.ts
new file mode 100644
index 0000000..b30ee89
--- /dev/null
+++ b/tests/api-resources/projects/projects.test.ts
@@ -0,0 +1,107 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Gitpod from '@gitpod/sdk';
+
+const client = new Gitpod({
+  bearerToken: 'My Bearer Token',
+  baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource projects', () => {
+  // skipped: tests are disabled for the time being
+  test.skip('create: only required params', async () => {
+    const responsePromise = client.projects.create({ environmentClass: {}, initializer: {} });
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('create: required and optional params', async () => {
+    const response = await client.projects.create({
+      environmentClass: { environmentClassId: 'd2c94c27-3b76-4a42-b88c-95a85e392c68', localRunner: true },
+      initializer: {
+        specs: [
+          {
+            contextUrl: { url: 'https://example.com' },
+            git: {
+              checkoutLocation: 'checkoutLocation',
+              cloneTarget: 'cloneTarget',
+              remoteUri: 'https://github.com/org/repo',
+              targetMode: 'CLONE_TARGET_MODE_UNSPECIFIED',
+              upstreamRemoteUri: 'upstreamRemoteUri',
+            },
+          },
+        ],
+      },
+      automationsFilePath: 'automationsFilePath',
+      devcontainerFilePath: 'devcontainerFilePath',
+      name: 'Web Application',
+      technicalDescription: 'technicalDescription',
+    });
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('retrieve', async () => {
+    const responsePromise = client.projects.retrieve({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('update', async () => {
+    const responsePromise = client.projects.update({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('list', async () => {
+    const responsePromise = client.projects.list({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('delete', async () => {
+    const responsePromise = client.projects.delete({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('createFromEnvironment', async () => {
+    const responsePromise = client.projects.createFromEnvironment({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+});
diff --git a/tests/api-resources/runners/configurations/configurations.test.ts b/tests/api-resources/runners/configurations/configurations.test.ts
new file mode 100644
index 0000000..c23963f
--- /dev/null
+++ b/tests/api-resources/runners/configurations/configurations.test.ts
@@ -0,0 +1,22 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Gitpod from '@gitpod/sdk';
+
+const client = new Gitpod({
+  bearerToken: 'My Bearer Token',
+  baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource configurations', () => {
+  // skipped: tests are disabled for the time being
+  test.skip('validate', async () => {
+    const responsePromise = client.runners.configurations.validate({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+});
diff --git a/tests/api-resources/runners/configurations/environment-classes.test.ts b/tests/api-resources/runners/configurations/environment-classes.test.ts
new file mode 100644
index 0000000..f7b49dd
--- /dev/null
+++ b/tests/api-resources/runners/configurations/environment-classes.test.ts
@@ -0,0 +1,58 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Gitpod from '@gitpod/sdk';
+
+const client = new Gitpod({
+  bearerToken: 'My Bearer Token',
+  baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource environmentClasses', () => {
+  // skipped: tests are disabled for the time being
+  test.skip('create', async () => {
+    const responsePromise = client.runners.configurations.environmentClasses.create({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('retrieve', async () => {
+    const responsePromise = client.runners.configurations.environmentClasses.retrieve({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('update', async () => {
+    const responsePromise = client.runners.configurations.environmentClasses.update({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('list', async () => {
+    const responsePromise = client.runners.configurations.environmentClasses.list({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+});
diff --git a/tests/api-resources/runners/configurations/host-authentication-tokens.test.ts b/tests/api-resources/runners/configurations/host-authentication-tokens.test.ts
new file mode 100644
index 0000000..a5c4a50
--- /dev/null
+++ b/tests/api-resources/runners/configurations/host-authentication-tokens.test.ts
@@ -0,0 +1,70 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Gitpod from '@gitpod/sdk';
+
+const client = new Gitpod({
+  bearerToken: 'My Bearer Token',
+  baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource hostAuthenticationTokens', () => {
+  // skipped: tests are disabled for the time being
+  test.skip('create', async () => {
+    const responsePromise = client.runners.configurations.hostAuthenticationTokens.create({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('retrieve', async () => {
+    const responsePromise = client.runners.configurations.hostAuthenticationTokens.retrieve({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('update', async () => {
+    const responsePromise = client.runners.configurations.hostAuthenticationTokens.update({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('list', async () => {
+    const responsePromise = client.runners.configurations.hostAuthenticationTokens.list({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('delete', async () => {
+    const responsePromise = client.runners.configurations.hostAuthenticationTokens.delete({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+});
diff --git a/tests/api-resources/runners/configurations/schema.test.ts b/tests/api-resources/runners/configurations/schema.test.ts
new file mode 100644
index 0000000..5315e4a
--- /dev/null
+++ b/tests/api-resources/runners/configurations/schema.test.ts
@@ -0,0 +1,22 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Gitpod from '@gitpod/sdk';
+
+const client = new Gitpod({
+  bearerToken: 'My Bearer Token',
+  baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource schema', () => {
+  // skipped: tests are disabled for the time being
+  test.skip('retrieve', async () => {
+    const responsePromise = client.runners.configurations.schema.retrieve({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+});
diff --git a/tests/api-resources/runners/configurations/scm-integrations.test.ts b/tests/api-resources/runners/configurations/scm-integrations.test.ts
new file mode 100644
index 0000000..6b59259
--- /dev/null
+++ b/tests/api-resources/runners/configurations/scm-integrations.test.ts
@@ -0,0 +1,70 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Gitpod from '@gitpod/sdk';
+
+const client = new Gitpod({
+  bearerToken: 'My Bearer Token',
+  baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource scmIntegrations', () => {
+  // skipped: tests are disabled for the time being
+  test.skip('create', async () => {
+    const responsePromise = client.runners.configurations.scmIntegrations.create({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('retrieve', async () => {
+    const responsePromise = client.runners.configurations.scmIntegrations.retrieve({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('update', async () => {
+    const responsePromise = client.runners.configurations.scmIntegrations.update({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('list', async () => {
+    const responsePromise = client.runners.configurations.scmIntegrations.list({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('delete', async () => {
+    const responsePromise = client.runners.configurations.scmIntegrations.delete({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+});
diff --git a/tests/api-resources/runners/policies.test.ts b/tests/api-resources/runners/policies.test.ts
new file mode 100644
index 0000000..533b1d8
--- /dev/null
+++ b/tests/api-resources/runners/policies.test.ts
@@ -0,0 +1,58 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Gitpod from '@gitpod/sdk';
+
+const client = new Gitpod({
+  bearerToken: 'My Bearer Token',
+  baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource policies', () => {
+  // skipped: tests are disabled for the time being
+  test.skip('create', async () => {
+    const responsePromise = client.runners.policies.create({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('update', async () => {
+    const responsePromise = client.runners.policies.update({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('list', async () => {
+    const responsePromise = client.runners.policies.list({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('delete', async () => {
+    const responsePromise = client.runners.policies.delete({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+});
diff --git a/tests/api-resources/runners/runners.test.ts b/tests/api-resources/runners/runners.test.ts
new file mode 100644
index 0000000..8a6573a
--- /dev/null
+++ b/tests/api-resources/runners/runners.test.ts
@@ -0,0 +1,106 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Gitpod from '@gitpod/sdk';
+
+const client = new Gitpod({
+  bearerToken: 'My Bearer Token',
+  baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource runners', () => {
+  // skipped: tests are disabled for the time being
+  test.skip('create', async () => {
+    const responsePromise = client.runners.create({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('retrieve', async () => {
+    const responsePromise = client.runners.retrieve({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('update', async () => {
+    const responsePromise = client.runners.update({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('list', async () => {
+    const responsePromise = client.runners.list({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('delete', async () => {
+    const responsePromise = client.runners.delete({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('checkAuthenticationForHost', async () => {
+    const responsePromise = client.runners.checkAuthenticationForHost({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('createRunnerToken', async () => {
+    const responsePromise = client.runners.createRunnerToken({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('parseContextURL', async () => {
+    const responsePromise = client.runners.parseContextURL({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+});
diff --git a/tests/api-resources/secrets.test.ts b/tests/api-resources/secrets.test.ts
new file mode 100644
index 0000000..fc3c4c7
--- /dev/null
+++ b/tests/api-resources/secrets.test.ts
@@ -0,0 +1,70 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Gitpod from '@gitpod/sdk';
+
+const client = new Gitpod({
+  bearerToken: 'My Bearer Token',
+  baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource secrets', () => {
+  // skipped: tests are disabled for the time being
+  test.skip('create', async () => {
+    const responsePromise = client.secrets.create({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('list', async () => {
+    const responsePromise = client.secrets.list({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('delete', async () => {
+    const responsePromise = client.secrets.delete({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('getValue', async () => {
+    const responsePromise = client.secrets.getValue({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('updateValue', async () => {
+    const responsePromise = client.secrets.updateValue({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+});
diff --git a/tests/api-resources/usage.test.ts b/tests/api-resources/usage.test.ts
new file mode 100644
index 0000000..1d2b3dc
--- /dev/null
+++ b/tests/api-resources/usage.test.ts
@@ -0,0 +1,22 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Gitpod from '@gitpod/sdk';
+
+const client = new Gitpod({
+  bearerToken: 'My Bearer Token',
+  baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource usage', () => {
+  // skipped: tests are disabled for the time being
+  test.skip('listEnvironmentSessions', async () => {
+    const responsePromise = client.usage.listEnvironmentSessions({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+});
diff --git a/tests/api-resources/users/dotfiles.test.ts b/tests/api-resources/users/dotfiles.test.ts
new file mode 100644
index 0000000..5694cb1
--- /dev/null
+++ b/tests/api-resources/users/dotfiles.test.ts
@@ -0,0 +1,34 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Gitpod from '@gitpod/sdk';
+
+const client = new Gitpod({
+  bearerToken: 'My Bearer Token',
+  baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource dotfiles', () => {
+  // skipped: tests are disabled for the time being
+  test.skip('get', async () => {
+    const responsePromise = client.users.dotfiles.get({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('set', async () => {
+    const responsePromise = client.users.dotfiles.set({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+});
diff --git a/tests/api-resources/users/pats.test.ts b/tests/api-resources/users/pats.test.ts
new file mode 100644
index 0000000..cb8544c
--- /dev/null
+++ b/tests/api-resources/users/pats.test.ts
@@ -0,0 +1,46 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Gitpod from '@gitpod/sdk';
+
+const client = new Gitpod({
+  bearerToken: 'My Bearer Token',
+  baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource pats', () => {
+  // skipped: tests are disabled for the time being
+  test.skip('list', async () => {
+    const responsePromise = client.users.pats.list({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('delete', async () => {
+    const responsePromise = client.users.pats.delete({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('get', async () => {
+    const responsePromise = client.users.pats.get({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+});
diff --git a/tests/api-resources/users/users.test.ts b/tests/api-resources/users/users.test.ts
new file mode 100644
index 0000000..4131035
--- /dev/null
+++ b/tests/api-resources/users/users.test.ts
@@ -0,0 +1,34 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import Gitpod from '@gitpod/sdk';
+
+const client = new Gitpod({
+  bearerToken: 'My Bearer Token',
+  baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+});
+
+describe('resource users', () => {
+  // skipped: tests are disabled for the time being
+  test.skip('getAuthenticatedUser', async () => {
+    const responsePromise = client.users.getAuthenticatedUser({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+
+  // skipped: tests are disabled for the time being
+  test.skip('setSuspended', async () => {
+    const responsePromise = client.users.setSuspended({});
+    const rawResponse = await responsePromise.asResponse();
+    expect(rawResponse).toBeInstanceOf(Response);
+    const response = await responsePromise;
+    expect(response).not.toBeInstanceOf(Response);
+    const dataAndResponse = await responsePromise.withResponse();
+    expect(dataAndResponse.data).toBe(response);
+    expect(dataAndResponse.response).toBe(rawResponse);
+  });
+});
diff --git a/tests/base64.test.ts b/tests/base64.test.ts
new file mode 100644
index 0000000..7ed687c
--- /dev/null
+++ b/tests/base64.test.ts
@@ -0,0 +1,80 @@
+import { fromBase64, toBase64 } from '@gitpod/sdk/internal/utils/base64';
+
+describe.each(['Buffer', 'atob'])('with %s', (mode) => {
+  let originalBuffer: BufferConstructor;
+  beforeAll(() => {
+    if (mode === 'atob') {
+      originalBuffer = globalThis.Buffer;
+      // @ts-expect-error Can't assign undefined to BufferConstructor
+      delete globalThis.Buffer;
+    }
+  });
+  afterAll(() => {
+    if (mode === 'atob') {
+      globalThis.Buffer = originalBuffer;
+    }
+  });
+  test('toBase64', () => {
+    const testCases = [
+      {
+        input: 'hello world',
+        expected: 'aGVsbG8gd29ybGQ=',
+      },
+      {
+        input: new Uint8Array([104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]),
+        expected: 'aGVsbG8gd29ybGQ=',
+      },
+      {
+        input: undefined,
+        expected: '',
+      },
+      {
+        input: new Uint8Array([
+          229, 102, 215, 230, 65, 22, 46, 87, 243, 176, 99, 99, 31, 174, 8, 242, 83, 142, 169, 64, 122, 123,
+          193, 71,
+        ]),
+        expected: '5WbX5kEWLlfzsGNjH64I8lOOqUB6e8FH',
+      },
+      {
+        input: '✓',
+        expected: '4pyT',
+      },
+      {
+        input: new Uint8Array([226, 156, 147]),
+        expected: '4pyT',
+      },
+    ];
+
+    testCases.forEach(({ input, expected }) => {
+      expect(toBase64(input)).toBe(expected);
+    });
+  });
+
+  test('fromBase64', () => {
+    const testCases = [
+      {
+        input: 'aGVsbG8gd29ybGQ=',
+        expected: new Uint8Array([104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]),
+      },
+      {
+        input: '',
+        expected: new Uint8Array([]),
+      },
+      {
+        input: '5WbX5kEWLlfzsGNjH64I8lOOqUB6e8FH',
+        expected: new Uint8Array([
+          229, 102, 215, 230, 65, 22, 46, 87, 243, 176, 99, 99, 31, 174, 8, 242, 83, 142, 169, 64, 122, 123,
+          193, 71,
+        ]),
+      },
+      {
+        input: '4pyT',
+        expected: new Uint8Array([226, 156, 147]),
+      },
+    ];
+
+    testCases.forEach(({ input, expected }) => {
+      expect(fromBase64(input)).toEqual(expected);
+    });
+  });
+});
diff --git a/tests/buildHeaders.test.ts b/tests/buildHeaders.test.ts
new file mode 100644
index 0000000..f2e10fe
--- /dev/null
+++ b/tests/buildHeaders.test.ts
@@ -0,0 +1,88 @@
+import { inspect } from 'node:util';
+import { buildHeaders, type HeadersLike, type NullableHeaders } from '@gitpod/sdk/internal/headers';
+
+function inspectNullableHeaders(headers: NullableHeaders) {
+  return `NullableHeaders {${[
+    ...[...headers.values.entries()].map(([name, value]) => ` ${inspect(name)}: ${inspect(value)}`),
+    ...[...headers.nulls].map((name) => ` ${inspect(name)}: null`),
+  ].join(', ')} }`;
+}
+
+describe('buildHeaders', () => {
+  const cases: [HeadersLike[], string][] = [
+    [[new Headers({ 'content-type': 'text/plain' })], `NullableHeaders { 'content-type': 'text/plain' }`],
+    [
+      [
+        {
+          'content-type': 'text/plain',
+        },
+        {
+          'Content-Type': undefined,
+        },
+      ],
+      `NullableHeaders { 'content-type': 'text/plain' }`,
+    ],
+    [
+      [
+        {
+          'content-type': 'text/plain',
+        },
+        {
+          'Content-Type': null,
+        },
+      ],
+      `NullableHeaders { 'content-type': null }`,
+    ],
+    [
+      [
+        {
+          cookie: 'name1=value1',
+          Cookie: 'name2=value2',
+        },
+      ],
+      `NullableHeaders { 'cookie': 'name2=value2' }`,
+    ],
+    [
+      [
+        {
+          cookie: 'name1=value1',
+          Cookie: undefined,
+        },
+      ],
+      `NullableHeaders { 'cookie': 'name1=value1' }`,
+    ],
+    [
+      [
+        {
+          cookie: ['name1=value1', 'name2=value2'],
+        },
+      ],
+      `NullableHeaders { 'cookie': 'name1=value1; name2=value2' }`,
+    ],
+    [
+      [
+        {
+          'x-foo': ['name1=value1', 'name2=value2'],
+        },
+      ],
+      `NullableHeaders { 'x-foo': 'name1=value1, name2=value2' }`,
+    ],
+    [
+      [
+        [
+          ['cookie', 'name1=value1'],
+          ['cookie', 'name2=value2'],
+          ['Cookie', 'name3=value3'],
+        ],
+      ],
+      `NullableHeaders { 'cookie': 'name1=value1; name2=value2; name3=value3' }`,
+    ],
+    [[undefined], `NullableHeaders { }`],
+    [[null], `NullableHeaders { }`],
+  ];
+  for (const [input, expected] of cases) {
+    test(expected, () => {
+      expect(inspectNullableHeaders(buildHeaders(input))).toEqual(expected);
+    });
+  }
+});
diff --git a/tests/form.test.ts b/tests/form.test.ts
new file mode 100644
index 0000000..cb575ba
--- /dev/null
+++ b/tests/form.test.ts
@@ -0,0 +1,85 @@
+import { multipartFormRequestOptions, createForm } from '@gitpod/sdk/internal/uploads';
+import { toFile } from '@gitpod/sdk/core/uploads';
+
+describe('form data validation', () => {
+  test('valid values do not error', async () => {
+    await multipartFormRequestOptions(
+      {
+        body: {
+          foo: 'foo',
+          string: 1,
+          bool: true,
+          file: await toFile(Buffer.from('some-content')),
+          blob: new Blob(['Some content'], { type: 'text/plain' }),
+        },
+      },
+      fetch,
+    );
+  });
+
+  test('null', async () => {
+    await expect(() =>
+      multipartFormRequestOptions(
+        {
+          body: {
+            null: null,
+          },
+        },
+        fetch,
+      ),
+    ).rejects.toThrow(TypeError);
+  });
+
+  test('undefined is stripped', async () => {
+    const form = await createForm(
+      {
+        foo: undefined,
+        bar: 'baz',
+      },
+      fetch,
+    );
+    expect(form.has('foo')).toBe(false);
+    expect(form.get('bar')).toBe('baz');
+  });
+
+  test('nested undefined property is stripped', async () => {
+    const form = await createForm(
+      {
+        bar: {
+          baz: undefined,
+        },
+      },
+      fetch,
+    );
+    expect(Array.from(form.entries())).toEqual([]);
+
+    const form2 = await createForm(
+      {
+        bar: {
+          foo: 'string',
+          baz: undefined,
+        },
+      },
+      fetch,
+    );
+    expect(Array.from(form2.entries())).toEqual([['bar[foo]', 'string']]);
+  });
+
+  test('nested undefined array item is stripped', async () => {
+    const form = await createForm(
+      {
+        bar: [undefined, undefined],
+      },
+      fetch,
+    );
+    expect(Array.from(form.entries())).toEqual([]);
+
+    const form2 = await createForm(
+      {
+        bar: [undefined, 'foo'],
+      },
+      fetch,
+    );
+    expect(Array.from(form2.entries())).toEqual([['bar[]', 'foo']]);
+  });
+});
diff --git a/tests/index.test.ts b/tests/index.test.ts
new file mode 100644
index 0000000..afe3fed
--- /dev/null
+++ b/tests/index.test.ts
@@ -0,0 +1,642 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { APIPromise } from '@gitpod/sdk/core/api-promise';
+
+import util from 'node:util';
+import Gitpod from '@gitpod/sdk';
+import { APIUserAbortError } from '@gitpod/sdk';
+const defaultFetch = fetch;
+
+describe('instantiate client', () => {
+  const env = process.env;
+
+  beforeEach(() => {
+    jest.resetModules();
+    process.env = { ...env };
+  });
+
+  afterEach(() => {
+    process.env = env;
+  });
+
+  describe('defaultHeaders', () => {
+    const client = new Gitpod({
+      baseURL: 'http://localhost:5000/',
+      defaultHeaders: { 'X-My-Default-Header': '2' },
+      bearerToken: 'My Bearer Token',
+    });
+
+    test('they are used in the request', () => {
+      const { req } = client.buildRequest({ path: '/foo', method: 'post' });
+      expect(req.headers.get('x-my-default-header')).toEqual('2');
+    });
+
+    test('can ignore `undefined` and leave the default', () => {
+      const { req } = client.buildRequest({
+        path: '/foo',
+        method: 'post',
+        headers: { 'X-My-Default-Header': undefined },
+      });
+      expect(req.headers.get('x-my-default-header')).toEqual('2');
+    });
+
+    test('can be removed with `null`', () => {
+      const { req } = client.buildRequest({
+        path: '/foo',
+        method: 'post',
+        headers: { 'X-My-Default-Header': null },
+      });
+      expect(req.headers.has('x-my-default-header')).toBe(false);
+    });
+  });
+  describe('logging', () => {
+    const env = process.env;
+
+    beforeEach(() => {
+      process.env = { ...env };
+      process.env['GITPOD_LOG'] = undefined;
+    });
+
+    afterEach(() => {
+      process.env = env;
+    });
+
+    const forceAPIResponseForClient = async (client: Gitpod) => {
+      await new APIPromise(
+        client,
+        Promise.resolve({
+          response: new Response(),
+          controller: new AbortController(),
+          requestLogID: 'log_000000',
+          retryOfRequestLogID: undefined,
+          startTime: Date.now(),
+          options: {
+            method: 'get',
+            path: '/',
+          },
+        }),
+      );
+    };
+
+    test('debug logs when log level is debug', async () => {
+      const debugMock = jest.fn();
+      const logger = {
+        debug: debugMock,
+        info: jest.fn(),
+        warn: jest.fn(),
+        error: jest.fn(),
+      };
+
+      const client = new Gitpod({ logger: logger, logLevel: 'debug', bearerToken: 'My Bearer Token' });
+
+      await forceAPIResponseForClient(client);
+      expect(debugMock).toHaveBeenCalled();
+    });
+
+    test('default logLevel is warn', async () => {
+      const client = new Gitpod({ bearerToken: 'My Bearer Token' });
+      expect(client.logLevel).toBe('warn');
+    });
+
+    test('debug logs are skipped when log level is info', async () => {
+      const debugMock = jest.fn();
+      const logger = {
+        debug: debugMock,
+        info: jest.fn(),
+        warn: jest.fn(),
+        error: jest.fn(),
+      };
+
+      const client = new Gitpod({ logger: logger, logLevel: 'info', bearerToken: 'My Bearer Token' });
+
+      await forceAPIResponseForClient(client);
+      expect(debugMock).not.toHaveBeenCalled();
+    });
+
+    test('debug logs happen with debug env var', async () => {
+      const debugMock = jest.fn();
+      const logger = {
+        debug: debugMock,
+        info: jest.fn(),
+        warn: jest.fn(),
+        error: jest.fn(),
+      };
+
+      process.env['GITPOD_LOG'] = 'debug';
+      const client = new Gitpod({ logger: logger, bearerToken: 'My Bearer Token' });
+      expect(client.logLevel).toBe('debug');
+
+      await forceAPIResponseForClient(client);
+      expect(debugMock).toHaveBeenCalled();
+    });
+
+    test('warn when env var level is invalid', async () => {
+      const warnMock = jest.fn();
+      const logger = {
+        debug: jest.fn(),
+        info: jest.fn(),
+        warn: warnMock,
+        error: jest.fn(),
+      };
+
+      process.env['GITPOD_LOG'] = 'not a log level';
+      const client = new Gitpod({ logger: logger, bearerToken: 'My Bearer Token' });
+      expect(client.logLevel).toBe('warn');
+      expect(warnMock).toHaveBeenCalledWith(
+        'process.env[\'GITPOD_LOG\'] was set to "not a log level", expected one of ["off","error","warn","info","debug"]',
+      );
+    });
+
+    test('client log level overrides env var', async () => {
+      const debugMock = jest.fn();
+      const logger = {
+        debug: debugMock,
+        info: jest.fn(),
+        warn: jest.fn(),
+        error: jest.fn(),
+      };
+
+      process.env['GITPOD_LOG'] = 'debug';
+      const client = new Gitpod({ logger: logger, logLevel: 'off', bearerToken: 'My Bearer Token' });
+
+      await forceAPIResponseForClient(client);
+      expect(debugMock).not.toHaveBeenCalled();
+    });
+
+    test('no warning logged for invalid env var level + valid client level', async () => {
+      const warnMock = jest.fn();
+      const logger = {
+        debug: jest.fn(),
+        info: jest.fn(),
+        warn: warnMock,
+        error: jest.fn(),
+      };
+
+      process.env['GITPOD_LOG'] = 'not a log level';
+      const client = new Gitpod({ logger: logger, logLevel: 'debug', bearerToken: 'My Bearer Token' });
+      expect(client.logLevel).toBe('debug');
+      expect(warnMock).not.toHaveBeenCalled();
+    });
+  });
+
+  describe('defaultQuery', () => {
+    test('with null query params given', () => {
+      const client = new Gitpod({
+        baseURL: 'http://localhost:5000/',
+        defaultQuery: { apiVersion: 'foo' },
+        bearerToken: 'My Bearer Token',
+      });
+      expect(client.buildURL('/foo', null)).toEqual('http://localhost:5000/foo?apiVersion=foo');
+    });
+
+    test('multiple default query params', () => {
+      const client = new Gitpod({
+        baseURL: 'http://localhost:5000/',
+        defaultQuery: { apiVersion: 'foo', hello: 'world' },
+        bearerToken: 'My Bearer Token',
+      });
+      expect(client.buildURL('/foo', null)).toEqual('http://localhost:5000/foo?apiVersion=foo&hello=world');
+    });
+
+    test('overriding with `undefined`', () => {
+      const client = new Gitpod({
+        baseURL: 'http://localhost:5000/',
+        defaultQuery: { hello: 'world' },
+        bearerToken: 'My Bearer Token',
+      });
+      expect(client.buildURL('/foo', { hello: undefined })).toEqual('http://localhost:5000/foo');
+    });
+  });
+
+  test('custom fetch', async () => {
+    const client = new Gitpod({
+      baseURL: 'http://localhost:5000/',
+      bearerToken: 'My Bearer Token',
+      fetch: (url) => {
+        return Promise.resolve(
+          new Response(JSON.stringify({ url, custom: true }), {
+            headers: { 'Content-Type': 'application/json' },
+          }),
+        );
+      },
+    });
+
+    const response = await client.get('/foo');
+    expect(response).toEqual({ url: 'http://localhost:5000/foo', custom: true });
+  });
+
+  test('explicit global fetch', async () => {
+    // make sure the global fetch type is assignable to our Fetch type
+    const client = new Gitpod({
+      baseURL: 'http://localhost:5000/',
+      bearerToken: 'My Bearer Token',
+      fetch: defaultFetch,
+    });
+  });
+
+  test('custom signal', async () => {
+    const client = new Gitpod({
+      baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010',
+      bearerToken: 'My Bearer Token',
+      fetch: (...args) => {
+        return new Promise((resolve, reject) =>
+          setTimeout(
+            () =>
+              defaultFetch(...args)
+                .then(resolve)
+                .catch(reject),
+            300,
+          ),
+        );
+      },
+    });
+
+    const controller = new AbortController();
+    setTimeout(() => controller.abort(), 200);
+
+    const spy = jest.spyOn(client, 'request');
+
+    await expect(client.get('/foo', { signal: controller.signal })).rejects.toThrowError(APIUserAbortError);
+    expect(spy).toHaveBeenCalledTimes(1);
+  });
+
+  test('normalized method', async () => {
+    let capturedRequest: RequestInit | undefined;
+    const testFetch = async (url: string | URL | Request, init: RequestInit = {}): Promise<Response> => {
+      capturedRequest = init;
+      return new Response(JSON.stringify({}), { headers: { 'Content-Type': 'application/json' } });
+    };
+
+    const client = new Gitpod({
+      baseURL: 'http://localhost:5000/',
+      bearerToken: 'My Bearer Token',
+      fetch: testFetch,
+    });
+
+    await client.patch('/foo');
+    expect(capturedRequest?.method).toEqual('PATCH');
+  });
+
+  describe('baseUrl', () => {
+    test('trailing slash', () => {
+      const client = new Gitpod({
+        baseURL: 'http://localhost:5000/custom/path/',
+        bearerToken: 'My Bearer Token',
+      });
+      expect(client.buildURL('/foo', null)).toEqual('http://localhost:5000/custom/path/foo');
+    });
+
+    test('no trailing slash', () => {
+      const client = new Gitpod({
+        baseURL: 'http://localhost:5000/custom/path',
+        bearerToken: 'My Bearer Token',
+      });
+      expect(client.buildURL('/foo', null)).toEqual('http://localhost:5000/custom/path/foo');
+    });
+
+    afterEach(() => {
+      process.env['GITPOD_BASE_URL'] = undefined;
+    });
+
+    test('explicit option', () => {
+      const client = new Gitpod({ baseURL: 'https://example.com', bearerToken: 'My Bearer Token' });
+      expect(client.baseURL).toEqual('https://example.com');
+    });
+
+    test('env variable', () => {
+      process.env['GITPOD_BASE_URL'] = 'https://example.com/from_env';
+      const client = new Gitpod({ bearerToken: 'My Bearer Token' });
+      expect(client.baseURL).toEqual('https://example.com/from_env');
+    });
+
+    test('empty env variable', () => {
+      process.env['GITPOD_BASE_URL'] = ''; // empty
+      const client = new Gitpod({ bearerToken: 'My Bearer Token' });
+      expect(client.baseURL).toEqual('https://app.gitpod.io/api');
+    });
+
+    test('blank env variable', () => {
+      process.env['GITPOD_BASE_URL'] = '  '; // blank
+      const client = new Gitpod({ bearerToken: 'My Bearer Token' });
+      expect(client.baseURL).toEqual('https://app.gitpod.io/api');
+    });
+  });
+
+  test('maxRetries option is correctly set', () => {
+    const client = new Gitpod({ maxRetries: 4, bearerToken: 'My Bearer Token' });
+    expect(client.maxRetries).toEqual(4);
+
+    // default
+    const client2 = new Gitpod({ bearerToken: 'My Bearer Token' });
+    expect(client2.maxRetries).toEqual(2);
+  });
+
+  test('with environment variable arguments', () => {
+    // set options via env var
+    process.env['GITPOD_API_KEY'] = 'My Bearer Token';
+    const client = new Gitpod();
+    expect(client.bearerToken).toBe('My Bearer Token');
+  });
+
+  test('with overridden environment variable arguments', () => {
+    // set options via env var
+    process.env['GITPOD_API_KEY'] = 'another My Bearer Token';
+    const client = new Gitpod({ bearerToken: 'My Bearer Token' });
+    expect(client.bearerToken).toBe('My Bearer Token');
+  });
+});
+
+describe('request building', () => {
+  const client = new Gitpod({ bearerToken: 'My Bearer Token' });
+
+  describe('custom headers', () => {
+    test('handles undefined', () => {
+      const { req } = client.buildRequest({
+        path: '/foo',
+        method: 'post',
+        body: { value: 'hello' },
+        headers: { 'X-Foo': 'baz', 'x-foo': 'bar', 'x-Foo': undefined, 'x-baz': 'bam', 'X-Baz': null },
+      });
+      expect(req.headers.get('x-foo')).toEqual('bar');
+      expect(req.headers.get('x-Foo')).toEqual('bar');
+      expect(req.headers.get('X-Foo')).toEqual('bar');
+      expect(req.headers.get('x-baz')).toEqual(null);
+    });
+  });
+});
+
+describe('default encoder', () => {
+  const client = new Gitpod({ bearerToken: 'My Bearer Token' });
+
+  class Serializable {
+    toJSON() {
+      return { $type: 'Serializable' };
+    }
+  }
+  class Collection<T> {
+    #things: T[];
+    constructor(things: T[]) {
+      this.#things = Array.from(things);
+    }
+    toJSON() {
+      return Array.from(this.#things);
+    }
+    [Symbol.iterator]() {
+      return this.#things[Symbol.iterator];
+    }
+  }
+  for (const jsonValue of [{}, [], { __proto__: null }, new Serializable(), new Collection(['item'])]) {
+    test(`serializes ${util.inspect(jsonValue)} as json`, () => {
+      const { req } = client.buildRequest({
+        path: '/foo',
+        method: 'post',
+        body: jsonValue,
+      });
+      expect(req.headers).toBeInstanceOf(Headers);
+      expect(req.headers.get('content-type')).toEqual('application/json');
+      expect(req.body).toBe(JSON.stringify(jsonValue));
+    });
+  }
+
+  const encoder = new TextEncoder();
+  const asyncIterable = (async function* () {
+    yield encoder.encode('a\n');
+    yield encoder.encode('b\n');
+    yield encoder.encode('c\n');
+  })();
+  for (const streamValue of [
+    [encoder.encode('a\nb\nc\n')][Symbol.iterator](),
+    new Response('a\nb\nc\n').body,
+    asyncIterable,
+  ]) {
+    test(`converts ${util.inspect(streamValue)} to ReadableStream`, async () => {
+      const { req } = client.buildRequest({
+        path: '/foo',
+        method: 'post',
+        body: streamValue,
+      });
+      expect(req.headers).toBeInstanceOf(Headers);
+      expect(req.headers.get('content-type')).toEqual(null);
+      expect(req.body).toBeInstanceOf(ReadableStream);
+      expect(await new Response(req.body).text()).toBe('a\nb\nc\n');
+    });
+  }
+
+  test(`can set content-type for ReadableStream`, async () => {
+    const { req } = client.buildRequest({
+      path: '/foo',
+      method: 'post',
+      body: new Response('a\nb\nc\n').body,
+      headers: { 'Content-Type': 'text/plain' },
+    });
+    expect(req.headers).toBeInstanceOf(Headers);
+    expect(req.headers.get('content-type')).toEqual('text/plain');
+    expect(req.body).toBeInstanceOf(ReadableStream);
+    expect(await new Response(req.body).text()).toBe('a\nb\nc\n');
+  });
+});
+
+describe('retries', () => {
+  test('retry on timeout', async () => {
+    let count = 0;
+    const testFetch = async (
+      url: string | URL | Request,
+      { signal }: RequestInit = {},
+    ): Promise<Response> => {
+      if (count++ === 0) {
+        return new Promise(
+          (resolve, reject) => signal?.addEventListener('abort', () => reject(new Error('timed out'))),
+        );
+      }
+      return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } });
+    };
+
+    const client = new Gitpod({ bearerToken: 'My Bearer Token', timeout: 10, fetch: testFetch });
+
+    expect(await client.request({ path: '/foo', method: 'get' })).toEqual({ a: 1 });
+    expect(count).toEqual(2);
+    expect(
+      await client
+        .request({ path: '/foo', method: 'get' })
+        .asResponse()
+        .then((r) => r.text()),
+    ).toEqual(JSON.stringify({ a: 1 }));
+    expect(count).toEqual(3);
+  });
+
+  test('retry count header', async () => {
+    let count = 0;
+    let capturedRequest: RequestInit | undefined;
+    const testFetch = async (url: string | URL | Request, init: RequestInit = {}): Promise<Response> => {
+      count++;
+      if (count <= 2) {
+        return new Response(undefined, {
+          status: 429,
+          headers: {
+            'Retry-After': '0.1',
+          },
+        });
+      }
+      capturedRequest = init;
+      return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } });
+    };
+
+    const client = new Gitpod({ bearerToken: 'My Bearer Token', fetch: testFetch, maxRetries: 4 });
+
+    expect(await client.request({ path: '/foo', method: 'get' })).toEqual({ a: 1 });
+
+    expect((capturedRequest!.headers as Headers).get('x-stainless-retry-count')).toEqual('2');
+    expect(count).toEqual(3);
+  });
+
+  test('omit retry count header', async () => {
+    let count = 0;
+    let capturedRequest: RequestInit | undefined;
+    const testFetch = async (url: string | URL | Request, init: RequestInit = {}): Promise<Response> => {
+      count++;
+      if (count <= 2) {
+        return new Response(undefined, {
+          status: 429,
+          headers: {
+            'Retry-After': '0.1',
+          },
+        });
+      }
+      capturedRequest = init;
+      return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } });
+    };
+    const client = new Gitpod({ bearerToken: 'My Bearer Token', fetch: testFetch, maxRetries: 4 });
+
+    expect(
+      await client.request({
+        path: '/foo',
+        method: 'get',
+        headers: { 'X-Stainless-Retry-Count': null },
+      }),
+    ).toEqual({ a: 1 });
+
+    expect((capturedRequest!.headers as Headers).has('x-stainless-retry-count')).toBe(false);
+  });
+
+  test('omit retry count header by default', async () => {
+    let count = 0;
+    let capturedRequest: RequestInit | undefined;
+    const testFetch = async (url: string | URL | Request, init: RequestInit = {}): Promise<Response> => {
+      count++;
+      if (count <= 2) {
+        return new Response(undefined, {
+          status: 429,
+          headers: {
+            'Retry-After': '0.1',
+          },
+        });
+      }
+      capturedRequest = init;
+      return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } });
+    };
+    const client = new Gitpod({
+      bearerToken: 'My Bearer Token',
+      fetch: testFetch,
+      maxRetries: 4,
+      defaultHeaders: { 'X-Stainless-Retry-Count': null },
+    });
+
+    expect(
+      await client.request({
+        path: '/foo',
+        method: 'get',
+      }),
+    ).toEqual({ a: 1 });
+
+    expect(capturedRequest!.headers as Headers).not.toHaveProperty('x-stainless-retry-count');
+  });
+
+  test('overwrite retry count header', async () => {
+    let count = 0;
+    let capturedRequest: RequestInit | undefined;
+    const testFetch = async (url: string | URL | Request, init: RequestInit = {}): Promise<Response> => {
+      count++;
+      if (count <= 2) {
+        return new Response(undefined, {
+          status: 429,
+          headers: {
+            'Retry-After': '0.1',
+          },
+        });
+      }
+      capturedRequest = init;
+      return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } });
+    };
+    const client = new Gitpod({ bearerToken: 'My Bearer Token', fetch: testFetch, maxRetries: 4 });
+
+    expect(
+      await client.request({
+        path: '/foo',
+        method: 'get',
+        headers: { 'X-Stainless-Retry-Count': '42' },
+      }),
+    ).toEqual({ a: 1 });
+
+    expect((capturedRequest!.headers as Headers).get('x-stainless-retry-count')).toEqual('42');
+  });
+
+  test('retry on 429 with retry-after', async () => {
+    let count = 0;
+    const testFetch = async (
+      url: string | URL | Request,
+      { signal }: RequestInit = {},
+    ): Promise<Response> => {
+      if (count++ === 0) {
+        return new Response(undefined, {
+          status: 429,
+          headers: {
+            'Retry-After': '0.1',
+          },
+        });
+      }
+      return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } });
+    };
+
+    const client = new Gitpod({ bearerToken: 'My Bearer Token', fetch: testFetch });
+
+    expect(await client.request({ path: '/foo', method: 'get' })).toEqual({ a: 1 });
+    expect(count).toEqual(2);
+    expect(
+      await client
+        .request({ path: '/foo', method: 'get' })
+        .asResponse()
+        .then((r) => r.text()),
+    ).toEqual(JSON.stringify({ a: 1 }));
+    expect(count).toEqual(3);
+  });
+
+  test('retry on 429 with retry-after-ms', async () => {
+    let count = 0;
+    const testFetch = async (
+      url: string | URL | Request,
+      { signal }: RequestInit = {},
+    ): Promise<Response> => {
+      if (count++ === 0) {
+        return new Response(undefined, {
+          status: 429,
+          headers: {
+            'Retry-After-Ms': '10',
+          },
+        });
+      }
+      return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } });
+    };
+
+    const client = new Gitpod({ bearerToken: 'My Bearer Token', fetch: testFetch });
+
+    expect(await client.request({ path: '/foo', method: 'get' })).toEqual({ a: 1 });
+    expect(count).toEqual(2);
+    expect(
+      await client
+        .request({ path: '/foo', method: 'get' })
+        .asResponse()
+        .then((r) => r.text()),
+    ).toEqual(JSON.stringify({ a: 1 }));
+    expect(count).toEqual(3);
+  });
+});
diff --git a/tests/internal/decoders/line.test.ts b/tests/internal/decoders/line.test.ts
new file mode 100644
index 0000000..9f45693
--- /dev/null
+++ b/tests/internal/decoders/line.test.ts
@@ -0,0 +1,128 @@
+import { findDoubleNewlineIndex, LineDecoder } from '@gitpod/sdk/internal/decoders/line';
+
+function decodeChunks(chunks: string[], { flush }: { flush: boolean } = { flush: false }): string[] {
+  const decoder = new LineDecoder();
+  const lines: string[] = [];
+  for (const chunk of chunks) {
+    lines.push(...decoder.decode(chunk));
+  }
+
+  if (flush) {
+    lines.push(...decoder.flush());
+  }
+
+  return lines;
+}
+
+describe('line decoder', () => {
+  test('basic', () => {
+    // baz is not included because the line hasn't ended yet
+    expect(decodeChunks(['foo', ' bar\nbaz'])).toEqual(['foo bar']);
+  });
+
+  test('basic with \\r', () => {
+    expect(decodeChunks(['foo', ' bar\r\nbaz'])).toEqual(['foo bar']);
+    expect(decodeChunks(['foo', ' bar\r\nbaz'], { flush: true })).toEqual(['foo bar', 'baz']);
+  });
+
+  test('trailing new lines', () => {
+    expect(decodeChunks(['foo', ' bar', 'baz\n', 'thing\n'])).toEqual(['foo barbaz', 'thing']);
+  });
+
+  test('trailing new lines with \\r', () => {
+    expect(decodeChunks(['foo', ' bar', 'baz\r\n', 'thing\r\n'])).toEqual(['foo barbaz', 'thing']);
+  });
+
+  test('escaped new lines', () => {
+    expect(decodeChunks(['foo', ' bar\\nbaz\n'])).toEqual(['foo bar\\nbaz']);
+  });
+
+  test('escaped new lines with \\r', () => {
+    expect(decodeChunks(['foo', ' bar\\r\\nbaz\n'])).toEqual(['foo bar\\r\\nbaz']);
+  });
+
+  test('\\r & \\n split across multiple chunks', () => {
+    expect(decodeChunks(['foo\r', '\n', 'bar'], { flush: true })).toEqual(['foo', 'bar']);
+  });
+
+  test('single \\r', () => {
+    expect(decodeChunks(['foo\r', 'bar'], { flush: true })).toEqual(['foo', 'bar']);
+  });
+
+  test('double \\r', () => {
+    expect(decodeChunks(['foo\r', 'bar\r'], { flush: true })).toEqual(['foo', 'bar']);
+    expect(decodeChunks(['foo\r', '\r', 'bar'], { flush: true })).toEqual(['foo', '', 'bar']);
+    // implementation detail that we don't yield the single \r line until a new \r or \n is encountered
+    expect(decodeChunks(['foo\r', '\r', 'bar'], { flush: false })).toEqual(['foo']);
+  });
+
+  test('double \\r then \\r\\n', () => {
+    expect(decodeChunks(['foo\r', '\r', '\r', '\n', 'bar', '\n'])).toEqual(['foo', '', '', 'bar']);
+    expect(decodeChunks(['foo\n', '\n', '\n', 'bar', '\n'])).toEqual(['foo', '', '', 'bar']);
+  });
+
+  test('double newline', () => {
+    expect(decodeChunks(['foo\n\nbar'], { flush: true })).toEqual(['foo', '', 'bar']);
+    expect(decodeChunks(['foo', '\n', '\nbar'], { flush: true })).toEqual(['foo', '', 'bar']);
+    expect(decodeChunks(['foo\n', '\n', 'bar'], { flush: true })).toEqual(['foo', '', 'bar']);
+    expect(decodeChunks(['foo', '\n', '\n', 'bar'], { flush: true })).toEqual(['foo', '', 'bar']);
+  });
+
+  test('multi-byte characters across chunks', () => {
+    const decoder = new LineDecoder();
+
+    // bytes taken from the string 'известни' and arbitrarily split
+    // so that some multi-byte characters span multiple chunks
+    expect(decoder.decode(new Uint8Array([0xd0]))).toHaveLength(0);
+    expect(decoder.decode(new Uint8Array([0xb8, 0xd0, 0xb7, 0xd0]))).toHaveLength(0);
+    expect(
+      decoder.decode(new Uint8Array([0xb2, 0xd0, 0xb5, 0xd1, 0x81, 0xd1, 0x82, 0xd0, 0xbd, 0xd0, 0xb8])),
+    ).toHaveLength(0);
+
+    const decoded = decoder.decode(new Uint8Array([0xa]));
+    expect(decoded).toEqual(['известни']);
+  });
+
+  test('flushing trailing newlines', () => {
+    expect(decodeChunks(['foo\n', '\nbar'], { flush: true })).toEqual(['foo', '', 'bar']);
+  });
+
+  test('flushing empty buffer', () => {
+    expect(decodeChunks([], { flush: true })).toEqual([]);
+  });
+});
+
+describe('findDoubleNewlineIndex', () => {
+  test('finds \\n\\n', () => {
+    expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\n\nbar'))).toBe(5);
+    expect(findDoubleNewlineIndex(new TextEncoder().encode('\n\nbar'))).toBe(2);
+    expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\n\n'))).toBe(5);
+    expect(findDoubleNewlineIndex(new TextEncoder().encode('\n\n'))).toBe(2);
+  });
+
+  test('finds \\r\\r', () => {
+    expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\r\rbar'))).toBe(5);
+    expect(findDoubleNewlineIndex(new TextEncoder().encode('\r\rbar'))).toBe(2);
+    expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\r\r'))).toBe(5);
+    expect(findDoubleNewlineIndex(new TextEncoder().encode('\r\r'))).toBe(2);
+  });
+
+  test('finds \\r\\n\\r\\n', () => {
+    expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\r\n\r\nbar'))).toBe(7);
+    expect(findDoubleNewlineIndex(new TextEncoder().encode('\r\n\r\nbar'))).toBe(4);
+    expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\r\n\r\n'))).toBe(7);
+    expect(findDoubleNewlineIndex(new TextEncoder().encode('\r\n\r\n'))).toBe(4);
+  });
+
+  test('returns -1 when no double newline found', () => {
+    expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\nbar'))).toBe(-1);
+    expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\rbar'))).toBe(-1);
+    expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\r\nbar'))).toBe(-1);
+    expect(findDoubleNewlineIndex(new TextEncoder().encode(''))).toBe(-1);
+  });
+
+  test('handles incomplete patterns', () => {
+    expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\r\n\r'))).toBe(-1);
+    expect(findDoubleNewlineIndex(new TextEncoder().encode('foo\r\n'))).toBe(-1);
+  });
+});
diff --git a/tests/path.test.ts b/tests/path.test.ts
new file mode 100644
index 0000000..3c95a0b
--- /dev/null
+++ b/tests/path.test.ts
@@ -0,0 +1,318 @@
+import { createPathTagFunction, encodeURIPath } from '@gitpod/sdk/internal/utils/path';
+import { inspect } from 'node:util';
+
+describe('path template tag function', () => {
+  test('validates input', () => {
+    const testParams = ['', '.', '..', 'x', '%2e', '%2E', '%2e%2e', '%2E%2e', '%2e%2E', '%2E%2E'];
+    const testCases = [
+      ['/path_params/', '/a'],
+      ['/path_params/', '/'],
+      ['/path_params/', ''],
+      ['', '/a'],
+      ['', '/'],
+      ['', ''],
+      ['a'],
+      [''],
+      ['/path_params/', ':initiate'],
+      ['/path_params/', '.json'],
+      ['/path_params/', '?beta=true'],
+      ['/path_params/', '.?beta=true'],
+      ['/path_params/', '/', '/download'],
+      ['/path_params/', '-', '/download'],
+      ['/path_params/', '', '/download'],
+      ['/path_params/', '.', '/download'],
+      ['/path_params/', '..', '/download'],
+      ['/plain/path'],
+    ];
+
+    function paramPermutations(len: number): string[][] {
+      if (len === 0) return [];
+      if (len === 1) return testParams.map((e) => [e]);
+      const rest = paramPermutations(len - 1);
+      return testParams.flatMap((e) => rest.map((r) => [e, ...r]));
+    }
+
+    // we need to test how %2E is handled so we use a custom encoder that does no escaping
+    const rawPath = createPathTagFunction((s) => s);
+
+    const results: {
+      [pathParts: string]: {
+        [params: string]: { valid: boolean; result?: string; error?: string };
+      };
+    } = {};
+
+    for (const pathParts of testCases) {
+      const pathResults: Record<string, { valid: boolean; result?: string; error?: string }> = {};
+      results[JSON.stringify(pathParts)] = pathResults;
+      for (const params of paramPermutations(pathParts.length - 1)) {
+        const stringRaw = String.raw({ raw: pathParts }, ...params);
+        const plainString = String.raw(
+          { raw: pathParts.map((e) => e.replace(/\./g, 'x')) },
+          ...params.map((e) => 'X'.repeat(e.length)),
+        );
+        const normalizedStringRaw = new URL(stringRaw, 'https://example.com').href;
+        const normalizedPlainString = new URL(plainString, 'https://example.com').href;
+        const pathResultsKey = JSON.stringify(params);
+        try {
+          const result = rawPath(pathParts, ...params);
+          expect(result).toBe(stringRaw);
+          // there are no special segments, so the length of the normalized path is
+          // equal to the length of the normalized plain path.
+          expect(normalizedStringRaw.length).toBe(normalizedPlainString.length);
+          pathResults[pathResultsKey] = {
+            valid: true,
+            result,
+          };
+        } catch (e) {
+          const error = String(e);
+          expect(error).toMatch(/Path parameters result in path with invalid segment/);
+          // there are special segments, so the length of the normalized path is
+          // different than the length of the normalized plain path.
+          expect(normalizedStringRaw.length).not.toBe(normalizedPlainString.length);
+          pathResults[pathResultsKey] = {
+            valid: false,
+            error,
+          };
+        }
+      }
+    }
+
+    expect(results).toMatchObject({
+      '["/path_params/","/a"]': {
+        '["x"]': { valid: true, result: '/path_params/x/a' },
+        '[""]': { valid: true, result: '/path_params//a' },
+        '["%2E%2e"]': {
+          valid: false,
+          error:
+            'Error: Path parameters result in path with invalid segments:\n' +
+            '/path_params/%2E%2e/a\n' +
+            '             ^^^^^^',
+        },
+        '["%2E"]': {
+          valid: false,
+          error:
+            'Error: Path parameters result in path with invalid segments:\n' +
+            '/path_params/%2E/a\n' +
+            '             ^^^',
+        },
+      },
+      '["/path_params/","/"]': {
+        '["x"]': { valid: true, result: '/path_params/x/' },
+        '[""]': { valid: true, result: '/path_params//' },
+        '["%2e%2E"]': {
+          valid: false,
+          error:
+            'Error: Path parameters result in path with invalid segments:\n' +
+            '/path_params/%2e%2E/\n' +
+            '             ^^^^^^',
+        },
+        '["%2e"]': {
+          valid: false,
+          error:
+            'Error: Path parameters result in path with invalid segments:\n' +
+            '/path_params/%2e/\n' +
+            '             ^^^',
+        },
+      },
+      '["/path_params/",""]': {
+        '[""]': { valid: true, result: '/path_params/' },
+        '["x"]': { valid: true, result: '/path_params/x' },
+        '["%2E"]': {
+          valid: false,
+          error:
+            'Error: Path parameters result in path with invalid segments:\n' +
+            '/path_params/%2E\n' +
+            '             ^^^',
+        },
+        '["%2E%2e"]': {
+          valid: false,
+          error:
+            'Error: Path parameters result in path with invalid segments:\n' +
+            '/path_params/%2E%2e\n' +
+            '             ^^^^^^',
+        },
+      },
+      '["","/a"]': {
+        '[""]': { valid: true, result: '/a' },
+        '["x"]': { valid: true, result: 'x/a' },
+        '["%2E"]': {
+          valid: false,
+          error: 'Error: Path parameters result in path with invalid segments:\n%2E/a\n^^^',
+        },
+        '["%2e%2E"]': {
+          valid: false,
+          error: 'Error: Path parameters result in path with invalid segments:\n' + '%2e%2E/a\n' + '^^^^^^',
+        },
+      },
+      '["","/"]': {
+        '["x"]': { valid: true, result: 'x/' },
+        '[""]': { valid: true, result: '/' },
+        '["%2E%2e"]': {
+          valid: false,
+          error: 'Error: Path parameters result in path with invalid segments:\n' + '%2E%2e/\n' + '^^^^^^',
+        },
+        '["."]': {
+          valid: false,
+          error: 'Error: Path parameters result in path with invalid segments:\n./\n^',
+        },
+      },
+      '["",""]': {
+        '[""]': { valid: true, result: '' },
+        '["x"]': { valid: true, result: 'x' },
+        '[".."]': {
+          valid: false,
+          error: 'Error: Path parameters result in path with invalid segments:\n..\n^^',
+        },
+        '["."]': {
+          valid: false,
+          error: 'Error: Path parameters result in path with invalid segments:\n.\n^',
+        },
+      },
+      '["a"]': {},
+      '[""]': {},
+      '["/path_params/",":initiate"]': {
+        '[""]': { valid: true, result: '/path_params/:initiate' },
+        '["."]': { valid: true, result: '/path_params/.:initiate' },
+      },
+      '["/path_params/",".json"]': {
+        '["x"]': { valid: true, result: '/path_params/x.json' },
+        '["."]': { valid: true, result: '/path_params/..json' },
+      },
+      '["/path_params/","?beta=true"]': {
+        '["x"]': { valid: true, result: '/path_params/x?beta=true' },
+        '[""]': { valid: true, result: '/path_params/?beta=true' },
+        '["%2E%2E"]': {
+          valid: false,
+          error:
+            'Error: Path parameters result in path with invalid segments:\n' +
+            '/path_params/%2E%2E?beta=true\n' +
+            '             ^^^^^^',
+        },
+        '["%2e%2E"]': {
+          valid: false,
+          error:
+            'Error: Path parameters result in path with invalid segments:\n' +
+            '/path_params/%2e%2E?beta=true\n' +
+            '             ^^^^^^',
+        },
+      },
+      '["/path_params/",".?beta=true"]': {
+        '[".."]': { valid: true, result: '/path_params/...?beta=true' },
+        '["x"]': { valid: true, result: '/path_params/x.?beta=true' },
+        '[""]': {
+          valid: false,
+          error:
+            'Error: Path parameters result in path with invalid segments:\n' +
+            '/path_params/.?beta=true\n' +
+            '             ^',
+        },
+        '["%2e"]': {
+          valid: false,
+          error:
+            'Error: Path parameters result in path with invalid segments:\n' +
+            '/path_params/%2e.?beta=true\n' +
+            '             ^^^^',
+        },
+      },
+      '["/path_params/","/","/download"]': {
+        '["",""]': { valid: true, result: '/path_params///download' },
+        '["","x"]': { valid: true, result: '/path_params//x/download' },
+        '[".","%2e"]': {
+          valid: false,
+          error:
+            'Error: Path parameters result in path with invalid segments:\n' +
+            '/path_params/./%2e/download\n' +
+            '             ^ ^^^',
+        },
+        '["%2E%2e","%2e"]': {
+          valid: false,
+          error:
+            'Error: Path parameters result in path with invalid segments:\n' +
+            '/path_params/%2E%2e/%2e/download\n' +
+            '             ^^^^^^ ^^^',
+        },
+      },
+      '["/path_params/","-","/download"]': {
+        '["","%2e"]': { valid: true, result: '/path_params/-%2e/download' },
+        '["%2E",".."]': { valid: true, result: '/path_params/%2E-../download' },
+      },
+      '["/path_params/","","/download"]': {
+        '["%2E%2e","%2e%2E"]': { valid: true, result: '/path_params/%2E%2e%2e%2E/download' },
+        '["%2E",".."]': { valid: true, result: '/path_params/%2E../download' },
+        '["","%2E"]': {
+          valid: false,
+          error:
+            'Error: Path parameters result in path with invalid segments:\n' +
+            '/path_params/%2E/download\n' +
+            '             ^^^',
+        },
+        '["%2E","."]': {
+          valid: false,
+          error:
+            'Error: Path parameters result in path with invalid segments:\n' +
+            '/path_params/%2E./download\n' +
+            '             ^^^^',
+        },
+      },
+      '["/path_params/",".","/download"]': {
+        '["%2e%2e",""]': { valid: true, result: '/path_params/%2e%2e./download' },
+        '["","%2e%2e"]': { valid: true, result: '/path_params/.%2e%2e/download' },
+        '["",""]': {
+          valid: false,
+          error:
+            'Error: Path parameters result in path with invalid segments:\n' +
+            '/path_params/./download\n' +
+            '             ^',
+        },
+        '["","."]': {
+          valid: false,
+          error:
+            'Error: Path parameters result in path with invalid segments:\n' +
+            '/path_params/../download\n' +
+            '             ^^',
+        },
+      },
+      '["/path_params/","..","/download"]': {
+        '["","%2E"]': { valid: true, result: '/path_params/..%2E/download' },
+        '["","x"]': { valid: true, result: '/path_params/..x/download' },
+        '["",""]': {
+          valid: false,
+          error:
+            'Error: Path parameters result in path with invalid segments:\n' +
+            '/path_params/../download\n' +
+            '             ^^',
+        },
+      },
+    });
+  });
+});
+
+describe('encodeURIPath', () => {
+  const testCases: string[] = [
+    '',
+    // Every ASCII character
+    ...Array.from({ length: 0x7f }, (_, i) => String.fromCharCode(i)),
+    // Unicode BMP codepoint
+    'å',
+    // Unicode supplementary codepoint
+    '😃',
+  ];
+
+  for (const param of testCases) {
+    test('properly encodes ' + inspect(param), () => {
+      const encoded = encodeURIPath(param);
+      const naiveEncoded = encodeURIComponent(param);
+      // we should never encode more characters than encodeURIComponent
+      expect(naiveEncoded.length).toBeGreaterThanOrEqual(encoded.length);
+      expect(decodeURIComponent(encoded)).toBe(param);
+    });
+  }
+
+  test("leaves ':' intact", () => {
+    expect(encodeURIPath(':')).toBe(':');
+  });
+
+  test("leaves '@' intact", () => {
+    expect(encodeURIPath('@')).toBe('@');
+  });
+});
diff --git a/tests/stringifyQuery.test.ts b/tests/stringifyQuery.test.ts
new file mode 100644
index 0000000..a5c8902
--- /dev/null
+++ b/tests/stringifyQuery.test.ts
@@ -0,0 +1,29 @@
+// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import { Gitpod } from '@gitpod/sdk';
+
+const { stringifyQuery } = Gitpod.prototype as any;
+
+describe(stringifyQuery, () => {
+  for (const [input, expected] of [
+    [{ a: '1', b: 2, c: true }, 'a=1&b=2&c=true'],
+    [{ a: null, b: false, c: undefined }, 'a=&b=false'],
+    [{ 'a/b': 1.28341 }, `${encodeURIComponent('a/b')}=1.28341`],
+    [
+      { 'a/b': 'c/d', 'e=f': 'g&h' },
+      `${encodeURIComponent('a/b')}=${encodeURIComponent('c/d')}&${encodeURIComponent(
+        'e=f',
+      )}=${encodeURIComponent('g&h')}`,
+    ],
+  ]) {
+    it(`${JSON.stringify(input)} -> ${expected}`, () => {
+      expect(stringifyQuery(input)).toEqual(expected);
+    });
+  }
+
+  for (const value of [[], {}, new Date()]) {
+    it(`${JSON.stringify(value)} -> <error>`, () => {
+      expect(() => stringifyQuery({ value })).toThrow(`Cannot stringify type ${typeof value}`);
+    });
+  }
+});
diff --git a/tests/uploads.test.ts b/tests/uploads.test.ts
new file mode 100644
index 0000000..27bf8b3
--- /dev/null
+++ b/tests/uploads.test.ts
@@ -0,0 +1,107 @@
+import fs from 'fs';
+import type { ResponseLike } from '@gitpod/sdk/internal/to-file';
+import { toFile } from '@gitpod/sdk/core/uploads';
+import { File } from 'node:buffer';
+
+class MyClass {
+  name: string = 'foo';
+}
+
+function mockResponse({ url, content }: { url: string; content?: Blob }): ResponseLike {
+  return {
+    url,
+    blob: async () => content || new Blob([]),
+  };
+}
+
+describe('toFile', () => {
+  it('throws a helpful error for mismatched types', async () => {
+    await expect(
+      // @ts-expect-error intentionally mismatched type
+      toFile({ foo: 'string' }),
+    ).rejects.toThrowErrorMatchingInlineSnapshot(
+      `"Unexpected data type: object; constructor: Object; props: ["foo"]"`,
+    );
+
+    await expect(
+      // @ts-expect-error intentionally mismatched type
+      toFile(new MyClass()),
+    ).rejects.toThrowErrorMatchingInlineSnapshot(
+      `"Unexpected data type: object; constructor: MyClass; props: ["name"]"`,
+    );
+  });
+
+  it('disallows string at the type-level', async () => {
+    // @ts-expect-error we intentionally do not type support for `string`
+    // to help people avoid passing a file path
+    const file = await toFile('contents');
+    expect(file.text()).resolves.toEqual('contents');
+  });
+
+  it('extracts a file name from a Response', async () => {
+    const response = mockResponse({ url: 'https://example.com/my/audio.mp3' });
+    const file = await toFile(response);
+    expect(file.name).toEqual('audio.mp3');
+  });
+
+  it('extracts a file name from a File', async () => {
+    const input = new File(['foo'], 'input.jsonl');
+    const file = await toFile(input);
+    expect(file.name).toEqual('input.jsonl');
+  });
+
+  it('extracts a file name from a ReadStream', async () => {
+    const input = fs.createReadStream('tests/uploads.test.ts');
+    const file = await toFile(input);
+    expect(file.name).toEqual('uploads.test.ts');
+  });
+
+  it('does not copy File objects', async () => {
+    const input = new File(['foo'], 'input.jsonl', { type: 'jsonl' });
+    const file = await toFile(input);
+    expect(file).toBe(input);
+    expect(file.name).toEqual('input.jsonl');
+    expect(file.type).toBe('jsonl');
+  });
+
+  it('is assignable to File and Blob', async () => {
+    const input = new File(['foo'], 'input.jsonl', { type: 'jsonl' });
+    const result = await toFile(input);
+    const file: File = result;
+    const blob: Blob = result;
+    void file, blob;
+  });
+});
+
+describe('missing File error message', () => {
+  let prevGlobalFile: unknown;
+  let prevNodeFile: unknown;
+  beforeEach(() => {
+    // The file shim captures the global File object when it's first imported.
+    // Reset modules before each test so we can test the error thrown when it's undefined.
+    jest.resetModules();
+    const buffer = require('node:buffer');
+    // @ts-ignore
+    prevGlobalFile = globalThis.File;
+    prevNodeFile = buffer.File;
+    // @ts-ignore
+    globalThis.File = undefined;
+    buffer.File = undefined;
+  });
+  afterEach(() => {
+    // Clean up
+    // @ts-ignore
+    globalThis.File = prevGlobalFile;
+    require('node:buffer').File = prevNodeFile;
+    jest.resetModules();
+  });
+
+  test('is thrown', async () => {
+    const uploads = await import('@gitpod/sdk/core/uploads');
+    await expect(
+      uploads.toFile(mockResponse({ url: 'https://example.com/my/audio.mp3' })),
+    ).rejects.toMatchInlineSnapshot(
+      `[Error: \`File\` is not defined as a global, which is required for file uploads.]`,
+    );
+  });
+});
diff --git a/tsc-multi.json b/tsc-multi.json
new file mode 100644
index 0000000..170bac7
--- /dev/null
+++ b/tsc-multi.json
@@ -0,0 +1,7 @@
+{
+  "targets": [
+    { "extname": ".js", "module": "commonjs", "shareHelpers": "internal/tslib.js" },
+    { "extname": ".mjs", "module": "esnext", "shareHelpers": "internal/tslib.mjs" }
+  ],
+  "projects": ["tsconfig.build.json"]
+}
diff --git a/tsconfig.build.json b/tsconfig.build.json
new file mode 100644
index 0000000..0460cd2
--- /dev/null
+++ b/tsconfig.build.json
@@ -0,0 +1,18 @@
+{
+  "extends": "./tsconfig.json",
+  "include": ["dist/src"],
+  "exclude": [],
+  "compilerOptions": {
+    "rootDir": "./dist/src",
+    "paths": {
+      "@gitpod/sdk/*": ["dist/src/*"],
+      "@gitpod/sdk": ["dist/src/index.ts"]
+    },
+    "noEmit": false,
+    "declaration": true,
+    "declarationMap": true,
+    "outDir": "dist",
+    "pretty": true,
+    "sourceMap": true
+  }
+}
diff --git a/tsconfig.deno.json b/tsconfig.deno.json
new file mode 100644
index 0000000..849e070
--- /dev/null
+++ b/tsconfig.deno.json
@@ -0,0 +1,15 @@
+{
+  "extends": "./tsconfig.json",
+  "include": ["dist-deno"],
+  "exclude": [],
+  "compilerOptions": {
+    "rootDir": "./dist-deno",
+    "lib": ["es2020", "DOM"],
+    "noEmit": true,
+    "declaration": true,
+    "declarationMap": true,
+    "outDir": "dist-deno",
+    "pretty": true,
+    "sourceMap": true
+  }
+}
diff --git a/tsconfig.dist-src.json b/tsconfig.dist-src.json
new file mode 100644
index 0000000..c550e29
--- /dev/null
+++ b/tsconfig.dist-src.json
@@ -0,0 +1,11 @@
+{
+  // this config is included in the published src directory to prevent TS errors
+  // from appearing when users go to source, and VSCode opens the source .ts file
+  // via declaration maps
+  "include": ["index.ts"],
+  "compilerOptions": {
+    "target": "ES2015",
+    "lib": ["DOM", "DOM.Iterable", "ES2018"],
+    "moduleResolution": "node"
+  }
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..db8ba0b
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,38 @@
+{
+  "include": ["src", "tests", "examples"],
+  "exclude": [],
+  "compilerOptions": {
+    "target": "es2020",
+    "lib": ["es2020"],
+    "module": "commonjs",
+    "moduleResolution": "node",
+    "esModuleInterop": true,
+    "baseUrl": "./",
+    "paths": {
+      "@gitpod/sdk/*": ["src/*"],
+      "@gitpod/sdk": ["src/index.ts"]
+    },
+    "noEmit": true,
+
+    "resolveJsonModule": true,
+
+    "forceConsistentCasingInFileNames": true,
+
+    "strict": true,
+    "noImplicitAny": true,
+    "strictNullChecks": true,
+    "strictFunctionTypes": true,
+    "strictBindCallApply": true,
+    "strictPropertyInitialization": true,
+    "noImplicitThis": true,
+    "noImplicitReturns": true,
+    "alwaysStrict": true,
+    "exactOptionalPropertyTypes": true,
+    "noUncheckedIndexedAccess": true,
+    "noImplicitOverride": true,
+    "noPropertyAccessFromIndexSignature": true,
+    "isolatedModules": false,
+
+    "skipLibCheck": true
+  }
+}
diff --git a/yarn.lock b/yarn.lock
new file mode 100644
index 0000000..43da555
--- /dev/null
+++ b/yarn.lock
@@ -0,0 +1,3506 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@aashutoshrathi/word-wrap@^1.2.3":
+  version "1.2.6"
+  resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf"
+  integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==
+
+"@ampproject/remapping@^2.2.0":
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630"
+  integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==
+  dependencies:
+    "@jridgewell/gen-mapping" "^0.3.0"
+    "@jridgewell/trace-mapping" "^0.3.9"
+
+"@andrewbranch/untar.js@^1.0.3":
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/@andrewbranch/untar.js/-/untar.js-1.0.3.tgz#ba9494f85eb83017c5c855763969caf1d0adea00"
+  integrity sha512-Jh15/qVmrLGhkKJBdXlK1+9tY4lZruYjsgkDFj08ZmDiWVBLJcqkok7Z0/R0In+i1rScBpJlSvrTS2Lm41Pbnw==
+
+"@arethetypeswrong/cli@^0.17.0":
+  version "0.17.0"
+  resolved "https://registry.yarnpkg.com/@arethetypeswrong/cli/-/cli-0.17.0.tgz#f97f10926b3f9f9eb5117550242d2e06c25cadac"
+  integrity sha512-xSMW7bfzVWpYw5JFgZqBXqr6PdR0/REmn3DkxCES5N0JTcB0CVgbIynJCvKBFmXaPc3hzmmTrb7+yPDRoOSZdA==
+  dependencies:
+    "@arethetypeswrong/core" "0.17.0"
+    chalk "^4.1.2"
+    cli-table3 "^0.6.3"
+    commander "^10.0.1"
+    marked "^9.1.2"
+    marked-terminal "^7.1.0"
+    semver "^7.5.4"
+
+"@arethetypeswrong/core@0.17.0":
+  version "0.17.0"
+  resolved "https://registry.yarnpkg.com/@arethetypeswrong/core/-/core-0.17.0.tgz#abb3b5f425056d37193644c2a2de4aecf866b76b"
+  integrity sha512-FHyhFizXNetigTVsIhqXKGYLpazPS5YNojEPpZEUcBPt9wVvoEbNIvG+hybuBR+pjlRcbyuqhukHZm1fr+bDgA==
+  dependencies:
+    "@andrewbranch/untar.js" "^1.0.3"
+    cjs-module-lexer "^1.2.3"
+    fflate "^0.8.2"
+    lru-cache "^10.4.3"
+    semver "^7.5.4"
+    typescript "5.6.1-rc"
+    validate-npm-package-name "^5.0.0"
+
+"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.13", "@babel/code-frame@^7.23.5":
+  version "7.23.5"
+  resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244"
+  integrity sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==
+  dependencies:
+    "@babel/highlight" "^7.23.4"
+    chalk "^2.4.2"
+
+"@babel/compat-data@^7.23.5":
+  version "7.23.5"
+  resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.23.5.tgz#ffb878728bb6bdcb6f4510aa51b1be9afb8cfd98"
+  integrity sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==
+
+"@babel/core@^7.11.6", "@babel/core@^7.12.3":
+  version "7.23.6"
+  resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.6.tgz#8be77cd77c55baadcc1eae1c33df90ab6d2151d4"
+  integrity sha512-FxpRyGjrMJXh7X3wGLGhNDCRiwpWEF74sKjTLDJSG5Kyvow3QZaG0Adbqzi9ZrVjTWpsX+2cxWXD71NMg93kdw==
+  dependencies:
+    "@ampproject/remapping" "^2.2.0"
+    "@babel/code-frame" "^7.23.5"
+    "@babel/generator" "^7.23.6"
+    "@babel/helper-compilation-targets" "^7.23.6"
+    "@babel/helper-module-transforms" "^7.23.3"
+    "@babel/helpers" "^7.23.6"
+    "@babel/parser" "^7.23.6"
+    "@babel/template" "^7.22.15"
+    "@babel/traverse" "^7.23.6"
+    "@babel/types" "^7.23.6"
+    convert-source-map "^2.0.0"
+    debug "^4.1.0"
+    gensync "^1.0.0-beta.2"
+    json5 "^2.2.3"
+    semver "^6.3.1"
+
+"@babel/generator@^7.23.6", "@babel/generator@^7.7.2":
+  version "7.23.6"
+  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.6.tgz#9e1fca4811c77a10580d17d26b57b036133f3c2e"
+  integrity sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==
+  dependencies:
+    "@babel/types" "^7.23.6"
+    "@jridgewell/gen-mapping" "^0.3.2"
+    "@jridgewell/trace-mapping" "^0.3.17"
+    jsesc "^2.5.1"
+
+"@babel/helper-compilation-targets@^7.23.6":
+  version "7.23.6"
+  resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz#4d79069b16cbcf1461289eccfbbd81501ae39991"
+  integrity sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==
+  dependencies:
+    "@babel/compat-data" "^7.23.5"
+    "@babel/helper-validator-option" "^7.23.5"
+    browserslist "^4.22.2"
+    lru-cache "^5.1.1"
+    semver "^6.3.1"
+
+"@babel/helper-environment-visitor@^7.22.20":
+  version "7.22.20"
+  resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167"
+  integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==
+
+"@babel/helper-function-name@^7.23.0":
+  version "7.23.0"
+  resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759"
+  integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==
+  dependencies:
+    "@babel/template" "^7.22.15"
+    "@babel/types" "^7.23.0"
+
+"@babel/helper-hoist-variables@^7.22.5":
+  version "7.22.5"
+  resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb"
+  integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==
+  dependencies:
+    "@babel/types" "^7.22.5"
+
+"@babel/helper-module-imports@^7.22.15":
+  version "7.22.15"
+  resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0"
+  integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==
+  dependencies:
+    "@babel/types" "^7.22.15"
+
+"@babel/helper-module-transforms@^7.23.3":
+  version "7.23.3"
+  resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1"
+  integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==
+  dependencies:
+    "@babel/helper-environment-visitor" "^7.22.20"
+    "@babel/helper-module-imports" "^7.22.15"
+    "@babel/helper-simple-access" "^7.22.5"
+    "@babel/helper-split-export-declaration" "^7.22.6"
+    "@babel/helper-validator-identifier" "^7.22.20"
+
+"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.0":
+  version "7.22.5"
+  resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295"
+  integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==
+
+"@babel/helper-simple-access@^7.22.5":
+  version "7.22.5"
+  resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de"
+  integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==
+  dependencies:
+    "@babel/types" "^7.22.5"
+
+"@babel/helper-split-export-declaration@^7.22.6":
+  version "7.22.6"
+  resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c"
+  integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==
+  dependencies:
+    "@babel/types" "^7.22.5"
+
+"@babel/helper-string-parser@^7.23.4":
+  version "7.23.4"
+  resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83"
+  integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==
+
+"@babel/helper-validator-identifier@^7.22.20":
+  version "7.22.20"
+  resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0"
+  integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==
+
+"@babel/helper-validator-option@^7.23.5":
+  version "7.23.5"
+  resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307"
+  integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==
+
+"@babel/helpers@^7.23.6":
+  version "7.23.6"
+  resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.6.tgz#d03af2ee5fb34691eec0cda90f5ecbb4d4da145a"
+  integrity sha512-wCfsbN4nBidDRhpDhvcKlzHWCTlgJYUUdSJfzXb2NuBssDSIjc3xcb+znA7l+zYsFljAcGM0aFkN40cR3lXiGA==
+  dependencies:
+    "@babel/template" "^7.22.15"
+    "@babel/traverse" "^7.23.6"
+    "@babel/types" "^7.23.6"
+
+"@babel/highlight@^7.23.4":
+  version "7.23.4"
+  resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b"
+  integrity sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==
+  dependencies:
+    "@babel/helper-validator-identifier" "^7.22.20"
+    chalk "^2.4.2"
+    js-tokens "^4.0.0"
+
+"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.22.15", "@babel/parser@^7.23.6":
+  version "7.23.6"
+  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.6.tgz#ba1c9e512bda72a47e285ae42aff9d2a635a9e3b"
+  integrity sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==
+
+"@babel/plugin-syntax-async-generators@^7.8.4":
+  version "7.8.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d"
+  integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-bigint@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea"
+  integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-class-properties@^7.8.3":
+  version "7.12.13"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10"
+  integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.12.13"
+
+"@babel/plugin-syntax-import-meta@^7.8.3":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51"
+  integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-syntax-json-strings@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a"
+  integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-jsx@^7.7.2":
+  version "7.23.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz#8f2e4f8a9b5f9aa16067e142c1ac9cd9f810f473"
+  integrity sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/plugin-syntax-logical-assignment-operators@^7.8.3":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699"
+  integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9"
+  integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-numeric-separator@^7.8.3":
+  version "7.10.4"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97"
+  integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.10.4"
+
+"@babel/plugin-syntax-object-rest-spread@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871"
+  integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-optional-catch-binding@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1"
+  integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-optional-chaining@^7.8.3":
+  version "7.8.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a"
+  integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-top-level-await@^7.8.3":
+  version "7.14.5"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c"
+  integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.14.5"
+
+"@babel/plugin-syntax-typescript@^7.7.2":
+  version "7.23.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz#24f460c85dbbc983cd2b9c4994178bcc01df958f"
+  integrity sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.22.5"
+
+"@babel/template@^7.22.15", "@babel/template@^7.3.3":
+  version "7.22.15"
+  resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38"
+  integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==
+  dependencies:
+    "@babel/code-frame" "^7.22.13"
+    "@babel/parser" "^7.22.15"
+    "@babel/types" "^7.22.15"
+
+"@babel/traverse@^7.23.6":
+  version "7.23.6"
+  resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.6.tgz#b53526a2367a0dd6edc423637f3d2d0f2521abc5"
+  integrity sha512-czastdK1e8YByZqezMPFiZ8ahwVMh/ESl9vPgvgdB9AmFMGP5jfpFax74AQgl5zj4XHzqeYAg2l8PuUeRS1MgQ==
+  dependencies:
+    "@babel/code-frame" "^7.23.5"
+    "@babel/generator" "^7.23.6"
+    "@babel/helper-environment-visitor" "^7.22.20"
+    "@babel/helper-function-name" "^7.23.0"
+    "@babel/helper-hoist-variables" "^7.22.5"
+    "@babel/helper-split-export-declaration" "^7.22.6"
+    "@babel/parser" "^7.23.6"
+    "@babel/types" "^7.23.6"
+    debug "^4.3.1"
+    globals "^11.1.0"
+
+"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.6", "@babel/types@^7.3.3":
+  version "7.23.6"
+  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.6.tgz#be33fdb151e1f5a56877d704492c240fc71c7ccd"
+  integrity sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==
+  dependencies:
+    "@babel/helper-string-parser" "^7.23.4"
+    "@babel/helper-validator-identifier" "^7.22.20"
+    to-fast-properties "^2.0.0"
+
+"@bcoe/v8-coverage@^0.2.3":
+  version "0.2.3"
+  resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
+  integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
+
+"@colors/colors@1.5.0":
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
+  integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==
+
+"@cspotcode/source-map-consumer@0.8.0":
+  version "0.8.0"
+  resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b"
+  integrity sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==
+
+"@cspotcode/source-map-support@0.7.0":
+  version "0.7.0"
+  resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz#4789840aa859e46d2f3173727ab707c66bf344f5"
+  integrity sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==
+  dependencies:
+    "@cspotcode/source-map-consumer" "0.8.0"
+
+"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0":
+  version "4.4.0"
+  resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59"
+  integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==
+  dependencies:
+    eslint-visitor-keys "^3.3.0"
+
+"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.12.1":
+  version "4.12.1"
+  resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0"
+  integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==
+
+"@eslint/config-array@^0.19.0":
+  version "0.19.2"
+  resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.19.2.tgz#3060b809e111abfc97adb0bb1172778b90cb46aa"
+  integrity sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==
+  dependencies:
+    "@eslint/object-schema" "^2.1.6"
+    debug "^4.3.1"
+    minimatch "^3.1.2"
+
+"@eslint/core@^0.10.0":
+  version "0.10.0"
+  resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.10.0.tgz#23727063c21b335f752dbb3a16450f6f9cbc9091"
+  integrity sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==
+  dependencies:
+    "@types/json-schema" "^7.0.15"
+
+"@eslint/core@^0.11.0":
+  version "0.11.0"
+  resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.11.0.tgz#7a9226e850922e42cbd2ba71361eacbe74352a12"
+  integrity sha512-DWUB2pksgNEb6Bz2fggIy1wh6fGgZP4Xyy/Mt0QZPiloKKXerbqq9D3SBQTlCRYOrcRPu4vuz+CGjwdfqxnoWA==
+  dependencies:
+    "@types/json-schema" "^7.0.15"
+
+"@eslint/eslintrc@^3.2.0":
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.2.0.tgz#57470ac4e2e283a6bf76044d63281196e370542c"
+  integrity sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==
+  dependencies:
+    ajv "^6.12.4"
+    debug "^4.3.2"
+    espree "^10.0.1"
+    globals "^14.0.0"
+    ignore "^5.2.0"
+    import-fresh "^3.2.1"
+    js-yaml "^4.1.0"
+    minimatch "^3.1.2"
+    strip-json-comments "^3.1.1"
+
+"@eslint/js@9.20.0":
+  version "9.20.0"
+  resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.20.0.tgz#7421bcbe74889fcd65d1be59f00130c289856eb4"
+  integrity sha512-iZA07H9io9Wn836aVTytRaNqh00Sad+EamwOVJT12GTLw1VGMFV/4JaME+JjLtr9fiGaoWgYnS54wrfWsSs4oQ==
+
+"@eslint/object-schema@^2.1.6":
+  version "2.1.6"
+  resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.6.tgz#58369ab5b5b3ca117880c0f6c0b0f32f6950f24f"
+  integrity sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==
+
+"@eslint/plugin-kit@^0.2.5":
+  version "0.2.5"
+  resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz#ee07372035539e7847ef834e3f5e7b79f09e3a81"
+  integrity sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==
+  dependencies:
+    "@eslint/core" "^0.10.0"
+    levn "^0.4.1"
+
+"@humanfs/core@^0.19.1":
+  version "0.19.1"
+  resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.1.tgz#17c55ca7d426733fe3c561906b8173c336b40a77"
+  integrity sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==
+
+"@humanfs/node@^0.16.6":
+  version "0.16.6"
+  resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.6.tgz#ee2a10eaabd1131987bf0488fd9b820174cd765e"
+  integrity sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==
+  dependencies:
+    "@humanfs/core" "^0.19.1"
+    "@humanwhocodes/retry" "^0.3.0"
+
+"@humanwhocodes/module-importer@^1.0.1":
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c"
+  integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==
+
+"@humanwhocodes/retry@^0.3.0":
+  version "0.3.1"
+  resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.1.tgz#c72a5c76a9fbaf3488e231b13dc52c0da7bab42a"
+  integrity sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==
+
+"@humanwhocodes/retry@^0.4.1":
+  version "0.4.1"
+  resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.1.tgz#9a96ce501bc62df46c4031fbd970e3cc6b10f07b"
+  integrity sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==
+
+"@istanbuljs/load-nyc-config@^1.0.0":
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
+  integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==
+  dependencies:
+    camelcase "^5.3.1"
+    find-up "^4.1.0"
+    get-package-type "^0.1.0"
+    js-yaml "^3.13.1"
+    resolve-from "^5.0.0"
+
+"@istanbuljs/schema@^0.1.2":
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98"
+  integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==
+
+"@jest/console@^29.7.0":
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.7.0.tgz#cd4822dbdb84529265c5a2bdb529a3c9cc950ffc"
+  integrity sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==
+  dependencies:
+    "@jest/types" "^29.6.3"
+    "@types/node" "*"
+    chalk "^4.0.0"
+    jest-message-util "^29.7.0"
+    jest-util "^29.7.0"
+    slash "^3.0.0"
+
+"@jest/core@^29.7.0":
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.7.0.tgz#b6cccc239f30ff36609658c5a5e2291757ce448f"
+  integrity sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==
+  dependencies:
+    "@jest/console" "^29.7.0"
+    "@jest/reporters" "^29.7.0"
+    "@jest/test-result" "^29.7.0"
+    "@jest/transform" "^29.7.0"
+    "@jest/types" "^29.6.3"
+    "@types/node" "*"
+    ansi-escapes "^4.2.1"
+    chalk "^4.0.0"
+    ci-info "^3.2.0"
+    exit "^0.1.2"
+    graceful-fs "^4.2.9"
+    jest-changed-files "^29.7.0"
+    jest-config "^29.7.0"
+    jest-haste-map "^29.7.0"
+    jest-message-util "^29.7.0"
+    jest-regex-util "^29.6.3"
+    jest-resolve "^29.7.0"
+    jest-resolve-dependencies "^29.7.0"
+    jest-runner "^29.7.0"
+    jest-runtime "^29.7.0"
+    jest-snapshot "^29.7.0"
+    jest-util "^29.7.0"
+    jest-validate "^29.7.0"
+    jest-watcher "^29.7.0"
+    micromatch "^4.0.4"
+    pretty-format "^29.7.0"
+    slash "^3.0.0"
+    strip-ansi "^6.0.0"
+
+"@jest/create-cache-key-function@^29.7.0":
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz#793be38148fab78e65f40ae30c36785f4ad859f0"
+  integrity sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==
+  dependencies:
+    "@jest/types" "^29.6.3"
+
+"@jest/environment@^29.7.0":
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.7.0.tgz#24d61f54ff1f786f3cd4073b4b94416383baf2a7"
+  integrity sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==
+  dependencies:
+    "@jest/fake-timers" "^29.7.0"
+    "@jest/types" "^29.6.3"
+    "@types/node" "*"
+    jest-mock "^29.7.0"
+
+"@jest/expect-utils@^29.7.0":
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.7.0.tgz#023efe5d26a8a70f21677d0a1afc0f0a44e3a1c6"
+  integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==
+  dependencies:
+    jest-get-type "^29.6.3"
+
+"@jest/expect@^29.7.0":
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.7.0.tgz#76a3edb0cb753b70dfbfe23283510d3d45432bf2"
+  integrity sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==
+  dependencies:
+    expect "^29.7.0"
+    jest-snapshot "^29.7.0"
+
+"@jest/fake-timers@^29.7.0":
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.7.0.tgz#fd91bf1fffb16d7d0d24a426ab1a47a49881a565"
+  integrity sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==
+  dependencies:
+    "@jest/types" "^29.6.3"
+    "@sinonjs/fake-timers" "^10.0.2"
+    "@types/node" "*"
+    jest-message-util "^29.7.0"
+    jest-mock "^29.7.0"
+    jest-util "^29.7.0"
+
+"@jest/globals@^29.7.0":
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.7.0.tgz#8d9290f9ec47ff772607fa864ca1d5a2efae1d4d"
+  integrity sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==
+  dependencies:
+    "@jest/environment" "^29.7.0"
+    "@jest/expect" "^29.7.0"
+    "@jest/types" "^29.6.3"
+    jest-mock "^29.7.0"
+
+"@jest/reporters@^29.7.0":
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.7.0.tgz#04b262ecb3b8faa83b0b3d321623972393e8f4c7"
+  integrity sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==
+  dependencies:
+    "@bcoe/v8-coverage" "^0.2.3"
+    "@jest/console" "^29.7.0"
+    "@jest/test-result" "^29.7.0"
+    "@jest/transform" "^29.7.0"
+    "@jest/types" "^29.6.3"
+    "@jridgewell/trace-mapping" "^0.3.18"
+    "@types/node" "*"
+    chalk "^4.0.0"
+    collect-v8-coverage "^1.0.0"
+    exit "^0.1.2"
+    glob "^7.1.3"
+    graceful-fs "^4.2.9"
+    istanbul-lib-coverage "^3.0.0"
+    istanbul-lib-instrument "^6.0.0"
+    istanbul-lib-report "^3.0.0"
+    istanbul-lib-source-maps "^4.0.0"
+    istanbul-reports "^3.1.3"
+    jest-message-util "^29.7.0"
+    jest-util "^29.7.0"
+    jest-worker "^29.7.0"
+    slash "^3.0.0"
+    string-length "^4.0.1"
+    strip-ansi "^6.0.0"
+    v8-to-istanbul "^9.0.1"
+
+"@jest/schemas@^29.6.3":
+  version "29.6.3"
+  resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03"
+  integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==
+  dependencies:
+    "@sinclair/typebox" "^0.27.8"
+
+"@jest/source-map@^29.6.3":
+  version "29.6.3"
+  resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4"
+  integrity sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==
+  dependencies:
+    "@jridgewell/trace-mapping" "^0.3.18"
+    callsites "^3.0.0"
+    graceful-fs "^4.2.9"
+
+"@jest/test-result@^29.7.0":
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.7.0.tgz#8db9a80aa1a097bb2262572686734baed9b1657c"
+  integrity sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==
+  dependencies:
+    "@jest/console" "^29.7.0"
+    "@jest/types" "^29.6.3"
+    "@types/istanbul-lib-coverage" "^2.0.0"
+    collect-v8-coverage "^1.0.0"
+
+"@jest/test-sequencer@^29.7.0":
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz#6cef977ce1d39834a3aea887a1726628a6f072ce"
+  integrity sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==
+  dependencies:
+    "@jest/test-result" "^29.7.0"
+    graceful-fs "^4.2.9"
+    jest-haste-map "^29.7.0"
+    slash "^3.0.0"
+
+"@jest/transform@^29.7.0":
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.7.0.tgz#df2dd9c346c7d7768b8a06639994640c642e284c"
+  integrity sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==
+  dependencies:
+    "@babel/core" "^7.11.6"
+    "@jest/types" "^29.6.3"
+    "@jridgewell/trace-mapping" "^0.3.18"
+    babel-plugin-istanbul "^6.1.1"
+    chalk "^4.0.0"
+    convert-source-map "^2.0.0"
+    fast-json-stable-stringify "^2.1.0"
+    graceful-fs "^4.2.9"
+    jest-haste-map "^29.7.0"
+    jest-regex-util "^29.6.3"
+    jest-util "^29.7.0"
+    micromatch "^4.0.4"
+    pirates "^4.0.4"
+    slash "^3.0.0"
+    write-file-atomic "^4.0.2"
+
+"@jest/types@^29.6.3":
+  version "29.6.3"
+  resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59"
+  integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==
+  dependencies:
+    "@jest/schemas" "^29.6.3"
+    "@types/istanbul-lib-coverage" "^2.0.0"
+    "@types/istanbul-reports" "^3.0.0"
+    "@types/node" "*"
+    "@types/yargs" "^17.0.8"
+    chalk "^4.0.0"
+
+"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2":
+  version "0.3.3"
+  resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098"
+  integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==
+  dependencies:
+    "@jridgewell/set-array" "^1.0.1"
+    "@jridgewell/sourcemap-codec" "^1.4.10"
+    "@jridgewell/trace-mapping" "^0.3.9"
+
+"@jridgewell/resolve-uri@^3.1.0":
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721"
+  integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==
+
+"@jridgewell/set-array@^1.0.1":
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
+  integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
+
+"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14":
+  version "1.4.15"
+  resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
+  integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
+
+"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.9":
+  version "0.3.20"
+  resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz#72e45707cf240fa6b081d0366f8265b0cd10197f"
+  integrity sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==
+  dependencies:
+    "@jridgewell/resolve-uri" "^3.1.0"
+    "@jridgewell/sourcemap-codec" "^1.4.14"
+
+"@nodelib/fs.scandir@2.1.5":
+  version "2.1.5"
+  resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
+  integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==
+  dependencies:
+    "@nodelib/fs.stat" "2.0.5"
+    run-parallel "^1.1.9"
+
+"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b"
+  integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
+
+"@nodelib/fs.walk@^1.2.3":
+  version "1.2.8"
+  resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a"
+  integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==
+  dependencies:
+    "@nodelib/fs.scandir" "2.1.5"
+    fastq "^1.6.0"
+
+"@pkgr/core@^0.1.0":
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31"
+  integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==
+
+"@sinclair/typebox@^0.27.8":
+  version "0.27.8"
+  resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e"
+  integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==
+
+"@sindresorhus/is@^4.6.0":
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f"
+  integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==
+
+"@sinonjs/commons@^3.0.0":
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-3.0.0.tgz#beb434fe875d965265e04722ccfc21df7f755d72"
+  integrity sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==
+  dependencies:
+    type-detect "4.0.8"
+
+"@sinonjs/fake-timers@^10.0.2":
+  version "10.3.0"
+  resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz#55fdff1ecab9f354019129daf4df0dd4d923ea66"
+  integrity sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==
+  dependencies:
+    "@sinonjs/commons" "^3.0.0"
+
+"@swc/core-darwin-arm64@1.4.16":
+  version "1.4.16"
+  resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.4.16.tgz#2cd45d709ce76d448d96bf8d0006849541436611"
+  integrity sha512-UOCcH1GvjRnnM/LWT6VCGpIk0OhHRq6v1U6QXuPt5wVsgXnXQwnf5k3sG5Cm56hQHDvhRPY6HCsHi/p0oek8oQ==
+
+"@swc/core-darwin-x64@1.4.16":
+  version "1.4.16"
+  resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.4.16.tgz#a5bc7d8b1dd850adb0bb95c6b5c742b92201fd01"
+  integrity sha512-t3bgqFoYLWvyVtVL6KkFNCINEoOrIlyggT/kJRgi1y0aXSr0oVgcrQ4ezJpdeahZZ4N+Q6vT3ffM30yIunELNA==
+
+"@swc/core-linux-arm-gnueabihf@1.4.16":
+  version "1.4.16"
+  resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.4.16.tgz#961744908ee5cbb79bc009dcf58cc8b831111f38"
+  integrity sha512-DvHuwvEF86YvSd0lwnzVcjOTZ0jcxewIbsN0vc/0fqm9qBdMMjr9ox6VCam1n3yYeRtj4VFgrjeNFksqbUejdQ==
+
+"@swc/core-linux-arm64-gnu@1.4.16":
+  version "1.4.16"
+  resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.4.16.tgz#43713be3f26757d82d2745dc25f8b63400e0a3d0"
+  integrity sha512-9Uu5YlPbyCvbidjKtYEsPpyZlu16roOZ5c2tP1vHfnU9bgf5Tz5q5VovSduNxPHx+ed2iC1b1URODHvDzbbDuQ==
+
+"@swc/core-linux-arm64-musl@1.4.16":
+  version "1.4.16"
+  resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.4.16.tgz#394a7d030f3a61902bd3947bb9d70d26d42f3c81"
+  integrity sha512-/YZq/qB1CHpeoL0eMzyqK5/tYZn/rzKoCYDviFU4uduSUIJsDJQuQA/skdqUzqbheOXKAd4mnJ1hT04RbJ8FPQ==
+
+"@swc/core-linux-x64-gnu@1.4.16":
+  version "1.4.16"
+  resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.4.16.tgz#71eb108b784f9d551ee8a35ebcdaed972f567981"
+  integrity sha512-UUjaW5VTngZYDcA8yQlrFmqs1tLi1TxbKlnaJwoNhel9zRQ0yG1YEVGrzTvv4YApSuIiDK18t+Ip927bwucuVQ==
+
+"@swc/core-linux-x64-musl@1.4.16":
+  version "1.4.16"
+  resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.4.16.tgz#10dbaedb4e3dfc7268e3a9a66ad3431471ef035b"
+  integrity sha512-aFhxPifevDTwEDKPi4eRYWzC0p/WYJeiFkkpNU5Uc7a7M5iMWPAbPFUbHesdlb9Jfqs5c07oyz86u+/HySBNPQ==
+
+"@swc/core-win32-arm64-msvc@1.4.16":
+  version "1.4.16"
+  resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.4.16.tgz#80247adff6c245ff32b44d773c1a148858cd655f"
+  integrity sha512-bTD43MbhIHL2s5QgCwyleaGwl96Gk/scF2TaVKdUe4QlJCDV/YK9h5oIBAp63ckHtE8GHlH4c8dZNBiAXn4Org==
+
+"@swc/core-win32-ia32-msvc@1.4.16":
+  version "1.4.16"
+  resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.4.16.tgz#e540afc3ccf3224267b4ddfb408f9d9737984686"
+  integrity sha512-/lmZeAN/qV5XbK2SEvi8e2RkIg8FQNYiSA8y2/Zb4gTUMKVO5JMLH0BSWMiIKMstKDPDSxMWgwJaQHF8UMyPmQ==
+
+"@swc/core-win32-x64-msvc@1.4.16":
+  version "1.4.16"
+  resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.4.16.tgz#f880939fca32c181adfe7e3abd2b6b7857bd3489"
+  integrity sha512-BPAfFfODWXtUu6SwaTTftDHvcbDyWBSI/oanUeRbQR5vVWkXoQ3cxLTsDluc3H74IqXS5z1Uyoe0vNo2hB1opA==
+
+"@swc/core@^1.3.102":
+  version "1.4.16"
+  resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.4.16.tgz#d175bae2acfecd53bcbd4293f1fba5ec316634a0"
+  integrity sha512-Xaf+UBvW6JNuV131uvSNyMXHn+bh6LyKN4tbv7tOUFQpXyz/t9YWRE04emtlUW9Y0qrm/GKFCbY8n3z6BpZbTA==
+  dependencies:
+    "@swc/counter" "^0.1.2"
+    "@swc/types" "^0.1.5"
+  optionalDependencies:
+    "@swc/core-darwin-arm64" "1.4.16"
+    "@swc/core-darwin-x64" "1.4.16"
+    "@swc/core-linux-arm-gnueabihf" "1.4.16"
+    "@swc/core-linux-arm64-gnu" "1.4.16"
+    "@swc/core-linux-arm64-musl" "1.4.16"
+    "@swc/core-linux-x64-gnu" "1.4.16"
+    "@swc/core-linux-x64-musl" "1.4.16"
+    "@swc/core-win32-arm64-msvc" "1.4.16"
+    "@swc/core-win32-ia32-msvc" "1.4.16"
+    "@swc/core-win32-x64-msvc" "1.4.16"
+
+"@swc/counter@^0.1.2", "@swc/counter@^0.1.3":
+  version "0.1.3"
+  resolved "https://registry.yarnpkg.com/@swc/counter/-/counter-0.1.3.tgz#cc7463bd02949611c6329596fccd2b0ec782b0e9"
+  integrity sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==
+
+"@swc/jest@^0.2.29":
+  version "0.2.36"
+  resolved "https://registry.yarnpkg.com/@swc/jest/-/jest-0.2.36.tgz#2797450a30d28b471997a17e901ccad946fe693e"
+  integrity sha512-8X80dp81ugxs4a11z1ka43FPhP+/e+mJNXJSxiNYk8gIX/jPBtY4gQTrKu/KIoco8bzKuPI5lUxjfLiGsfvnlw==
+  dependencies:
+    "@jest/create-cache-key-function" "^29.7.0"
+    "@swc/counter" "^0.1.3"
+    jsonc-parser "^3.2.0"
+
+"@swc/types@^0.1.5":
+  version "0.1.6"
+  resolved "https://registry.yarnpkg.com/@swc/types/-/types-0.1.6.tgz#2f13f748995b247d146de2784d3eb7195410faba"
+  integrity sha512-/JLo/l2JsT/LRd80C3HfbmVpxOAJ11FO2RCEslFrgzLltoP9j8XIbsyDcfCt2WWyX+CM96rBoNM+IToAkFOugg==
+  dependencies:
+    "@swc/counter" "^0.1.3"
+
+"@tsconfig/node10@^1.0.7":
+  version "1.0.8"
+  resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9"
+  integrity sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==
+
+"@tsconfig/node12@^1.0.7":
+  version "1.0.9"
+  resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.9.tgz#62c1f6dee2ebd9aead80dc3afa56810e58e1a04c"
+  integrity sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==
+
+"@tsconfig/node14@^1.0.0":
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.1.tgz#95f2d167ffb9b8d2068b0b235302fafd4df711f2"
+  integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==
+
+"@tsconfig/node16@^1.0.2":
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e"
+  integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==
+
+"@types/babel__core@^7.1.14":
+  version "7.20.5"
+  resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017"
+  integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==
+  dependencies:
+    "@babel/parser" "^7.20.7"
+    "@babel/types" "^7.20.7"
+    "@types/babel__generator" "*"
+    "@types/babel__template" "*"
+    "@types/babel__traverse" "*"
+
+"@types/babel__generator@*":
+  version "7.6.8"
+  resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.8.tgz#f836c61f48b1346e7d2b0d93c6dacc5b9535d3ab"
+  integrity sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==
+  dependencies:
+    "@babel/types" "^7.0.0"
+
+"@types/babel__template@*":
+  version "7.4.4"
+  resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.4.tgz#5672513701c1b2199bc6dad636a9d7491586766f"
+  integrity sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==
+  dependencies:
+    "@babel/parser" "^7.1.0"
+    "@babel/types" "^7.0.0"
+
+"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6":
+  version "7.20.4"
+  resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.20.4.tgz#ec2c06fed6549df8bc0eb4615b683749a4a92e1b"
+  integrity sha512-mSM/iKUk5fDDrEV/e83qY+Cr3I1+Q3qqTuEn++HAWYjEa1+NxZr6CNrcJGf2ZTnq4HoFGC3zaTPZTobCzCFukA==
+  dependencies:
+    "@babel/types" "^7.20.7"
+
+"@types/estree@^1.0.6":
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50"
+  integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==
+
+"@types/graceful-fs@^4.1.3":
+  version "4.1.9"
+  resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4"
+  integrity sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==
+  dependencies:
+    "@types/node" "*"
+
+"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1":
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz#7739c232a1fee9b4d3ce8985f314c0c6d33549d7"
+  integrity sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==
+
+"@types/istanbul-lib-report@*":
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz#53047614ae72e19fc0401d872de3ae2b4ce350bf"
+  integrity sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==
+  dependencies:
+    "@types/istanbul-lib-coverage" "*"
+
+"@types/istanbul-reports@^3.0.0":
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz#0f03e3d2f670fbdac586e34b433783070cc16f54"
+  integrity sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==
+  dependencies:
+    "@types/istanbul-lib-report" "*"
+
+"@types/jest@^29.4.0":
+  version "29.5.11"
+  resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.5.11.tgz#0c13aa0da7d0929f078ab080ae5d4ced80fa2f2c"
+  integrity sha512-S2mHmYIVe13vrm6q4kN6fLYYAka15ALQki/vgDC3mIukEOx8WJlv0kQPM+d4w8Gp6u0uSdKND04IlTXBv0rwnQ==
+  dependencies:
+    expect "^29.0.0"
+    pretty-format "^29.0.0"
+
+"@types/json-schema@^7.0.15":
+  version "7.0.15"
+  resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
+  integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
+
+"@types/node@*":
+  version "20.10.5"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-20.10.5.tgz#47ad460b514096b7ed63a1dae26fad0914ed3ab2"
+  integrity sha512-nNPsNE65wjMxEKI93yOP+NPGGBJz/PoN3kZsVLee0XMiJolxSekEVD8wRwBUBqkwc7UWop0edW50yrCQW4CyRw==
+  dependencies:
+    undici-types "~5.26.4"
+
+"@types/node@^20.17.6":
+  version "20.17.6"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-20.17.6.tgz#6e4073230c180d3579e8c60141f99efdf5df0081"
+  integrity sha512-VEI7OdvK2wP7XHnsuXbAJnEpEkF6NjSN45QJlL4VGqZSXsnicpesdTWsg9RISeSdYd3yeRj/y3k5KGjUXYnFwQ==
+  dependencies:
+    undici-types "~6.19.2"
+
+"@types/stack-utils@^2.0.0":
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8"
+  integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==
+
+"@types/yargs-parser@*":
+  version "21.0.3"
+  resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15"
+  integrity sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==
+
+"@types/yargs@^17.0.8":
+  version "17.0.32"
+  resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.32.tgz#030774723a2f7faafebf645f4e5a48371dca6229"
+  integrity sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==
+  dependencies:
+    "@types/yargs-parser" "*"
+
+"@typescript-eslint/eslint-plugin@8.31.1":
+  version "8.31.1"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.31.1.tgz#62f1befe59647524994e89de4516d8dcba7a850a"
+  integrity sha512-oUlH4h1ABavI4F0Xnl8/fOtML/eu8nI2A1nYd+f+55XI0BLu+RIqKoCiZKNo6DtqZBEQm5aNKA20G3Z5w3R6GQ==
+  dependencies:
+    "@eslint-community/regexpp" "^4.10.0"
+    "@typescript-eslint/scope-manager" "8.31.1"
+    "@typescript-eslint/type-utils" "8.31.1"
+    "@typescript-eslint/utils" "8.31.1"
+    "@typescript-eslint/visitor-keys" "8.31.1"
+    graphemer "^1.4.0"
+    ignore "^5.3.1"
+    natural-compare "^1.4.0"
+    ts-api-utils "^2.0.1"
+
+"@typescript-eslint/parser@8.31.1":
+  version "8.31.1"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.31.1.tgz#e9b0ccf30d37dde724ee4d15f4dbc195995cce1b"
+  integrity sha512-oU/OtYVydhXnumd0BobL9rkJg7wFJ9bFFPmSmB/bf/XWN85hlViji59ko6bSKBXyseT9V8l+CN1nwmlbiN0G7Q==
+  dependencies:
+    "@typescript-eslint/scope-manager" "8.31.1"
+    "@typescript-eslint/types" "8.31.1"
+    "@typescript-eslint/typescript-estree" "8.31.1"
+    "@typescript-eslint/visitor-keys" "8.31.1"
+    debug "^4.3.4"
+
+"@typescript-eslint/scope-manager@8.31.1":
+  version "8.31.1"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.31.1.tgz#1eb52e76878f545e4add142e0d8e3e97e7aa443b"
+  integrity sha512-BMNLOElPxrtNQMIsFHE+3P0Yf1z0dJqV9zLdDxN/xLlWMlXK/ApEsVEKzpizg9oal8bAT5Sc7+ocal7AC1HCVw==
+  dependencies:
+    "@typescript-eslint/types" "8.31.1"
+    "@typescript-eslint/visitor-keys" "8.31.1"
+
+"@typescript-eslint/type-utils@8.31.1":
+  version "8.31.1"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.31.1.tgz#be0f438fb24b03568e282a0aed85f776409f970c"
+  integrity sha512-fNaT/m9n0+dpSp8G/iOQ05GoHYXbxw81x+yvr7TArTuZuCA6VVKbqWYVZrV5dVagpDTtj/O8k5HBEE/p/HM5LA==
+  dependencies:
+    "@typescript-eslint/typescript-estree" "8.31.1"
+    "@typescript-eslint/utils" "8.31.1"
+    debug "^4.3.4"
+    ts-api-utils "^2.0.1"
+
+"@typescript-eslint/types@8.31.1":
+  version "8.31.1"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.31.1.tgz#478ed6f7e8aee1be7b63a60212b6bffe1423b5d4"
+  integrity sha512-SfepaEFUDQYRoA70DD9GtytljBePSj17qPxFHA/h3eg6lPTqGJ5mWOtbXCk1YrVU1cTJRd14nhaXWFu0l2troQ==
+
+"@typescript-eslint/typescript-estree@8.31.1":
+  version "8.31.1"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.31.1.tgz#37792fe7ef4d3021c7580067c8f1ae66daabacdf"
+  integrity sha512-kaA0ueLe2v7KunYOyWYtlf/QhhZb7+qh4Yw6Ni5kgukMIG+iP773tjgBiLWIXYumWCwEq3nLW+TUywEp8uEeag==
+  dependencies:
+    "@typescript-eslint/types" "8.31.1"
+    "@typescript-eslint/visitor-keys" "8.31.1"
+    debug "^4.3.4"
+    fast-glob "^3.3.2"
+    is-glob "^4.0.3"
+    minimatch "^9.0.4"
+    semver "^7.6.0"
+    ts-api-utils "^2.0.1"
+
+"@typescript-eslint/utils@8.31.1":
+  version "8.31.1"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.31.1.tgz#5628ea0393598a0b2f143d0fc6d019f0dee9dd14"
+  integrity sha512-2DSI4SNfF5T4oRveQ4nUrSjUqjMND0nLq9rEkz0gfGr3tg0S5KB6DhwR+WZPCjzkZl3cH+4x2ce3EsL50FubjQ==
+  dependencies:
+    "@eslint-community/eslint-utils" "^4.4.0"
+    "@typescript-eslint/scope-manager" "8.31.1"
+    "@typescript-eslint/types" "8.31.1"
+    "@typescript-eslint/typescript-estree" "8.31.1"
+
+"@typescript-eslint/visitor-keys@8.31.1":
+  version "8.31.1"
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.31.1.tgz#6742b0e3ba1e0c1e35bdaf78c03e759eb8dd8e75"
+  integrity sha512-I+/rgqOVBn6f0o7NDTmAPWWC6NuqhV174lfYvAm9fUaWeiefLdux9/YI3/nLugEn9L8fcSi0XmpKi/r5u0nmpw==
+  dependencies:
+    "@typescript-eslint/types" "8.31.1"
+    eslint-visitor-keys "^4.2.0"
+
+acorn-jsx@^5.3.2:
+  version "5.3.2"
+  resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
+  integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
+
+acorn-walk@^8.1.1:
+  version "8.2.0"
+  resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
+  integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
+
+acorn@^8.14.0:
+  version "8.14.0"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0"
+  integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==
+
+acorn@^8.4.1:
+  version "8.7.0"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf"
+  integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==
+
+aggregate-error@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a"
+  integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==
+  dependencies:
+    clean-stack "^2.0.0"
+    indent-string "^4.0.0"
+
+ajv@^6.12.4:
+  version "6.12.6"
+  resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
+  integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
+  dependencies:
+    fast-deep-equal "^3.1.1"
+    fast-json-stable-stringify "^2.0.0"
+    json-schema-traverse "^0.4.1"
+    uri-js "^4.2.2"
+
+ansi-escapes@^4.2.1:
+  version "4.3.2"
+  resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e"
+  integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==
+  dependencies:
+    type-fest "^0.21.3"
+
+ansi-escapes@^7.0.0:
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-7.0.0.tgz#00fc19f491bbb18e1d481b97868204f92109bfe7"
+  integrity sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==
+  dependencies:
+    environment "^1.0.0"
+
+ansi-regex@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
+  integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
+
+ansi-regex@^6.1.0:
+  version "6.1.0"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654"
+  integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==
+
+ansi-styles@^3.2.1:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
+  integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
+  dependencies:
+    color-convert "^1.9.0"
+
+ansi-styles@^4.0.0, ansi-styles@^4.1.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
+  integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
+  dependencies:
+    color-convert "^2.0.1"
+
+ansi-styles@^5.0.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b"
+  integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==
+
+any-promise@^1.0.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
+  integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==
+
+anymatch@^3.0.3:
+  version "3.1.3"
+  resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e"
+  integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==
+  dependencies:
+    normalize-path "^3.0.0"
+    picomatch "^2.0.4"
+
+arg@^4.1.0:
+  version "4.1.3"
+  resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089"
+  integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==
+
+argparse@^1.0.7:
+  version "1.0.10"
+  resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
+  integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==
+  dependencies:
+    sprintf-js "~1.0.2"
+
+argparse@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
+  integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
+
+babel-jest@^29.7.0:
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5"
+  integrity sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==
+  dependencies:
+    "@jest/transform" "^29.7.0"
+    "@types/babel__core" "^7.1.14"
+    babel-plugin-istanbul "^6.1.1"
+    babel-preset-jest "^29.6.3"
+    chalk "^4.0.0"
+    graceful-fs "^4.2.9"
+    slash "^3.0.0"
+
+babel-plugin-istanbul@^6.1.1:
+  version "6.1.1"
+  resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73"
+  integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@istanbuljs/load-nyc-config" "^1.0.0"
+    "@istanbuljs/schema" "^0.1.2"
+    istanbul-lib-instrument "^5.0.4"
+    test-exclude "^6.0.0"
+
+babel-plugin-jest-hoist@^29.6.3:
+  version "29.6.3"
+  resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz#aadbe943464182a8922c3c927c3067ff40d24626"
+  integrity sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==
+  dependencies:
+    "@babel/template" "^7.3.3"
+    "@babel/types" "^7.3.3"
+    "@types/babel__core" "^7.1.14"
+    "@types/babel__traverse" "^7.0.6"
+
+babel-preset-current-node-syntax@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b"
+  integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==
+  dependencies:
+    "@babel/plugin-syntax-async-generators" "^7.8.4"
+    "@babel/plugin-syntax-bigint" "^7.8.3"
+    "@babel/plugin-syntax-class-properties" "^7.8.3"
+    "@babel/plugin-syntax-import-meta" "^7.8.3"
+    "@babel/plugin-syntax-json-strings" "^7.8.3"
+    "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3"
+    "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3"
+    "@babel/plugin-syntax-numeric-separator" "^7.8.3"
+    "@babel/plugin-syntax-object-rest-spread" "^7.8.3"
+    "@babel/plugin-syntax-optional-catch-binding" "^7.8.3"
+    "@babel/plugin-syntax-optional-chaining" "^7.8.3"
+    "@babel/plugin-syntax-top-level-await" "^7.8.3"
+
+babel-preset-jest@^29.6.3:
+  version "29.6.3"
+  resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz#fa05fa510e7d493896d7b0dd2033601c840f171c"
+  integrity sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==
+  dependencies:
+    babel-plugin-jest-hoist "^29.6.3"
+    babel-preset-current-node-syntax "^1.0.0"
+
+balanced-match@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
+  integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
+
+brace-expansion@^1.1.7:
+  version "1.1.11"
+  resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+  integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+  dependencies:
+    balanced-match "^1.0.0"
+    concat-map "0.0.1"
+
+brace-expansion@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
+  integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
+  dependencies:
+    balanced-match "^1.0.0"
+
+braces@^3.0.3:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
+  integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
+  dependencies:
+    fill-range "^7.1.1"
+
+browserslist@^4.22.2:
+  version "4.22.2"
+  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.2.tgz#704c4943072bd81ea18997f3bd2180e89c77874b"
+  integrity sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==
+  dependencies:
+    caniuse-lite "^1.0.30001565"
+    electron-to-chromium "^1.4.601"
+    node-releases "^2.0.14"
+    update-browserslist-db "^1.0.13"
+
+bs-logger@0.x:
+  version "0.2.6"
+  resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8"
+  integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==
+  dependencies:
+    fast-json-stable-stringify "2.x"
+
+bser@2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05"
+  integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==
+  dependencies:
+    node-int64 "^0.4.0"
+
+buffer-from@^1.0.0:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
+  integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
+
+callsites@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
+  integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
+
+camelcase@^5.3.1:
+  version "5.3.1"
+  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
+  integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
+
+camelcase@^6.2.0:
+  version "6.3.0"
+  resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
+  integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
+
+caniuse-lite@^1.0.30001565:
+  version "1.0.30001570"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001570.tgz#b4e5c1fa786f733ab78fc70f592df6b3f23244ca"
+  integrity sha512-+3e0ASu4sw1SWaoCtvPeyXp+5PsjigkSt8OXZbF9StH5pQWbxEjLAZE3n8Aup5udop1uRiKA7a4utUk/uoSpUw==
+
+chalk@^2.4.2:
+  version "2.4.2"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
+  integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
+  dependencies:
+    ansi-styles "^3.2.1"
+    escape-string-regexp "^1.0.5"
+    supports-color "^5.3.0"
+
+chalk@^4.0.0, chalk@^4.1.2:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
+  integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
+  dependencies:
+    ansi-styles "^4.1.0"
+    supports-color "^7.1.0"
+
+chalk@^5.3.0:
+  version "5.3.0"
+  resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385"
+  integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==
+
+char-regex@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf"
+  integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==
+
+ci-info@^3.2.0:
+  version "3.9.0"
+  resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4"
+  integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==
+
+cjs-module-lexer@^1.0.0:
+  version "1.2.3"
+  resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107"
+  integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==
+
+cjs-module-lexer@^1.2.3:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz#707413784dbb3a72aa11c2f2b042a0bef4004170"
+  integrity sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==
+
+clean-stack@^2.0.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b"
+  integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==
+
+cli-highlight@^2.1.11:
+  version "2.1.11"
+  resolved "https://registry.yarnpkg.com/cli-highlight/-/cli-highlight-2.1.11.tgz#49736fa452f0aaf4fae580e30acb26828d2dc1bf"
+  integrity sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==
+  dependencies:
+    chalk "^4.0.0"
+    highlight.js "^10.7.1"
+    mz "^2.4.0"
+    parse5 "^5.1.1"
+    parse5-htmlparser2-tree-adapter "^6.0.0"
+    yargs "^16.0.0"
+
+cli-table3@^0.6.3, cli-table3@^0.6.5:
+  version "0.6.5"
+  resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.5.tgz#013b91351762739c16a9567c21a04632e449bf2f"
+  integrity sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==
+  dependencies:
+    string-width "^4.2.0"
+  optionalDependencies:
+    "@colors/colors" "1.5.0"
+
+cliui@^7.0.2:
+  version "7.0.4"
+  resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f"
+  integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==
+  dependencies:
+    string-width "^4.2.0"
+    strip-ansi "^6.0.0"
+    wrap-ansi "^7.0.0"
+
+cliui@^8.0.1:
+  version "8.0.1"
+  resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa"
+  integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==
+  dependencies:
+    string-width "^4.2.0"
+    strip-ansi "^6.0.1"
+    wrap-ansi "^7.0.0"
+
+co@^4.6.0:
+  version "4.6.0"
+  resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
+  integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==
+
+collect-v8-coverage@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz#c0b29bcd33bcd0779a1344c2136051e6afd3d9e9"
+  integrity sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==
+
+color-convert@^1.9.0:
+  version "1.9.3"
+  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
+  integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
+  dependencies:
+    color-name "1.1.3"
+
+color-convert@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
+  integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
+  dependencies:
+    color-name "~1.1.4"
+
+color-name@1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
+  integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
+
+color-name@~1.1.4:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+  integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
+commander@^10.0.1:
+  version "10.0.1"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06"
+  integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==
+
+concat-map@0.0.1:
+  version "0.0.1"
+  resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+  integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
+
+convert-source-map@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a"
+  integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==
+
+create-jest@^29.7.0:
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/create-jest/-/create-jest-29.7.0.tgz#a355c5b3cb1e1af02ba177fe7afd7feee49a5320"
+  integrity sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==
+  dependencies:
+    "@jest/types" "^29.6.3"
+    chalk "^4.0.0"
+    exit "^0.1.2"
+    graceful-fs "^4.2.9"
+    jest-config "^29.7.0"
+    jest-util "^29.7.0"
+    prompts "^2.0.1"
+
+create-require@^1.1.0:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
+  integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
+
+cross-spawn@^7.0.3, cross-spawn@^7.0.6:
+  version "7.0.6"
+  resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
+  integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==
+  dependencies:
+    path-key "^3.1.0"
+    shebang-command "^2.0.0"
+    which "^2.0.1"
+
+debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2:
+  version "4.3.4"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
+  integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
+  dependencies:
+    ms "2.1.2"
+
+debug@^4.3.4, debug@^4.3.7:
+  version "4.3.7"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52"
+  integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==
+  dependencies:
+    ms "^2.1.3"
+
+dedent@^1.0.0:
+  version "1.5.1"
+  resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.1.tgz#4f3fc94c8b711e9bb2800d185cd6ad20f2a90aff"
+  integrity sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==
+
+deep-is@^0.1.3:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
+  integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
+
+deepmerge@^4.2.2:
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a"
+  integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==
+
+detect-newline@^3.0.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651"
+  integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==
+
+diff-sequences@^29.6.3:
+  version "29.6.3"
+  resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921"
+  integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==
+
+diff@^4.0.1:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
+  integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
+
+electron-to-chromium@^1.4.601:
+  version "1.4.614"
+  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.614.tgz#2fe789d61fa09cb875569f37c309d0c2701f91c0"
+  integrity sha512-X4ze/9Sc3QWs6h92yerwqv7aB/uU8vCjZcrMjA8N9R1pjMFRe44dLsck5FzLilOYvcXuDn93B+bpGYyufc70gQ==
+
+emittery@^0.13.1:
+  version "0.13.1"
+  resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad"
+  integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==
+
+emoji-regex@^8.0.0:
+  version "8.0.0"
+  resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
+  integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
+
+emojilib@^2.4.0:
+  version "2.4.0"
+  resolved "https://registry.yarnpkg.com/emojilib/-/emojilib-2.4.0.tgz#ac518a8bb0d5f76dda57289ccb2fdf9d39ae721e"
+  integrity sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==
+
+environment@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/environment/-/environment-1.1.0.tgz#8e86c66b180f363c7ab311787e0259665f45a9f1"
+  integrity sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==
+
+error-ex@^1.3.1:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
+  integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==
+  dependencies:
+    is-arrayish "^0.2.1"
+
+escalade@^3.1.1:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
+  integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
+
+escape-string-regexp@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+  integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
+
+escape-string-regexp@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344"
+  integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==
+
+escape-string-regexp@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
+  integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
+
+eslint-plugin-prettier@^5.2.3:
+  version "5.2.3"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.3.tgz#c4af01691a6fa9905207f0fbba0d7bea0902cce5"
+  integrity sha512-qJ+y0FfCp/mQYQ/vWQ3s7eUlFEL4PyKfAJxsnYTJ4YT73nsJBWqmEpFryxV9OeUiqmsTsYJ5Y+KDNaeP31wrRw==
+  dependencies:
+    prettier-linter-helpers "^1.0.0"
+    synckit "^0.9.1"
+
+eslint-plugin-unused-imports@^4.1.4:
+  version "4.1.4"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.1.4.tgz#62ddc7446ccbf9aa7b6f1f0b00a980423cda2738"
+  integrity sha512-YptD6IzQjDardkl0POxnnRBhU1OEePMV0nd6siHaRBbd+lyh6NAhFEobiznKU7kTsSsDeSD62Pe7kAM1b7dAZQ==
+
+eslint-scope@^8.2.0:
+  version "8.2.0"
+  resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.2.0.tgz#377aa6f1cb5dc7592cfd0b7f892fd0cf352ce442"
+  integrity sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==
+  dependencies:
+    esrecurse "^4.3.0"
+    estraverse "^5.2.0"
+
+eslint-visitor-keys@^3.3.0:
+  version "3.4.3"
+  resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800"
+  integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
+
+eslint-visitor-keys@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45"
+  integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==
+
+eslint@^9.20.1:
+  version "9.20.1"
+  resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.20.1.tgz#923924c078f5226832449bac86662dd7e53c91d6"
+  integrity sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g==
+  dependencies:
+    "@eslint-community/eslint-utils" "^4.2.0"
+    "@eslint-community/regexpp" "^4.12.1"
+    "@eslint/config-array" "^0.19.0"
+    "@eslint/core" "^0.11.0"
+    "@eslint/eslintrc" "^3.2.0"
+    "@eslint/js" "9.20.0"
+    "@eslint/plugin-kit" "^0.2.5"
+    "@humanfs/node" "^0.16.6"
+    "@humanwhocodes/module-importer" "^1.0.1"
+    "@humanwhocodes/retry" "^0.4.1"
+    "@types/estree" "^1.0.6"
+    "@types/json-schema" "^7.0.15"
+    ajv "^6.12.4"
+    chalk "^4.0.0"
+    cross-spawn "^7.0.6"
+    debug "^4.3.2"
+    escape-string-regexp "^4.0.0"
+    eslint-scope "^8.2.0"
+    eslint-visitor-keys "^4.2.0"
+    espree "^10.3.0"
+    esquery "^1.5.0"
+    esutils "^2.0.2"
+    fast-deep-equal "^3.1.3"
+    file-entry-cache "^8.0.0"
+    find-up "^5.0.0"
+    glob-parent "^6.0.2"
+    ignore "^5.2.0"
+    imurmurhash "^0.1.4"
+    is-glob "^4.0.0"
+    json-stable-stringify-without-jsonify "^1.0.1"
+    lodash.merge "^4.6.2"
+    minimatch "^3.1.2"
+    natural-compare "^1.4.0"
+    optionator "^0.9.3"
+
+espree@^10.0.1, espree@^10.3.0:
+  version "10.3.0"
+  resolved "https://registry.yarnpkg.com/espree/-/espree-10.3.0.tgz#29267cf5b0cb98735b65e64ba07e0ed49d1eed8a"
+  integrity sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==
+  dependencies:
+    acorn "^8.14.0"
+    acorn-jsx "^5.3.2"
+    eslint-visitor-keys "^4.2.0"
+
+esprima@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
+  integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
+
+esquery@^1.5.0:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7"
+  integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==
+  dependencies:
+    estraverse "^5.1.0"
+
+esrecurse@^4.3.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921"
+  integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==
+  dependencies:
+    estraverse "^5.2.0"
+
+estraverse@^5.1.0, estraverse@^5.2.0:
+  version "5.3.0"
+  resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
+  integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
+
+esutils@^2.0.2:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
+  integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
+
+execa@^5.0.0:
+  version "5.1.1"
+  resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
+  integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==
+  dependencies:
+    cross-spawn "^7.0.3"
+    get-stream "^6.0.0"
+    human-signals "^2.1.0"
+    is-stream "^2.0.0"
+    merge-stream "^2.0.0"
+    npm-run-path "^4.0.1"
+    onetime "^5.1.2"
+    signal-exit "^3.0.3"
+    strip-final-newline "^2.0.0"
+
+exit@^0.1.2:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c"
+  integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==
+
+expect@^29.0.0, expect@^29.7.0:
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/expect/-/expect-29.7.0.tgz#578874590dcb3214514084c08115d8aee61e11bc"
+  integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==
+  dependencies:
+    "@jest/expect-utils" "^29.7.0"
+    jest-get-type "^29.6.3"
+    jest-matcher-utils "^29.7.0"
+    jest-message-util "^29.7.0"
+    jest-util "^29.7.0"
+
+fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
+  version "3.1.3"
+  resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
+  integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
+
+fast-diff@^1.1.2:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0"
+  integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==
+
+fast-glob@^3.3.2:
+  version "3.3.2"
+  resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129"
+  integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==
+  dependencies:
+    "@nodelib/fs.stat" "^2.0.2"
+    "@nodelib/fs.walk" "^1.2.3"
+    glob-parent "^5.1.2"
+    merge2 "^1.3.0"
+    micromatch "^4.0.4"
+
+fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
+  integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
+
+fast-levenshtein@^2.0.6:
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+  integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
+
+fastq@^1.6.0:
+  version "1.17.1"
+  resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47"
+  integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==
+  dependencies:
+    reusify "^1.0.4"
+
+fb-watchman@^2.0.0:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c"
+  integrity sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==
+  dependencies:
+    bser "2.1.1"
+
+fflate@^0.8.2:
+  version "0.8.2"
+  resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.8.2.tgz#fc8631f5347812ad6028bbe4a2308b2792aa1dea"
+  integrity sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==
+
+file-entry-cache@^8.0.0:
+  version "8.0.0"
+  resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f"
+  integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==
+  dependencies:
+    flat-cache "^4.0.0"
+
+fill-range@^7.1.1:
+  version "7.1.1"
+  resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
+  integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
+  dependencies:
+    to-regex-range "^5.0.1"
+
+find-up@^4.0.0, find-up@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
+  integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
+  dependencies:
+    locate-path "^5.0.0"
+    path-exists "^4.0.0"
+
+find-up@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
+  integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==
+  dependencies:
+    locate-path "^6.0.0"
+    path-exists "^4.0.0"
+
+flat-cache@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c"
+  integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==
+  dependencies:
+    flatted "^3.2.9"
+    keyv "^4.5.4"
+
+flatted@^3.2.9:
+  version "3.3.2"
+  resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.2.tgz#adba1448a9841bec72b42c532ea23dbbedef1a27"
+  integrity sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==
+
+fs.realpath@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+  integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
+
+fsevents@^2.3.2:
+  version "2.3.3"
+  resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
+  integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
+
+function-bind@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
+  integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
+
+gensync@^1.0.0-beta.2:
+  version "1.0.0-beta.2"
+  resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
+  integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
+
+get-caller-file@^2.0.5:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
+  integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
+
+get-package-type@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a"
+  integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==
+
+get-stdin@^8.0.0:
+  version "8.0.0"
+  resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-8.0.0.tgz#cbad6a73feb75f6eeb22ba9e01f89aa28aa97a53"
+  integrity sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==
+
+get-stream@^6.0.0:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7"
+  integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==
+
+glob-parent@^5.1.2:
+  version "5.1.2"
+  resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
+  integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
+  dependencies:
+    is-glob "^4.0.1"
+
+glob-parent@^6.0.2:
+  version "6.0.2"
+  resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3"
+  integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==
+  dependencies:
+    is-glob "^4.0.3"
+
+glob@^7.1.3, glob@^7.1.4:
+  version "7.2.3"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
+  integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
+  dependencies:
+    fs.realpath "^1.0.0"
+    inflight "^1.0.4"
+    inherits "2"
+    minimatch "^3.1.1"
+    once "^1.3.0"
+    path-is-absolute "^1.0.0"
+
+glob@^8.0.1:
+  version "8.1.0"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e"
+  integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==
+  dependencies:
+    fs.realpath "^1.0.0"
+    inflight "^1.0.4"
+    inherits "2"
+    minimatch "^5.0.1"
+    once "^1.3.0"
+
+globals@^11.1.0:
+  version "11.12.0"
+  resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
+  integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
+
+globals@^14.0.0:
+  version "14.0.0"
+  resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e"
+  integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==
+
+graceful-fs@^4.2.9:
+  version "4.2.11"
+  resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
+  integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
+
+graphemer@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6"
+  integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==
+
+has-flag@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
+  integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==
+
+has-flag@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
+  integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+
+hasown@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c"
+  integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==
+  dependencies:
+    function-bind "^1.1.2"
+
+highlight.js@^10.7.1:
+  version "10.7.3"
+  resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531"
+  integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==
+
+html-escaper@^2.0.0:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453"
+  integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==
+
+human-signals@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
+  integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
+
+iconv-lite@^0.6.3:
+  version "0.6.3"
+  resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
+  integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
+  dependencies:
+    safer-buffer ">= 2.1.2 < 3.0.0"
+
+ignore-walk@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-5.0.1.tgz#5f199e23e1288f518d90358d461387788a154776"
+  integrity sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw==
+  dependencies:
+    minimatch "^5.0.1"
+
+ignore@^5.2.0, ignore@^5.3.1:
+  version "5.3.2"
+  resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5"
+  integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==
+
+import-fresh@^3.2.1:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
+  integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
+  dependencies:
+    parent-module "^1.0.0"
+    resolve-from "^4.0.0"
+
+import-local@^3.0.2:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4"
+  integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==
+  dependencies:
+    pkg-dir "^4.2.0"
+    resolve-cwd "^3.0.0"
+
+imurmurhash@^0.1.4:
+  version "0.1.4"
+  resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
+  integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==
+
+indent-string@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251"
+  integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==
+
+inflight@^1.0.4:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+  integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
+  dependencies:
+    once "^1.3.0"
+    wrappy "1"
+
+inherits@2, inherits@^2.0.3:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
+  integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
+
+is-arrayish@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
+  integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==
+
+is-core-module@^2.13.0:
+  version "2.13.1"
+  resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384"
+  integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==
+  dependencies:
+    hasown "^2.0.0"
+
+is-extglob@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
+  integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
+
+is-fullwidth-code-point@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
+  integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
+
+is-generator-fn@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118"
+  integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==
+
+is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
+  integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
+  dependencies:
+    is-extglob "^2.1.1"
+
+is-number@^7.0.0:
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
+  integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
+
+is-stream@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077"
+  integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
+
+isexe@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+  integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
+
+istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0:
+  version "3.2.2"
+  resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756"
+  integrity sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==
+
+istanbul-lib-instrument@^5.0.4:
+  version "5.2.1"
+  resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz#d10c8885c2125574e1c231cacadf955675e1ce3d"
+  integrity sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==
+  dependencies:
+    "@babel/core" "^7.12.3"
+    "@babel/parser" "^7.14.7"
+    "@istanbuljs/schema" "^0.1.2"
+    istanbul-lib-coverage "^3.2.0"
+    semver "^6.3.0"
+
+istanbul-lib-instrument@^6.0.0:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz#71e87707e8041428732518c6fb5211761753fbdf"
+  integrity sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==
+  dependencies:
+    "@babel/core" "^7.12.3"
+    "@babel/parser" "^7.14.7"
+    "@istanbuljs/schema" "^0.1.2"
+    istanbul-lib-coverage "^3.2.0"
+    semver "^7.5.4"
+
+istanbul-lib-report@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d"
+  integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==
+  dependencies:
+    istanbul-lib-coverage "^3.0.0"
+    make-dir "^4.0.0"
+    supports-color "^7.1.0"
+
+istanbul-lib-source-maps@^4.0.0:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551"
+  integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==
+  dependencies:
+    debug "^4.1.1"
+    istanbul-lib-coverage "^3.0.0"
+    source-map "^0.6.1"
+
+istanbul-reports@^3.1.3:
+  version "3.1.6"
+  resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.6.tgz#2544bcab4768154281a2f0870471902704ccaa1a"
+  integrity sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==
+  dependencies:
+    html-escaper "^2.0.0"
+    istanbul-lib-report "^3.0.0"
+
+jest-changed-files@^29.7.0:
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.7.0.tgz#1c06d07e77c78e1585d020424dedc10d6e17ac3a"
+  integrity sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==
+  dependencies:
+    execa "^5.0.0"
+    jest-util "^29.7.0"
+    p-limit "^3.1.0"
+
+jest-circus@^29.7.0:
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.7.0.tgz#b6817a45fcc835d8b16d5962d0c026473ee3668a"
+  integrity sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==
+  dependencies:
+    "@jest/environment" "^29.7.0"
+    "@jest/expect" "^29.7.0"
+    "@jest/test-result" "^29.7.0"
+    "@jest/types" "^29.6.3"
+    "@types/node" "*"
+    chalk "^4.0.0"
+    co "^4.6.0"
+    dedent "^1.0.0"
+    is-generator-fn "^2.0.0"
+    jest-each "^29.7.0"
+    jest-matcher-utils "^29.7.0"
+    jest-message-util "^29.7.0"
+    jest-runtime "^29.7.0"
+    jest-snapshot "^29.7.0"
+    jest-util "^29.7.0"
+    p-limit "^3.1.0"
+    pretty-format "^29.7.0"
+    pure-rand "^6.0.0"
+    slash "^3.0.0"
+    stack-utils "^2.0.3"
+
+jest-cli@^29.7.0:
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.7.0.tgz#5592c940798e0cae677eec169264f2d839a37995"
+  integrity sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==
+  dependencies:
+    "@jest/core" "^29.7.0"
+    "@jest/test-result" "^29.7.0"
+    "@jest/types" "^29.6.3"
+    chalk "^4.0.0"
+    create-jest "^29.7.0"
+    exit "^0.1.2"
+    import-local "^3.0.2"
+    jest-config "^29.7.0"
+    jest-util "^29.7.0"
+    jest-validate "^29.7.0"
+    yargs "^17.3.1"
+
+jest-config@^29.7.0:
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.7.0.tgz#bcbda8806dbcc01b1e316a46bb74085a84b0245f"
+  integrity sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==
+  dependencies:
+    "@babel/core" "^7.11.6"
+    "@jest/test-sequencer" "^29.7.0"
+    "@jest/types" "^29.6.3"
+    babel-jest "^29.7.0"
+    chalk "^4.0.0"
+    ci-info "^3.2.0"
+    deepmerge "^4.2.2"
+    glob "^7.1.3"
+    graceful-fs "^4.2.9"
+    jest-circus "^29.7.0"
+    jest-environment-node "^29.7.0"
+    jest-get-type "^29.6.3"
+    jest-regex-util "^29.6.3"
+    jest-resolve "^29.7.0"
+    jest-runner "^29.7.0"
+    jest-util "^29.7.0"
+    jest-validate "^29.7.0"
+    micromatch "^4.0.4"
+    parse-json "^5.2.0"
+    pretty-format "^29.7.0"
+    slash "^3.0.0"
+    strip-json-comments "^3.1.1"
+
+jest-diff@^29.7.0:
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.7.0.tgz#017934a66ebb7ecf6f205e84699be10afd70458a"
+  integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==
+  dependencies:
+    chalk "^4.0.0"
+    diff-sequences "^29.6.3"
+    jest-get-type "^29.6.3"
+    pretty-format "^29.7.0"
+
+jest-docblock@^29.7.0:
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.7.0.tgz#8fddb6adc3cdc955c93e2a87f61cfd350d5d119a"
+  integrity sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==
+  dependencies:
+    detect-newline "^3.0.0"
+
+jest-each@^29.7.0:
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.7.0.tgz#162a9b3f2328bdd991beaabffbb74745e56577d1"
+  integrity sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==
+  dependencies:
+    "@jest/types" "^29.6.3"
+    chalk "^4.0.0"
+    jest-get-type "^29.6.3"
+    jest-util "^29.7.0"
+    pretty-format "^29.7.0"
+
+jest-environment-node@^29.7.0:
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376"
+  integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==
+  dependencies:
+    "@jest/environment" "^29.7.0"
+    "@jest/fake-timers" "^29.7.0"
+    "@jest/types" "^29.6.3"
+    "@types/node" "*"
+    jest-mock "^29.7.0"
+    jest-util "^29.7.0"
+
+jest-get-type@^29.6.3:
+  version "29.6.3"
+  resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1"
+  integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==
+
+jest-haste-map@^29.7.0:
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.7.0.tgz#3c2396524482f5a0506376e6c858c3bbcc17b104"
+  integrity sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==
+  dependencies:
+    "@jest/types" "^29.6.3"
+    "@types/graceful-fs" "^4.1.3"
+    "@types/node" "*"
+    anymatch "^3.0.3"
+    fb-watchman "^2.0.0"
+    graceful-fs "^4.2.9"
+    jest-regex-util "^29.6.3"
+    jest-util "^29.7.0"
+    jest-worker "^29.7.0"
+    micromatch "^4.0.4"
+    walker "^1.0.8"
+  optionalDependencies:
+    fsevents "^2.3.2"
+
+jest-leak-detector@^29.7.0:
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz#5b7ec0dadfdfec0ca383dc9aa016d36b5ea4c728"
+  integrity sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==
+  dependencies:
+    jest-get-type "^29.6.3"
+    pretty-format "^29.7.0"
+
+jest-matcher-utils@^29.7.0:
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz#ae8fec79ff249fd592ce80e3ee474e83a6c44f12"
+  integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==
+  dependencies:
+    chalk "^4.0.0"
+    jest-diff "^29.7.0"
+    jest-get-type "^29.6.3"
+    pretty-format "^29.7.0"
+
+jest-message-util@^29.7.0:
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.7.0.tgz#8bc392e204e95dfe7564abbe72a404e28e51f7f3"
+  integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==
+  dependencies:
+    "@babel/code-frame" "^7.12.13"
+    "@jest/types" "^29.6.3"
+    "@types/stack-utils" "^2.0.0"
+    chalk "^4.0.0"
+    graceful-fs "^4.2.9"
+    micromatch "^4.0.4"
+    pretty-format "^29.7.0"
+    slash "^3.0.0"
+    stack-utils "^2.0.3"
+
+jest-mock@^29.7.0:
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.7.0.tgz#4e836cf60e99c6fcfabe9f99d017f3fdd50a6347"
+  integrity sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==
+  dependencies:
+    "@jest/types" "^29.6.3"
+    "@types/node" "*"
+    jest-util "^29.7.0"
+
+jest-pnp-resolver@^1.2.2:
+  version "1.2.3"
+  resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e"
+  integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==
+
+jest-regex-util@^29.6.3:
+  version "29.6.3"
+  resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52"
+  integrity sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==
+
+jest-resolve-dependencies@^29.7.0:
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz#1b04f2c095f37fc776ff40803dc92921b1e88428"
+  integrity sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==
+  dependencies:
+    jest-regex-util "^29.6.3"
+    jest-snapshot "^29.7.0"
+
+jest-resolve@^29.7.0:
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.7.0.tgz#64d6a8992dd26f635ab0c01e5eef4399c6bcbc30"
+  integrity sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==
+  dependencies:
+    chalk "^4.0.0"
+    graceful-fs "^4.2.9"
+    jest-haste-map "^29.7.0"
+    jest-pnp-resolver "^1.2.2"
+    jest-util "^29.7.0"
+    jest-validate "^29.7.0"
+    resolve "^1.20.0"
+    resolve.exports "^2.0.0"
+    slash "^3.0.0"
+
+jest-runner@^29.7.0:
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.7.0.tgz#809af072d408a53dcfd2e849a4c976d3132f718e"
+  integrity sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==
+  dependencies:
+    "@jest/console" "^29.7.0"
+    "@jest/environment" "^29.7.0"
+    "@jest/test-result" "^29.7.0"
+    "@jest/transform" "^29.7.0"
+    "@jest/types" "^29.6.3"
+    "@types/node" "*"
+    chalk "^4.0.0"
+    emittery "^0.13.1"
+    graceful-fs "^4.2.9"
+    jest-docblock "^29.7.0"
+    jest-environment-node "^29.7.0"
+    jest-haste-map "^29.7.0"
+    jest-leak-detector "^29.7.0"
+    jest-message-util "^29.7.0"
+    jest-resolve "^29.7.0"
+    jest-runtime "^29.7.0"
+    jest-util "^29.7.0"
+    jest-watcher "^29.7.0"
+    jest-worker "^29.7.0"
+    p-limit "^3.1.0"
+    source-map-support "0.5.13"
+
+jest-runtime@^29.7.0:
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.7.0.tgz#efecb3141cf7d3767a3a0cc8f7c9990587d3d817"
+  integrity sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==
+  dependencies:
+    "@jest/environment" "^29.7.0"
+    "@jest/fake-timers" "^29.7.0"
+    "@jest/globals" "^29.7.0"
+    "@jest/source-map" "^29.6.3"
+    "@jest/test-result" "^29.7.0"
+    "@jest/transform" "^29.7.0"
+    "@jest/types" "^29.6.3"
+    "@types/node" "*"
+    chalk "^4.0.0"
+    cjs-module-lexer "^1.0.0"
+    collect-v8-coverage "^1.0.0"
+    glob "^7.1.3"
+    graceful-fs "^4.2.9"
+    jest-haste-map "^29.7.0"
+    jest-message-util "^29.7.0"
+    jest-mock "^29.7.0"
+    jest-regex-util "^29.6.3"
+    jest-resolve "^29.7.0"
+    jest-snapshot "^29.7.0"
+    jest-util "^29.7.0"
+    slash "^3.0.0"
+    strip-bom "^4.0.0"
+
+jest-snapshot@^29.7.0:
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.7.0.tgz#c2c574c3f51865da1bb329036778a69bf88a6be5"
+  integrity sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==
+  dependencies:
+    "@babel/core" "^7.11.6"
+    "@babel/generator" "^7.7.2"
+    "@babel/plugin-syntax-jsx" "^7.7.2"
+    "@babel/plugin-syntax-typescript" "^7.7.2"
+    "@babel/types" "^7.3.3"
+    "@jest/expect-utils" "^29.7.0"
+    "@jest/transform" "^29.7.0"
+    "@jest/types" "^29.6.3"
+    babel-preset-current-node-syntax "^1.0.0"
+    chalk "^4.0.0"
+    expect "^29.7.0"
+    graceful-fs "^4.2.9"
+    jest-diff "^29.7.0"
+    jest-get-type "^29.6.3"
+    jest-matcher-utils "^29.7.0"
+    jest-message-util "^29.7.0"
+    jest-util "^29.7.0"
+    natural-compare "^1.4.0"
+    pretty-format "^29.7.0"
+    semver "^7.5.3"
+
+jest-util@^29.0.0, jest-util@^29.7.0:
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc"
+  integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==
+  dependencies:
+    "@jest/types" "^29.6.3"
+    "@types/node" "*"
+    chalk "^4.0.0"
+    ci-info "^3.2.0"
+    graceful-fs "^4.2.9"
+    picomatch "^2.2.3"
+
+jest-validate@^29.7.0:
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c"
+  integrity sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==
+  dependencies:
+    "@jest/types" "^29.6.3"
+    camelcase "^6.2.0"
+    chalk "^4.0.0"
+    jest-get-type "^29.6.3"
+    leven "^3.1.0"
+    pretty-format "^29.7.0"
+
+jest-watcher@^29.7.0:
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.7.0.tgz#7810d30d619c3a62093223ce6bb359ca1b28a2f2"
+  integrity sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==
+  dependencies:
+    "@jest/test-result" "^29.7.0"
+    "@jest/types" "^29.6.3"
+    "@types/node" "*"
+    ansi-escapes "^4.2.1"
+    chalk "^4.0.0"
+    emittery "^0.13.1"
+    jest-util "^29.7.0"
+    string-length "^4.0.1"
+
+jest-worker@^29.7.0:
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a"
+  integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==
+  dependencies:
+    "@types/node" "*"
+    jest-util "^29.7.0"
+    merge-stream "^2.0.0"
+    supports-color "^8.0.0"
+
+jest@^29.4.0:
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613"
+  integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==
+  dependencies:
+    "@jest/core" "^29.7.0"
+    "@jest/types" "^29.6.3"
+    import-local "^3.0.2"
+    jest-cli "^29.7.0"
+
+js-tokens@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
+  integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
+
+js-yaml@^3.13.1:
+  version "3.14.1"
+  resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537"
+  integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==
+  dependencies:
+    argparse "^1.0.7"
+    esprima "^4.0.0"
+
+js-yaml@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
+  integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
+  dependencies:
+    argparse "^2.0.1"
+
+jsesc@^2.5.1:
+  version "2.5.2"
+  resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
+  integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
+
+json-buffer@3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
+  integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==
+
+json-parse-even-better-errors@^2.3.0:
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
+  integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
+
+json-schema-traverse@^0.4.1:
+  version "0.4.1"
+  resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
+  integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
+
+json-stable-stringify-without-jsonify@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
+  integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
+
+json5@^2.2.2, json5@^2.2.3:
+  version "2.2.3"
+  resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
+  integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
+
+jsonc-parser@^3.2.0:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.1.tgz#031904571ccf929d7670ee8c547545081cb37f1a"
+  integrity sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==
+
+keyv@^4.5.4:
+  version "4.5.4"
+  resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
+  integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==
+  dependencies:
+    json-buffer "3.0.1"
+
+kleur@^3.0.3:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
+  integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
+
+leven@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2"
+  integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==
+
+levn@^0.4.1:
+  version "0.4.1"
+  resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
+  integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==
+  dependencies:
+    prelude-ls "^1.2.1"
+    type-check "~0.4.0"
+
+lines-and-columns@^1.1.6:
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
+  integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
+
+locate-path@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
+  integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==
+  dependencies:
+    p-locate "^4.1.0"
+
+locate-path@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286"
+  integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==
+  dependencies:
+    p-locate "^5.0.0"
+
+lodash.memoize@4.x:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
+  integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==
+
+lodash.merge@^4.6.2:
+  version "4.6.2"
+  resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
+  integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
+
+lru-cache@^10.4.3:
+  version "10.4.3"
+  resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119"
+  integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==
+
+lru-cache@^5.1.1:
+  version "5.1.1"
+  resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
+  integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==
+  dependencies:
+    yallist "^3.0.2"
+
+lru-cache@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
+  integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
+  dependencies:
+    yallist "^4.0.0"
+
+make-dir@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e"
+  integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==
+  dependencies:
+    semver "^7.5.3"
+
+make-error@1.x, make-error@^1.1.1:
+  version "1.3.6"
+  resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
+  integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
+
+makeerror@1.0.12:
+  version "1.0.12"
+  resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a"
+  integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==
+  dependencies:
+    tmpl "1.0.5"
+
+marked-terminal@^7.1.0:
+  version "7.2.1"
+  resolved "https://registry.yarnpkg.com/marked-terminal/-/marked-terminal-7.2.1.tgz#9c1ae073a245a03c6a13e3eeac6f586f29856068"
+  integrity sha512-rQ1MoMFXZICWNsKMiiHwP/Z+92PLKskTPXj+e7uwXmuMPkNn7iTqC+IvDekVm1MPeC9wYQeLxeFaOvudRR/XbQ==
+  dependencies:
+    ansi-escapes "^7.0.0"
+    ansi-regex "^6.1.0"
+    chalk "^5.3.0"
+    cli-highlight "^2.1.11"
+    cli-table3 "^0.6.5"
+    node-emoji "^2.1.3"
+    supports-hyperlinks "^3.1.0"
+
+marked@^9.1.2:
+  version "9.1.6"
+  resolved "https://registry.yarnpkg.com/marked/-/marked-9.1.6.tgz#5d2a3f8180abfbc5d62e3258a38a1c19c0381695"
+  integrity sha512-jcByLnIFkd5gSXZmjNvS1TlmRhCXZjIzHYlaGkPlLIekG55JDR2Z4va9tZwCiP+/RDERiNhMOFu01xd6O5ct1Q==
+
+merge-stream@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
+  integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
+
+merge2@^1.3.0:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
+  integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
+
+micromatch@^4.0.4:
+  version "4.0.8"
+  resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202"
+  integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==
+  dependencies:
+    braces "^3.0.3"
+    picomatch "^2.3.1"
+
+mimic-fn@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
+  integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
+
+minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
+  integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
+  dependencies:
+    brace-expansion "^1.1.7"
+
+minimatch@^5.0.1:
+  version "5.1.6"
+  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96"
+  integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==
+  dependencies:
+    brace-expansion "^2.0.1"
+
+minimatch@^9.0.4:
+  version "9.0.5"
+  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5"
+  integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==
+  dependencies:
+    brace-expansion "^2.0.1"
+
+minimist@^1.2.6:
+  version "1.2.6"
+  resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
+  integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
+
+mri@^1.1.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b"
+  integrity sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==
+
+ms@2.1.2:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
+  integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
+
+ms@^2.1.3:
+  version "2.1.3"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
+  integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
+
+mz@^2.4.0:
+  version "2.7.0"
+  resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32"
+  integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==
+  dependencies:
+    any-promise "^1.0.0"
+    object-assign "^4.0.1"
+    thenify-all "^1.0.0"
+
+natural-compare@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
+  integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
+
+node-emoji@^2.1.3:
+  version "2.1.3"
+  resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-2.1.3.tgz#93cfabb5cc7c3653aa52f29d6ffb7927d8047c06"
+  integrity sha512-E2WEOVsgs7O16zsURJ/eH8BqhF029wGpEOnv7Urwdo2wmQanOACwJQh0devF9D9RhoZru0+9JXIS0dBXIAz+lA==
+  dependencies:
+    "@sindresorhus/is" "^4.6.0"
+    char-regex "^1.0.2"
+    emojilib "^2.4.0"
+    skin-tone "^2.0.0"
+
+node-int64@^0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
+  integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==
+
+node-releases@^2.0.14:
+  version "2.0.14"
+  resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b"
+  integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==
+
+normalize-path@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
+  integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
+
+npm-bundled@^2.0.0:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-2.0.1.tgz#94113f7eb342cd7a67de1e789f896b04d2c600f4"
+  integrity sha512-gZLxXdjEzE/+mOstGDqR6b0EkhJ+kM6fxM6vUuckuctuVPh80Q6pw/rSZj9s4Gex9GxWtIicO1pc8DB9KZWudw==
+  dependencies:
+    npm-normalize-package-bin "^2.0.0"
+
+npm-normalize-package-bin@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz#9447a1adaaf89d8ad0abe24c6c84ad614a675fff"
+  integrity sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==
+
+npm-packlist@^5.1.3:
+  version "5.1.3"
+  resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-5.1.3.tgz#69d253e6fd664b9058b85005905012e00e69274b"
+  integrity sha512-263/0NGrn32YFYi4J533qzrQ/krmmrWwhKkzwTuM4f/07ug51odoaNjUexxO4vxlzURHcmYMH1QjvHjsNDKLVg==
+  dependencies:
+    glob "^8.0.1"
+    ignore-walk "^5.0.1"
+    npm-bundled "^2.0.0"
+    npm-normalize-package-bin "^2.0.0"
+
+npm-run-path@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"
+  integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==
+  dependencies:
+    path-key "^3.0.0"
+
+object-assign@^4.0.1:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
+  integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
+
+once@^1.3.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+  integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
+  dependencies:
+    wrappy "1"
+
+onetime@^5.1.2:
+  version "5.1.2"
+  resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
+  integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
+  dependencies:
+    mimic-fn "^2.1.0"
+
+optionator@^0.9.3:
+  version "0.9.3"
+  resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64"
+  integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==
+  dependencies:
+    "@aashutoshrathi/word-wrap" "^1.2.3"
+    deep-is "^0.1.3"
+    fast-levenshtein "^2.0.6"
+    levn "^0.4.1"
+    prelude-ls "^1.2.1"
+    type-check "^0.4.0"
+
+p-all@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/p-all/-/p-all-3.0.0.tgz#077c023c37e75e760193badab2bad3ccd5782bfb"
+  integrity sha512-qUZbvbBFVXm6uJ7U/WDiO0fv6waBMbjlCm4E66oZdRR+egswICarIdHyVSZZHudH8T5SF8x/JG0q0duFzPnlBw==
+  dependencies:
+    p-map "^4.0.0"
+
+p-limit@^2.2.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
+  integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==
+  dependencies:
+    p-try "^2.0.0"
+
+p-limit@^3.0.2, p-limit@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
+  integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
+  dependencies:
+    yocto-queue "^0.1.0"
+
+p-locate@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07"
+  integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==
+  dependencies:
+    p-limit "^2.2.0"
+
+p-locate@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834"
+  integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==
+  dependencies:
+    p-limit "^3.0.2"
+
+p-map@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b"
+  integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==
+  dependencies:
+    aggregate-error "^3.0.0"
+
+p-try@^2.0.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
+  integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
+
+parent-module@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
+  integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
+  dependencies:
+    callsites "^3.0.0"
+
+parse-json@^5.2.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
+  integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==
+  dependencies:
+    "@babel/code-frame" "^7.0.0"
+    error-ex "^1.3.1"
+    json-parse-even-better-errors "^2.3.0"
+    lines-and-columns "^1.1.6"
+
+parse5-htmlparser2-tree-adapter@^6.0.0:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6"
+  integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==
+  dependencies:
+    parse5 "^6.0.1"
+
+parse5@^5.1.1:
+  version "5.1.1"
+  resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178"
+  integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==
+
+parse5@^6.0.1:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
+  integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==
+
+path-exists@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
+  integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
+
+path-is-absolute@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+  integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
+
+path-key@^3.0.0, path-key@^3.1.0:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
+  integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
+
+path-parse@^1.0.7:
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
+  integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
+
+picocolors@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
+  integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
+
+picocolors@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
+  integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
+
+picomatch@^2.0.4, picomatch@^2.2.3, picomatch@^2.3.1:
+  version "2.3.1"
+  resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
+  integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
+
+pirates@^4.0.4:
+  version "4.0.6"
+  resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9"
+  integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==
+
+pkg-dir@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3"
+  integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==
+  dependencies:
+    find-up "^4.0.0"
+
+prelude-ls@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
+  integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
+
+prettier-linter-helpers@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b"
+  integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==
+  dependencies:
+    fast-diff "^1.1.2"
+
+prettier@^3.0.0:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.1.1.tgz#6ba9f23165d690b6cbdaa88cb0807278f7019848"
+  integrity sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==
+
+pretty-format@^29.0.0, pretty-format@^29.7.0:
+  version "29.7.0"
+  resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.7.0.tgz#ca42c758310f365bfa71a0bda0a807160b776812"
+  integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==
+  dependencies:
+    "@jest/schemas" "^29.6.3"
+    ansi-styles "^5.0.0"
+    react-is "^18.0.0"
+
+prompts@^2.0.1:
+  version "2.4.2"
+  resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069"
+  integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==
+  dependencies:
+    kleur "^3.0.3"
+    sisteransi "^1.0.5"
+
+publint@^0.2.12:
+  version "0.2.12"
+  resolved "https://registry.yarnpkg.com/publint/-/publint-0.2.12.tgz#d25cd6bd243d5bdd640344ecdddb3eeafdcc4059"
+  integrity sha512-YNeUtCVeM4j9nDiTT2OPczmlyzOkIXNtdDZnSuajAxS/nZ6j3t7Vs9SUB4euQNddiltIwu7Tdd3s+hr08fAsMw==
+  dependencies:
+    npm-packlist "^5.1.3"
+    picocolors "^1.1.1"
+    sade "^1.8.1"
+
+punycode@^2.1.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f"
+  integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==
+
+pure-rand@^6.0.0:
+  version "6.0.4"
+  resolved "https://registry.yarnpkg.com/pure-rand/-/pure-rand-6.0.4.tgz#50b737f6a925468679bff00ad20eade53f37d5c7"
+  integrity sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==
+
+queue-microtask@^1.2.2:
+  version "1.2.3"
+  resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
+  integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
+
+react-is@^18.0.0:
+  version "18.2.0"
+  resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
+  integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
+
+readable-stream@^3.4.0:
+  version "3.6.2"
+  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967"
+  integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==
+  dependencies:
+    inherits "^2.0.3"
+    string_decoder "^1.1.1"
+    util-deprecate "^1.0.1"
+
+require-directory@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
+  integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==
+
+resolve-cwd@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
+  integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==
+  dependencies:
+    resolve-from "^5.0.0"
+
+resolve-from@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
+  integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
+
+resolve-from@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
+  integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
+
+resolve.exports@^2.0.0:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800"
+  integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==
+
+resolve@^1.20.0:
+  version "1.22.8"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d"
+  integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==
+  dependencies:
+    is-core-module "^2.13.0"
+    path-parse "^1.0.7"
+    supports-preserve-symlinks-flag "^1.0.0"
+
+reusify@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
+  integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
+
+run-parallel@^1.1.9:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
+  integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==
+  dependencies:
+    queue-microtask "^1.2.2"
+
+sade@^1.8.1:
+  version "1.8.1"
+  resolved "https://registry.yarnpkg.com/sade/-/sade-1.8.1.tgz#0a78e81d658d394887be57d2a409bf703a3b2701"
+  integrity sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==
+  dependencies:
+    mri "^1.1.0"
+
+safe-buffer@~5.2.0:
+  version "5.2.1"
+  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
+  integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
+
+"safer-buffer@>= 2.1.2 < 3.0.0":
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
+  integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
+
+semver@^6.3.0, semver@^6.3.1:
+  version "6.3.1"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
+  integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
+
+semver@^7.5.3:
+  version "7.5.4"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
+  integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
+  dependencies:
+    lru-cache "^6.0.0"
+
+semver@^7.5.4:
+  version "7.6.3"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143"
+  integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
+
+semver@^7.6.0:
+  version "7.7.1"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f"
+  integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==
+
+shebang-command@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
+  integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
+  dependencies:
+    shebang-regex "^3.0.0"
+
+shebang-regex@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
+  integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
+
+signal-exit@^3.0.3, signal-exit@^3.0.7:
+  version "3.0.7"
+  resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
+  integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
+
+sisteransi@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed"
+  integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==
+
+skin-tone@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/skin-tone/-/skin-tone-2.0.0.tgz#4e3933ab45c0d4f4f781745d64b9f4c208e41237"
+  integrity sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==
+  dependencies:
+    unicode-emoji-modifier-base "^1.0.0"
+
+slash@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
+  integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
+
+source-map-support@0.5.13:
+  version "0.5.13"
+  resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932"
+  integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==
+  dependencies:
+    buffer-from "^1.0.0"
+    source-map "^0.6.0"
+
+source-map@^0.6.0, source-map@^0.6.1:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
+  integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+
+sprintf-js@~1.0.2:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
+  integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==
+
+stack-utils@^2.0.3:
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.6.tgz#aaf0748169c02fc33c8232abccf933f54a1cc34f"
+  integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==
+  dependencies:
+    escape-string-regexp "^2.0.0"
+
+string-length@^4.0.1:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a"
+  integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==
+  dependencies:
+    char-regex "^1.0.2"
+    strip-ansi "^6.0.0"
+
+string-to-stream@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/string-to-stream/-/string-to-stream-3.0.1.tgz#480e6fb4d5476d31cb2221f75307a5dcb6638a42"
+  integrity sha512-Hl092MV3USJuUCC6mfl9sPzGloA3K5VwdIeJjYIkXY/8K+mUvaeEabWJgArp+xXrsWxCajeT2pc4axbVhIZJyg==
+  dependencies:
+    readable-stream "^3.4.0"
+
+string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
+  version "4.2.3"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+  integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+  dependencies:
+    emoji-regex "^8.0.0"
+    is-fullwidth-code-point "^3.0.0"
+    strip-ansi "^6.0.1"
+
+string_decoder@^1.1.1:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
+  integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
+  dependencies:
+    safe-buffer "~5.2.0"
+
+strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+  integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+  dependencies:
+    ansi-regex "^5.0.1"
+
+strip-bom@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
+  integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==
+
+strip-bom@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878"
+  integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==
+
+strip-final-newline@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
+  integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
+
+strip-json-comments@^3.1.1:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
+  integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
+
+superstruct@^1.0.4:
+  version "1.0.4"
+  resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-1.0.4.tgz#0adb99a7578bd2f1c526220da6571b2d485d91ca"
+  integrity sha512-7JpaAoX2NGyoFlI9NBh66BQXGONc+uE+MRS5i2iOBKuS4e+ccgMDjATgZldkah+33DakBxDHiss9kvUcGAO8UQ==
+
+supports-color@^5.3.0:
+  version "5.5.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
+  integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
+  dependencies:
+    has-flag "^3.0.0"
+
+supports-color@^7.0.0, supports-color@^7.1.0:
+  version "7.2.0"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
+  integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
+  dependencies:
+    has-flag "^4.0.0"
+
+supports-color@^8.0.0:
+  version "8.1.1"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
+  integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
+  dependencies:
+    has-flag "^4.0.0"
+
+supports-hyperlinks@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-3.1.0.tgz#b56150ff0173baacc15f21956450b61f2b18d3ac"
+  integrity sha512-2rn0BZ+/f7puLOHZm1HOJfwBggfaHXUpPUSSG/SWM4TWp5KCfmNYwnC3hruy2rZlMnmWZ+QAGpZfchu3f3695A==
+  dependencies:
+    has-flag "^4.0.0"
+    supports-color "^7.0.0"
+
+supports-preserve-symlinks-flag@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
+  integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
+
+synckit@0.8.8, synckit@^0.9.1:
+  version "0.8.8"
+  resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.8.8.tgz#fe7fe446518e3d3d49f5e429f443cf08b6edfcd7"
+  integrity sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==
+  dependencies:
+    "@pkgr/core" "^0.1.0"
+    tslib "^2.6.2"
+
+test-exclude@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e"
+  integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==
+  dependencies:
+    "@istanbuljs/schema" "^0.1.2"
+    glob "^7.1.4"
+    minimatch "^3.0.4"
+
+thenify-all@^1.0.0:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726"
+  integrity sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==
+  dependencies:
+    thenify ">= 3.1.0 < 4"
+
+"thenify@>= 3.1.0 < 4":
+  version "3.3.1"
+  resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f"
+  integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==
+  dependencies:
+    any-promise "^1.0.0"
+
+tmpl@1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc"
+  integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==
+
+to-fast-properties@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
+  integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==
+
+to-regex-range@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
+  integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
+  dependencies:
+    is-number "^7.0.0"
+
+ts-api-utils@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.0.1.tgz#660729385b625b939aaa58054f45c058f33f10cd"
+  integrity sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==
+
+ts-jest@^29.1.0:
+  version "29.1.1"
+  resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.1.1.tgz#f58fe62c63caf7bfcc5cc6472082f79180f0815b"
+  integrity sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==
+  dependencies:
+    bs-logger "0.x"
+    fast-json-stable-stringify "2.x"
+    jest-util "^29.0.0"
+    json5 "^2.2.3"
+    lodash.memoize "4.x"
+    make-error "1.x"
+    semver "^7.5.3"
+    yargs-parser "^21.0.1"
+
+ts-node@^10.5.0:
+  version "10.7.0"
+  resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.7.0.tgz#35d503d0fab3e2baa672a0e94f4b40653c2463f5"
+  integrity sha512-TbIGS4xgJoX2i3do417KSaep1uRAW/Lu+WAL2doDHC0D6ummjirVOXU5/7aiZotbQ5p1Zp9tP7U6cYhA0O7M8A==
+  dependencies:
+    "@cspotcode/source-map-support" "0.7.0"
+    "@tsconfig/node10" "^1.0.7"
+    "@tsconfig/node12" "^1.0.7"
+    "@tsconfig/node14" "^1.0.0"
+    "@tsconfig/node16" "^1.0.2"
+    acorn "^8.4.1"
+    acorn-walk "^8.1.1"
+    arg "^4.1.0"
+    create-require "^1.1.0"
+    diff "^4.0.1"
+    make-error "^1.1.1"
+    v8-compile-cache-lib "^3.0.0"
+    yn "3.1.1"
+
+"tsc-multi@https://github.com/stainless-api/tsc-multi/releases/download/v1.1.4/tsc-multi-1.1.4.tgz":
+  version "1.1.4"
+  resolved "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.4/tsc-multi-1.1.4.tgz#cbed459a9e902f5295ec3daaf1c7aa3b10427e55"
+  dependencies:
+    debug "^4.3.7"
+    fast-glob "^3.3.2"
+    get-stdin "^8.0.0"
+    p-all "^3.0.0"
+    picocolors "^1.1.1"
+    signal-exit "^3.0.7"
+    string-to-stream "^3.0.1"
+    superstruct "^1.0.4"
+    tslib "^2.8.1"
+    yargs "^17.7.2"
+
+tsconfig-paths@^4.0.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz#ef78e19039133446d244beac0fd6a1632e2d107c"
+  integrity sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==
+  dependencies:
+    json5 "^2.2.2"
+    minimist "^1.2.6"
+    strip-bom "^3.0.0"
+
+tslib@^2.6.2:
+  version "2.6.2"
+  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
+  integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
+
+tslib@^2.8.1:
+  version "2.8.1"
+  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"
+  integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
+
+type-check@^0.4.0, type-check@~0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
+  integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==
+  dependencies:
+    prelude-ls "^1.2.1"
+
+type-detect@4.0.8:
+  version "4.0.8"
+  resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
+  integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
+
+type-fest@^0.21.3:
+  version "0.21.3"
+  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
+  integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
+
+typescript-eslint@8.31.1:
+  version "8.31.1"
+  resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.31.1.tgz#b77ab1e48ced2daab9225ff94bab54391a4af69b"
+  integrity sha512-j6DsEotD/fH39qKzXTQRwYYWlt7D+0HmfpOK+DVhwJOFLcdmn92hq3mBb7HlKJHbjjI/gTOqEcc9d6JfpFf/VA==
+  dependencies:
+    "@typescript-eslint/eslint-plugin" "8.31.1"
+    "@typescript-eslint/parser" "8.31.1"
+    "@typescript-eslint/utils" "8.31.1"
+
+typescript@5.6.1-rc:
+  version "5.6.1-rc"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.1-rc.tgz#d5e4d7d8170174fed607b74cc32aba3d77018e02"
+  integrity sha512-E3b2+1zEFu84jB0YQi9BORDjz9+jGbwwy1Zi3G0LUNw7a7cePUrHMRNy8aPh53nXpkFGVHSxIZo5vKTfYaFiBQ==
+
+typescript@5.8.3:
+  version "5.8.3"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e"
+  integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==
+
+undici-types@~5.26.4:
+  version "5.26.5"
+  resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
+  integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
+
+undici-types@~6.19.2:
+  version "6.19.8"
+  resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02"
+  integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==
+
+unicode-emoji-modifier-base@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz#dbbd5b54ba30f287e2a8d5a249da6c0cef369459"
+  integrity sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==
+
+update-browserslist-db@^1.0.13:
+  version "1.0.13"
+  resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4"
+  integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==
+  dependencies:
+    escalade "^3.1.1"
+    picocolors "^1.0.0"
+
+uri-js@^4.2.2:
+  version "4.4.1"
+  resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
+  integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
+  dependencies:
+    punycode "^2.1.0"
+
+util-deprecate@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+  integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
+
+v8-compile-cache-lib@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.0.tgz#0582bcb1c74f3a2ee46487ceecf372e46bce53e8"
+  integrity sha512-mpSYqfsFvASnSn5qMiwrr4VKfumbPyONLCOPmsR3A6pTY/r0+tSaVbgPWSAIuzbk3lCTa+FForeTiO+wBQGkjA==
+
+v8-to-istanbul@^9.0.1:
+  version "9.2.0"
+  resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz#2ed7644a245cddd83d4e087b9b33b3e62dfd10ad"
+  integrity sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==
+  dependencies:
+    "@jridgewell/trace-mapping" "^0.3.12"
+    "@types/istanbul-lib-coverage" "^2.0.1"
+    convert-source-map "^2.0.0"
+
+validate-npm-package-name@^5.0.0:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz#a316573e9b49f3ccd90dbb6eb52b3f06c6d604e8"
+  integrity sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==
+
+walker@^1.0.8:
+  version "1.0.8"
+  resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f"
+  integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==
+  dependencies:
+    makeerror "1.0.12"
+
+which@^2.0.1:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
+  integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
+  dependencies:
+    isexe "^2.0.0"
+
+wrap-ansi@^7.0.0:
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
+  integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
+  dependencies:
+    ansi-styles "^4.0.0"
+    string-width "^4.1.0"
+    strip-ansi "^6.0.0"
+
+wrappy@1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+  integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
+
+write-file-atomic@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd"
+  integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==
+  dependencies:
+    imurmurhash "^0.1.4"
+    signal-exit "^3.0.7"
+
+y18n@^5.0.5:
+  version "5.0.8"
+  resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
+  integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
+
+yallist@^3.0.2:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
+  integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
+
+yallist@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
+  integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
+
+yargs-parser@^20.2.2:
+  version "20.2.9"
+  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
+  integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
+
+yargs-parser@^21.0.1, yargs-parser@^21.1.1:
+  version "21.1.1"
+  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"
+  integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==
+
+yargs@^16.0.0:
+  version "16.2.0"
+  resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66"
+  integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==
+  dependencies:
+    cliui "^7.0.2"
+    escalade "^3.1.1"
+    get-caller-file "^2.0.5"
+    require-directory "^2.1.1"
+    string-width "^4.2.0"
+    y18n "^5.0.5"
+    yargs-parser "^20.2.2"
+
+yargs@^17.3.1, yargs@^17.7.2:
+  version "17.7.2"
+  resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269"
+  integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==
+  dependencies:
+    cliui "^8.0.1"
+    escalade "^3.1.1"
+    get-caller-file "^2.0.5"
+    require-directory "^2.1.1"
+    string-width "^4.2.3"
+    y18n "^5.0.5"
+    yargs-parser "^21.1.1"
+
+yn@3.1.1:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
+  integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==
+
+yocto-queue@^0.1.0:
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
+  integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==