diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 763462f..43fd5a7 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -9,9 +9,7 @@
   "postCreateCommand": "yarn install",
   "customizations": {
     "vscode": {
-      "extensions": [
-        "esbenp.prettier-vscode"
-      ]
+      "extensions": ["esbenp.prettier-vscode"]
     }
   }
 }
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index e3530dc..90365fe 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,26 +1,25 @@
 name: CI
 on:
   push:
-    branches:
-      - main
-  pull_request:
-    branches:
-      - main
-      - next
+    branches-ignore:
+      - 'generated'
+      - 'codegen/**'
+      - 'integrated/**'
+      - 'stl-preview-head/**'
+      - 'stl-preview-base/**'
 
 jobs:
   lint:
+    timeout-minutes: 10
     name: lint
-    runs-on: ubuntu-latest
-    
-
+    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'
+          node-version: '20'
 
       - name: Bootstrap
         run: ./scripts/bootstrap
@@ -29,38 +28,54 @@ jobs:
         run: ./scripts/lint
 
   build:
+    timeout-minutes: 5
     name: build
-    runs-on: ubuntu-latest
-    
-
+    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'
+          node-version: '20'
 
       - 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: ubuntu-latest
-
+    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'
+          node-version: '20'
 
       - 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
index d11b246..9d34b14 100644
--- a/.github/workflows/publish-npm.yml
+++ b/.github/workflows/publish-npm.yml
@@ -19,7 +19,7 @@ jobs:
       - name: Set up Node
         uses: actions/setup-node@v3
         with:
-          node-version: '18'
+          node-version: '20'
 
       - name: Install dependencies
         run: |
diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml
index 742b303..f875bac 100644
--- a/.github/workflows/release-doctor.yml
+++ b/.github/workflows/release-doctor.yml
@@ -19,4 +19,3 @@ jobs:
           bash ./bin/check-release-environment
         env:
           NPM_TOKEN: ${{ secrets.GITPOD_NPM_TOKEN || secrets.NPM_TOKEN }}
-
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index f1c1e58..bcd0522 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
 {
-  ".": "0.5.0"
+  ".": "0.6.0"
 }
diff --git a/.stats.yml b/.stats.yml
index 2c4d8ac..d375176 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,2 +1,4 @@
-configured_endpoints: 111
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-3655d5ad0ac3e228c1519af70dbf3d0bfa3c47a2d06d4cac92a650da051b49a6.yml
+configured_endpoints: 119
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gitpod%2Fgitpod-ca9a49ac7fbb63f55611fd7cd48a22a3ff8b38a797125c8513e891d9b7345550.yml
+openapi_spec_hash: fd6ffbdfaefcc555e61ca1c565e05214
+config_hash: bb9d0a0bdadbee0985dd7c1e4f0e9e8a
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b82eb11..930b4e9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,68 @@
 # Changelog
 
+## 0.6.0 (2025-06-06)
+
+Full Changelog: [v0.5.0...v0.6.0](https://github.com/gitpod-io/gitpod-sdk-typescript/compare/v0.5.0...v0.6.0)
+
+### Features
+
+* **api:** manual updates ([3c6b1d3](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/3c6b1d33ef500d073008f79a05a58efe8aae6eb2))
+* **api:** manual updates ([078548f](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/078548f4b1cd2101e258bd27c5c0fd5628566a88))
+* **api:** manual updates ([77b6f44](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/77b6f44b695808f66ce955683f86965c3b8605fb))
+* **api:** manual updates ([f0edc96](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/f0edc9696d24e0a4e4ed648b585883616cde251f))
+* **api:** manual updates ([f50f5ad](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/f50f5adf1ea13bb39845ff8644e2f670bc01856d))
+
+
+### Bug Fixes
+
+* **api:** improve type resolution when importing as a package ([#66](https://github.com/gitpod-io/gitpod-sdk-typescript/issues/66)) ([8aa007b](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/8aa007bc39d87e8b96861748a23d4faa5d084c8a))
+* **client:** fix TypeError with undefined File ([#50](https://github.com/gitpod-io/gitpod-sdk-typescript/issues/50)) ([1262a7b](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/1262a7bcd5e0619e1eaef399ee967b629c79ce09))
+* **client:** send `X-Stainless-Timeout` in seconds ([#63](https://github.com/gitpod-io/gitpod-sdk-typescript/issues/63)) ([dab2433](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/dab243394f6b0f60cedc65f3eabcf1bfe64ed640))
+* **client:** send all configured auth headers ([#68](https://github.com/gitpod-io/gitpod-sdk-typescript/issues/68)) ([3ced793](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/3ced7939c98da7bc8c42a457da3aee4510a778a7))
+* **exports:** ensure resource imports don't require /index ([#57](https://github.com/gitpod-io/gitpod-sdk-typescript/issues/57)) ([23166e6](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/23166e607ec2b8915a97e974e09cdc0abdbc6c23))
+* **internal:** add mts file + crypto shim types ([#58](https://github.com/gitpod-io/gitpod-sdk-typescript/issues/58)) ([716b94c](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/716b94c4be5a42cfaf9f59fcdb9332b912113869))
+* **internal:** clean up undefined File test ([#51](https://github.com/gitpod-io/gitpod-sdk-typescript/issues/51)) ([e1e0fb5](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/e1e0fb509bfd526c9a8183480ad88330f0c7b240))
+* **internal:** fix file uploads in node 18 jest ([702757c](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/702757cc250c54fa31731233f3b88841b42baa32))
+* **internal:** return in castToError instead of throwing ([#43](https://github.com/gitpod-io/gitpod-sdk-typescript/issues/43)) ([2f70ad9](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/2f70ad9e95854605f9f38c401d49f8422d62af75))
+* **mcp:** remove unused tools.ts ([#67](https://github.com/gitpod-io/gitpod-sdk-typescript/issues/67)) ([65686bf](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/65686bf96f2a2147c620810605bc66876ec0c13e))
+* **tests:** manually reset node:buffer File ([#52](https://github.com/gitpod-io/gitpod-sdk-typescript/issues/52)) ([2eded46](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/2eded46344af89fbaef371ab685056b8952aa946))
+
+
+### Chores
+
+* **ci:** add timeout thresholds for CI jobs ([d78258c](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/d78258ce7b00f01f7714c59bda0d12d3f70b7ec3))
+* **ci:** only use depot for staging repos ([678516c](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/678516c59e2a0a39caa817aa847f9a3f197172b7))
+* **client:** make jsonl methods consistent with other streaming methods ([#65](https://github.com/gitpod-io/gitpod-sdk-typescript/issues/65)) ([62c4790](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/62c4790ed0515d7644fca6075b5d9304bd4b1642))
+* **client:** minor internal fixes ([e3c6fb8](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/e3c6fb879bc94b55e66f65a5238102ba390387a8))
+* **client:** move misc public files to new `core/` directory, deprecate old paths ([#62](https://github.com/gitpod-io/gitpod-sdk-typescript/issues/62)) ([e4008c3](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/e4008c3ab36557410e2124287eb9ab861e5d81d2))
+* **client:** only accept standard types for file uploads ([#47](https://github.com/gitpod-io/gitpod-sdk-typescript/issues/47)) ([cd888bc](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/cd888bc3c16d0d2cbf3b3c96ab23dc7d46360598))
+* **docs:** improve docs for withResponse/asResponse ([#54](https://github.com/gitpod-io/gitpod-sdk-typescript/issues/54)) ([25092c5](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/25092c5070acc3602094bf34f304105cb7bd7157))
+* **exports:** cleaner resource index imports ([#60](https://github.com/gitpod-io/gitpod-sdk-typescript/issues/60)) ([0049aac](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/0049aac07585fb4a1536ef6ff191b4ba5d5b9720))
+* **exports:** stop using path fallbacks ([#61](https://github.com/gitpod-io/gitpod-sdk-typescript/issues/61)) ([a9df2c1](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/a9df2c166e44d19ff8b374e5225d29971c72bb3e))
+* **internal:** add aliases for Record and Array ([#64](https://github.com/gitpod-io/gitpod-sdk-typescript/issues/64)) ([38e00c9](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/38e00c9995d8528c361bf709d3951a0f00238ada))
+* **internal:** codegen related update ([e94c558](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/e94c55895af8caf60720420c33c1e22c6e2d5bd4))
+* **internal:** codegen related update ([c60c38f](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/c60c38f7f805d86e56d6a2f96c742b1549d62e5c))
+* **internal:** codegen related update ([#55](https://github.com/gitpod-io/gitpod-sdk-typescript/issues/55)) ([71a1bef](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/71a1bef58884eb34434a3e590cf0942c8166d33b))
+* **internal:** constrain synckit dev dependency ([#49](https://github.com/gitpod-io/gitpod-sdk-typescript/issues/49)) ([41da630](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/41da630123709c225f8c173bbd2aace382d0e865))
+* **internal:** fix tests failing on node v18 ([#48](https://github.com/gitpod-io/gitpod-sdk-typescript/issues/48)) ([c1031bd](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/c1031bd67090cc27d55472a5a32ee70df9ee781e))
+* **internal:** improve node 18 shims ([726127a](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/726127ae7ad639fc5587724e20559893ea3c67eb))
+* **internal:** minor client file refactoring ([#59](https://github.com/gitpod-io/gitpod-sdk-typescript/issues/59)) ([51d47fd](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/51d47fd93e6be04336019ec05c60841a5d25195c))
+* **internal:** reduce CI branch coverage ([e8cd029](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/e8cd029655896872fa1ccd8b71807f8c0ac565c9))
+* **internal:** refactor utils ([eafa310](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/eafa310bb3964addb8bbbf8c8811564f6985068e))
+* **internal:** remove extra empty newlines ([#56](https://github.com/gitpod-io/gitpod-sdk-typescript/issues/56)) ([6431dc9](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/6431dc9927a315b9faf1e906c95930bcec65f3d5))
+* **internal:** remove unnecessary todo ([#45](https://github.com/gitpod-io/gitpod-sdk-typescript/issues/45)) ([bd9e536](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/bd9e5361115c7f9adc8c8d9798f38a04b55ab03c))
+* **internal:** share typescript helpers ([b52aa07](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/b52aa0747ab51dbdf0eeb63e3fce2c255f47a06c))
+* **internal:** upload builds and expand CI branch coverage ([dbd4446](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/dbd4446148041e01ec058c0a19568a17ad7384f7))
+* **perf:** faster base64 decoding ([b3a1e96](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/b3a1e96efe94fd726a49c050eb1a6e0069171983))
+* **tests:** improve enum examples ([#69](https://github.com/gitpod-io/gitpod-sdk-typescript/issues/69)) ([af4a60a](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/af4a60aa5f1bc957cdb96b0996f4ee02c0d7d469))
+* **types:** improved go to definition on fetchOptions ([#53](https://github.com/gitpod-io/gitpod-sdk-typescript/issues/53)) ([54a7db8](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/54a7db864f182bd872aeceae04747930a3e419a7))
+
+
+### Documentation
+
+* **readme:** fix typo ([fea4ecb](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/fea4ecb31efa7b73aec94c3aed1f574b80611110))
+* update URLs from stainlessapi.com to stainless.com ([#46](https://github.com/gitpod-io/gitpod-sdk-typescript/issues/46)) ([6450e47](https://github.com/gitpod-io/gitpod-sdk-typescript/commit/6450e47a5f12103274528a67028b91a01b9c55b8))
+
 ## 0.5.0 (2025-02-21)
 
 Full Changelog: [v0.4.0...v0.5.0](https://github.com/gitpod-io/gitpod-sdk-typescript/compare/v0.4.0...v0.5.0)
diff --git a/README.md b/README.md
index e071c46..31de3bb 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@ This library provides convenient access to the Gitpod REST API from server-side
 
 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.stainlessapi.com/).
+It is generated with [Stainless](https://www.stainless.com/).
 
 ## Installation
 
@@ -26,13 +26,9 @@ 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();
+const response = await client.identity.getAuthenticatedIdentity();
 
-  console.log(response.organizationId);
-}
-
-main();
+console.log(response.organizationId);
 ```
 
 ### Request & Response types
@@ -47,12 +43,8 @@ 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();
+const response: Gitpod.IdentityGetAuthenticatedIdentityResponse =
+  await client.identity.getAuthenticatedIdentity();
 ```
 
 Documentation for each method, request param, and response field are available in docstrings and will appear on hover in most modern editors.
@@ -65,22 +57,18 @@ a subclass of `APIError` will be thrown:
 
 
 ```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();
+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;
+  }
+});
 ```
 
-Error codes are as followed:
+Error codes are as follows:
 
 | Status Code | Error Type                 |
 | ----------- | -------------------------- |
@@ -171,8 +159,10 @@ while (page.hasNextPage()) {
 ### 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.
 
 
 ```ts
@@ -384,7 +374,7 @@ 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.
+- Node.js 20 LTS or later ([non-EOL](https://endoflife.date/nodejs)) versions.
 - Deno v1.28.0 or higher.
 - Bun 1.0 or later.
 - Cloudflare Workers.
diff --git a/SECURITY.md b/SECURITY.md
index 0985c82..efd9088 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -2,9 +2,9 @@
 
 ## Reporting Security Issues
 
-This SDK is generated by [Stainless Software Inc](http://stainlessapi.com). Stainless takes security seriously, and encourages you to report any security vulnerability promptly so that appropriate action can be taken.
+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@stainlessapi.com.
+To report a security issue, please contact the Stainless team at security@stainless.com.
 
 ## Responsible Disclosure
 
@@ -16,11 +16,11 @@ 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.
+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.
+Please contact dev-feedback@gitpod.com for any questions or concerns regarding the security of our services.
 
 ---
 
diff --git a/api.md b/api.md
index 8e80f38..228b9e2 100644
--- a/api.md
+++ b/api.md
@@ -6,6 +6,7 @@ Types:
 - EnvironmentClass
 - ErrorCode
 - FieldValue
+- Gateway
 - OrganizationRole
 - Principal
 - RunsOn
@@ -31,12 +32,14 @@ Types:
 - AccountRetrieveResponse
 - AccountDeleteResponse
 - AccountGetSSOLoginURLResponse
+- AccountListJoinableOrganizationsResponse
 
 Methods:
 
 - client.accounts.retrieve({ ...params }) -> AccountRetrieveResponse
 - client.accounts.delete({ ...params }) -> unknown
 - client.accounts.getSSOLoginURL({ ...params }) -> AccountGetSSOLoginURLResponse
+- client.accounts.listJoinableOrganizations({ ...params }) -> AccountListJoinableOrganizationsResponse
 - client.accounts.listLoginProviders({ ...params }) -> LoginProvidersLoginProvidersPage
 
 # Editors
@@ -68,11 +71,13 @@ Types:
 - EnvironmentRetrieveResponse
 - EnvironmentUpdateResponse
 - EnvironmentDeleteResponse
+- EnvironmentCreateEnvironmentTokenResponse
 - EnvironmentCreateFromProjectResponse
 - EnvironmentCreateLogsTokenResponse
 - EnvironmentMarkActiveResponse
 - EnvironmentStartResponse
 - EnvironmentStopResponse
+- EnvironmentUnarchiveResponse
 
 Methods:
 
@@ -81,11 +86,13 @@ Methods:
 - client.environments.update({ ...params }) -> unknown
 - client.environments.list({ ...params }) -> EnvironmentsEnvironmentsPage
 - client.environments.delete({ ...params }) -> unknown
+- client.environments.createEnvironmentToken({ ...params }) -> EnvironmentCreateEnvironmentTokenResponse
 - client.environments.createFromProject({ ...params }) -> EnvironmentCreateFromProjectResponse
 - client.environments.createLogsToken({ ...params }) -> EnvironmentCreateLogsTokenResponse
 - client.environments.markActive({ ...params }) -> unknown
 - client.environments.start({ ...params }) -> unknown
 - client.environments.stop({ ...params }) -> unknown
+- client.environments.unarchive({ ...params }) -> unknown
 
 ## Automations
 
@@ -174,7 +181,13 @@ Types:
 Methods:
 
 - client.events.list({ ...params }) -> EventListResponsesEntriesPage
-- client.events.watch({ ...params }) -> JSONLDecoder<EventWatchResponse>
+- client.events.watch({ ...params }) -> EventWatchResponse
+
+# Gateways
+
+Methods:
+
+- client.gateways.list({ ...params }) -> GatewaysGatewaysPage
 
 # Groups
 
@@ -190,6 +203,7 @@ Methods:
 
 Types:
 
+- IDTokenVersion
 - IdentityExchangeTokenResponse
 - IdentityGetAuthenticatedIdentityResponse
 - IdentityGetIDTokenResponse
@@ -207,7 +221,7 @@ Types:
 - InviteDomains
 - Organization
 - OrganizationMember
-- Scope
+- OrganizationTier
 - OrganizationCreateResponse
 - OrganizationRetrieveResponse
 - OrganizationUpdateResponse
@@ -221,7 +235,6 @@ Methods:
 - client.organizations.create({ ...params }) -> OrganizationCreateResponse
 - client.organizations.retrieve({ ...params }) -> OrganizationRetrieveResponse
 - client.organizations.update({ ...params }) -> OrganizationUpdateResponse
-- client.organizations.list({ ...params }) -> OrganizationsOrganizationsPage
 - client.organizations.delete({ ...params }) -> unknown
 - client.organizations.join({ ...params }) -> OrganizationJoinResponse
 - client.organizations.leave({ ...params }) -> unknown
@@ -262,6 +275,19 @@ Methods:
 - client.organizations.invites.retrieve({ ...params }) -> InviteRetrieveResponse
 - client.organizations.invites.getSummary({ ...params }) -> InviteGetSummaryResponse
 
+## Policies
+
+Types:
+
+- OrganizationPolicies
+- PolicyRetrieveResponse
+- PolicyUpdateResponse
+
+Methods:
+
+- client.organizations.policies.retrieve({ ...params }) -> PolicyRetrieveResponse
+- client.organizations.policies.update({ ...params }) -> unknown
+
 ## SSOConfigurations
 
 Types:
@@ -326,6 +352,9 @@ Methods:
 
 Types:
 
+- GatewayInfo
+- LogLevel
+- MetricsConfiguration
 - Runner
 - RunnerCapability
 - RunnerConfiguration
@@ -453,6 +482,7 @@ Methods:
 Types:
 
 - Secret
+- SecretScope
 - SecretCreateResponse
 - SecretDeleteResponse
 - SecretGetValueResponse
@@ -466,6 +496,16 @@ Methods:
 - client.secrets.getValue({ ...params }) -> SecretGetValueResponse
 - client.secrets.updateValue({ ...params }) -> unknown
 
+# Usage
+
+Types:
+
+- EnvironmentUsageRecord
+
+Methods:
+
+- client.usage.listEnvironmentRuntimeRecords({ ...params }) -> EnvironmentUsageRecordsRecordsPage
+
 # Users
 
 Types:
@@ -479,6 +519,19 @@ Methods:
 - client.users.getAuthenticatedUser({ ...params }) -> UserGetAuthenticatedUserResponse
 - client.users.setSuspended({ ...params }) -> unknown
 
+## Dotfiles
+
+Types:
+
+- DotfilesConfiguration
+- DotfileGetResponse
+- DotfileSetResponse
+
+Methods:
+
+- client.users.dotfiles.get({ ...params }) -> DotfileGetResponse
+- client.users.dotfiles.set({ ...params }) -> unknown
+
 ## Pats
 
 Types:
diff --git a/bin/publish-npm b/bin/publish-npm
index 4c21181..2505dec 100644
--- a/bin/publish-npm
+++ b/bin/publish-npm
@@ -4,19 +4,35 @@ 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
+# Get latest version from npm
+#
+# If the package doesn't exist, yarn will return
+# {"type":"error","data":"Received invalid response from npm."}
+# where .data.version doesn't exist so LAST_VERSION will be an empty string.
+LAST_VERSION="$(yarn info --json 2> /dev/null | jq -r '.data.version')"
+
+# Get current version from package.json
 VERSION="$(node -p "require('./package.json').version")"
 
-# Extract the pre-release tag if it exists
+# Check if current version is pre-release (e.g. alpha / beta / rc)
+CURRENT_IS_PRERELEASE=false
 if [[ "$VERSION" =~ -([a-zA-Z]+) ]]; then
-  # Extract the part before any dot in the pre-release identifier
-  TAG="${BASH_REMATCH[1]}"
+  CURRENT_IS_PRERELEASE=true
+  CURRENT_TAG="${BASH_REMATCH[1]}"
+fi
+
+# Check if last version is a stable release
+LAST_IS_STABLE_RELEASE=true
+if [[ -z "$LAST_VERSION" || "$LAST_VERSION" =~ -([a-zA-Z]+) ]]; then
+  LAST_IS_STABLE_RELEASE=false
+fi
+
+# Use a corresponding alpha/beta tag if there already is a stable release and we're publishing a prerelease.
+if $CURRENT_IS_PRERELEASE && $LAST_IS_STABLE_RELEASE; then
+  TAG="$CURRENT_TAG"
 else
   TAG="latest"
 fi
diff --git a/eslint.config.mjs b/eslint.config.mjs
index bd8e0cb..a73ce38 100644
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -10,7 +10,7 @@ export default tseslint.config(
       parserOptions: { sourceType: 'module' },
     },
     files: ['**/*.ts', '**/*.mts', '**/*.cts', '**/*.js', '**/*.mjs', '**/*.cjs'],
-    ignores: ['dist/**'],
+    ignores: ['dist/'],
     plugins: {
       '@typescript-eslint': tseslint.plugin,
       'unused-imports': unusedImports,
diff --git a/jest.config.ts b/jest.config.ts
index d185444..7b14a51 100644
--- a/jest.config.ts
+++ b/jest.config.ts
@@ -15,6 +15,7 @@ const config: JestConfigWithTsJest = {
     '/dist/',
     '/deno/',
     '/deno_tests/',
+    '/packages/',
   ],
   testPathIgnorePatterns: ['scripts'],
 };
diff --git a/package.json b/package.json
index d59480b..445157f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@gitpod/sdk",
-  "version": "0.5.0",
+  "version": "0.6.0",
   "description": "The official TypeScript library for the Gitpod API",
   "author": "Gitpod ",
   "types": "dist/index.d.ts",
@@ -17,7 +17,7 @@
     "test": "./scripts/test",
     "build": "./scripts/build",
     "prepublishOnly": "echo 'to publish, run yarn build && (cd dist; yarn publish)' && exit 1",
-    "format": "prettier --write --cache --cache-strategy metadata . !dist",
+    "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",
@@ -32,10 +32,10 @@
     "@types/node": "^20.17.6",
     "@types/ssh2": "^1.15.4",
     "@types/sshpk": "^1.17.4",
-    "@typescript-eslint/eslint-plugin": "^8.24.0",
-    "@typescript-eslint/parser": "^8.24.0",
+    "@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-prettier": "^5.4.1",
     "eslint-plugin-unused-imports": "^4.1.4",
     "iconv-lite": "^0.6.3",
     "jest": "^29.4.0",
@@ -45,10 +45,10 @@
     "sshpk": "^1.18.0",
     "ts-jest": "^29.1.0",
     "ts-node": "^10.5.0",
-    "tsc-multi": "https://github.com/stainless-api/tsc-multi/releases/download/v1.1.3/tsc-multi.tgz",
+    "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": "^4.8.2",
-    "typescript-eslint": "^8.24.0"
+    "typescript": "5.8.3",
+    "typescript-eslint": "8.31.1"
   },
   "imports": {
     "@gitpod/sdk": ".",
diff --git a/release-please-config.json b/release-please-config.json
index 624ed99..1ebd0bd 100644
--- a/release-please-config.json
+++ b/release-please-config.json
@@ -60,8 +60,5 @@
     }
   ],
   "release-type": "node",
-  "extra-files": [
-    "src/version.ts",
-    "README.md"
-  ]
+  "extra-files": ["src/version.ts", "README.md"]
 }
diff --git a/scripts/bootstrap b/scripts/bootstrap
index 05dd47a..0af58e2 100755
--- a/scripts/bootstrap
+++ b/scripts/bootstrap
@@ -4,7 +4,7 @@ set -e
 
 cd "$(dirname "$0")/.."
 
-if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ]; then
+if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ]; then
   brew bundle check >/dev/null 2>&1 || {
     echo "==> Installing Homebrew dependencies…"
     brew bundle
diff --git a/scripts/build b/scripts/build
index 08bfe2e..1909619 100755
--- a/scripts/build
+++ b/scripts/build
@@ -19,29 +19,24 @@ for file in LICENSE CHANGELOG.md; do
   if [ -e "${file}" ]; then cp "${file}" dist; fi
 done
 if [ -e "bin/cli" ]; then
-  mkdir dist/bin
+  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 add exports = module.exports = Gitpod to index.js;
-# No way to get that from index.ts because it would cause compile errors
+# 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
-# with "moduleResolution": "nodenext", if ESM resolves to index.d.ts,
-# it'll have TS errors on the default import.  But if it resolves to
-# index.d.mts the default import will work (even though both files have
-# the same export default statement)
-cp dist/index.d.ts dist/index.d.mts
 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
-mkdir -p dist/internal/polyfill
-cp src/internal/polyfill/*.{mjs,js,d.ts} dist/internal/polyfill
 
 node scripts/utils/postprocess-files.cjs
 
diff --git a/scripts/format b/scripts/format
index 903b1ef..7a75640 100755
--- a/scripts/format
+++ b/scripts/format
@@ -6,3 +6,7 @@ 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/utils/postprocess-files.cjs b/scripts/utils/postprocess-files.cjs
index d16c864..deae575 100644
--- a/scripts/utils/postprocess-files.cjs
+++ b/scripts/utils/postprocess-files.cjs
@@ -50,14 +50,14 @@ async function postprocess() {
     if (entry.isDirectory() && entry.name !== 'src' && entry.name !== 'internal' && entry.name !== 'bin') {
       const subpath = './' + entry.name;
       newExports[subpath + '/*.mjs'] = {
-        default: [subpath + '/*.mjs', subpath + '/*/index.mjs'],
+        default: subpath + '/*.mjs',
       };
       newExports[subpath + '/*.js'] = {
-        default: [subpath + '/*.js', subpath + '/*/index.js'],
+        default: subpath + '/*.js',
       };
       newExports[subpath + '/*'] = {
-        import: [subpath + '/*.mjs', subpath + '/*/index.mjs'],
-        require: [subpath + '/*.js', subpath + '/*/index.js'],
+        import: subpath + '/*.mjs',
+        require: subpath + '/*.js',
       };
     } else if (entry.isFile() && /\.[cm]?js$/.test(entry.name)) {
       const { name, ext } = path.parse(entry.name);
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
index a7416b0..8c775ee 100644
--- a/src/api-promise.ts
+++ b/src/api-promise.ts
@@ -1,92 +1,2 @@
-// 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 extends Promise {
-  private parsedPromise: Promise | undefined;
-  #client: Gitpod;
-
-  constructor(
-    client: Gitpod,
-    private responsePromise: Promise,
-    private parseResponse: (
-      client: Gitpod,
-      props: APIResponseProps,
-    ) => PromiseOrValue = 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(transform: (data: T, props: APIResponseProps) => U): APIPromise {
-    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 {
-    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 {
-    if (!this.parsedPromise) {
-      this.parsedPromise = this.responsePromise.then((data) => this.parseResponse(this.#client, data));
-    }
-    return this.parsedPromise;
-  }
-
-  override then(
-    onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null,
-    onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null,
-  ): Promise {
-    return this.parse().then(onfulfilled, onrejected);
-  }
-
-  override catch(
-    onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null,
-  ): Promise {
-    return this.parse().catch(onrejected);
-  }
-
-  override finally(onfinally?: (() => void) | undefined | null): Promise {
-    return this.parse().finally(onfinally);
-  }
-}
+/** @deprecated Import from ./core/api-promise instead */
+export * from './core/api-promise';
diff --git a/src/client.ts b/src/client.ts
index 9836801..ac677ba 100644
--- a/src/client.ts
+++ b/src/client.ts
@@ -1,18 +1,20 @@
 // 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 } from './internal/types';
+import type { HTTPMethod, PromiseOrValue, MergedRequestInit, FinalizedRequestInit } from './internal/types';
 import { uuid4 } from './internal/utils/uuid';
-import { validatePositiveInteger, isAbsoluteURL, hasOwn } from './internal/utils/values';
+import { validatePositiveInteger, isAbsoluteURL, safeJSON } from './internal/utils/values';
 import { sleep } from './internal/utils/sleep';
+import { type Logger, type LogLevel as LogLevelClient, parseLogLevel } from './internal/utils/log';
+export type { Logger, LogLevel as LogLevelClient } 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 './error';
-import * as Pagination from './pagination';
+import * as Errors from './core/error';
+import * as Pagination from './core/pagination';
 import {
   AbstractPage,
   type DomainVerificationsPageParams,
@@ -25,6 +27,8 @@ import {
   EnvironmentClassesPageResponse,
   type EnvironmentsPageParams,
   EnvironmentsPageResponse,
+  type GatewaysPageParams,
+  GatewaysPageResponse,
   type GroupsPageParams,
   GroupsPageResponse,
   type IntegrationsPageParams,
@@ -33,14 +37,14 @@ import {
   LoginProvidersPageResponse,
   type MembersPageParams,
   MembersPageResponse,
-  type OrganizationsPageParams,
-  OrganizationsPageResponse,
   type PersonalAccessTokensPageParams,
   PersonalAccessTokensPageResponse,
   type PoliciesPageParams,
   PoliciesPageResponse,
   type ProjectsPageParams,
   ProjectsPageResponse,
+  type RecordsPageParams,
+  RecordsPageResponse,
   type RunnersPageParams,
   RunnersPageResponse,
   type SSOConfigurationsPageParams,
@@ -55,10 +59,10 @@ import {
   TasksPageResponse,
   type TokensPageParams,
   TokensPageResponse,
-} from './pagination';
-import * as Uploads from './uploads';
+} from './core/pagination';
+import * as Uploads from './core/uploads';
 import * as API from './resources/index';
-import { APIPromise } from './api-promise';
+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';
@@ -68,6 +72,8 @@ import {
   AccountDeleteResponse,
   AccountGetSSOLoginURLParams,
   AccountGetSSOLoginURLResponse,
+  AccountListJoinableOrganizationsParams,
+  AccountListJoinableOrganizationsResponse,
   AccountListLoginProvidersParams,
   AccountMembership,
   AccountRetrieveParams,
@@ -97,8 +103,10 @@ import {
   ResourceOperation,
   ResourceType,
 } from './resources/events';
+import { GatewayListParams, Gateways } from './resources/gateways';
 import { Group, GroupListParams, Groups, GroupsGroupsPage } from './resources/groups';
 import {
+  IDTokenVersion,
   Identity,
   IdentityExchangeTokenParams,
   IdentityExchangeTokenResponse,
@@ -116,11 +124,18 @@ import {
   SecretGetValueParams,
   SecretGetValueResponse,
   SecretListParams,
+  SecretScope,
   SecretUpdateValueParams,
   SecretUpdateValueResponse,
   Secrets,
   SecretsSecretsPage,
 } from './resources/secrets';
+import {
+  EnvironmentUsageRecord,
+  EnvironmentUsageRecordsRecordsPage,
+  Usage,
+  UsageListEnvironmentRuntimeRecordsParams,
+} from './resources/usage';
 import { readEnv } from './internal/utils/env';
 import { formatRequestDetails, loggerFor } from './internal/utils/log';
 import { isEmptyObj } from './internal/utils/values';
@@ -128,6 +143,8 @@ import {
   AdmissionLevel,
   Environment,
   EnvironmentActivitySignal,
+  EnvironmentCreateEnvironmentTokenParams,
+  EnvironmentCreateEnvironmentTokenResponse,
   EnvironmentCreateFromProjectParams,
   EnvironmentCreateFromProjectResponse,
   EnvironmentCreateLogsTokenParams,
@@ -149,6 +166,8 @@ import {
   EnvironmentStatus,
   EnvironmentStopParams,
   EnvironmentStopResponse,
+  EnvironmentUnarchiveParams,
+  EnvironmentUnarchiveResponse,
   EnvironmentUpdateParams,
   EnvironmentUpdateResponse,
   Environments,
@@ -166,18 +185,16 @@ import {
   OrganizationLeaveParams,
   OrganizationLeaveResponse,
   OrganizationListMembersParams,
-  OrganizationListParams,
   OrganizationMember,
   OrganizationMembersMembersPage,
   OrganizationRetrieveParams,
   OrganizationRetrieveResponse,
   OrganizationSetRoleParams,
   OrganizationSetRoleResponse,
+  OrganizationTier,
   OrganizationUpdateParams,
   OrganizationUpdateResponse,
   Organizations,
-  OrganizationsOrganizationsPage,
-  Scope,
 } from './resources/organizations/organizations';
 import {
   EnvironmentInitializer,
@@ -199,6 +216,9 @@ import {
   ProjectsProjectsPage,
 } from './resources/projects/projects';
 import {
+  GatewayInfo,
+  LogLevel,
+  MetricsConfiguration,
   Runner,
   RunnerCapability,
   RunnerCheckAuthenticationForHostParams,
@@ -235,48 +255,6 @@ import {
   Users,
 } from './resources/users/users';
 
-const safeJSON = (text: string) => {
-  try {
-    return JSON.parse(text);
-  } catch (err) {
-    return undefined;
-  }
-};
-
-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 parseLogLevel = (
-  maybeLevel: string | undefined,
-  sourceName: string,
-  client: Gitpod,
-): LogLevel | undefined => {
-  if (!maybeLevel) {
-    return undefined;
-  }
-  const levels: Record = {
-    off: true,
-    error: true,
-    warn: true,
-    info: true,
-    debug: true,
-  };
-  if (hasOwn(levels, maybeLevel)) {
-    return maybeLevel;
-  }
-  loggerFor(client).warn(
-    `${sourceName} was set to ${JSON.stringify(maybeLevel)}, expected one of ${JSON.stringify(
-      Object.keys(levels),
-    )}`,
-  );
-  return undefined;
-};
-
 export interface ClientOptions {
   /**
    * Defaults to process.env['GITPOD_API_KEY'].
@@ -340,7 +318,7 @@ export interface ClientOptions {
    *
    * Defaults to process.env['GITPOD_LOG'] or 'warn' if it isn't set.
    */
-  logLevel?: LogLevel | undefined;
+  logLevel?: LogLevelClient | undefined;
 
   /**
    * Set the logger.
@@ -350,8 +328,6 @@ export interface ClientOptions {
   logger?: Logger | undefined;
 }
 
-type FinalizedRequestInit = RequestInit & { headers: Headers };
-
 /**
  * API Client for interfacing with the Gitpod API.
  */
@@ -362,7 +338,7 @@ export class Gitpod {
   maxRetries: number;
   timeout: number;
   logger: Logger | undefined;
-  logLevel: LogLevel | undefined;
+  logLevel: LogLevelClient | undefined;
   fetchOptions: MergedRequestInit | undefined;
 
   private fetch: Fetch;
@@ -419,6 +395,23 @@ export class Gitpod {
     this.bearerToken = bearerToken;
   }
 
+  /**
+   * Create a new client instance re-using the same options given to the current client with optional overriding.
+   */
+  withOptions(options: Partial): this {
+    return new (this.constructor as any as new (props: ClientOptions) => typeof this)({
+      ...this._options,
+      baseURL: this.baseURL,
+      maxRetries: this.maxRetries,
+      timeout: this.timeout,
+      logger: this.logger,
+      logLevel: this.logLevel,
+      fetchOptions: this.fetchOptions,
+      bearerToken: this.bearerToken,
+      ...options,
+    });
+  }
+
   protected defaultQuery(): Record | undefined {
     return this._options.defaultQuery;
   }
@@ -427,8 +420,8 @@ export class Gitpod {
     return;
   }
 
-  protected authHeaders(opts: FinalRequestOptions): Headers | undefined {
-    return new Headers({ Authorization: `Bearer ${this.bearerToken}` });
+  protected authHeaders(opts: FinalRequestOptions): NullableHeaders | undefined {
+    return buildHeaders([{ Authorization: `Bearer ${this.bearerToken}` }]);
   }
 
   /**
@@ -726,7 +719,9 @@ export class Gitpod {
 
     const timeout = setTimeout(() => controller.abort(), ms);
 
-    const isReadableBody = Shims.isReadableLike(options.body);
+    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,
@@ -740,12 +735,12 @@ export class Gitpod {
       fetchOptions.method = method.toUpperCase();
     }
 
-    return (
+    try {
       // use undefined this binding; fetch errors if bound to something else in browser/cloudflare
-      this.fetch.call(undefined, url, fetchOptions).finally(() => {
-        clearTimeout(timeout);
-      })
-    );
+      return await this.fetch.call(undefined, url, fetchOptions);
+    } finally {
+      clearTimeout(timeout);
+    }
   }
 
   private shouldRetry(response: Response): boolean {
@@ -826,17 +821,17 @@ export class Gitpod {
   }
 
   buildRequest(
-    options: FinalRequestOptions,
+    inputOptions: FinalRequestOptions,
     { retryCount = 0 }: { retryCount?: number } = {},
   ): { req: FinalizedRequestInit; url: string; timeout: number } {
-    options = { ...options };
+    const options = { ...inputOptions };
     const { method, path, query } = options;
 
     const url = this.buildURL(path!, query as Record);
     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, method, bodyHeaders, retryCount });
+    const reqHeaders = this.buildHeaders({ options: inputOptions, method, bodyHeaders, retryCount });
 
     const req: FinalizedRequestInit = {
       method,
@@ -875,7 +870,7 @@ export class Gitpod {
         Accept: 'application/json',
         'User-Agent': this.getUserAgent(),
         'X-Stainless-Retry-Count': String(retryCount),
-        ...(options.timeout ? { 'X-Stainless-Timeout': String(options.timeout) } : {}),
+        ...(options.timeout ? { 'X-Stainless-Timeout': String(Math.trunc(options.timeout / 1000)) } : {}),
         ...getPlatformHeaders(),
       },
       this.authHeaders(options),
@@ -949,24 +944,28 @@ export class Gitpod {
   editors: API.Editors = new API.Editors(this);
   environments: API.Environments = new API.Environments(this);
   events: API.Events = new API.Events(this);
+  gateways: API.Gateways = new API.Gateways(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.Gateways = Gateways;
 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;
@@ -995,6 +994,9 @@ export declare namespace Gitpod {
     type EnvironmentsPageResponse as EnvironmentsPageResponse,
   };
 
+  export import GatewaysPage = Pagination.GatewaysPage;
+  export { type GatewaysPageParams as GatewaysPageParams, type GatewaysPageResponse as GatewaysPageResponse };
+
   export import GroupsPage = Pagination.GroupsPage;
   export { type GroupsPageParams as GroupsPageParams, type GroupsPageResponse as GroupsPageResponse };
 
@@ -1013,12 +1015,6 @@ export declare namespace Gitpod {
   export import MembersPage = Pagination.MembersPage;
   export { type MembersPageParams as MembersPageParams, type MembersPageResponse as MembersPageResponse };
 
-  export import OrganizationsPage = Pagination.OrganizationsPage;
-  export {
-    type OrganizationsPageParams as OrganizationsPageParams,
-    type OrganizationsPageResponse as OrganizationsPageResponse,
-  };
-
   export import PersonalAccessTokensPage = Pagination.PersonalAccessTokensPage;
   export {
     type PersonalAccessTokensPageParams as PersonalAccessTokensPageParams,
@@ -1031,6 +1027,9 @@ export declare namespace Gitpod {
   export import ProjectsPage = Pagination.ProjectsPage;
   export { type ProjectsPageParams as ProjectsPageParams, type ProjectsPageResponse as ProjectsPageResponse };
 
+  export import RecordsPage = Pagination.RecordsPage;
+  export { type RecordsPageParams as RecordsPageParams, type RecordsPageResponse as RecordsPageResponse };
+
   export import RunnersPage = Pagination.RunnersPage;
   export { type RunnersPageParams as RunnersPageParams, type RunnersPageResponse as RunnersPageResponse };
 
@@ -1067,10 +1066,12 @@ export declare namespace Gitpod {
     type AccountRetrieveResponse as AccountRetrieveResponse,
     type AccountDeleteResponse as AccountDeleteResponse,
     type AccountGetSSOLoginURLResponse as AccountGetSSOLoginURLResponse,
+    type AccountListJoinableOrganizationsResponse as AccountListJoinableOrganizationsResponse,
     type LoginProvidersLoginProvidersPage as LoginProvidersLoginProvidersPage,
     type AccountRetrieveParams as AccountRetrieveParams,
     type AccountDeleteParams as AccountDeleteParams,
     type AccountGetSSOLoginURLParams as AccountGetSSOLoginURLParams,
+    type AccountListJoinableOrganizationsParams as AccountListJoinableOrganizationsParams,
     type AccountListLoginProvidersParams as AccountListLoginProvidersParams,
   };
 
@@ -1098,22 +1099,26 @@ export declare namespace Gitpod {
     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 EnvironmentUnarchiveResponse as EnvironmentUnarchiveResponse,
     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,
+    type EnvironmentUnarchiveParams as EnvironmentUnarchiveParams,
   };
 
   export {
@@ -1127,6 +1132,8 @@ export declare namespace Gitpod {
     type EventWatchParams as EventWatchParams,
   };
 
+  export { Gateways as Gateways, type GatewayListParams as GatewayListParams };
+
   export {
     Groups as Groups,
     type Group as Group,
@@ -1136,6 +1143,7 @@ export declare namespace Gitpod {
 
   export {
     Identity as Identity,
+    type IDTokenVersion as IDTokenVersion,
     type IdentityExchangeTokenResponse as IdentityExchangeTokenResponse,
     type IdentityGetAuthenticatedIdentityResponse as IdentityGetAuthenticatedIdentityResponse,
     type IdentityGetIDTokenResponse as IdentityGetIDTokenResponse,
@@ -1149,7 +1157,7 @@ export declare namespace Gitpod {
     type InviteDomains as InviteDomains,
     type Organization as Organization,
     type OrganizationMember as OrganizationMember,
-    type Scope as Scope,
+    type OrganizationTier as OrganizationTier,
     type OrganizationCreateResponse as OrganizationCreateResponse,
     type OrganizationRetrieveResponse as OrganizationRetrieveResponse,
     type OrganizationUpdateResponse as OrganizationUpdateResponse,
@@ -1157,12 +1165,10 @@ export declare namespace Gitpod {
     type OrganizationJoinResponse as OrganizationJoinResponse,
     type OrganizationLeaveResponse as OrganizationLeaveResponse,
     type OrganizationSetRoleResponse as OrganizationSetRoleResponse,
-    type OrganizationsOrganizationsPage as OrganizationsOrganizationsPage,
     type OrganizationMembersMembersPage as OrganizationMembersMembersPage,
     type OrganizationCreateParams as OrganizationCreateParams,
     type OrganizationRetrieveParams as OrganizationRetrieveParams,
     type OrganizationUpdateParams as OrganizationUpdateParams,
-    type OrganizationListParams as OrganizationListParams,
     type OrganizationDeleteParams as OrganizationDeleteParams,
     type OrganizationJoinParams as OrganizationJoinParams,
     type OrganizationLeaveParams as OrganizationLeaveParams,
@@ -1192,6 +1198,9 @@ export declare namespace Gitpod {
 
   export {
     Runners as Runners,
+    type GatewayInfo as GatewayInfo,
+    type LogLevel as LogLevel,
+    type MetricsConfiguration as MetricsConfiguration,
     type Runner as Runner,
     type RunnerCapability as RunnerCapability,
     type RunnerConfiguration as RunnerConfiguration,
@@ -1222,6 +1231,7 @@ export declare namespace Gitpod {
   export {
     Secrets as Secrets,
     type Secret as Secret,
+    type SecretScope as SecretScope,
     type SecretCreateResponse as SecretCreateResponse,
     type SecretDeleteResponse as SecretDeleteResponse,
     type SecretGetValueResponse as SecretGetValueResponse,
@@ -1234,6 +1244,13 @@ export declare namespace Gitpod {
     type SecretUpdateValueParams as SecretUpdateValueParams,
   };
 
+  export {
+    Usage as Usage,
+    type EnvironmentUsageRecord as EnvironmentUsageRecord,
+    type EnvironmentUsageRecordsRecordsPage as EnvironmentUsageRecordsRecordsPage,
+    type UsageListEnvironmentRuntimeRecordsParams as UsageListEnvironmentRuntimeRecordsParams,
+  };
+
   export {
     Users as Users,
     type User as User,
@@ -1247,6 +1264,7 @@ export declare namespace Gitpod {
   export type EnvironmentClass = API.EnvironmentClass;
   export type ErrorCode = API.ErrorCode;
   export type FieldValue = API.FieldValue;
+  export type Gateway = API.Gateway;
   export type OrganizationRole = API.OrganizationRole;
   export type Principal = API.Principal;
   export type RunsOn = API.RunsOn;
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 extends Promise {
+  private parsedPromise: Promise | undefined;
+  #client: Gitpod;
+
+  constructor(
+    client: Gitpod,
+    private responsePromise: Promise,
+    private parseResponse: (
+      client: Gitpod,
+      props: APIResponseProps,
+    ) => PromiseOrValue = 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(transform: (data: T, props: APIResponseProps) => U): APIPromise {
+    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 {
+    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 {
+    if (!this.parsedPromise) {
+      this.parsedPromise = this.responsePromise.then((data) => this.parseResponse(this.#client, data));
+    }
+    return this.parsedPromise;
+  }
+
+  override then(
+    onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null,
+    onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null,
+  ): Promise {
+    return this.parse().then(onfulfilled, onrejected);
+  }
+
+  override catch(
+    onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null,
+  ): Promise {
+    return this.parse().catch(onrejected);
+  }
+
+  override finally(onfinally?: (() => void) | undefined | null): Promise {
+    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;
+    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;
+
+    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 {
+  constructor({ message }: { message?: string } = {}) {
+    super(undefined, undefined, message || 'Request was aborted.', undefined);
+  }
+}
+
+export class APIConnectionError extends APIError {
+  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 {}
diff --git a/src/core/pagination.ts b/src/core/pagination.ts
new file mode 100644
index 0000000..77ed264
--- /dev/null
+++ b/src/core/pagination.ts
@@ -0,0 +1,1275 @@
+// 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;
+
+export abstract class AbstractPage-  implements AsyncIterable
-  {
+  #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 {
+    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 {
+    let page: this = this;
+    yield page;
+    while (page.hasNextPage()) {
+      page = await page.getNextPage();
+      yield page;
+    }
+  }
+
+  async *[Symbol.asyncIterator](): AsyncGenerator
-  {
+    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 = ReturnType[number],
+  >
+  extends APIPromise
+  implements AsyncIterable
- 
+{
+  constructor(
+    client: Gitpod,
+    request: Promise,
+    Page: new (...args: ConstructorParameters) => 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](): AsyncGenerator
-  {
+    const page = await this;
+    for await (const item of page) {
+      yield item;
+    }
+  }
+}
+
+export interface DomainVerificationsPageResponse
-  {
+  domainVerifications: Array
- ;
+
+  pagination: DomainVerificationsPageResponse.Pagination;
+}
+
+export namespace DomainVerificationsPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface DomainVerificationsPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class DomainVerificationsPage
- 
+  extends AbstractPage
- 
+  implements DomainVerificationsPageResponse
- 
+{
+  domainVerifications: Array
- ;
+
+  pagination: DomainVerificationsPageResponse.Pagination;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: DomainVerificationsPageResponse
- ,
+    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
-  {
+  editors: Array
- ;
+
+  pagination: EditorsPageResponse.Pagination;
+}
+
+export namespace EditorsPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface EditorsPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class EditorsPage
-  extends AbstractPage
-  implements EditorsPageResponse
-  {
+  editors: Array
- ;
+
+  pagination: EditorsPageResponse.Pagination;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: EditorsPageResponse
- ,
+    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
-  {
+  entries: Array
- ;
+
+  pagination: EntriesPageResponse.Pagination;
+}
+
+export namespace EntriesPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface EntriesPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class EntriesPage
-  extends AbstractPage
-  implements EntriesPageResponse
-  {
+  entries: Array
- ;
+
+  pagination: EntriesPageResponse.Pagination;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: EntriesPageResponse
- ,
+    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
-  {
+  environmentClasses: Array
- ;
+
+  pagination: EnvironmentClassesPageResponse.Pagination;
+}
+
+export namespace EnvironmentClassesPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface EnvironmentClassesPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class EnvironmentClassesPage
- 
+  extends AbstractPage
- 
+  implements EnvironmentClassesPageResponse
- 
+{
+  environmentClasses: Array
- ;
+
+  pagination: EnvironmentClassesPageResponse.Pagination;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: EnvironmentClassesPageResponse
- ,
+    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
-  {
+  environments: Array
- ;
+
+  pagination: EnvironmentsPageResponse.Pagination;
+}
+
+export namespace EnvironmentsPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface EnvironmentsPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class EnvironmentsPage
-  extends AbstractPage
-  implements EnvironmentsPageResponse
-  {
+  environments: Array
- ;
+
+  pagination: EnvironmentsPageResponse.Pagination;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: EnvironmentsPageResponse
- ,
+    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 GatewaysPageResponse
-  {
+  gateways: Array
- ;
+
+  pagination: GatewaysPageResponse.Pagination;
+}
+
+export namespace GatewaysPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface GatewaysPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class GatewaysPage
-  extends AbstractPage
-  implements GatewaysPageResponse
-  {
+  gateways: Array
- ;
+
+  pagination: GatewaysPageResponse.Pagination;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: GatewaysPageResponse
- ,
+    options: FinalRequestOptions,
+  ) {
+    super(client, response, body, options);
+
+    this.gateways = body.gateways || [];
+    this.pagination = body.pagination || {};
+  }
+
+  getPaginatedItems(): Item[] {
+    return this.gateways ?? [];
+  }
+
+  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
-  {
+  groups: Array
- ;
+
+  pagination: GroupsPageResponse.Pagination;
+}
+
+export namespace GroupsPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface GroupsPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class GroupsPage
-  extends AbstractPage
-  implements GroupsPageResponse
-  {
+  groups: Array
- ;
+
+  pagination: GroupsPageResponse.Pagination;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: GroupsPageResponse
- ,
+    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
-  {
+  integrations: Array
- ;
+
+  pagination: IntegrationsPageResponse.Pagination;
+}
+
+export namespace IntegrationsPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface IntegrationsPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class IntegrationsPage
-  extends AbstractPage
-  implements IntegrationsPageResponse
-  {
+  integrations: Array
- ;
+
+  pagination: IntegrationsPageResponse.Pagination;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: IntegrationsPageResponse
- ,
+    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
-  {
+  loginProviders: Array
- ;
+
+  pagination: LoginProvidersPageResponse.Pagination;
+}
+
+export namespace LoginProvidersPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface LoginProvidersPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class LoginProvidersPage
-  extends AbstractPage
-  implements LoginProvidersPageResponse
-  {
+  loginProviders: Array
- ;
+
+  pagination: LoginProvidersPageResponse.Pagination;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: LoginProvidersPageResponse
- ,
+    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
-  {
+  members: Array
- ;
+
+  pagination: MembersPageResponse.Pagination;
+}
+
+export namespace MembersPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface MembersPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class MembersPage
-  extends AbstractPage
-  implements MembersPageResponse
-  {
+  members: Array
- ;
+
+  pagination: MembersPageResponse.Pagination;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: MembersPageResponse
- ,
+    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
-  {
+  pagination: PersonalAccessTokensPageResponse.Pagination;
+
+  personalAccessTokens: Array
- ;
+}
+
+export namespace PersonalAccessTokensPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface PersonalAccessTokensPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class PersonalAccessTokensPage
- 
+  extends AbstractPage
- 
+  implements PersonalAccessTokensPageResponse
- 
+{
+  pagination: PersonalAccessTokensPageResponse.Pagination;
+
+  personalAccessTokens: Array
- ;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: PersonalAccessTokensPageResponse
- ,
+    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
-  {
+  pagination: PoliciesPageResponse.Pagination;
+
+  policies: Array
- ;
+}
+
+export namespace PoliciesPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface PoliciesPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class PoliciesPage
-  extends AbstractPage
-  implements PoliciesPageResponse
-  {
+  pagination: PoliciesPageResponse.Pagination;
+
+  policies: Array
- ;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: PoliciesPageResponse
- ,
+    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
-  {
+  pagination: ProjectsPageResponse.Pagination;
+
+  projects: Array
- ;
+}
+
+export namespace ProjectsPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface ProjectsPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class ProjectsPage
-  extends AbstractPage
-  implements ProjectsPageResponse
-  {
+  pagination: ProjectsPageResponse.Pagination;
+
+  projects: Array
- ;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: ProjectsPageResponse
- ,
+    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 RecordsPageResponse
-  {
+  pagination: RecordsPageResponse.Pagination;
+
+  records: Array
- ;
+}
+
+export namespace RecordsPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface RecordsPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class RecordsPage
-  extends AbstractPage
-  implements RecordsPageResponse
-  {
+  pagination: RecordsPageResponse.Pagination;
+
+  records: Array
- ;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: RecordsPageResponse
- ,
+    options: FinalRequestOptions,
+  ) {
+    super(client, response, body, options);
+
+    this.pagination = body.pagination || {};
+    this.records = body.records || [];
+  }
+
+  getPaginatedItems(): Item[] {
+    return this.records ?? [];
+  }
+
+  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
-  {
+  pagination: RunnersPageResponse.Pagination;
+
+  runners: Array
- ;
+}
+
+export namespace RunnersPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface RunnersPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class RunnersPage
-  extends AbstractPage
-  implements RunnersPageResponse
-  {
+  pagination: RunnersPageResponse.Pagination;
+
+  runners: Array
- ;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: RunnersPageResponse
- ,
+    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
-  {
+  pagination: SecretsPageResponse.Pagination;
+
+  secrets: Array
- ;
+}
+
+export namespace SecretsPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface SecretsPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class SecretsPage
-  extends AbstractPage
-  implements SecretsPageResponse
-  {
+  pagination: SecretsPageResponse.Pagination;
+
+  secrets: Array
- ;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: SecretsPageResponse
- ,
+    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
-  {
+  pagination: ServicesPageResponse.Pagination;
+
+  services: Array
- ;
+}
+
+export namespace ServicesPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface ServicesPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class ServicesPage
-  extends AbstractPage
-  implements ServicesPageResponse
-  {
+  pagination: ServicesPageResponse.Pagination;
+
+  services: Array
- ;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: ServicesPageResponse
- ,
+    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 SSOConfigurationsPageResponse
-  {
+  pagination: SSOConfigurationsPageResponse.Pagination;
+
+  ssoConfigurations: Array
- ;
+}
+
+export namespace SSOConfigurationsPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface SSOConfigurationsPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class SSOConfigurationsPage
- 
+  extends AbstractPage
- 
+  implements SSOConfigurationsPageResponse
- 
+{
+  pagination: SSOConfigurationsPageResponse.Pagination;
+
+  ssoConfigurations: Array
- ;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: SSOConfigurationsPageResponse
- ,
+    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
-  {
+  pagination: TaskExecutionsPageResponse.Pagination;
+
+  taskExecutions: Array
- ;
+}
+
+export namespace TaskExecutionsPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface TaskExecutionsPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class TaskExecutionsPage
-  extends AbstractPage
-  implements TaskExecutionsPageResponse
-  {
+  pagination: TaskExecutionsPageResponse.Pagination;
+
+  taskExecutions: Array
- ;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: TaskExecutionsPageResponse
- ,
+    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
-  {
+  pagination: TasksPageResponse.Pagination;
+
+  tasks: Array
- ;
+}
+
+export namespace TasksPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface TasksPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class TasksPage
-  extends AbstractPage
-  implements TasksPageResponse
-  {
+  pagination: TasksPageResponse.Pagination;
+
+  tasks: Array
- ;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: TasksPageResponse
- ,
+    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
-  {
+  pagination: TokensPageResponse.Pagination;
+
+  tokens: Array
- ;
+}
+
+export namespace TokensPageResponse {
+  export interface Pagination {
+    nextToken?: string;
+  }
+}
+
+export interface TokensPageParams {
+  pageSize?: number;
+
+  token?: string;
+}
+
+export class TokensPage
-  extends AbstractPage
-  implements TokensPageResponse
-  {
+  pagination: TokensPageResponse.Pagination;
+
+  tokens: Array
- ;
+
+  constructor(
+    client: Gitpod,
+    response: Response,
+    body: TokensPageResponse
- ,
+    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
index 67145c6..fc55f46 100644
--- a/src/error.ts
+++ b/src/error.ts
@@ -1,140 +1,2 @@
-// 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;
-    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;
-
-    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 {
-  constructor({ message }: { message?: string } = {}) {
-    super(undefined, undefined, message || 'Request was aborted.', undefined);
-  }
-}
-
-export class APIConnectionError extends APIError {
-  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 {}
+/** @deprecated Import from ./core/error instead */
+export * from './core/error';
diff --git a/src/index.ts b/src/index.ts
index 2b3917a..0eaf4c8 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -2,10 +2,10 @@
 
 export { Gitpod as default } from './client';
 
-export { type Uploadable, toFile } from './uploads';
-export { APIPromise } from './api-promise';
+export { type Uploadable, toFile } from './core/uploads';
+export { APIPromise } from './core/api-promise';
 export { Gitpod, type ClientOptions } from './client';
-export { PagePromise } from './pagination';
+export { PagePromise } from './core/pagination';
 export {
   GitpodError,
   APIError,
@@ -20,4 +20,4 @@ export {
   InternalServerError,
   PermissionDeniedError,
   UnprocessableEntityError,
-} from './error';
+} 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
index b2e598a..c23d3bd 100644
--- a/src/internal/builtin-types.ts
+++ b/src/internal/builtin-types.ts
@@ -39,9 +39,23 @@ type _HeadersInit = RequestInit['headers'];
  */
 type _BodyInit = RequestInit['body'];
 
+/**
+ * An alias to the builtin `Array` type so we can
+ * easily alias it in import statements if there are name clashes.
+ */
+type _Array = Array;
+
+/**
+ * An alias to the builtin `Record` type so we can
+ * easily alias it in import statements if there are name clashes.
+ */
+type _Record = Record;
+
 export type {
+  _Array as Array,
   _BodyInit as BodyInit,
   _HeadersInit as HeadersInit,
+  _Record as Record,
   _RequestInfo as RequestInfo,
   _RequestInit as RequestInit,
   _Response as Response,
diff --git a/src/internal/decoders/jsonl.ts b/src/internal/decoders/jsonl.ts
index cb40d92..79ec5f8 100644
--- a/src/internal/decoders/jsonl.ts
+++ b/src/internal/decoders/jsonl.ts
@@ -1,4 +1,4 @@
-import { GitpodError } from '../../error';
+import { GitpodError } from '../../core/error';
 import { ReadableStreamToAsyncIterable } from '../shims';
 import { LineDecoder, type Bytes } from './line';
 
diff --git a/src/internal/decoders/line.ts b/src/internal/decoders/line.ts
index 0295286..b3bfa97 100644
--- a/src/internal/decoders/line.ts
+++ b/src/internal/decoders/line.ts
@@ -1,4 +1,4 @@
-import { GitpodError } from '../../error';
+import { concatBytes, decodeUTF8, encodeUTF8 } from '../utils/bytes';
 
 export type Bytes = string | ArrayBuffer | Uint8Array | null | undefined;
 
@@ -13,16 +13,11 @@ export class LineDecoder {
   static NEWLINE_CHARS = new Set(['\n', '\r']);
   static NEWLINE_REGEXP = /\r\n|[\n\r]/g;
 
-  buffer: Uint8Array;
+  #buffer: Uint8Array;
   #carriageReturnIndex: number | null;
-  textDecoder:
-    | undefined
-    | {
-        decode(buffer: Uint8Array | ArrayBuffer): string;
-      };
 
   constructor() {
-    this.buffer = new Uint8Array();
+    this.#buffer = new Uint8Array();
     this.#carriageReturnIndex = null;
   }
 
@@ -33,17 +28,14 @@ export class LineDecoder {
 
     const binaryChunk =
       chunk instanceof ArrayBuffer ? new Uint8Array(chunk)
-      : typeof chunk === 'string' ? new TextEncoder().encode(chunk)
+      : typeof chunk === 'string' ? encodeUTF8(chunk)
       : chunk;
 
-    let newData = new Uint8Array(this.buffer.length + binaryChunk.length);
-    newData.set(this.buffer);
-    newData.set(binaryChunk, this.buffer.length);
-    this.buffer = newData;
+    this.#buffer = concatBytes([this.#buffer, binaryChunk]);
 
     const lines: string[] = [];
     let patternIndex;
-    while ((patternIndex = findNewlineIndex(this.buffer, this.#carriageReturnIndex)) != null) {
+    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;
@@ -55,8 +47,8 @@ export class LineDecoder {
         this.#carriageReturnIndex != null &&
         (patternIndex.index !== this.#carriageReturnIndex + 1 || patternIndex.carriage)
       ) {
-        lines.push(this.decodeText(this.buffer.slice(0, this.#carriageReturnIndex - 1)));
-        this.buffer = this.buffer.slice(this.#carriageReturnIndex);
+        lines.push(decodeUTF8(this.#buffer.subarray(0, this.#carriageReturnIndex - 1)));
+        this.#buffer = this.#buffer.subarray(this.#carriageReturnIndex);
         this.#carriageReturnIndex = null;
         continue;
       }
@@ -64,55 +56,18 @@ export class LineDecoder {
       const endIndex =
         this.#carriageReturnIndex !== null ? patternIndex.preceding - 1 : patternIndex.preceding;
 
-      const line = this.decodeText(this.buffer.slice(0, endIndex));
+      const line = decodeUTF8(this.#buffer.subarray(0, endIndex));
       lines.push(line);
 
-      this.buffer = this.buffer.slice(patternIndex.index);
+      this.#buffer = this.#buffer.subarray(patternIndex.index);
       this.#carriageReturnIndex = null;
     }
 
     return lines;
   }
 
-  decodeText(bytes: Bytes): string {
-    if (bytes == null) return '';
-    if (typeof bytes === 'string') return bytes;
-
-    // Node:
-    if (typeof (globalThis as any).Buffer !== 'undefined') {
-      if (bytes instanceof (globalThis as any).Buffer) {
-        return bytes.toString();
-      }
-      if (bytes instanceof Uint8Array) {
-        return (globalThis as any).Buffer.from(bytes).toString();
-      }
-
-      throw new GitpodError(
-        `Unexpected: received non-Uint8Array (${bytes.constructor.name}) stream chunk in an environment with a global "Buffer" defined, which this library assumes to be Node. Please report this error.`,
-      );
-    }
-
-    // Browser
-    if (typeof (globalThis as any).TextDecoder !== 'undefined') {
-      if (bytes instanceof Uint8Array || bytes instanceof ArrayBuffer) {
-        this.textDecoder ??= new (globalThis as any).TextDecoder('utf8');
-        return this.textDecoder!.decode(bytes);
-      }
-
-      throw new GitpodError(
-        `Unexpected: received non-Uint8Array/ArrayBuffer (${
-          (bytes as any).constructor.name
-        }) in a web platform. Please report this error.`,
-      );
-    }
-
-    throw new GitpodError(
-      `Unexpected: neither Buffer nor TextDecoder are available as globals. Please report this error.`,
-    );
-  }
-
   flush(): string[] {
-    if (!this.buffer.length) {
+    if (!this.#buffer.length) {
       return [];
     }
     return this.decode('\n');
diff --git a/src/internal/detect-platform.ts b/src/internal/detect-platform.ts
index c5e273b..e82d95c 100644
--- a/src/internal/detect-platform.ts
+++ b/src/internal/detect-platform.ts
@@ -85,10 +85,10 @@ const getPlatformProperties = (): PlatformProperties => {
     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-OS': normalizePlatform((globalThis as any).process.platform ?? 'unknown'),
+      'X-Stainless-Arch': normalizeArch((globalThis as any).process.arch ?? 'unknown'),
       'X-Stainless-Runtime': 'node',
-      'X-Stainless-Runtime-Version': (globalThis as any).process.version,
+      'X-Stainless-Runtime-Version': (globalThis as any).process.version ?? 'unknown',
     };
   }
 
diff --git a/src/internal/errors.ts b/src/internal/errors.ts
index 653a6ec..82c7b14 100644
--- a/src/internal/errors.ts
+++ b/src/internal/errors.ts
@@ -22,7 +22,7 @@ export const castToError = (err: any): Error => {
         // @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;
-        throw error;
+        return error;
       }
     } catch {}
     try {
diff --git a/src/internal/headers.ts b/src/internal/headers.ts
index a110a12..5cc03ce 100644
--- a/src/internal/headers.ts
+++ b/src/internal/headers.ts
@@ -3,7 +3,7 @@
 type HeaderValue = string | undefined | null;
 export type HeadersLike =
   | Headers
-  | readonly [string, HeaderValue][]
+  | readonly HeaderValue[][]
   | Record
   | undefined
   | null
@@ -40,7 +40,7 @@ function* iterateHeaders(headers: HeadersLike): IterableIterator;
+  let iter: Iterable;
   if (headers instanceof Headers) {
     iter = headers.entries();
   } else if (isArray(headers)) {
@@ -51,6 +51,7 @@ function* iterateHeaders(headers: HeadersLike): IterableIterator {
   const targetHeaders = new Headers();
   const nullHeaders = new Set();
-  const seenHeaders = new Set();
   for (const headers of newHeaders) {
+    const seenHeaders = new Set();
     for (const [name, value] of iterateHeaders(headers)) {
       const lowerName = name.toLowerCase();
       if (!seenHeaders.has(lowerName)) {
diff --git a/src/internal/parse.ts b/src/internal/parse.ts
index 799b71c..a12647d 100644
--- a/src/internal/parse.ts
+++ b/src/internal/parse.ts
@@ -26,16 +26,14 @@ export async function defaultParseResponse(client: Gitpod, props: APIResponse
     }
 
     const contentType = response.headers.get('content-type');
-    const isJSON =
-      contentType?.includes('application/json') || contentType?.includes('application/vnd.api+json');
+    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();
-
-    // TODO handle blob, arraybuffer, other content types, etc.
     return text as unknown as T;
   })();
   loggerFor(client).debug(
diff --git a/src/internal/polyfill/crypto.node.d.ts b/src/internal/polyfill/crypto.node.d.ts
deleted file mode 100644
index dc7caac..0000000
--- a/src/internal/polyfill/crypto.node.d.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-export declare const crypto: {
-  /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Crypto/getRandomValues) */
-  getRandomValues(array: T): T;
-  /**
-   * Available only in secure contexts.
-   *
-   * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Crypto/randomUUID)
-   */
-  randomUUID?: () => string;
-};
diff --git a/src/internal/polyfill/crypto.node.js b/src/internal/polyfill/crypto.node.js
deleted file mode 100644
index 83062a3..0000000
--- a/src/internal/polyfill/crypto.node.js
+++ /dev/null
@@ -1,11 +0,0 @@
-if (typeof require !== 'undefined') {
-  if (globalThis.crypto) {
-    exports.crypto = globalThis.crypto;
-  } else {
-    try {
-      // Use [require][0](...) and not require(...) so bundlers don't try to bundle the
-      // crypto module.
-      exports.crypto = [require][0]('node:crypto').webcrypto;
-    } catch (e) {}
-  }
-}
diff --git a/src/internal/polyfill/crypto.node.mjs b/src/internal/polyfill/crypto.node.mjs
deleted file mode 100644
index 24c6f3b..0000000
--- a/src/internal/polyfill/crypto.node.mjs
+++ /dev/null
@@ -1,2 +0,0 @@
-import * as mod from './crypto.node.js';
-export const crypto = globalThis.crypto || mod.crypto;
diff --git a/src/internal/polyfill/file.node.d.ts b/src/internal/polyfill/file.node.d.ts
deleted file mode 100644
index b2a59bf..0000000
--- a/src/internal/polyfill/file.node.d.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-/**
- * This file polyfills the global `File` object for you if it's not already defined
- * when running on Node.js
- *
- * This is only needed on Node.js v18 & v19. Newer versions already define `File`
- * as a global.
- */
-
-export {};
diff --git a/src/internal/polyfill/file.node.js b/src/internal/polyfill/file.node.js
deleted file mode 100644
index eba997e..0000000
--- a/src/internal/polyfill/file.node.js
+++ /dev/null
@@ -1,17 +0,0 @@
-/**
- * This file polyfills the global `File` object for you if it's not already defined
- * when running on Node.js
- *
- * This is only needed on Node.js v18 & v19. Newer versions already define `File`
- * as a global.
- */
-
-if (typeof require !== 'undefined') {
-  if (!globalThis.File) {
-    try {
-      // Use [require][0](...) and not require(...) so bundlers don't try to bundle the
-      // buffer module.
-      globalThis.File = [require][0]('node:buffer').File;
-    } catch (e) {}
-  }
-}
diff --git a/src/internal/polyfill/file.node.mjs b/src/internal/polyfill/file.node.mjs
deleted file mode 100644
index 520dcb8..0000000
--- a/src/internal/polyfill/file.node.mjs
+++ /dev/null
@@ -1,9 +0,0 @@
-/**
- * This file polyfills the global `File` object for you if it's not already defined
- * when running on Node.js
- *
- * This is only needed on Node.js v18 & v19. Newer versions already define `File`
- * as a global.
- */
-
-import './file.node.js';
diff --git a/src/internal/shim-types.d.ts b/src/internal/shim-types.d.ts
deleted file mode 100644
index fe48144..0000000
--- a/src/internal/shim-types.d.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-// 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 = keyof T extends never ? false : true;
-
-// @ts-ignore
-type _ReadableStream =
-  // @ts-ignore
-  HasProperties extends true ? NodeJS.ReadableStream : ReadableStream;
-
-// @ts-ignore
-declare const _ReadableStream: unknown extends typeof ReadableStream ? never : typeof ReadableStream;
-export { _ReadableStream as ReadableStream };
diff --git a/src/internal/shim-types.ts b/src/internal/shim-types.ts
new file mode 100644
index 0000000..8ddf7b0
--- /dev/null
+++ b/src/internal/shim-types.ts
@@ -0,0 +1,26 @@
+// 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.
+ */
+
+type NeverToAny = T extends never ? any : T;
+
+/** @ts-ignore */
+type _DOMReadableStream = globalThis.ReadableStream;
+
+/** @ts-ignore */
+type _NodeReadableStream = import('stream/web').ReadableStream;
+
+type _ConditionalNodeReadableStream =
+  typeof globalThis extends { ReadableStream: any } ? never : _NodeReadableStream;
+
+type _ReadableStream = NeverToAny<
+  | ([0] extends [1 & _DOMReadableStream] ? never : _DOMReadableStream)
+  | ([0] extends [1 & _ConditionalNodeReadableStream] ? never : _ConditionalNodeReadableStream)
+>;
+
+export type { _ReadableStream as ReadableStream };
diff --git a/src/internal/shims.ts b/src/internal/shims.ts
index cb91e94..284d15c 100644
--- a/src/internal/shims.ts
+++ b/src/internal/shims.ts
@@ -7,8 +7,8 @@
  * messages in cases where an environment isn't fully supported.
  */
 
-import { type Fetch } from './builtin-types';
-import { type ReadableStream } from './shim-types';
+import type { Fetch } from './builtin-types';
+import type { ReadableStream } from './shim-types';
 
 export function getDefaultFetch(): Fetch {
   if (typeof fetch !== 'undefined') {
@@ -20,62 +20,6 @@ export function getDefaultFetch(): Fetch {
   );
 }
 
-/**
- * A minimal copy of the NodeJS `stream.Readable` class so that we can
- * accept the NodeJS types in certain places, e.g. file uploads
- *
- * https://nodejs.org/api/stream.html#class-streamreadable
- */
-export interface ReadableLike {
-  readable: boolean;
-  readonly readableEnded: boolean;
-  readonly readableFlowing: boolean | null;
-  readonly readableHighWaterMark: number;
-  readonly readableLength: number;
-  readonly readableObjectMode: boolean;
-  destroyed: boolean;
-  read(size?: number): any;
-  pause(): this;
-  resume(): this;
-  isPaused(): boolean;
-  destroy(error?: Error): this;
-  [Symbol.asyncIterator](): AsyncIterableIterator;
-}
-
-/**
- * Determines if the given value looks like a NodeJS `stream.Readable`
- * object and that it is readable, i.e. has not been consumed.
- *
- * https://nodejs.org/api/stream.html#class-streamreadable
- */
-export function isReadableLike(value: any) {
-  // We declare our own class of Readable here, so it's not feasible to
-  // do an 'instanceof' check. Instead, check for Readable-like properties.
-  return !!value && value.readable === true && typeof value.read === 'function';
-}
-
-/**
- * A minimal copy of the NodeJS `fs.ReadStream` class for usage within file uploads.
- *
- * https://nodejs.org/api/fs.html#class-fsreadstream
- */
-export interface FsReadStreamLike extends ReadableLike {
-  path: {}; // real type is string | Buffer but we can't reference `Buffer` here
-}
-
-/**
- * Determines if the given value looks like a NodeJS `fs.ReadStream`
- * object.
- *
- * This just checks if the object matches our `Readable` interface
- * and defines a `path` property, there may be false positives.
- *
- * https://nodejs.org/api/fs.html#class-fsreadstream
- */
-export function isFsReadStreamLike(value: any): value is FsReadStreamLike {
-  return isReadableLike(value) && 'path' in value;
-}
-
 type ReadableStreamArgs = ConstructorParameters;
 
 export function makeReadableStream(...args: ReadableStreamArgs): ReadableStream {
diff --git a/src/internal/to-file.ts b/src/internal/to-file.ts
new file mode 100644
index 0000000..245e849
--- /dev/null
+++ b/src/internal/to-file.ts
@@ -0,0 +1,154 @@
+import { BlobPart, getName, makeFile, isAsyncIterable } from './uploads';
+import type { FilePropertyBag } from './builtin-types';
+import { checkFileSupport } from './uploads';
+
+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;
+  /** [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 } =>
+  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 } =>
+  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;
+}
+
+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
+  | AsyncIterable;
+
+/**
+ * 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,
+  name?: string | null | undefined,
+  options?: FilePropertyBag | undefined,
+): Promise