Repository: kurtosis-tech/kurtosis
Pull request #2042 (2024-01-11 13:29:07)

chore: ete emui cypress
## Description:
This PR adds ETE cypress testing for the new emui.

## Is this change user facing?
NO


Patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 7f4dcdaa7..a17ac536a 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -780,6 +780,41 @@ jobs:
               false
             fi
 
+  test_enclave_manager_web_ui:
+    executor: ubuntu_vm
+    steps:
+      - checkout
+
+      - <<: *abort_job_if_only_docs_changes
+
+      # Set up Kurtosis
+      - attach_workspace:
+          at: "<< pipeline.parameters.workspace-with-cli-binary-and-images-mountpoint >>"
+
+      - run: |
+          echo "deb [trusted=yes] https://apt.fury.io/kurtosis-tech/ /" | sudo tee /etc/apt/sources.list.d/kurtosis.list
+          sudo apt update
+          sudo apt install kurtosis-cli
+      # We don't send metrics to avoid polluting our logs
+      - run: |
+          echo 'export KURTOSIS_BINPATH="<< pipeline.parameters.workspace-with-cli-binary-and-images-mountpoint >>/<< pipeline.parameters.cli-dist-home-relative-dirpath >>/<< pipeline.parameters.cli-linux-amd-64-binary-relative-filepath >>"' >> "${BASH_ENV}"
+      - run: "${KURTOSIS_BINPATH} analytics disable"
+
+      - <<: *run_prepare_testing_docker
+
+      - run: npm install -g yarn
+
+      - run: enclave-manager/api/scripts/build.sh
+
+      - run:
+          command: yarn cleanInstall
+          working_directory: enclave-manager/web
+
+      - run:
+          command: yarn cypress:ete
+          working_directory: enclave-manager/web
+
+
   test_basic_cli_functionality:
     executor: ubuntu_vm
     parameters:
@@ -1423,6 +1458,18 @@ workflows:
 
       - build_cli:
           <<: *filters_ignore_main
+
+      - test_enclave_manager_web_ui:
+          name: "Test Basic Web UI Functionality in Docker"
+          context:
+            - docker-user
+          requires:
+            - build_cli
+            - build_api_container_server
+            - build_engine_server
+            - build_files_artifacts_expander
+          <<: *filters_ignore_main
+
       - test_basic_cli_functionality:
           name: "Test Basic CLI Functionality in Docker"
           cli-cluster-backend: "docker"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bf2e5c1b5..4d20c9778 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
 # Changelog
 
+## [0.86.5](https://github.com/kurtosis-tech/kurtosis/compare/0.86.4...0.86.5) (2024-01-15)
+
+
+### Features
+
+* docker compose integration pt. 2 ([#2043](https://github.com/kurtosis-tech/kurtosis/issues/2043)) ([2a2793b](https://github.com/kurtosis-tech/kurtosis/commit/2a2793bfd6014ff375fc0bb27018df77a5af330f))
+
 ## [0.86.4](https://github.com/kurtosis-tech/kurtosis/compare/0.86.3...0.86.4) (2024-01-10)
 
 
diff --git a/LICENSE.md b/LICENSE.md
index 218428024..e6752cccf 100644
--- a/LICENSE.md
+++ b/LICENSE.md
@@ -3,7 +3,7 @@ Business Source License 1.1
 Parameters
 
 Licensor:             Kurtosis Technologies, Inc.
-Licensed Work:        Kurtosis 0.86.4
+Licensed Work:        Kurtosis 0.86.5
 The Licensed Work is (c) 2024 Kurtosis Technologies, Inc.
 Additional Use Grant: You may make use of the Licensed Work, provided that
 you may not use the Licensed Work for an Environment Orchestration Service.
@@ -12,7 +12,7 @@ you may not use the Licensed Work for an Environment Orchestration Service.
                       allows third parties (other than your employees and
                       contractors) to create distributed system environments.
 
-Change Date:          2028-01-10
+Change Date:          2028-01-15
 
 Change License:       Apache 2.0 (Apache License, Version 2.0)
 
diff --git a/api/golang/core/lib/enclaves/enclave_context.go b/api/golang/core/lib/enclaves/enclave_context.go
index 75a2b4e80..f373530b4 100644
--- a/api/golang/core/lib/enclaves/enclave_context.go
+++ b/api/golang/core/lib/enclaves/enclave_context.go
@@ -47,8 +47,22 @@ const (
 	osPathSeparatorString = string(os.PathSeparator)
 
 	dotRelativePathIndicatorString = "."
+
+	// required to get around "only Github URLs" validation
+	composePackageIdPlaceholder = "github.com/NOTIONAL_USER/USER_UPLOADED_COMPOSE_PACKAGE"
 )
 
+// TODO Remove this once package ID is detected ONLY the APIC side (i.e. the CLI doesn't need to tell the APIC what package ID it's using)
+// Doing so requires that we upload completely anonymous packages to the APIC, and it figures things out from there
+var supportedDockerComposeYmlFilenames = []string{
+	"compose.yml",
+	"compose.yaml",
+	"docker-compose.yml",
+	"docker-compose.yaml",
+	"docker_compose.yml",
+	"docker_compose.yaml",
+}
+
 // Docs available at https://docs.kurtosis.com/sdk/#enclavecontext
 type EnclaveContext struct {
 	client kurtosis_core_rpc_api_bindings.ApiContainerServiceClient
@@ -143,12 +157,22 @@ func (enclaveCtx *EnclaveContext) RunStarlarkPackage(
 
 	starlarkResponseLineChan := make(chan *kurtosis_core_rpc_api_bindings.StarlarkRunResponseLine)
 
-	kurtosisYml, err := getKurtosisYaml(packageRootPath)
+	packageName, packageReplaceOptions, err := getPackageNameAndReplaceOptions(packageRootPath)
 	if err != nil {
-		return nil, nil, stacktrace.Propagate(err, "An error occurred getting Kurtosis yaml file from path '%s'", packageRootPath)
+		return nil, nil, err
 	}
 
-	executeStartosisPackageArgs, err := enclaveCtx.assembleRunStartosisPackageArg(kurtosisYml, runConfig.RelativePathToMainFile, runConfig.MainFunctionName, serializedParams, runConfig.DryRun, runConfig.Parallelism, runConfig.ExperimentalFeatureFlags, runConfig.CloudInstanceId, runConfig.CloudUserId, runConfig.ImageDownload)
+	executeStartosisPackageArgs, err := enclaveCtx.assembleRunStartosisPackageArg(
+		packageName,
+		runConfig.RelativePathToMainFile,
+		runConfig.MainFunctionName,
+		serializedParams,
+		runConfig.DryRun,
+		runConfig.Parallelism,
+		runConfig.ExperimentalFeatureFlags,
+		runConfig.CloudInstanceId,
+		runConfig.CloudUserId,
+		runConfig.ImageDownload)
 	if err != nil {
 		return nil, nil, stacktrace.Propagate(err, "Error preparing package '%s' for execution", packageRootPath)
 	}
@@ -158,9 +182,9 @@ func (enclaveCtx *EnclaveContext) RunStarlarkPackage(
 		return nil, nil, stacktrace.Propagate(err, "Error uploading package '%s' prior to executing it", packageRootPath)
 	}
 
-	if len(kurtosisYml.PackageReplaceOptions) > 0 {
-		if err = enclaveCtx.uploadLocalStarlarkPackageDependencies(packageRootPath, kurtosisYml.PackageReplaceOptions); err != nil {
-			return nil, nil, stacktrace.Propagate(err, "An error occurred while uploading the local starlark package dependencies from the replace options '%+v'", kurtosisYml.PackageReplaceOptions)
+	if len(packageReplaceOptions) > 0 {
+		if err = enclaveCtx.uploadLocalStarlarkPackageDependencies(packageRootPath, packageReplaceOptions); err != nil {
+			return nil, nil, stacktrace.Propagate(err, "An error occurred while uploading the local starlark package dependencies from the replace options '%+v'", packageReplaceOptions)
 		}
 	}
 
@@ -174,6 +198,45 @@ func (enclaveCtx *EnclaveContext) RunStarlarkPackage(
 	return starlarkResponseLineChan, cancelCtxFunc, nil
 }
 
+// Determines the package name and replace options based on [packageRootPath]
+// If a kurtosis.yml is detected, package is a kurtosis package
+// If a valid [supportedDockerComposeYaml] is detected, package is a docker compose package
+func getPackageNameAndReplaceOptions(packageRootPath string) (string, map[string]string, error) {
+	var packageName string
+	var packageReplaceOptions map[string]string
+
+	// use kurtosis package if it exists
+	if _, err := os.Stat(path.Join(packageRootPath, kurtosisYamlFilename)); err == nil {
+		kurtosisYml, err := getKurtosisYaml(packageRootPath)
+		if err != nil {
+			return "", map[string]string{}, stacktrace.Propagate(err, "An error occurred getting Kurtosis yaml file from path '%s'", packageRootPath)
+		}
+		packageName = kurtosisYml.PackageName
+		packageReplaceOptions = kurtosisYml.PackageReplaceOptions
+	} else {
+		// use compose package if it exists
+		composeAbsFilepath := ""
+		for _, candidateComposeFilename := range supportedDockerComposeYmlFilenames {
+			candidateComposeAbsFilepath := path.Join(packageRootPath, candidateComposeFilename)
+			if _, err := os.Stat(candidateComposeAbsFilepath); err == nil {
+				composeAbsFilepath = candidateComposeAbsFilepath
+				break
+			}
+		}
+		if composeAbsFilepath == "" {
+			return "", map[string]string{}, stacktrace.NewError(
+				"Neither a '%s' file nor one of the default Compose files (%s) was found in the package root; at least one of these is required",
+				kurtosisYamlFilename,
+				strings.Join(supportedDockerComposeYmlFilenames, ", "),
+			)
+		}
+		packageName = composePackageIdPlaceholder
+		packageReplaceOptions = map[string]string{}
+	}
+
+	return packageName, packageReplaceOptions, nil
+}
+
 func (enclaveCtx *EnclaveContext) uploadLocalStarlarkPackageDependencies(packageRootPath string, packageReplaceOptions map[string]string) error {
 	for dependencyPackageId, replaceOption := range packageReplaceOptions {
 		if isLocalDependencyReplace(replaceOption) {
@@ -523,7 +586,7 @@ func getErrFromStarlarkRunResult(result *StarlarkRunResult) error {
 }
 
 func (enclaveCtx *EnclaveContext) assembleRunStartosisPackageArg(
-	kurtosisYaml *KurtosisYaml,
+	packageName string,
 	relativePathToMainFile string,
 	mainFunctionName string,
 	serializedParams string,
@@ -535,7 +598,7 @@ func (enclaveCtx *EnclaveContext) assembleRunStartosisPackageArg(
 	imageDownloadMode kurtosis_core_rpc_api_bindings.ImageDownloadMode,
 ) (*kurtosis_core_rpc_api_bindings.RunStarlarkPackageArgs, error) {
 
-	return binding_constructors.NewRunStarlarkPackageArgs(kurtosisYaml.PackageName, relativePathToMainFile, mainFunctionName, serializedParams, dryRun, parallelism, experimentalFeatures, cloudInstanceId, cloudUserId, imageDownloadMode), nil
+	return binding_constructors.NewRunStarlarkPackageArgs(packageName, relativePathToMainFile, mainFunctionName, serializedParams, dryRun, parallelism, experimentalFeatures, cloudInstanceId, cloudUserId, imageDownloadMode), nil
 }
 
 func (enclaveCtx *EnclaveContext) uploadStarlarkPackage(packageId string, packageRootPath string) error {
diff --git a/api/golang/core/lib/shared_utils/parsed_git_url.go b/api/golang/core/lib/shared_utils/parsed_git_url.go
index b2944f20f..215398b65 100644
--- a/api/golang/core/lib/shared_utils/parsed_git_url.go
+++ b/api/golang/core/lib/shared_utils/parsed_git_url.go
@@ -12,7 +12,7 @@ const (
 	GithubDomainPrefix = "github.com"
 	httpsSchema        = "https"
 	UrlPathSeparator   = "/"
-	//MinimumSubPathsForValidGitURL for a valid GitURl we need it to look like github.com/author/moduleName
+	//MinimumSubPathsForValidGitURL for a valid GitURl we need it to look like github.com/author/repositoryName
 	// the last two are the minimum requirements for a valid Startosis URL
 	MinimumSubPathsForValidGitURL = 2
 
@@ -26,13 +26,13 @@ const (
 
 // ParsedGitURL an object representing a parsed moduleURL
 type ParsedGitURL struct {
-	// moduleAuthor the git of the module (GitHub user or org)
-	moduleAuthor string
-	// moduleName the name of the module
-	moduleName string
+	// repositoryAuthor the git of the module (GitHub user or org)
+	repositoryAuthor string
+	// repositoryName the name of the module
+	repositoryName string
 	// gitURL the url ending with `.git` where the module lives
 	gitURL string
-	// relativeRepoPath the relative path to the repo this would be moduleAuthor/moduleName/
+	// relativeRepoPath the relative path to the repo this would be repositoryAuthor/repositoryName/
 	relativeRepoPath string
 	// relativeFilePath the full path of the file relative to the module store relativeRepoPath/path/to/file.star
 	// empty if there is no file
@@ -45,8 +45,8 @@ type ParsedGitURL struct {
 
 func newParsedGitURL(moduleAuthor, moduleName, gitURL, relativeRepoPath, relativeFilePath string, tagBranchOrCommit string) *ParsedGitURL {
 	return &ParsedGitURL{
-		moduleAuthor:      moduleAuthor,
-		moduleName:        moduleName,
+		repositoryAuthor:  moduleAuthor,
+		repositoryName:    moduleName,
 		gitURL:            gitURL,
 		relativeRepoPath:  relativeRepoPath,
 		relativeFilePath:  relativeFilePath,
@@ -54,12 +54,12 @@ func newParsedGitURL(moduleAuthor, moduleName, gitURL, relativeRepoPath, relativ
 	}
 }
 
-func (parsedUrl *ParsedGitURL) GetModuleAuthor() string {
-	return parsedUrl.moduleAuthor
+func (parsedUrl *ParsedGitURL) GetRepositoryAuthor() string {
+	return parsedUrl.repositoryAuthor
 }
 
-func (parsedUrl *ParsedGitURL) GetModuleName() string {
-	return parsedUrl.moduleName
+func (parsedUrl *ParsedGitURL) GetRepositoryName() string {
+	return parsedUrl.repositoryName
 }
 
 func (parsedUrl *ParsedGitURL) GetGitURL() string {
diff --git a/api/golang/kurtosis_version/kurtosis_version.go b/api/golang/kurtosis_version/kurtosis_version.go
index 5218bd452..614bdd298 100644
--- a/api/golang/kurtosis_version/kurtosis_version.go
+++ b/api/golang/kurtosis_version/kurtosis_version.go
@@ -9,6 +9,6 @@ const (
 	// !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!!
 	// This is necessary so that Kurt Core consumers will know if they're compatible with the currently-running
 	//  API container
-	KurtosisVersion = "0.86.4"
+	KurtosisVersion = "0.86.5"
 	// !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!!
 )
diff --git a/api/rust/Cargo.toml b/api/rust/Cargo.toml
index 2e820e11d..20233f0a5 100644
--- a/api/rust/Cargo.toml
+++ b/api/rust/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "kurtosis-sdk"
-version = "0.86.4"
+version = "0.86.5"
 license = "BUSL-1.1"
 description = "Rust SDK for Kurtosis"
 edition = "2021"
diff --git a/api/typescript/package.json b/api/typescript/package.json
index 22c95f1ee..b556e1259 100644
--- a/api/typescript/package.json
+++ b/api/typescript/package.json
@@ -1,7 +1,7 @@
 {
     "name": "kurtosis-sdk",
     "//": "NOTE: DO NOT UPDATE THIS VERSION MANUALLY - IT WILL BE UPDATED DURING THE RELEASE PROCESS!",
-    "version": "0.86.4",
+    "version": "0.86.5",
     "main": "./build/index",
     "description": "This repo contains a Typescript client for communicating with the Kurtosis Engine server, which is responsible for creating, managing and destroying Kurtosis Enclaves.",
     "types": "./build/index",
diff --git a/api/typescript/src/core/lib/enclaves/enclave_context.ts b/api/typescript/src/core/lib/enclaves/enclave_context.ts
index d38035913..1c35d1666 100644
--- a/api/typescript/src/core/lib/enclaves/enclave_context.ts
+++ b/api/typescript/src/core/lib/enclaves/enclave_context.ts
@@ -3,7 +3,7 @@
  * All Rights Reserved.
  */
 
-import {ok, err, Result} from "neverthrow";
+import {ok, err, Result, Err} from "neverthrow";
 import * as jspb from "google-protobuf";
 import type {
     Port,
@@ -34,6 +34,7 @@ import {
     GetStarlarkRunResponse,
 } from "../../kurtosis_core_rpc_api_bindings/api_container_service_pb";
 import * as path from "path";
+import * as fs from 'fs';
 import {parseKurtosisYaml, KurtosisYaml} from "./kurtosis_yaml";
 import {Readable} from "stream";
 import {readStreamContentUntilClosed, StarlarkRunResult} from "./starlark_run_blocking";
@@ -48,6 +49,20 @@ const OS_PATH_SEPARATOR_STRING = "/"
 
 const DOT_RELATIVE_PATH_INDICATOR_STRING = "."
 
+// required to get around the "only Github URLs" validation
+const composePackageIdPlaceholder = 'github.com/NOTIONAL_USER/COMPOSE-PACKAGE'
+
+
+// TODO Remove this once package ID is detected ONLY the APIC side (i.e. the CLI doesn't need to tell the APIC what package ID it's using)
+// Doing so requires that we upload completely anonymous packages to the APIC, and it figures things out from there
+let supportedDockerComposeYmlFilenames = [
+    "compose.yml",
+    "compose.yaml",
+    "docker-compose.yml",
+    "docker-compose.yaml",
+    "docker_compose.yml",
+    "docker_compose.yaml",
+]
 
 // Docs available at https://docs.kurtosis.com/sdk/#enclavecontext
 export class EnclaveContext {
@@ -146,16 +161,20 @@ export class EnclaveContext {
         packageRootPath: string,
         runConfig: StarlarkRunConfig,
     ): Promise<Result<Readable, Error>> {
-        const kurtosisYmlResult = await this.getKurtosisYaml(packageRootPath)
-        if (kurtosisYmlResult.isErr()) {
-            return err(new Error(`Unexpected error while getting the Kurtosis yaml file from path '${packageRootPath}'`))
+        const packageNameAndReplaceOptionsResult = await this.getPackageNameAndReplaceOptions(packageRootPath);
+        if (packageNameAndReplaceOptionsResult.isErr()) {
+            return err(new Error(`Unexpected error occurred while trying to get package name and replace options:\n${packageNameAndReplaceOptionsResult.error}`))
         }
-
-        const kurtosisYaml: KurtosisYaml = kurtosisYmlResult.value
-        const packageId: string = kurtosisYaml.name
-        const packageReplaceOptions: Map<string, string> = kurtosisYaml.packageReplaceOptions
-
-        const args = await this.assembleRunStarlarkPackageArg(kurtosisYaml, runConfig.relativePathToMainFile, runConfig.mainFunctionName, runConfig.serializedParams, runConfig.dryRun, runConfig.cloudInstanceId, runConfig.cloudUserId)
+        const [packageName, packageReplaceOptions] = packageNameAndReplaceOptionsResult.value;
+
+        const args = await this.assembleRunStarlarkPackageArg(
+            packageName,
+            runConfig.relativePathToMainFile,
+            runConfig.mainFunctionName,
+            runConfig.serializedParams,
+            runConfig.dryRun,
+            runConfig.cloudInstanceId,
+            runConfig.cloudUserId)
         if (args.isErr()) {
             return err(new Error(`Unexpected error while assembling arguments to pass to the Starlark executor \n${args.error}`))
         }
@@ -165,9 +184,9 @@ export class EnclaveContext {
             return err(new Error(`Unexpected error while creating the package's tgs file from '${packageRootPath}'\n${archiverResponse.error}`))
         }
 
-        const uploadStarlarkPackageResponse = await this.backend.uploadStarlarkPackage(packageId, archiverResponse.value)
+        const uploadStarlarkPackageResponse = await this.backend.uploadStarlarkPackage(packageName, archiverResponse.value)
         if (uploadStarlarkPackageResponse.isErr()){
-            return err(new Error(`Unexpected error while uploading Starlark package '${packageId}'\n${uploadStarlarkPackageResponse.error}`))
+            return err(new Error(`Unexpected error while uploading Starlark package '${packageName}'\n${uploadStarlarkPackageResponse.error}`))
         }
 
         if (packageReplaceOptions !== undefined && packageReplaceOptions.size > 0) {
@@ -388,6 +407,44 @@ export class EnclaveContext {
     // ====================================================================================================
     //                                       Private helper functions
     // ====================================================================================================
+    // Determines the package name and replace options based on [packageRootPath]
+    // If a kurtosis.yml is detected, package is a kurtosis package
+    // If a valid [supportedDockerComposeYaml] is detected, package is a docker compose package
+    private async getPackageNameAndReplaceOptions(packageRootPath: string): Promise<Result<[string, Map<string, string>], Error>>
+    {
+        let packageName: string;
+        let packageReplaceOptions: Map<string, string>;
+
+        // Use kurtosis package if it exists
+        if (fs.existsSync(path.join(packageRootPath, KURTOSIS_YAML_FILENAME))) {
+            const kurtosisYmlResult = await this.getKurtosisYaml(packageRootPath)
+            if (kurtosisYmlResult.isErr()) {
+                return err(new Error(`Unexpected error while getting the Kurtosis yaml file from path '${packageRootPath}'`))
+            }
+            const kurtosisYml: KurtosisYaml = kurtosisYmlResult.value
+            packageName = kurtosisYml.name;
+            packageReplaceOptions = kurtosisYml.packageReplaceOptions;
+        } else {
+            // Use compose package if it exists
+            let composeAbsFilepath = '';
+            for (const candidateComposeFilename of supportedDockerComposeYmlFilenames) {
+                const candidateComposeAbsFilepath = path.join(packageRootPath, candidateComposeFilename);
+                if (fs.existsSync(candidateComposeAbsFilepath)) {
+                    composeAbsFilepath = candidateComposeAbsFilepath;
+                    break;
+                }
+            }
+            if (composeAbsFilepath === '') {
+                return err(new Error(
+                    `Neither a '${KURTOSIS_YAML_FILENAME}' file nor one of the default Compose files (${supportedDockerComposeYmlFilenames.join(', ')}) was found in the package root; at least one of these is required`,
+                ));
+            }
+            packageName = composePackageIdPlaceholder;
+            packageReplaceOptions = new Map<string, string>();
+        }
+
+        return ok([packageName, packageReplaceOptions]);
+    }
 
     // convertApiPortsToServiceContextPorts returns a converted map where Port objects associated with strings in [apiPorts] are
     // properly converted to PortSpec objects.
@@ -412,7 +469,7 @@ export class EnclaveContext {
     }
 
     private async assembleRunStarlarkPackageArg(
-        kurtosisYaml: KurtosisYaml,
+        packageName: string,
         relativePathToMainFile: string,
         mainFunctionName: string,
         serializedParams: string,
@@ -422,7 +479,7 @@ export class EnclaveContext {
         ): Promise<Result<RunStarlarkPackageArgs, Error>> {
 
         const args = new RunStarlarkPackageArgs;
-        args.setPackageId(kurtosisYaml.name)
+        args.setPackageId(packageName)
         args.setSerializedParams(serializedParams)
         args.setDryRun(dryRun)
         args.setRelativePathToMainFile(relativePathToMainFile)
diff --git a/api/typescript/src/kurtosis_version/kurtosis_version.ts b/api/typescript/src/kurtosis_version/kurtosis_version.ts
index 990de80cd..62bf51fa3 100644
--- a/api/typescript/src/kurtosis_version/kurtosis_version.ts
+++ b/api/typescript/src/kurtosis_version/kurtosis_version.ts
@@ -1,5 +1,5 @@
 // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!!
 // This is necessary so that Kurt Core consumers (e.g. modules) will know if they're compatible with the currently-running
 //  API container
-export const KURTOSIS_VERSION: string = "0.86.4"
+export const KURTOSIS_VERSION: string = "0.86.5"
 // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!!
diff --git a/core/server/api_container/server/api_container_service.go b/core/server/api_container/server/api_container_service.go
index 082fb38d1..7fbd5af34 100644
--- a/core/server/api_container/server/api_container_service.go
+++ b/core/server/api_container/server/api_container_service.go
@@ -12,6 +12,7 @@ import (
 	"compress/gzip"
 	"context"
 	"fmt"
+	"github.com/kurtosis-tech/kurtosis/core/server/api_container/server/docker_compose_transpiler"
 	"io"
 	"math"
 	"net/http"
@@ -21,7 +22,6 @@ import (
 	"time"
 	"unicode"
 
-	"github.com/kurtosis-tech/kurtosis/core/server/commons/yaml_parser"
 	"github.com/kurtosis-tech/kurtosis/metrics-library/golang/lib/metrics_client"
 
 	"github.com/kurtosis-tech/kurtosis/container-engine-lib/lib/uuid_generator"
@@ -73,6 +73,15 @@ const (
 	defaultParallelism       = 4
 )
 
+var defaultComposeFilenames = []string{
+	"compose.yml",
+	"compose.yaml",
+	"docker-compose.yml",
+	"docker-compose.yaml",
+	"docker_compose.yml",
+	"docker_compose.yaml",
+}
+
 // Guaranteed (by a unit test) to be a 1:1 mapping between API port protos and port spec protos
 var apiContainerPortProtoToPortSpecPortProto = map[kurtosis_core_rpc_api_bindings.Port_TransportProtocol]port_spec.TransportProtocol{
 	kurtosis_core_rpc_api_bindings.Port_TCP:  port_spec.TransportProtocol_TCP,
@@ -87,7 +96,7 @@ type ApiContainerService struct {
 
 	startosisRunner *startosis_engine.StartosisRunner
 
-	startosisModuleContentProvider startosis_packages.PackageContentProvider
+	packageContentProvider startosis_packages.PackageContentProvider
 
 	restartPolicy kurtosis_core_rpc_api_bindings.RestartPolicy
 
@@ -105,11 +114,11 @@ func NewApiContainerService(
 	metricsClient metrics_client.MetricsClient,
 ) (*ApiContainerService, error) {
 	service := &ApiContainerService{
-		filesArtifactStore:             filesArtifactStore,
-		serviceNetwork:                 serviceNetwork,
-		startosisRunner:                startosisRunner,
-		startosisModuleContentProvider: startosisModuleContentProvider,
-		restartPolicy:                  restartPolicy,
+		filesArtifactStore:     filesArtifactStore,
+		serviceNetwork:         serviceNetwork,
+		startosisRunner:        startosisRunner,
+		packageContentProvider: startosisModuleContentProvider,
+		restartPolicy:          restartPolicy,
 		starlarkRun: &kurtosis_core_rpc_api_bindings.GetStarlarkRunResponse{
 			PackageId:              startosis_constants.PackageIdPlaceholderForStandaloneScript,
 			SerializedScript:       "",
@@ -162,6 +171,7 @@ func (apicService *ApiContainerService) RunStarlarkScript(args *kurtosis_core_rp
 	return nil
 }
 
+// Uploads a Starlark package for later execution
 func (apicService *ApiContainerService) UploadStarlarkPackage(server kurtosis_core_rpc_api_bindings.ApiContainerService_UploadStarlarkPackageServer) error {
 	var packageId string
 	serverStream := grpc_file_streaming.NewServerStream[kurtosis_core_rpc_api_bindings.StreamedDataChunk, emptypb.Empty](server)
@@ -182,7 +192,7 @@ func (apicService *ApiContainerService) UploadStarlarkPackage(server kurtosis_co
 			}
 
 			// finished receiving all the chunks and assembling them into a single byte array
-			_, interpretationError := apicService.startosisModuleContentProvider.StorePackageContents(packageId, assembledContent, doOverwriteExistingModule)
+			_, interpretationError := apicService.packageContentProvider.StorePackageContents(packageId, assembledContent, doOverwriteExistingModule)
 			if interpretationError != nil {
 				return nil, stacktrace.Propagate(interpretationError, "Error storing package in APIC once received")
 			}
@@ -227,60 +237,57 @@ func (apicService *ApiContainerService) InspectFilesArtifactContents(_ context.C
 }
 
 func (apicService *ApiContainerService) RunStarlarkPackage(args *kurtosis_core_rpc_api_bindings.RunStarlarkPackageArgs, stream kurtosis_core_rpc_api_bindings.ApiContainerService_RunStarlarkPackageServer) error {
-	packageId := args.GetPackageId()
+	packageIdFromArgs := args.GetPackageId()
 	parallelism := int(args.GetParallelism())
 	if parallelism == 0 {
 		parallelism = defaultParallelism
 	}
 	dryRun := shared_utils.GetOrDefaultBool(args.DryRun, defaultStartosisDryRun)
 	serializedParams := args.GetSerializedParams()
-	relativePathToMainFile := args.GetRelativePathToMainFile()
+	requestedRelativePathToMainFile := args.GetRelativePathToMainFile()
 	mainFuncName := args.GetMainFunctionName()
 	ApiDownloadMode := shared_utils.GetOrDefault(args.ImageDownloadMode, defaultImageDownloadMode)
-
 	downloadMode := convertFromImageDownloadModeAPI(ApiDownloadMode)
 
-	if relativePathToMainFile == "" {
-		relativePathToMainFile = startosis_constants.MainFileName
-	}
-
-	// TODO: remove this fork once everything uses the UploadStarlarkPackage endpoint prior to calling this
-	//  right now the TS SDK still uses the old deprecated behavior
 	var scriptWithRunFunction string
 	var interpretationError *startosis_errors.InterpretationError
 	var isRemote bool
-	var kurtosisYml *yaml_parser.KurtosisYaml
+	var detectedPackageId string
+	var detectedPackageReplaceOptions map[string]string
+	var actualRelativePathToMainFile string
 	if args.ClonePackage != nil {
-		scriptWithRunFunction, kurtosisYml, interpretationError = apicService.runStarlarkPackageSetup(packageId, args.GetClonePackage(), nil, relativePathToMainFile)
+		scriptWithRunFunction, actualRelativePathToMainFile, detectedPackageId, detectedPackageReplaceOptions, interpretationError =
+			apicService.runStarlarkPackageSetup(packageIdFromArgs, args.GetClonePackage(), nil, requestedRelativePathToMainFile)
 		isRemote = args.GetClonePackage()
 	} else {
-		// old deprecated syntax in use
+		// OLD DEPRECATED SYNTAX
+		// TODO: remove this fork once everything uses the UploadStarlarkPackage endpoint prior to calling this
+		//  right now the TS SDK still uses the old deprecated behavior
 		moduleContentIfLocal := args.GetLocal()
 		isRemote = args.GetRemote()
-		scriptWithRunFunction, kurtosisYml, interpretationError = apicService.runStarlarkPackageSetup(packageId, args.GetRemote(), moduleContentIfLocal, relativePathToMainFile)
+		scriptWithRunFunction, actualRelativePathToMainFile, detectedPackageId, detectedPackageReplaceOptions, interpretationError =
+			apicService.runStarlarkPackageSetup(packageIdFromArgs, args.GetRemote(), moduleContentIfLocal, requestedRelativePathToMainFile)
 	}
 	if interpretationError != nil {
 		if err := stream.SendMsg(binding_constructors.NewStarlarkRunResponseLineFromInterpretationError(interpretationError.ToAPIType())); err != nil {
-			return stacktrace.Propagate(err, "Error preparing for package execution and this error could not be sent through the output stream: '%s'", packageId)
+			return stacktrace.Propagate(err, "Error preparing for package execution and this error could not be sent through the output stream: '%s'", packageIdFromArgs)
 		}
 		return nil
 	}
-	packageName := kurtosisYml.GetPackageName()
-	packageReplaceOptions := kurtosisYml.GetPackageReplaceOptions()
-	logrus.Debugf("package replace options received '%+v'", packageReplaceOptions)
+	logrus.Debugf("package replace options received '%+v'", detectedPackageReplaceOptions)
 
-	metricsErr := apicService.metricsClient.TrackKurtosisRun(packageName, isRemote, dryRun, isNotScript)
+	metricsErr := apicService.metricsClient.TrackKurtosisRun(detectedPackageId, isRemote, dryRun, isNotScript)
 	if metricsErr != nil {
 		logrus.Warn("An error occurred tracking kurtosis run event")
 	}
-	apicService.runStarlark(parallelism, dryRun, packageName, packageReplaceOptions, mainFuncName, relativePathToMainFile, scriptWithRunFunction, serializedParams, downloadMode, args.ExperimentalFeatures, stream)
+	apicService.runStarlark(parallelism, dryRun, detectedPackageId, detectedPackageReplaceOptions, mainFuncName, actualRelativePathToMainFile, scriptWithRunFunction, serializedParams, downloadMode, args.ExperimentalFeatures, stream)
 
 	apicService.starlarkRun = &kurtosis_core_rpc_api_bindings.GetStarlarkRunResponse{
-		PackageId:              packageId,
+		PackageId:              packageIdFromArgs,
 		SerializedScript:       scriptWithRunFunction,
 		SerializedParams:       serializedParams,
 		Parallelism:            int32(parallelism),
-		RelativePathToMainFile: relativePathToMainFile,
+		RelativePathToMainFile: requestedRelativePathToMainFile,
 		MainFunctionName:       mainFuncName,
 		ExperimentalFeatures:   args.ExperimentalFeatures,
 		RestartPolicy:          apicService.restartPolicy,
@@ -728,47 +735,82 @@ func (apicService *ApiContainerService) getServiceInfoForIdentifier(ctx context.
 }
 
 func (apicService *ApiContainerService) runStarlarkPackageSetup(
-	packageId string,
+	packageIdFromArgs string,
 	clonePackage bool,
-	moduleContentIfLocal []byte,
-	relativePathToMainFile string,
-) (string, *yaml_parser.KurtosisYaml, *startosis_errors.InterpretationError) {
+	moduleContentIfLocal []byte, // empty if clonePackage is set to true
+	relativePathToMainFile string, // could be empty
+) (
+	string, // Entrypoint script to execute
+	string, // Detected relative path (from package root) to main script
+	string, // Detected Package ID detected from [clonePackage] or [moduleContentIfLocal]
+	map[string]string, // Replace options detected from [clonePackage] or [moduleContentIfLocal]
+	*startosis_errors.InterpretationError) {
 	var packageRootPathOnDisk string
 	var interpretationError *startosis_errors.InterpretationError
 
 	if clonePackage {
-		packageRootPathOnDisk, interpretationError = apicService.startosisModuleContentProvider.ClonePackage(packageId)
+		packageRootPathOnDisk, interpretationError = apicService.packageContentProvider.ClonePackage(packageIdFromArgs)
 	} else if moduleContentIfLocal != nil {
-		// TODO: remove this once UploadStarlarkPackage is called prior to calling RunStarlarkPackage by all consumers
-		//  of this API
-		packageRootPathOnDisk, interpretationError = apicService.startosisModuleContentProvider.StorePackageContents(packageId, bytes.NewReader(moduleContentIfLocal), doOverwriteExistingModule)
+		// TODO: remove this once UploadStarlarkPackage is called prior to calling RunStarlarkPackage by all consumers of this API
+		packageRootPathOnDisk, interpretationError = apicService.packageContentProvider.StorePackageContents(packageIdFromArgs, bytes.NewReader(moduleContentIfLocal), doOverwriteExistingModule)
 	} else {
-		// We just need to retrieve the absolute path, the content is will not be stored here since it has been uploaded
-		// prior to this call
-		packageRootPathOnDisk, interpretationError = apicService.startosisModuleContentProvider.GetOnDiskAbsolutePackagePath(packageId)
-
+		// We just need to retrieve the absolute path, the content will not be stored here since it has been uploaded prior to this call
+		// This is used in the flow where we `replace` with a local call, in which case we need to store multiple packages on the APIC
+		// before we actually do the run
+		packageRootPathOnDisk, interpretationError = apicService.packageContentProvider.GetOnDiskAbsolutePackagePath(packageIdFromArgs)
 	}
 	if interpretationError != nil {
-		return "", nil, interpretationError
+		return "", "", "", nil, interpretationError
 	}
 
-	kurtosisYml, interpretationError := apicService.startosisModuleContentProvider.GetKurtosisYaml(packageRootPathOnDisk)
-	if interpretationError != nil {
-		return "", nil, interpretationError
+	// If kurtosis.yml exists in root, treat as kurtosis package
+	candidateKurtosisYmlAbsFilepath := path.Join(packageRootPathOnDisk, startosis_constants.KurtosisYamlName)
+	if _, err := os.Stat(candidateKurtosisYmlAbsFilepath); err == nil {
+		kurtosisYml, interpretationError := apicService.packageContentProvider.GetKurtosisYaml(packageRootPathOnDisk)
+		if interpretationError != nil {
+			return "", "", "", nil, interpretationError
+		}
+		if relativePathToMainFile == "" {
+			relativePathToMainFile = startosis_constants.MainFileName
+		}
+		pathToMainFile := path.Join(packageRootPathOnDisk, relativePathToMainFile)
+		if _, err := os.Stat(pathToMainFile); err != nil {
+			return "", "", "", nil, startosis_errors.WrapWithInterpretationError(err, "An error occurred while verifying that '%v' exists in the package '%v' at '%v'", startosis_constants.MainFileName, packageIdFromArgs, pathToMainFile)
+		}
+		mainScriptToExecuteBytes, err := os.ReadFile(pathToMainFile)
+		if err != nil {
+			return "", "", "", nil, startosis_errors.WrapWithInterpretationError(err, "An error occurred while reading '%v' in the package '%v' at '%v'", startosis_constants.MainFileName, packageIdFromArgs, pathToMainFile)
+		}
+		return string(mainScriptToExecuteBytes), relativePathToMainFile, kurtosisYml.PackageName, kurtosisYml.PackageReplaceOptions, nil
 	}
 
-	pathToMainFile := path.Join(packageRootPathOnDisk, relativePathToMainFile)
-
-	if _, err := os.Stat(pathToMainFile); err != nil {
-		return "", nil, startosis_errors.WrapWithInterpretationError(err, "An error occurred while verifying that '%v' exists in the package '%v' at '%v'", startosis_constants.MainFileName, packageId, pathToMainFile)
+	// If kurtosis.yml doesn't exist, assume a Compose package and transpile compose into starlark
+	if relativePathToMainFile == "" {
+		for _, defaultComposeFilename := range defaultComposeFilenames {
+			candidateComposeAbsFilepath := path.Join(packageRootPathOnDisk, defaultComposeFilename)
+			if _, err := os.Stat(candidateComposeAbsFilepath); err == nil {
+				relativePathToMainFile = defaultComposeFilename
+				break
+			}
+		}
+		if relativePathToMainFile == "" {
+			return "", "", "", nil, startosis_errors.NewInterpretationError(
+				"No '%s' file was found in the package root so fell back to Docker Compose package, but no "+
+					"default Compose files (%s) were found. Either add a '%s' file to the package root or add one of the "+
+					"default Compose files.",
+				startosis_constants.KurtosisYamlName,
+				strings.Join(defaultComposeFilenames, ", "),
+				startosis_constants.KurtosisYamlName,
+			)
+		}
 	}
-
-	mainScriptToExecute, err := os.ReadFile(pathToMainFile)
-	if err != nil {
-		return "", nil, startosis_errors.WrapWithInterpretationError(err, "An error occurred while reading '%v' in the package '%v' at '%v'", startosis_constants.MainFileName, packageId, pathToMainFile)
+	mainScriptToExecute, transpilationErr := docker_compose_transpiler.TranspileDockerComposePackageToStarlark(packageRootPathOnDisk, relativePathToMainFile)
+	if transpilationErr != nil {
+		return "", "", "", nil, startosis_errors.WrapWithInterpretationError(transpilationErr, "An error occurred transpiling the Docker Compose package '%v' to Starlark", packageIdFromArgs)
 	}
 
-	return string(mainScriptToExecute), kurtosisYml, nil
+	replacesForComposePackage := map[string]string{}
+	return mainScriptToExecute, relativePathToMainFile, packageIdFromArgs, replacesForComposePackage, nil
 }
 
 func (apicService *ApiContainerService) runStarlark(
diff --git a/core/server/api_container/server/docker_compose_transpiler/docker_compose_transpiler.go b/core/server/api_container/server/docker_compose_transpiler/docker_compose_transpiler.go
index 1ff690e47..52b46956f 100644
--- a/core/server/api_container/server/docker_compose_transpiler/docker_compose_transpiler.go
+++ b/core/server/api_container/server/docker_compose_transpiler/docker_compose_transpiler.go
@@ -55,6 +55,9 @@ const (
 	defRunStr = "def run(plan):\n"
 
 	newStarlarkLineFmtStr = "    %s\n"
+
+	unixHomePathSymbol         = "~"
+	upstreamRelativePathSymbol = ".."
 )
 
 type ComposeService types.ServiceConfig
@@ -69,11 +72,11 @@ var dockerPortProtosToKurtosisPortProtos = map[string]port_spec.TransportProtoco
 
 var CyclicalDependencyError = stacktrace.NewError("A cycle was detected in the service dependency graph.")
 
-func TranspileDockerComposePackageToStarlark(packageAbsDirpath string, composeRelativeFilepath string) (string, error) {
-	composeAbsFilepath := path.Join(packageAbsDirpath, composeRelativeFilepath)
+func TranspileDockerComposePackageToStarlark(packageAbsDirpath string, relativePathToComposeFile string) (string, error) {
+	composeAbsFilepath := path.Join(packageAbsDirpath, relativePathToComposeFile)
 
 	// Useful for logging to prevent leaking internals of APIC
-	composeFilename := path.Base(composeRelativeFilepath)
+	composeFilename := path.Base(relativePathToComposeFile)
 
 	composeBytes, err := os.ReadFile(composeAbsFilepath)
 	if err != nil {
@@ -436,8 +439,7 @@ func getStarlarkEnvVars(composeEnvironment types.MappingWithEquals) (*starlark.D
 // <abs path on host>:<path on container>:= create a persistent directory on container at <path on container>
 // <abs path on host> := create a persistent directory on container at <abs path on host>
 // <rel path on host> := create a persistent directory on container at <rel path on host>
-// Named volumes are currently not supported
-// TODO: Support named volumes https://docs.docker.com/storage/volumes/
+// Named volumes are treated https://docs.docker.com/storage/volumes/ as absolute paths persistence layers, and thus a persistent directory is created
 func getStarlarkFilesArtifacts(composeVolumes []types.ServiceVolumeConfig, serviceName string) (starlark.Value, map[string]string, error) {
 	filesArgSLDict := starlark.NewDict(len(composeVolumes))
 	filesArtifactsToUpload := map[string]string{}
@@ -448,6 +450,21 @@ func getStarlarkFilesArtifacts(composeVolumes []types.ServiceVolumeConfig, servi
 		var shouldPersist bool
 		switch volumeType {
 		case types.VolumeTypeBind:
+			// Handle case where home path is reference
+			if strings.Contains(volume.Source, unixHomePathSymbol) {
+				return nil, map[string]string{}, stacktrace.NewError(
+					"Volume path '%v' uses '%v', likely referencing home path on a unix filesystem. Currently, Kurtosis does not support uploading from host filesystem. "+
+						"Place the contents of '%v' directory inside the package where the compose yaml exists and update the volume filepath to be a relative path",
+					volume.Source, unixHomePathSymbol, volume.Source)
+			}
+			// Handle case where upstream relative path is reference
+			if strings.Contains(volume.Source, upstreamRelativePathSymbol) {
+				return nil, map[string]string{}, stacktrace.NewError(
+					"Volume path '%v' uses '%v', likely referencing an upstream path on a filesystem. Currently, Kurtosis does not support uploading from host filesystem. "+
+						"Place the contents of '%v' directory inside the package where the compose yaml exists and update the volume filepath to be a relative path within the package.",
+					volume.Source, upstreamRelativePathSymbol, volume.Source)
+			}
+
 			// Assume that if an absolute path is specified, user wants to use volume as a persistence layer
 			// Additionally, assume relative paths are read-only
 			shouldPersist = path.IsAbs(volume.Source)
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/upload_files/upload_files.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/upload_files/upload_files.go
index f23923762..cf2135e22 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/upload_files/upload_files.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/upload_files/upload_files.go
@@ -116,7 +116,7 @@ func (builtin *UploadFilesCapabilities) Interpret(locatorOfModuleInWhichThisBuil
 		return nil, startosis_errors.WrapWithInterpretationError(interpretationErr, "Tried to convert locator '%v' into absolute locator but failed", src.GoString())
 	}
 
-	pathOnDisk, interpretationErr := builtin.packageContentProvider.GetOnDiskAbsoluteFilePath(absoluteLocator)
+	pathOnDisk, interpretationErr := builtin.packageContentProvider.GetOnDiskAbsolutePath(absoluteLocator)
 	if interpretationErr != nil {
 		return nil, interpretationErr
 	}
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/image_build_spec.go b/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/image_build_spec.go
index 37205897e..b7107d6ad 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/image_build_spec.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_types/service_config/image_build_spec.go
@@ -175,7 +175,8 @@ func getOnDiskImageBuildSpecPaths(
 
 	// get on disk directory path of Dockerfile
 	containerImageAbsoluteLocator := path.Join(contextDirAbsoluteLocator, defaultContainerImageFileName)
-	containerImagePathOnDisk, interpretationErr := packageContentProvider.GetOnDiskAbsoluteFilePath(containerImageAbsoluteLocator)
+
+	containerImagePathOnDisk, interpretationErr := packageContentProvider.GetOnDiskAbsolutePackageFilePath(containerImageAbsoluteLocator)
 	if interpretationErr != nil {
 		return "", "", interpretationErr
 	}
diff --git a/core/server/api_container/server/startosis_engine/startosis_packages/git_package_content_provider/git_package_content_provider.go b/core/server/api_container/server/startosis_engine/startosis_packages/git_package_content_provider/git_package_content_provider.go
index 9dc8c7e58..1580f8c93 100644
--- a/core/server/api_container/server/startosis_engine/startosis_packages/git_package_content_provider/git_package_content_provider.go
+++ b/core/server/api_container/server/startosis_engine/startosis_packages/git_package_content_provider/git_package_content_provider.go
@@ -44,16 +44,16 @@ const (
 )
 
 type GitPackageContentProvider struct {
-	// Where to temporarily store packages while
-	packagesTmpDir                  string
-	packagesDir                     string
+	// Where to temporarily store repositories while
+	repositoriesTmpDir              string
+	repositoriesDir                 string
 	packageReplaceOptionsRepository *packageReplaceOptionsRepository
 }
 
-func NewGitPackageContentProvider(moduleDir string, tmpDir string, enclaveDb *enclave_db.EnclaveDB) *GitPackageContentProvider {
+func NewGitPackageContentProvider(repositoriesDir string, tmpDir string, enclaveDb *enclave_db.EnclaveDB) *GitPackageContentProvider {
 	return &GitPackageContentProvider{
-		packagesDir:                     moduleDir,
-		packagesTmpDir:                  tmpDir,
+		repositoriesDir:                 repositoriesDir,
+		repositoriesTmpDir:              tmpDir,
 		packageReplaceOptionsRepository: newPackageReplaceOptionsRepository(enclaveDb),
 	}
 }
@@ -64,13 +64,12 @@ func (provider *GitPackageContentProvider) ClonePackage(packageId string) (strin
 		return "", startosis_errors.WrapWithInterpretationError(err, "An error occurred parsing Git URL for package ID '%s'", packageId)
 	}
 
-	interpretationError := provider.atomicClone(parsedURL)
-	if interpretationError != nil {
+	if interpretationError := provider.atomicClone(parsedURL); interpretationError != nil {
 		return "", interpretationError
 	}
 
 	relPackagePathToPackagesDir := getPathToPackageRoot(parsedURL)
-	packageAbsolutePathOnDisk := path.Join(provider.packagesDir, relPackagePathToPackagesDir)
+	packageAbsolutePathOnDisk := path.Join(provider.repositoriesDir, relPackagePathToPackagesDir)
 
 	return packageAbsolutePathOnDisk, nil
 }
@@ -82,7 +81,7 @@ func (provider *GitPackageContentProvider) GetKurtosisYaml(packageAbsolutePathOn
 			startosis_constants.KurtosisYamlName, packageAbsolutePathOnDisk, startosis_constants.KurtosisYamlName, user_support_constants.StarlarkPackagesReferenceURL)
 	}
 
-	kurtosisYaml, interpretationError := validateAndGetKurtosisYaml(pathToKurtosisYaml, provider.packagesDir)
+	kurtosisYaml, interpretationError := validateAndGetKurtosisYaml(pathToKurtosisYaml, provider.repositoriesDir)
 	if interpretationError != nil {
 		return nil, interpretationError
 	}
@@ -90,16 +89,26 @@ func (provider *GitPackageContentProvider) GetKurtosisYaml(packageAbsolutePathOn
 	return kurtosisYaml, nil
 }
 
-func (provider *GitPackageContentProvider) GetOnDiskAbsoluteFilePath(absoluteFileLocator string) (string, *startosis_errors.InterpretationError) {
-	parsedURL, err := shared_utils.ParseGitURL(absoluteFileLocator)
+func (provider *GitPackageContentProvider) GetOnDiskAbsolutePath(repositoryPathURL string) (string, *startosis_errors.InterpretationError) {
+	shouldOnlyAcceptsPackagePath := false
+	return provider.getOnDiskAbsolutePath(repositoryPathURL, shouldOnlyAcceptsPackagePath)
+}
+
+func (provider *GitPackageContentProvider) GetOnDiskAbsolutePackageFilePath(repositoryPathURL string) (string, *startosis_errors.InterpretationError) {
+	shouldOnlyAcceptsPackagePath := true
+	return provider.getOnDiskAbsolutePath(repositoryPathURL, shouldOnlyAcceptsPackagePath)
+}
+
+func (provider *GitPackageContentProvider) getOnDiskAbsolutePath(repositoryPathURL string, shouldOnlyAcceptsPackageFilePath bool) (string, *startosis_errors.InterpretationError) {
+	parsedURL, err := shared_utils.ParseGitURL(repositoryPathURL)
 	if err != nil {
-		return "", startosis_errors.WrapWithInterpretationError(err, "An error occurred parsing Git URL for absolute file locator '%s'", absoluteFileLocator)
+		return "", startosis_errors.WrapWithInterpretationError(err, "An error occurred parsing Git URL for absolute file locator '%s'", repositoryPathURL)
 	}
-	if parsedURL.GetRelativeFilePath() == "" {
-		return "", startosis_errors.NewInterpretationError("The path '%v' needs to point to a specific file but it didn't. Users can only read or import specific files and not entire packages.", absoluteFileLocator)
+	if shouldOnlyAcceptsPackageFilePath && parsedURL.GetRelativeFilePath() == "" {
+		return "", startosis_errors.NewInterpretationError("The path '%v' needs to point to a specific file but it didn't. Users can only read or import specific files and not entire packages.", repositoryPathURL)
 	}
-	pathToFileOnDisk := path.Join(provider.packagesDir, parsedURL.GetRelativeFilePath())
-	packagePath := path.Join(provider.packagesDir, parsedURL.GetRelativeRepoPath())
+	pathToFileOnDisk := path.Join(provider.repositoriesDir, parsedURL.GetRelativeFilePath())
+	packagePath := path.Join(provider.repositoriesDir, parsedURL.GetRelativeRepoPath())
 
 	// Return the file path straight if it exists
 	if _, err := os.Stat(pathToFileOnDisk); err == nil {
@@ -114,22 +123,25 @@ func (provider *GitPackageContentProvider) GetOnDiskAbsoluteFilePath(absoluteFil
 	}
 
 	// Otherwise clone the repo and return the absolute path of the requested file
-	interpretationError := provider.atomicClone(parsedURL)
-	if interpretationError != nil {
+	if interpretationError := provider.atomicClone(parsedURL); interpretationError != nil {
 		return "", interpretationError
 	}
 
+	if !shouldOnlyAcceptsPackageFilePath {
+		return pathToFileOnDisk, nil
+	}
+
 	// check whether kurtosis yaml exists in the path
-	maybeKurtosisYamlPath, interpretationError := getKurtosisYamlPathForFileUrl(pathToFileOnDisk, provider.packagesDir)
+	maybeKurtosisYamlPath, interpretationError := getKurtosisYamlPathForFileUrl(pathToFileOnDisk, provider.repositoriesDir)
 	if interpretationError != nil {
-		return "", startosis_errors.WrapWithInterpretationError(err, "Error occurred while verifying whether '%v' belongs to a Kurtosis package.", absoluteFileLocator)
+		return "", startosis_errors.WrapWithInterpretationError(err, "Error occurred while verifying whether '%v' belongs to a Kurtosis package.", repositoryPathURL)
 	}
 
 	if maybeKurtosisYamlPath == filePathToKurtosisYamlNotFound {
-		return "", startosis_errors.NewInterpretationError("%v is not found in the path of '%v'; files can only be accessed from Kurtosis packages. For more information, go to: %v", startosis_constants.KurtosisYamlName, absoluteFileLocator, user_support_constants.HowImportWorksLink)
+		return "", startosis_errors.NewInterpretationError("%v is not found in the path of '%v'; files can only be accessed from Kurtosis packages. For more information, go to: %v", startosis_constants.KurtosisYamlName, repositoryPathURL, user_support_constants.HowImportWorksLink)
 	}
 
-	if _, interpretationError = validateAndGetKurtosisYaml(maybeKurtosisYamlPath, provider.packagesDir); interpretationError != nil {
+	if _, interpretationError = validateAndGetKurtosisYaml(maybeKurtosisYamlPath, provider.repositoriesDir); interpretationError != nil {
 		return "", interpretationError
 	}
 
@@ -137,7 +149,7 @@ func (provider *GitPackageContentProvider) GetOnDiskAbsoluteFilePath(absoluteFil
 }
 
 func (provider *GitPackageContentProvider) GetModuleContents(fileInsideModuleUrl string) (string, *startosis_errors.InterpretationError) {
-	pathToFile, interpretationError := provider.GetOnDiskAbsoluteFilePath(fileInsideModuleUrl)
+	pathToFile, interpretationError := provider.GetOnDiskAbsolutePackageFilePath(fileInsideModuleUrl)
 	if interpretationError != nil {
 		return "", interpretationError
 	}
@@ -158,7 +170,7 @@ func (provider *GitPackageContentProvider) GetOnDiskAbsolutePackagePath(packageI
 	}
 
 	relPackagePathToPackagesDir := getPathToPackageRoot(parsedPackageId)
-	packageAbsolutePathOnDisk := path.Join(provider.packagesDir, relPackagePathToPackagesDir)
+	packageAbsolutePathOnDisk := path.Join(provider.repositoriesDir, relPackagePathToPackagesDir)
 
 	_, err = os.Stat(packageAbsolutePathOnDisk)
 	if err != nil {
@@ -174,7 +186,7 @@ func (provider *GitPackageContentProvider) StorePackageContents(packageId string
 	}
 
 	relPackagePathToPackagesDir := getPathToPackageRoot(parsedPackageId)
-	packageAbsolutePathOnDisk := path.Join(provider.packagesDir, relPackagePathToPackagesDir)
+	packageAbsolutePathOnDisk := path.Join(provider.repositoriesDir, relPackagePathToPackagesDir)
 
 	if overwriteExisting {
 		err := os.RemoveAll(packageAbsolutePathOnDisk)
@@ -285,13 +297,13 @@ func (provider *GitPackageContentProvider) CloneReplacedPackagesIfNeeded(current
 	return nil
 }
 
-// atomicClone This first clones to a temporary directory and then moves it
+// atomicClone This first clones to a temporary directory and then moves it into the package file system
 // TODO make this support versioning via tags, commit hashes or branches
 func (provider *GitPackageContentProvider) atomicClone(parsedURL *shared_utils.ParsedGitURL) *startosis_errors.InterpretationError {
 	// First we clone into a temporary directory
-	tempRepoDirPath, err := os.MkdirTemp(provider.packagesTmpDir, temporaryRepoDirPattern)
+	tempRepoDirPath, err := os.MkdirTemp(provider.repositoriesTmpDir, temporaryRepoDirPattern)
 	if err != nil {
-		return startosis_errors.WrapWithInterpretationError(err, "Cloning the module '%s' failed. Error creating temporary directory for the repository to be cloned into", parsedURL.GetGitURL())
+		return startosis_errors.WrapWithInterpretationError(err, "Cloning the repository '%s' failed. Error creating temporary directory for the repository to be cloned into", parsedURL.GetGitURL())
 	}
 	defer os.RemoveAll(tempRepoDirPath)
 	gitClonePath := path.Join(tempRepoDirPath, parsedURL.GetRelativeRepoPath())
@@ -301,6 +313,9 @@ func (provider *GitPackageContentProvider) atomicClone(parsedURL *shared_utils.P
 		depth = depthAssumingBranchTagsCommitsAreSpecified
 	}
 
+	//TODO evaluate to use the GitHub client GetContents call instead, because we are cloning the entire repository's workspace with this approach
+	//TODO and the startosis package could be just a small sub-folder inside a giant mono-repository
+	//TODO and even now, in the upload_files instruction, we are allowing to upload files or a folder for any repository, but we are cloning the entire repository for this
 	repo, err := git.PlainClone(gitClonePath, isNotBareClone, &git.CloneOptions{
 		URL:               parsedURL.GetGitURL(),
 		Auth:              nil,
@@ -360,25 +375,25 @@ func (provider *GitPackageContentProvider) atomicClone(parsedURL *shared_utils.P
 	}
 
 	// Then we move it into the target directory
-	packageAuthorPath := path.Join(provider.packagesDir, parsedURL.GetModuleAuthor())
-	packagePath := path.Join(provider.packagesDir, parsedURL.GetRelativeRepoPath())
+	packageAuthorPath := path.Join(provider.repositoriesDir, parsedURL.GetRepositoryAuthor())
+	packagePath := path.Join(provider.repositoriesDir, parsedURL.GetRelativeRepoPath())
 	fileMode, err := os.Stat(packageAuthorPath)
 	if err == nil && !fileMode.IsDir() {
 		return startosis_errors.WrapWithInterpretationError(err, "Expected '%s' to be a directory but it is something else", packageAuthorPath)
 	}
 	if err != nil {
 		if err = os.Mkdir(packageAuthorPath, moduleDirPermission); err != nil {
-			return startosis_errors.WrapWithInterpretationError(err, "Cloning the package '%s' failed. An error occurred while creating the directory '%s'.", parsedURL.GetGitURL(), packageAuthorPath)
+			return startosis_errors.WrapWithInterpretationError(err, "Cloning the repository '%s' failed. An error occurred while creating the directory '%s'.", parsedURL.GetGitURL(), packageAuthorPath)
 		}
 	}
 	if _, err = os.Stat(packagePath); !os.IsNotExist(err) {
 		logrus.Debugf("Package '%s' already exists in enclave at path '%s'. Going to remove previous version.", parsedURL.GetGitURL(), packagePath)
 		if err = os.RemoveAll(packagePath); err != nil {
-			return startosis_errors.NewInterpretationError("Unable to remove a previous version of package '%s' existing inside Kurtosis enclave at '%s'.", parsedURL.GetGitURL(), packagePath)
+			return startosis_errors.NewInterpretationError("Unable to remove a previous version of repository '%s' existing inside Kurtosis enclave at '%s'.", parsedURL.GetGitURL(), packagePath)
 		}
 	}
 	if err = os.Rename(gitClonePath, packagePath); err != nil {
-		return startosis_errors.NewInterpretationError("Cloning the package '%s' failed. An error occurred while moving package at temporary destination '%s' to final destination '%s'", parsedURL.GetGitURL(), gitClonePath, packagePath)
+		return startosis_errors.NewInterpretationError("Cloning the repository '%s' failed. An error occurred while moving repository at temporary destination '%s' to final destination '%s'", parsedURL.GetGitURL(), gitClonePath, packagePath)
 	}
 	return nil
 }
diff --git a/core/server/api_container/server/startosis_engine/startosis_packages/mock_package_content_provider.go b/core/server/api_container/server/startosis_engine/startosis_packages/mock_package_content_provider.go
index f5ca1273b..cab172daa 100644
--- a/core/server/api_container/server/startosis_engine/startosis_packages/mock_package_content_provider.go
+++ b/core/server/api_container/server/startosis_engine/startosis_packages/mock_package_content_provider.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.23.1. DO NOT EDIT.
+// Code generated by mockery v2.29.0. DO NOT EDIT.
 
 package startosis_packages
 
@@ -288,8 +288,8 @@ func (_c *MockPackageContentProvider_GetModuleContents_Call) RunAndReturn(run fu
 	return _c
 }
 
-// GetOnDiskAbsoluteFilePath provides a mock function with given fields: fileInsidePackageUrl
-func (_m *MockPackageContentProvider) GetOnDiskAbsoluteFilePath(fileInsidePackageUrl string) (string, *startosis_errors.InterpretationError) {
+// GetOnDiskAbsolutePackageFilePath provides a mock function with given fields: fileInsidePackageUrl
+func (_m *MockPackageContentProvider) GetOnDiskAbsolutePackageFilePath(fileInsidePackageUrl string) (string, *startosis_errors.InterpretationError) {
 	ret := _m.Called(fileInsidePackageUrl)
 
 	var r0 string
@@ -314,30 +314,30 @@ func (_m *MockPackageContentProvider) GetOnDiskAbsoluteFilePath(fileInsidePackag
 	return r0, r1
 }
 
-// MockPackageContentProvider_GetOnDiskAbsoluteFilePath_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetOnDiskAbsoluteFilePath'
-type MockPackageContentProvider_GetOnDiskAbsoluteFilePath_Call struct {
+// MockPackageContentProvider_GetOnDiskAbsolutePackageFilePath_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetOnDiskAbsolutePackageFilePath'
+type MockPackageContentProvider_GetOnDiskAbsolutePackageFilePath_Call struct {
 	*mock.Call
 }
 
-// GetOnDiskAbsoluteFilePath is a helper method to define mock.On call
+// GetOnDiskAbsolutePackageFilePath is a helper method to define mock.On call
 //   - fileInsidePackageUrl string
-func (_e *MockPackageContentProvider_Expecter) GetOnDiskAbsoluteFilePath(fileInsidePackageUrl interface{}) *MockPackageContentProvider_GetOnDiskAbsoluteFilePath_Call {
-	return &MockPackageContentProvider_GetOnDiskAbsoluteFilePath_Call{Call: _e.mock.On("GetOnDiskAbsoluteFilePath", fileInsidePackageUrl)}
+func (_e *MockPackageContentProvider_Expecter) GetOnDiskAbsolutePackageFilePath(fileInsidePackageUrl interface{}) *MockPackageContentProvider_GetOnDiskAbsolutePackageFilePath_Call {
+	return &MockPackageContentProvider_GetOnDiskAbsolutePackageFilePath_Call{Call: _e.mock.On("GetOnDiskAbsolutePackageFilePath", fileInsidePackageUrl)}
 }
 
-func (_c *MockPackageContentProvider_GetOnDiskAbsoluteFilePath_Call) Run(run func(fileInsidePackageUrl string)) *MockPackageContentProvider_GetOnDiskAbsoluteFilePath_Call {
+func (_c *MockPackageContentProvider_GetOnDiskAbsolutePackageFilePath_Call) Run(run func(fileInsidePackageUrl string)) *MockPackageContentProvider_GetOnDiskAbsolutePackageFilePath_Call {
 	_c.Call.Run(func(args mock.Arguments) {
 		run(args[0].(string))
 	})
 	return _c
 }
 
-func (_c *MockPackageContentProvider_GetOnDiskAbsoluteFilePath_Call) Return(_a0 string, _a1 *startosis_errors.InterpretationError) *MockPackageContentProvider_GetOnDiskAbsoluteFilePath_Call {
+func (_c *MockPackageContentProvider_GetOnDiskAbsolutePackageFilePath_Call) Return(_a0 string, _a1 *startosis_errors.InterpretationError) *MockPackageContentProvider_GetOnDiskAbsolutePackageFilePath_Call {
 	_c.Call.Return(_a0, _a1)
 	return _c
 }
 
-func (_c *MockPackageContentProvider_GetOnDiskAbsoluteFilePath_Call) RunAndReturn(run func(string) (string, *startosis_errors.InterpretationError)) *MockPackageContentProvider_GetOnDiskAbsoluteFilePath_Call {
+func (_c *MockPackageContentProvider_GetOnDiskAbsolutePackageFilePath_Call) RunAndReturn(run func(string) (string, *startosis_errors.InterpretationError)) *MockPackageContentProvider_GetOnDiskAbsolutePackageFilePath_Call {
 	_c.Call.Return(run)
 	return _c
 }
@@ -396,6 +396,60 @@ func (_c *MockPackageContentProvider_GetOnDiskAbsolutePackagePath_Call) RunAndRe
 	return _c
 }
 
+// GetOnDiskAbsolutePath provides a mock function with given fields: repositoryPathURL
+func (_m *MockPackageContentProvider) GetOnDiskAbsolutePath(repositoryPathURL string) (string, *startosis_errors.InterpretationError) {
+	ret := _m.Called(repositoryPathURL)
+
+	var r0 string
+	var r1 *startosis_errors.InterpretationError
+	if rf, ok := ret.Get(0).(func(string) (string, *startosis_errors.InterpretationError)); ok {
+		return rf(repositoryPathURL)
+	}
+	if rf, ok := ret.Get(0).(func(string) string); ok {
+		r0 = rf(repositoryPathURL)
+	} else {
+		r0 = ret.Get(0).(string)
+	}
+
+	if rf, ok := ret.Get(1).(func(string) *startosis_errors.InterpretationError); ok {
+		r1 = rf(repositoryPathURL)
+	} else {
+		if ret.Get(1) != nil {
+			r1 = ret.Get(1).(*startosis_errors.InterpretationError)
+		}
+	}
+
+	return r0, r1
+}
+
+// MockPackageContentProvider_GetOnDiskAbsolutePath_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetOnDiskAbsolutePath'
+type MockPackageContentProvider_GetOnDiskAbsolutePath_Call struct {
+	*mock.Call
+}
+
+// GetOnDiskAbsolutePath is a helper method to define mock.On call
+//   - repositoryPathURL string
+func (_e *MockPackageContentProvider_Expecter) GetOnDiskAbsolutePath(repositoryPathURL interface{}) *MockPackageContentProvider_GetOnDiskAbsolutePath_Call {
+	return &MockPackageContentProvider_GetOnDiskAbsolutePath_Call{Call: _e.mock.On("GetOnDiskAbsolutePath", repositoryPathURL)}
+}
+
+func (_c *MockPackageContentProvider_GetOnDiskAbsolutePath_Call) Run(run func(repositoryPathURL string)) *MockPackageContentProvider_GetOnDiskAbsolutePath_Call {
+	_c.Call.Run(func(args mock.Arguments) {
+		run(args[0].(string))
+	})
+	return _c
+}
+
+func (_c *MockPackageContentProvider_GetOnDiskAbsolutePath_Call) Return(_a0 string, _a1 *startosis_errors.InterpretationError) *MockPackageContentProvider_GetOnDiskAbsolutePath_Call {
+	_c.Call.Return(_a0, _a1)
+	return _c
+}
+
+func (_c *MockPackageContentProvider_GetOnDiskAbsolutePath_Call) RunAndReturn(run func(string) (string, *startosis_errors.InterpretationError)) *MockPackageContentProvider_GetOnDiskAbsolutePath_Call {
+	_c.Call.Return(run)
+	return _c
+}
+
 // StorePackageContents provides a mock function with given fields: packageId, packageContent, overwriteExisting
 func (_m *MockPackageContentProvider) StorePackageContents(packageId string, packageContent io.Reader, overwriteExisting bool) (string, *startosis_errors.InterpretationError) {
 	ret := _m.Called(packageId, packageContent, overwriteExisting)
diff --git a/core/server/api_container/server/startosis_engine/startosis_packages/mock_package_content_provider/mock_package_content_provider.go b/core/server/api_container/server/startosis_engine/startosis_packages/mock_package_content_provider/mock_package_content_provider.go
index 9e4ac411c..acd1e7c18 100644
--- a/core/server/api_container/server/startosis_engine/startosis_packages/mock_package_content_provider/mock_package_content_provider.go
+++ b/core/server/api_container/server/startosis_engine/startosis_packages/mock_package_content_provider/mock_package_content_provider.go
@@ -32,13 +32,24 @@ func NewMockPackageContentProvider() *MockPackageContentProvider {
 	}
 }
 
-func (provider *MockPackageContentProvider) GetOnDiskAbsoluteFilePath(packageId string) (string, *startosis_errors.InterpretationError) {
-	absFilePath, found := provider.starlarkPackages[packageId]
+func (provider *MockPackageContentProvider) GetOnDiskAbsolutePackageFilePath(fileInsidePackageUrl string) (string, *startosis_errors.InterpretationError) {
+	absFilePath, found := provider.starlarkPackages[fileInsidePackageUrl]
+	if !found {
+		return "", startosis_errors.NewInterpretationError("Module '%v' not found", fileInsidePackageUrl)
+	}
+	if _, err := os.Stat(absFilePath); err != nil {
+		return "", startosis_errors.NewInterpretationError("Unable to read content of package '%v'", fileInsidePackageUrl)
+	}
+	return absFilePath, nil
+}
+
+func (provider *MockPackageContentProvider) GetOnDiskAbsolutePath(repositoryPathURL string) (string, *startosis_errors.InterpretationError) {
+	absFilePath, found := provider.starlarkPackages[repositoryPathURL]
 	if !found {
-		return "", startosis_errors.NewInterpretationError("Module '%v' not found", packageId)
+		return "", startosis_errors.NewInterpretationError("Module '%v' not found", repositoryPathURL)
 	}
 	if _, err := os.Stat(absFilePath); err != nil {
-		return "", startosis_errors.NewInterpretationError("Unable to read content of package '%v'", packageId)
+		return "", startosis_errors.NewInterpretationError("Unable to read content of package '%v'", repositoryPathURL)
 	}
 	return absFilePath, nil
 }
diff --git a/core/server/api_container/server/startosis_engine/startosis_packages/package_content_provider.go b/core/server/api_container/server/startosis_engine/startosis_packages/package_content_provider.go
index 8744e0325..d7a0003f7 100644
--- a/core/server/api_container/server/startosis_engine/startosis_packages/package_content_provider.go
+++ b/core/server/api_container/server/startosis_engine/startosis_packages/package_content_provider.go
@@ -12,11 +12,15 @@ import (
 // Regenerate mock with the following command from core/server directory:
 // mockery -r --name=PackageContentProvider --filename=mock_package_content_provider.go --structname=MockPackageContentProvider --with-expecter --inpackage
 type PackageContentProvider interface {
-	// GetOnDiskAbsoluteFilePath returns the absolute file path of a file inside a module.
+	// GetOnDiskAbsolutePackageFilePath returns the absolute file path of a file inside a module.
 	// The corresponding GitHub repo will be cloned if necessary
-	GetOnDiskAbsoluteFilePath(fileInsidePackageUrl string) (string, *startosis_errors.InterpretationError)
+	GetOnDiskAbsolutePackageFilePath(fileInsidePackageUrl string) (string, *startosis_errors.InterpretationError)
 
-	// GetModuleContents returns the stringified content of a file inside a module
+	// GetOnDiskAbsolutePath returns the absolute path (it can be a filer or a folder and, it can be from a package or not) on APIC's disk.
+	// The corresponding GitHub repo will be cloned if necessary
+	GetOnDiskAbsolutePath(repositoryPathURL string) (string, *startosis_errors.InterpretationError)
+
+	// GetModuleContents returns the stringifies content of a file inside a module
 	GetModuleContents(fileInsidePackageUrl string) (string, *startosis_errors.InterpretationError)
 
 	// GetOnDiskAbsolutePackagePath returns the absolute folder path containing this package
@@ -30,7 +34,7 @@ type PackageContentProvider interface {
 	ClonePackage(packageId string) (string, *startosis_errors.InterpretationError)
 
 	// GetAbsoluteLocator does:
-	// 1. if the given locator is relative, translates it to absolute using sourceModuleLocator (if it's already absolute, does nothing)
+	// 1. if the given locator is relative, translates it to absolute locator using sourceModuleLocator (if it's already absolute, does nothing)
 	// 2. applies any replace rules, if they match the now-absolute locator
 	GetAbsoluteLocator(packageId string, locatorOfModuleInWhichThisBuiltInIsBeingCalled string, relativeOrAbsoluteLocator string, packageReplaceOptions map[string]string) (string, *startosis_errors.InterpretationError)
 
diff --git a/core/server/commons/enclave_data_directory/enclave_data_directory.go b/core/server/commons/enclave_data_directory/enclave_data_directory.go
index e3134ede2..e9506b336 100644
--- a/core/server/commons/enclave_data_directory/enclave_data_directory.go
+++ b/core/server/commons/enclave_data_directory/enclave_data_directory.go
@@ -20,12 +20,12 @@ const (
 	artifactStoreDirname = "artifact-store"
 
 	// The name of the directory INSIDE THE ENCLAVE DATA DIR where Starlark packages will be stored
-	startosisPackageStoreDirname = "startosis-packages"
+	repositoriesStoreDirname = "repositories"
 
-	// The name of the directory INSIDE THE ENCLAVE DATA DIR where temporary packages will be stored
+	// The name of the directory INSIDE THE ENCLAVE DATA DIR where temporary repositories will be stored
 	// We place the temp folder here so that the move to the final destination is atomic
-	// Move from places outside of the enclave data dir are not atomic as they're over the network
-	tmpPackageStoreDirname = "tmp-startosis-packages"
+	// Move from places outside the enclave data dir are not atomic as they're over the network
+	tmpRepositoriesStoreDirname = "tmp-repositories"
 )
 
 // A directory containing all the data associated with a certain enclave (i.e. a Docker subnetwork where services are spun up)
@@ -67,15 +67,15 @@ func (dir EnclaveDataDirectory) GetFilesArtifactStore() (*FilesArtifactStore, er
 }
 
 func (dir EnclaveDataDirectory) GetGitPackageContentProvider(enclaveDb *enclave_db.EnclaveDB) (*git_package_content_provider.GitPackageContentProvider, error) {
-	packageStoreDirpath := path.Join(dir.absMountDirpath, startosisPackageStoreDirname)
-	if err := ensureDirpathExists(packageStoreDirpath); err != nil {
-		return nil, stacktrace.Propagate(err, "An error occurred ensuring the Starlark package store dirpath '%v' exists.", packageStoreDirpath)
+	repositoriesStoreDirpath := path.Join(dir.absMountDirpath, repositoriesStoreDirname)
+	if err := ensureDirpathExists(repositoriesStoreDirpath); err != nil {
+		return nil, stacktrace.Propagate(err, "An error occurred ensuring the repositories store dirpath '%v' exists.", repositoriesStoreDirpath)
 	}
 
-	tempPackageStoreDirpath := path.Join(dir.absMountDirpath, tmpPackageStoreDirname)
-	if err := ensureDirpathExists(tempPackageStoreDirpath); err != nil {
-		return nil, stacktrace.Propagate(err, "An error occurred ensuring the Starlark temporary package store dirpath '%v' exists.", tempPackageStoreDirpath)
+	tempRepositoriesStoreDirpath := path.Join(dir.absMountDirpath, tmpRepositoriesStoreDirname)
+	if err := ensureDirpathExists(tempRepositoriesStoreDirpath); err != nil {
+		return nil, stacktrace.Propagate(err, "An error occurred ensuring the temporary repositories store dirpath '%v' exists.", tempRepositoriesStoreDirpath)
 	}
 
-	return git_package_content_provider.NewGitPackageContentProvider(packageStoreDirpath, tempPackageStoreDirpath, enclaveDb), nil
+	return git_package_content_provider.NewGitPackageContentProvider(repositoriesStoreDirpath, tempRepositoriesStoreDirpath, enclaveDb), nil
 }
diff --git a/docs/docs/api-reference/starlark-reference/plan.md b/docs/docs/api-reference/starlark-reference/plan.md
index 2544afb6f..ee8ec2f62 100644
--- a/docs/docs/api-reference/starlark-reference/plan.md
+++ b/docs/docs/api-reference/starlark-reference/plan.md
@@ -622,7 +622,7 @@ The `upload_files` instruction packages the files specified by the [locator][loc
 ```python
 artifact_name = plan.upload_files(
     # The file to upload into a files artifact
-    # Must be a Kurtosis locator.
+    # Must be any GitHub URL without the '/blob/main' part.
     # MANDATORY
     src = "github.com/foo/bar/static/example.txt",
 
diff --git a/enclave-manager/web/README.md b/enclave-manager/web/README.md
index 26a4a305d..c00f351f8 100644
--- a/enclave-manager/web/README.md
+++ b/enclave-manager/web/README.md
@@ -58,6 +58,15 @@ to auto apply prettier changes on file save, see instructions:
 - [Here](https://plugins.jetbrains.com/plugin/10456-prettier) for Intellij
 - [Here](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) for vscode
 
+### `yarn cypress:ete`
+
+Run the cypress ETE tests
+
+### `yarn cypress:open`
+
+Open the cypress console for debugging/developing new cypress tests. The cypress test suite runs against the locally
+served EMUI from port 9711.
+
 ### `yarn eject`
 
 **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
diff --git a/enclave-manager/web/cypress.config.ts b/enclave-manager/web/cypress.config.ts
new file mode 100644
index 000000000..17161e32e
--- /dev/null
+++ b/enclave-manager/web/cypress.config.ts
@@ -0,0 +1,9 @@
+import { defineConfig } from "cypress";
+
+export default defineConfig({
+  e2e: {
+    setupNodeEvents(on, config) {
+      // implement node event listeners here
+    },
+  },
+});
diff --git a/enclave-manager/web/cypress/support/commands.ts b/enclave-manager/web/cypress/support/commands.ts
new file mode 100644
index 000000000..aa5578694
--- /dev/null
+++ b/enclave-manager/web/cypress/support/commands.ts
@@ -0,0 +1,68 @@
+/// <reference types="cypress" />
+
+export {};
+
+Cypress.Commands.add("goToEnclaveList", () => {
+  return cy.visit("http://localhost:9711");
+});
+
+Cypress.Commands.add("goToCatalog", () => {
+  cy.visit("http://localhost:9711").get('nav button[aria-label="View catalog"]').click();
+});
+
+Cypress.Commands.add("createAndGoToEnclave", (enclaveName: string) => {
+  cy.goToEnclaveList();
+  cy.contains("Enclaves");
+
+  // Create a Postgres enclave
+  cy.contains("New Enclave").click();
+  cy.focused().type("postgres");
+  const configurationDrawer = cy.contains("[role='dialog']", "Enclave Configuration");
+  configurationDrawer.contains("Postgres Package").click();
+
+  configurationDrawer.focusInputWithLabel("Enclave name").type(enclaveName);
+
+  cy.contains("button", "Run").click();
+
+  cy.url({ timeout: 10 * 1000 }).should("match", /enclave\/[^/]+\/logs/);
+
+  cy.contains("button", "Edit").should("be.disabled");
+  cy.contains("Validating", { timeout: 10 * 1000 });
+  cy.contains("Script completed", { timeout: 10 * 1000 });
+  cy.contains("button", "Edit").should("be.enabled");
+
+  // Go to the enclave overview
+  cy.contains("Go to Enclave Overview").click();
+  cy.contains("[role='dialog'] button", "Continue").click();
+  cy.url().should("match", /enclave\/[^/]+/);
+});
+
+Cypress.Commands.add("focusInputWithLabel", (label: string) => {
+  cy.contains("label", label, { matchCase: false }).parents(".chakra-form-control").first().find("input").click();
+});
+
+Cypress.Commands.add("findCardWithName", (name: string) => {
+  return cy.contains(".chakra-card", name, { matchCase: false });
+});
+
+Cypress.Commands.add("deleteEnclave", (enclaveName: string) => {
+  cy.goToEnclaveList();
+  cy.contains("tr", enclaveName).find(".chakra-checkbox").click();
+  cy.contains("button", "Delete").click();
+  cy.contains("[role='dialog'] button", "Delete").click();
+  cy.contains("[role='dialog']", "Delete", { timeout: 10 * 1000 }).should("not.exist");
+  cy.contains("tr", enclaveName).should("not.exist");
+});
+
+declare global {
+  namespace Cypress {
+    interface Chainable {
+      createAndGoToEnclave(enclaveName: string): Chainable<void>;
+      deleteEnclave(enclaveName: string): Chainable<void>;
+      focusInputWithLabel(label: string): Chainable<void>;
+      findCardWithName(label: string): Chainable<JQuery<HTMLElement>>;
+      goToCatalog(): Chainable<void>;
+      goToEnclaveList(): Chainable<AUTWindow>;
+    }
+  }
+}
diff --git a/enclave-manager/web/lerna.json b/enclave-manager/web/lerna.json
index 08ebd88e8..7a66cd261 100644
--- a/enclave-manager/web/lerna.json
+++ b/enclave-manager/web/lerna.json
@@ -1,6 +1,6 @@
 {
   "packages": ["packages/*"],
-  "version": "0.86.4",
+  "version": "0.86.5",
   "npmClient": "yarn",
   "$schema": "node_modules/lerna/schemas/lerna-schema.json",
   "useNx": false,
diff --git a/enclave-manager/web/package.json b/enclave-manager/web/package.json
index 496910969..4e49e0a5b 100644
--- a/enclave-manager/web/package.json
+++ b/enclave-manager/web/package.json
@@ -20,6 +20,7 @@
     "@types/node": "^16.7.13",
     "@types/react": "^18.0.0",
     "@types/react-dom": "^18.0.0",
+    "cypress": "^13.6.2",
     "dotenv-cli": "^6.0.0",
     "lerna": "^5.6.2",
     "prettier": "3.0.3",
@@ -31,6 +32,8 @@
     "analyze": "lerna run analyze",
     "cleanInstall": "rm -rf node_modules; rm -rf packages/*/node_modules; yarn install; lerna init",
     "clean": "lerna run clean",
+    "cypress:open": "cypress open",
+    "cypress:ete": "cypress run --browser chrome",
     "prebuild": "rm -rf ../../engine/server/webapp",
     "postbuild": "cp -r packages/app/build/ ../../engine/server/webapp",
     "build": "REACT_APP_VERSION=$(git fetch origin --tags -q && git describe --dirty --match '[0-9]*' --tags) lerna run --stream build",
diff --git a/enclave-manager/web/packages/app/package.json b/enclave-manager/web/packages/app/package.json
index dd9824450..81420d45c 100644
--- a/enclave-manager/web/packages/app/package.json
+++ b/enclave-manager/web/packages/app/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@kurtosis/emui-app",
-  "version": "0.86.4",
+  "version": "0.86.5",
   "private": true,
   "homepage": ".",
   "dependencies": {
@@ -9,7 +9,7 @@
     "html-react-parser": "^4.2.2",
     "js-cookie": "^3.0.5",
     "kurtosis-cloud-indexer-sdk": "^0.0.2",
-    "kurtosis-ui-components": "0.86.4",
+    "kurtosis-ui-components": "0.86.5",
     "react-error-boundary": "^4.0.11",
     "react-hook-form": "^7.47.0",
     "yaml": "^2.3.4"
diff --git a/enclave-manager/web/packages/components/package.json b/enclave-manager/web/packages/components/package.json
index 7b43c2b3e..59951a2dc 100644
--- a/enclave-manager/web/packages/components/package.json
+++ b/enclave-manager/web/packages/components/package.json
@@ -1,6 +1,6 @@
 {
   "name": "kurtosis-ui-components",
-  "version": "0.86.4",
+  "version": "0.86.5",
   "private": false,
   "main": "build/index",
   "description": "This repo contains components used by Kurtosis UI applications.",
diff --git a/enclave-manager/web/yarn.lock b/enclave-manager/web/yarn.lock
index b2f75ab0e..ca53ceb7f 100644
--- a/enclave-manager/web/yarn.lock
+++ b/enclave-manager/web/yarn.lock
@@ -1975,6 +1975,11 @@
   resolved "https://registry.yarnpkg.com/@chakra-ui/visually-hidden/-/visually-hidden-2.2.0.tgz#9b0ecef8f01263ab808ba3bda7b36a0d91b4d5c1"
   integrity sha512-KmKDg01SrQ7VbTD3+cPWf/UfpF5MSwm3v7MWi0n5t8HnnadT13MF0MJCDSXbBWnzLv1ZKJ6zlyAOeARWX+DpjQ==
 
+"@colors/colors@1.5.0":
+  version "1.5.0"
+  resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
+  integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==
+
 "@connectrpc/connect-web@^0.13.0":
   version "0.13.2"
   resolved "https://registry.yarnpkg.com/@connectrpc/connect-web/-/connect-web-0.13.2.tgz#14b8f30458196897578ce773f3ab0ec42d114228"
@@ -2118,6 +2123,38 @@
   resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz#2cbcf822bf3764c9658c4d2e568bd0c0cb748016"
   integrity sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw==
 
+"@cypress/request@^3.0.0":
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/@cypress/request/-/request-3.0.1.tgz#72d7d5425236a2413bd3d8bb66d02d9dc3168960"
+  integrity sha512-TWivJlJi8ZDx2wGOw1dbLuHJKUYX7bWySw377nlnGOW3hP9/MUKIsEdXT/YngWxVdgNCHRBmFlBipE+5/2ZZlQ==
+  dependencies:
+    aws-sign2 "~0.7.0"
+    aws4 "^1.8.0"
+    caseless "~0.12.0"
+    combined-stream "~1.0.6"
+    extend "~3.0.2"
+    forever-agent "~0.6.1"
+    form-data "~2.3.2"
+    http-signature "~1.3.6"
+    is-typedarray "~1.0.0"
+    isstream "~0.1.2"
+    json-stringify-safe "~5.0.1"
+    mime-types "~2.1.19"
+    performance-now "^2.1.0"
+    qs "6.10.4"
+    safe-buffer "^5.1.2"
+    tough-cookie "^4.1.3"
+    tunnel-agent "^0.6.0"
+    uuid "^8.3.2"
+
+"@cypress/xvfb@^1.2.4":
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/@cypress/xvfb/-/xvfb-1.2.4.tgz#2daf42e8275b39f4aa53c14214e557bd14e7748a"
+  integrity sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==
+  dependencies:
+    debug "^3.1.0"
+    lodash.once "^4.1.1"
+
 "@emotion/babel-plugin@^11.11.0":
   version "11.11.0"
   resolved "https://registry.yarnpkg.com/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz#c2d872b6a7767a9d176d007f5b31f7d504bb5d6c"
@@ -4138,6 +4175,13 @@
   resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.68.tgz#3155f64a961b3d8d10246c80657f9a7292e3421a"
   integrity sha512-sG3hPIQwJLoewrN7cr0dwEy+yF5nD4D/4FxtQpFciRD/xwUzgD+G05uxZHv5mhfXo4F9Jkp13jjn0CC2q325sg==
 
+"@types/node@^18.17.5":
+  version "18.19.6"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.6.tgz#537beece2c8ad4d9abdaa3b0f428e601eb57dac8"
+  integrity sha512-X36s5CXMrrJOs2lQCdDF68apW4Rfx9ixYMawlepwmE4Anezv/AV2LSpKD1Ub8DAc+urp5bk0BGZ6NtmBitfnsg==
+  dependencies:
+    undici-types "~5.26.4"
+
 "@types/normalize-package-data@^2.4.0":
   version "2.4.4"
   resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz#56e2cc26c397c038fab0e3a917a12d5c5909e901"
@@ -4235,6 +4279,16 @@
     "@types/mime" "*"
     "@types/node" "*"
 
+"@types/sinonjs__fake-timers@8.1.1":
+  version "8.1.1"
+  resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz#b49c2c70150141a15e0fa7e79cf1f92a72934ce3"
+  integrity sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==
+
+"@types/sizzle@^2.3.2":
+  version "2.3.8"
+  resolved "https://registry.yarnpkg.com/@types/sizzle/-/sizzle-2.3.8.tgz#518609aefb797da19bf222feb199e8f653ff7627"
+  integrity sha512-0vWLNK2D5MT9dg0iOo8GlKguPAU02QjmZitPEsXRuJXU/OGIOt9vT9Fc26wtYuavLxtO45v9PGleoL9Z0k1LHg==
+
 "@types/sockjs@^0.3.33":
   version "0.3.36"
   resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.36.tgz#ce322cf07bcc119d4cbf7f88954f3a3bd0f67535"
@@ -4293,6 +4347,13 @@
   dependencies:
     "@types/yargs-parser" "*"
 
+"@types/yauzl@^2.9.1":
+  version "2.10.3"
+  resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.3.tgz#e9b2808b4f109504a03cda958259876f61017999"
+  integrity sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==
+  dependencies:
+    "@types/node" "*"
+
 "@typescript-eslint/eslint-plugin@^5.5.0":
   version "5.62.0"
   resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db"
@@ -4727,7 +4788,7 @@ ansi-colors@^4.1.1:
   resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b"
   integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==
 
-ansi-escapes@^4.2.1, ansi-escapes@^4.3.1:
+ansi-escapes@^4.2.1, ansi-escapes@^4.3.0, ansi-escapes@^4.3.1:
   version "4.3.2"
   resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e"
   integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==
@@ -4972,12 +5033,29 @@ asap@^2.0.0, asap@~2.0.6:
   resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
   integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==
 
+asn1@~0.2.3:
+  version "0.2.6"
+  resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d"
+  integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==
+  dependencies:
+    safer-buffer "~2.1.0"
+
+assert-plus@1.0.0, assert-plus@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
+  integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==
+
 ast-types-flow@^0.0.8:
   version "0.0.8"
   resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.8.tgz#0a85e1c92695769ac13a428bb653e7538bea27d6"
   integrity sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==
 
-async@^3.2.3:
+astral-regex@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31"
+  integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==
+
+async@^3.2.0, async@^3.2.3:
   version "3.2.5"
   resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66"
   integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==
@@ -5016,6 +5094,16 @@ available-typed-arrays@^1.0.5:
   resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7"
   integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==
 
+aws-sign2@~0.7.0:
+  version "0.7.0"
+  resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
+  integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==
+
+aws4@^1.8.0:
+  version "1.12.0"
+  resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.12.0.tgz#ce1c9d143389679e253b314241ea9aa5cec980d3"
+  integrity sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==
+
 axe-core@=4.7.0:
   version "4.7.0"
   resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.7.0.tgz#34ba5a48a8b564f67e103f0aa5768d76e15bbbbf"
@@ -5193,6 +5281,13 @@ batch@0.6.1:
   resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16"
   integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==
 
+bcrypt-pbkdf@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
+  integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==
+  dependencies:
+    tweetnacl "^0.14.3"
+
 before-after-hook@^2.2.0:
   version "2.2.3"
   resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.3.tgz#c51e809c81a4e354084422b9b26bad88249c517c"
@@ -5240,6 +5335,11 @@ bl@^4.0.3, bl@^4.1.0:
     inherits "^2.0.4"
     readable-stream "^3.4.0"
 
+blob-util@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/blob-util/-/blob-util-2.0.2.tgz#3b4e3c281111bb7f11128518006cdc60b403a1eb"
+  integrity sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==
+
 bluebird@^3.7.2:
   version "3.7.2"
   resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
@@ -5341,12 +5441,17 @@ btoa@^1.2.1:
   resolved "https://registry.yarnpkg.com/btoa/-/btoa-1.2.1.tgz#01a9909f8b2c93f6bf680ba26131eb30f7fa3d73"
   integrity sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==
 
+buffer-crc32@~0.2.3:
+  version "0.2.13"
+  resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
+  integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==
+
 buffer-from@^1.0.0:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
   integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
 
-buffer@^5.5.0:
+buffer@^5.5.0, buffer@^5.6.0:
   version "5.7.1"
   resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
   integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
@@ -5410,6 +5515,11 @@ cacache@^16.0.0, cacache@^16.0.6, cacache@^16.1.0:
     tar "^6.1.11"
     unique-filename "^2.0.0"
 
+cachedir@^2.3.0:
+  version "2.4.0"
+  resolved "https://registry.yarnpkg.com/cachedir/-/cachedir-2.4.0.tgz#7fef9cf7367233d7c88068fe6e34ed0d355a610d"
+  integrity sha512-9EtFOZR8g22CL7BWjJ9BUx1+A/djkofnyW3aOXZORNW2kxoUpx2h+uN2cOqwPmFhnpVmxg+KW2OjOSgChTEvsQ==
+
 call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.4, call-bind@^1.0.5:
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513"
@@ -5481,6 +5591,11 @@ case-sensitive-paths-webpack-plugin@^2.4.0:
   resolved "https://registry.yarnpkg.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz#db64066c6422eed2e08cc14b986ca43796dbc6d4"
   integrity sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==
 
+caseless@~0.12.0:
+  version "0.12.0"
+  resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
+  integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==
+
 ccount@^2.0.0:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/ccount/-/ccount-2.0.1.tgz#17a3bf82302e0870d6da43a01311a8bc02a3ecf5"
@@ -5555,6 +5670,11 @@ chardet@^0.7.0:
   resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
   integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
 
+check-more-types@^2.24.0:
+  version "2.24.0"
+  resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600"
+  integrity sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==
+
 check-types@^11.2.3:
   version "11.2.3"
   resolved "https://registry.yarnpkg.com/check-types/-/check-types-11.2.3.tgz#1ffdf68faae4e941fce252840b1787b8edc93b71"
@@ -5634,6 +5754,23 @@ cli-spinners@^2.5.0:
   resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41"
   integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==
 
+cli-table3@~0.6.1:
+  version "0.6.3"
+  resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.3.tgz#61ab765aac156b52f222954ffc607a6f01dbeeb2"
+  integrity sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==
+  dependencies:
+    string-width "^4.2.0"
+  optionalDependencies:
+    "@colors/colors" "1.5.0"
+
+cli-truncate@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7"
+  integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==
+  dependencies:
+    slice-ansi "^3.0.0"
+    string-width "^4.2.0"
+
 cli-width@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6"
@@ -5745,7 +5882,7 @@ colord@^2.9.1:
   resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43"
   integrity sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==
 
-colorette@^2.0.10:
+colorette@^2.0.10, colorette@^2.0.16:
   version "2.0.20"
   resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a"
   integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==
@@ -5758,7 +5895,7 @@ columnify@^1.6.0:
     strip-ansi "^6.0.1"
     wcwidth "^1.0.0"
 
-combined-stream@^1.0.8:
+combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6:
   version "1.0.8"
   resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
   integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
@@ -5780,6 +5917,11 @@ commander@^4.0.0:
   resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
   integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==
 
+commander@^6.2.1:
+  version "6.2.1"
+  resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
+  integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
+
 commander@^7.2.0:
   version "7.2.0"
   resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
@@ -6024,6 +6166,11 @@ core-js@^3.19.2:
   resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.34.0.tgz#5705e6ad5982678612e96987d05b27c6c7c274a5"
   integrity sha512-aDdvlDder8QmY91H88GzNi9EtQi2TjvQhpCX6B1v/dAZHU1AuLgHvRh54RiOerpEhEW46Tkf+vgAViB/CWC0ag==
 
+core-util-is@1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
+  integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==
+
 core-util-is@~1.0.0:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
@@ -6064,7 +6211,7 @@ create-require@^1.1.0:
   resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
   integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
 
-cross-spawn@^7.0.2, cross-spawn@^7.0.3:
+cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
   version "7.0.3"
   resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
   integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
@@ -6275,6 +6422,55 @@ csstype@^3.0.2, csstype@^3.1.2:
   resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81"
   integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
 
+cypress@^13.6.2:
+  version "13.6.2"
+  resolved "https://registry.yarnpkg.com/cypress/-/cypress-13.6.2.tgz#c70df09db0a45063298b3cecba2fa21109768e08"
+  integrity sha512-TW3bGdPU4BrfvMQYv1z3oMqj71YI4AlgJgnrycicmPZAXtvywVFZW9DAToshO65D97rCWfG/kqMFsYB6Kp91gQ==
+  dependencies:
+    "@cypress/request" "^3.0.0"
+    "@cypress/xvfb" "^1.2.4"
+    "@types/node" "^18.17.5"
+    "@types/sinonjs__fake-timers" "8.1.1"
+    "@types/sizzle" "^2.3.2"
+    arch "^2.2.0"
+    blob-util "^2.0.2"
+    bluebird "^3.7.2"
+    buffer "^5.6.0"
+    cachedir "^2.3.0"
+    chalk "^4.1.0"
+    check-more-types "^2.24.0"
+    cli-cursor "^3.1.0"
+    cli-table3 "~0.6.1"
+    commander "^6.2.1"
+    common-tags "^1.8.0"
+    dayjs "^1.10.4"
+    debug "^4.3.4"
+    enquirer "^2.3.6"
+    eventemitter2 "6.4.7"
+    execa "4.1.0"
+    executable "^4.1.1"
+    extract-zip "2.0.1"
+    figures "^3.2.0"
+    fs-extra "^9.1.0"
+    getos "^3.2.1"
+    is-ci "^3.0.0"
+    is-installed-globally "~0.4.0"
+    lazy-ass "^1.6.0"
+    listr2 "^3.8.3"
+    lodash "^4.17.21"
+    log-symbols "^4.0.0"
+    minimist "^1.2.8"
+    ospath "^1.2.2"
+    pretty-bytes "^5.6.0"
+    process "^0.11.10"
+    proxy-from-env "1.0.0"
+    request-progress "^3.0.0"
+    semver "^7.5.3"
+    supports-color "^8.1.1"
+    tmp "~0.2.1"
+    untildify "^4.0.0"
+    yauzl "^2.10.0"
+
 damerau-levenshtein@^1.0.8:
   version "1.0.8"
   resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7"
@@ -6285,6 +6481,13 @@ dargs@^7.0.0:
   resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc"
   integrity sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==
 
+dashdash@^1.12.0:
+  version "1.14.1"
+  resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
+  integrity sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==
+  dependencies:
+    assert-plus "^1.0.0"
+
 data-urls@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b"
@@ -6299,6 +6502,11 @@ dateformat@^3.0.0:
   resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
   integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==
 
+dayjs@^1.10.4:
+  version "1.11.10"
+  resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.10.tgz#68acea85317a6e164457d6d6947564029a6a16a0"
+  integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==
+
 debug@2.6.9, debug@^2.6.0:
   version "2.6.9"
   resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
@@ -6313,7 +6521,7 @@ debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, d
   dependencies:
     ms "2.1.2"
 
-debug@^3.2.7:
+debug@^3.1.0, debug@^3.2.7:
   version "3.2.7"
   resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
   integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==
@@ -6695,6 +6903,14 @@ eastasianwidth@^0.2.0:
   resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb"
   integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
 
+ecc-jsbn@~0.1.1:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
+  integrity sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==
+  dependencies:
+    jsbn "~0.1.0"
+    safer-buffer "^2.1.0"
+
 ee-first@1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
@@ -6756,7 +6972,7 @@ encoding@^0.1.13:
   dependencies:
     iconv-lite "^0.6.2"
 
-end-of-stream@^1.4.1:
+end-of-stream@^1.1.0, end-of-stream@^1.4.1:
   version "1.4.4"
   resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
   integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
@@ -6771,6 +6987,14 @@ enhanced-resolve@^5.15.0:
     graceful-fs "^4.2.4"
     tapable "^2.2.0"
 
+enquirer@^2.3.6:
+  version "2.4.1"
+  resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.4.1.tgz#93334b3fbd74fc7097b224ab4a8fb7e40bf4ae56"
+  integrity sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==
+  dependencies:
+    ansi-colors "^4.1.1"
+    strip-ansi "^6.0.1"
+
 enquirer@~2.3.6:
   version "2.3.6"
   resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d"
@@ -7239,6 +7463,11 @@ etag@~1.8.1:
   resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
   integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
 
+eventemitter2@6.4.7:
+  version "6.4.7"
+  resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.7.tgz#a7f6c4d7abf28a14c1ef3442f21cb306a054271d"
+  integrity sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==
+
 eventemitter3@^4.0.0, eventemitter3@^4.0.4:
   version "4.0.7"
   resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
@@ -7249,6 +7478,21 @@ events@^3.2.0:
   resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
   integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
 
+execa@4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/execa/-/execa-4.1.0.tgz#4e5491ad1572f2f17a77d388c6c857135b22847a"
+  integrity sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==
+  dependencies:
+    cross-spawn "^7.0.0"
+    get-stream "^5.0.0"
+    human-signals "^1.1.1"
+    is-stream "^2.0.0"
+    merge-stream "^2.0.0"
+    npm-run-path "^4.0.0"
+    onetime "^5.1.0"
+    signal-exit "^3.0.2"
+    strip-final-newline "^2.0.0"
+
 execa@^5.0.0, execa@^5.1.1:
   version "5.1.1"
   resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
@@ -7264,6 +7508,13 @@ execa@^5.0.0, execa@^5.1.1:
     signal-exit "^3.0.3"
     strip-final-newline "^2.0.0"
 
+executable@^4.1.1:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/executable/-/executable-4.1.1.tgz#41532bff361d3e57af4d763b70582db18f5d133c"
+  integrity sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==
+  dependencies:
+    pify "^2.2.0"
+
 exit@^0.1.2:
   version "0.1.2"
   resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c"
@@ -7321,7 +7572,7 @@ express@^4.17.3:
     utils-merge "1.0.1"
     vary "~1.1.2"
 
-extend@^3.0.0:
+extend@^3.0.0, extend@~3.0.2:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
   integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
@@ -7335,6 +7586,27 @@ external-editor@^3.0.3:
     iconv-lite "^0.4.24"
     tmp "^0.0.33"
 
+extract-zip@2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a"
+  integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==
+  dependencies:
+    debug "^4.1.1"
+    get-stream "^5.1.0"
+    yauzl "^2.10.0"
+  optionalDependencies:
+    "@types/yauzl" "^2.9.1"
+
+extsprintf@1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
+  integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==
+
+extsprintf@^1.2.0:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07"
+  integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==
+
 fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
   version "3.1.3"
   resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
@@ -7400,7 +7672,14 @@ fb-watchman@^2.0.0:
   dependencies:
     bser "2.1.1"
 
-figures@3.2.0, figures@^3.0.0:
+fd-slicer@~1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
+  integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==
+  dependencies:
+    pend "~1.2.0"
+
+figures@3.2.0, figures@^3.0.0, figures@^3.2.0:
   version "3.2.0"
   resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af"
   integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==
@@ -7536,6 +7815,11 @@ for-each@^0.3.3:
   dependencies:
     is-callable "^1.1.3"
 
+forever-agent@~0.6.1:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
+  integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==
+
 fork-ts-checker-webpack-plugin@^6.5.0:
   version "6.5.3"
   resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.3.tgz#eda2eff6e22476a2688d10661688c47f611b37f3"
@@ -7573,6 +7857,15 @@ form-data@^4.0.0:
     combined-stream "^1.0.8"
     mime-types "^2.1.12"
 
+form-data@~2.3.2:
+  version "2.3.3"
+  resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
+  integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==
+  dependencies:
+    asynckit "^0.4.0"
+    combined-stream "^1.0.6"
+    mime-types "^2.1.12"
+
 forwarded@0.2.0:
   version "0.2.0"
   resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
@@ -7743,6 +8036,13 @@ get-port@^5.1.1:
   resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193"
   integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==
 
+get-stream@^5.0.0, get-stream@^5.1.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3"
+  integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==
+  dependencies:
+    pump "^3.0.0"
+
 get-stream@^6.0.0:
   version "6.0.1"
   resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7"
@@ -7756,6 +8056,20 @@ get-symbol-description@^1.0.0:
     call-bind "^1.0.2"
     get-intrinsic "^1.1.1"
 
+getos@^3.2.1:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/getos/-/getos-3.2.1.tgz#0134d1f4e00eb46144c5a9c0ac4dc087cbb27dc5"
+  integrity sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==
+  dependencies:
+    async "^3.2.0"
+
+getpass@^0.1.1:
+  version "0.1.7"
+  resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
+  integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==
+  dependencies:
+    assert-plus "^1.0.0"
+
 git-raw-commits@^2.0.8:
   version "2.0.11"
   resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-2.0.11.tgz#bc3576638071d18655e1cc60d7f524920008d723"
@@ -7871,6 +8185,13 @@ glob@^8.0.1:
     minimatch "^5.0.1"
     once "^1.3.0"
 
+global-dirs@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.1.tgz#0c488971f066baceda21447aecb1a8b911d22485"
+  integrity sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==
+  dependencies:
+    ini "2.0.0"
+
 global-modules@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780"
@@ -8259,6 +8580,15 @@ http-proxy@^1.18.1:
     follow-redirects "^1.0.0"
     requires-port "^1.0.0"
 
+http-signature@~1.3.6:
+  version "1.3.6"
+  resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.3.6.tgz#cb6fbfdf86d1c974f343be94e87f7fc128662cf9"
+  integrity sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==
+  dependencies:
+    assert-plus "^1.0.0"
+    jsprim "^2.0.2"
+    sshpk "^1.14.1"
+
 https-proxy-agent@^5.0.0:
   version "5.0.1"
   resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
@@ -8267,6 +8597,11 @@ https-proxy-agent@^5.0.0:
     agent-base "6"
     debug "4"
 
+human-signals@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3"
+  integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==
+
 human-signals@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
@@ -8381,6 +8716,11 @@ inherits@2.0.3:
   resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
   integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==
 
+ini@2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/ini/-/ini-2.0.0.tgz#e5fd556ecdd5726be978fa1001862eacb0a94bc5"
+  integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==
+
 ini@^1.3.2, ini@^1.3.4, ini@^1.3.5, ini@~1.3.0:
   version "1.3.8"
   resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
@@ -8524,6 +8864,13 @@ is-ci@^2.0.0:
   dependencies:
     ci-info "^2.0.0"
 
+is-ci@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.1.tgz#db6ecbed1bd659c43dac0f45661e7674103d1867"
+  integrity sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==
+  dependencies:
+    ci-info "^3.2.0"
+
 is-core-module@^2.13.0, is-core-module@^2.13.1, is-core-module@^2.5.0, is-core-module@^2.8.1:
   version "2.13.1"
   resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384"
@@ -8589,6 +8936,14 @@ is-hexadecimal@^2.0.0:
   resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz#86b5bf668fca307498d319dfc03289d781a90027"
   integrity sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==
 
+is-installed-globally@~0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.4.0.tgz#9a0fd407949c30f86eb6959ef1b7994ed0b7b520"
+  integrity sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==
+  dependencies:
+    global-dirs "^3.0.0"
+    is-path-inside "^3.0.2"
+
 is-interactive@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e"
@@ -8636,7 +8991,7 @@ is-obj@^2.0.0:
   resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982"
   integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==
 
-is-path-inside@^3.0.3:
+is-path-inside@^3.0.2, is-path-inside@^3.0.3:
   version "3.0.3"
   resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
   integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
@@ -8753,7 +9108,7 @@ is-typed-array@^1.1.10, is-typed-array@^1.1.12, is-typed-array@^1.1.9:
   dependencies:
     which-typed-array "^1.1.11"
 
-is-typedarray@^1.0.0:
+is-typedarray@^1.0.0, is-typedarray@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
   integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==
@@ -8810,6 +9165,11 @@ isobject@^3.0.1:
   resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
   integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==
 
+isstream@~0.1.2:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
+  integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==
+
 istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0:
   version "3.2.2"
   resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756"
@@ -9385,6 +9745,11 @@ js-yaml@^3.10.0, js-yaml@^3.13.1:
     argparse "^1.0.7"
     esprima "^4.0.0"
 
+jsbn@~0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
+  integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==
+
 jsdom@^16.6.0:
   version "16.7.0"
   resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710"
@@ -9453,7 +9818,7 @@ json-schema-traverse@^1.0.0:
   resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
   integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
 
-json-schema@^0.4.0:
+json-schema@0.4.0, json-schema@^0.4.0:
   version "0.4.0"
   resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5"
   integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==
@@ -9468,7 +9833,7 @@ json-stringify-nice@^1.1.4:
   resolved "https://registry.yarnpkg.com/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz#2c937962b80181d3f317dd39aa323e14f5a60a67"
   integrity sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw==
 
-json-stringify-safe@^5.0.1:
+json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1:
   version "5.0.1"
   resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
   integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==
@@ -9518,6 +9883,16 @@ jsonpointer@^5.0.0:
   resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-5.0.1.tgz#2110e0af0900fd37467b5907ecd13a7884a1b559"
   integrity sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==
 
+jsprim@^2.0.2:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-2.0.2.tgz#77ca23dbcd4135cd364800d22ff82c2185803d4d"
+  integrity sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==
+  dependencies:
+    assert-plus "1.0.0"
+    extsprintf "1.3.0"
+    json-schema "0.4.0"
+    verror "1.10.0"
+
 "jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.5:
   version "3.3.5"
   resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz#4766bd05a8e2a11af222becd19e15575e52a853a"
@@ -9587,6 +9962,11 @@ launch-editor@^2.6.0:
     picocolors "^1.0.0"
     shell-quote "^1.8.1"
 
+lazy-ass@^1.6.0:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513"
+  integrity sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==
+
 lerna@^5.6.2:
   version "5.6.2"
   resolved "https://registry.yarnpkg.com/lerna/-/lerna-5.6.2.tgz#cdcdfe4e8bf07eccb4ecff1c216def9c67e62af2"
@@ -9678,6 +10058,20 @@ lines-and-columns@~2.0.3:
   resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-2.0.4.tgz#d00318855905d2660d8c0822e3f5a4715855fc42"
   integrity sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==
 
+listr2@^3.8.3:
+  version "3.14.0"
+  resolved "https://registry.yarnpkg.com/listr2/-/listr2-3.14.0.tgz#23101cc62e1375fd5836b248276d1d2b51fdbe9e"
+  integrity sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==
+  dependencies:
+    cli-truncate "^2.1.0"
+    colorette "^2.0.16"
+    log-update "^4.0.0"
+    p-map "^4.0.0"
+    rfdc "^1.3.0"
+    rxjs "^7.5.1"
+    through "^2.3.8"
+    wrap-ansi "^7.0.0"
+
 load-json-file@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b"
@@ -9772,6 +10166,11 @@ lodash.mergewith@4.6.2:
   resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55"
   integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==
 
+lodash.once@^4.1.1:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
+  integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==
+
 lodash.sortby@^4.7.0:
   version "4.7.0"
   resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
@@ -9787,7 +10186,7 @@ lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.7.0:
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
   integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
 
-log-symbols@^4.1.0:
+log-symbols@^4.0.0, log-symbols@^4.1.0:
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503"
   integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==
@@ -9795,6 +10194,16 @@ log-symbols@^4.1.0:
     chalk "^4.1.0"
     is-unicode-supported "^0.1.0"
 
+log-update@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1"
+  integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==
+  dependencies:
+    ansi-escapes "^4.3.0"
+    cli-cursor "^3.1.0"
+    slice-ansi "^4.0.0"
+    wrap-ansi "^6.2.0"
+
 longest-streak@^3.0.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-3.1.0.tgz#62fa67cd958742a1574af9f39866364102d90cd4"
@@ -10293,7 +10702,7 @@ mime-types@2.1.18:
   dependencies:
     mime-db "~1.33.0"
 
-mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34:
+mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.34:
   version "2.1.35"
   resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
   integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
@@ -10357,7 +10766,7 @@ minimist-options@4.1.0:
     is-plain-obj "^1.1.0"
     kind-of "^6.0.3"
 
-minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6:
+minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6, minimist@^1.2.8:
   version "1.2.8"
   resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
   integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
@@ -10732,7 +11141,7 @@ npm-registry-fetch@^13.0.0, npm-registry-fetch@^13.0.1, npm-registry-fetch@^13.3
     npm-package-arg "^9.0.1"
     proc-log "^2.0.0"
 
-npm-run-path@^4.0.1:
+npm-run-path@^4.0.0, npm-run-path@^4.0.1:
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"
   integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==
@@ -10922,7 +11331,7 @@ on-headers@~1.0.2:
   resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f"
   integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==
 
-once@^1.3.0, once@^1.4.0:
+once@^1.3.0, once@^1.3.1, once@^1.4.0:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
   integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
@@ -10997,6 +11406,11 @@ os-tmpdir@~1.0.2:
   resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
   integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==
 
+ospath@^1.2.2:
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/ospath/-/ospath-1.2.2.tgz#1276639774a3f8ef2572f7fe4280e0ea4550c07b"
+  integrity sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==
+
 p-finally@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
@@ -11280,6 +11694,11 @@ path-type@^4.0.0:
   resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
   integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
 
+pend@~1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
+  integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==
+
 performance-now@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
@@ -11300,7 +11719,7 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3, picomatc
   resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
   integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
 
-pify@^2.3.0:
+pify@^2.2.0, pify@^2.3.0:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
   integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==
@@ -11911,7 +12330,7 @@ prettier@3.0.3:
   resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.0.3.tgz#432a51f7ba422d1469096c0fdc28e235db8f9643"
   integrity sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==
 
-pretty-bytes@^5.3.0, pretty-bytes@^5.4.1:
+pretty-bytes@^5.3.0, pretty-bytes@^5.4.1, pretty-bytes@^5.6.0:
   version "5.6.0"
   resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb"
   integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==
@@ -11953,6 +12372,11 @@ process-nextick-args@~2.0.0:
   resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
   integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
 
+process@^0.11.10:
+  version "0.11.10"
+  resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
+  integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==
+
 promise-all-reject-late@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz#f8ebf13483e5ca91ad809ccc2fcf25f26f8643c2"
@@ -12030,6 +12454,11 @@ proxy-addr@~2.0.7:
     forwarded "0.2.0"
     ipaddr.js "1.9.1"
 
+proxy-from-env@1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee"
+  integrity sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==
+
 proxy-from-env@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
@@ -12040,6 +12469,14 @@ psl@^1.1.33:
   resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7"
   integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==
 
+pump@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
+  integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
+  dependencies:
+    end-of-stream "^1.1.0"
+    once "^1.3.1"
+
 punycode@^1.3.2:
   version "1.4.1"
   resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
@@ -12055,6 +12492,13 @@ q@^1.1.2, q@^1.5.1:
   resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
   integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==
 
+qs@6.10.4:
+  version "6.10.4"
+  resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.4.tgz#6a3003755add91c0ec9eacdc5f878b034e73f9e7"
+  integrity sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g==
+  dependencies:
+    side-channel "^1.0.4"
+
 qs@6.11.0:
   version "6.11.0"
   resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a"
@@ -12621,6 +13065,13 @@ renderkid@^3.0.0:
     lodash "^4.17.21"
     strip-ansi "^6.0.1"
 
+request-progress@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/request-progress/-/request-progress-3.0.0.tgz#4ca754081c7fec63f505e4faa825aa06cd669dbe"
+  integrity sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==
+  dependencies:
+    throttleit "^1.0.0"
+
 require-directory@^2.1.1:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
@@ -12710,6 +13161,11 @@ reusify@^1.0.4:
   resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
   integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
 
+rfdc@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b"
+  integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==
+
 rimraf@^3.0.0, rimraf@^3.0.2:
   version "3.0.2"
   resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
@@ -12753,7 +13209,7 @@ run-parallel@^1.1.9:
   dependencies:
     queue-microtask "^1.2.2"
 
-rxjs@^7.5.5:
+rxjs@^7.5.1, rxjs@^7.5.5:
   version "7.8.1"
   resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543"
   integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==
@@ -12775,7 +13231,7 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
   resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
   integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
 
-safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0:
+safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@~5.2.0:
   version "5.2.1"
   resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
   integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
@@ -12789,7 +13245,7 @@ safe-regex-test@^1.0.0:
     get-intrinsic "^1.1.3"
     is-regex "^1.1.4"
 
-"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0":
+"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
   version "2.1.2"
   resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
   integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
@@ -13067,6 +13523,24 @@ slash@^4.0.0:
   resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7"
   integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==
 
+slice-ansi@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787"
+  integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==
+  dependencies:
+    ansi-styles "^4.0.0"
+    astral-regex "^2.0.0"
+    is-fullwidth-code-point "^3.0.0"
+
+slice-ansi@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b"
+  integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==
+  dependencies:
+    ansi-styles "^4.0.0"
+    astral-regex "^2.0.0"
+    is-fullwidth-code-point "^3.0.0"
+
 smart-buffer@^4.2.0:
   version "4.2.0"
   resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae"
@@ -13257,6 +13731,21 @@ sprintf-js@~1.0.2:
   resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
   integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==
 
+sshpk@^1.14.1:
+  version "1.18.0"
+  resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.18.0.tgz#1663e55cddf4d688b86a46b77f0d5fe363aba028"
+  integrity sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==
+  dependencies:
+    asn1 "~0.2.3"
+    assert-plus "^1.0.0"
+    bcrypt-pbkdf "^1.0.0"
+    dashdash "^1.12.0"
+    ecc-jsbn "~0.1.1"
+    getpass "^0.1.1"
+    jsbn "~0.1.0"
+    safer-buffer "^2.0.2"
+    tweetnacl "~0.14.0"
+
 ssri@^9.0.0, ssri@^9.0.1:
   version "9.0.1"
   resolved "https://registry.yarnpkg.com/ssri/-/ssri-9.0.1.tgz#544d4c357a8d7b71a19700074b6883fcb4eae057"
@@ -13546,7 +14035,7 @@ supports-color@^7.0.0, supports-color@^7.1.0:
   dependencies:
     has-flag "^4.0.0"
 
-supports-color@^8.0.0:
+supports-color@^8.0.0, supports-color@^8.1.1:
   version "8.1.1"
   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
   integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
@@ -13764,6 +14253,11 @@ throat@^6.0.1:
   resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.2.tgz#51a3fbb5e11ae72e2cf74861ed5c8020f89f29fe"
   integrity sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==
 
+throttleit@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.1.tgz#304ec51631c3b770c65c6c6f76938b384000f4d5"
+  integrity sha512-vDZpf9Chs9mAdfY046mcPt8fg5QSZr37hEH4TXYBnDF+izxgrbRGUAAaBvIk/fJm9aOFCGFd1EsNg5AZCbnQCQ==
+
 through2@^2.0.0:
   version "2.0.5"
   resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd"
@@ -13779,7 +14273,7 @@ through2@^4.0.0:
   dependencies:
     readable-stream "3"
 
-through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6:
+through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6, through@^2.3.8:
   version "2.3.8"
   resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
   integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==
@@ -13835,7 +14329,7 @@ toidentifier@1.0.1:
   resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
   integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
 
-tough-cookie@^4.0.0:
+tough-cookie@^4.0.0, tough-cookie@^4.1.3:
   version "4.1.3"
   resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.1.3.tgz#97b9adb0728b42280aa3d814b6b999b2ff0318bf"
   integrity sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==
@@ -13959,6 +14453,18 @@ tsutils@^3.21.0:
   dependencies:
     tslib "^1.8.1"
 
+tunnel-agent@^0.6.0:
+  version "0.6.0"
+  resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
+  integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==
+  dependencies:
+    safe-buffer "^5.0.1"
+
+tweetnacl@^0.14.3, tweetnacl@~0.14.0:
+  version "0.14.5"
+  resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
+  integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==
+
 type-check@^0.4.0, type-check@~0.4.0:
   version "0.4.0"
   resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
@@ -14235,6 +14741,11 @@ unquote@~1.1.1:
   resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544"
   integrity sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==
 
+untildify@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b"
+  integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==
+
 upath@^1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894"
@@ -14367,6 +14878,15 @@ vary@~1.1.2:
   resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
   integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
 
+verror@1.10.0:
+  version "1.10.0"
+  resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
+  integrity sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==
+  dependencies:
+    assert-plus "^1.0.0"
+    core-util-is "1.0.2"
+    extsprintf "^1.2.0"
+
 vfile-message@^4.0.0:
   version "4.0.2"
   resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-4.0.2.tgz#c883c9f677c72c166362fd635f21fc165a7d1181"
@@ -14886,7 +15406,7 @@ workbox-window@6.6.1:
     "@types/trusted-types" "^2.0.2"
     workbox-core "6.6.1"
 
-wrap-ansi@^6.0.1:
+wrap-ansi@^6.0.1, wrap-ansi@^6.2.0:
   version "6.2.0"
   resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53"
   integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==
@@ -15069,6 +15589,14 @@ yargs@^17.6.2:
     y18n "^5.0.5"
     yargs-parser "^21.1.1"
 
+yauzl@^2.10.0:
+  version "2.10.0"
+  resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
+  integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==
+  dependencies:
+    buffer-crc32 "~0.2.3"
+    fd-slicer "~1.1.0"
+
 yn@3.1.1:
   version "3.1.1"
   resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50"
diff --git a/version.txt b/version.txt
index 4deabfe10..f81166c30 100644
--- a/version.txt
+++ b/version.txt
@@ -1,1 +1,1 @@
-0.86.4
+0.86.5


Test patch:
diff --git a/api/golang/core/lib/shared_utils/parsed_git_url_test.go b/api/golang/core/lib/shared_utils/parsed_git_url_test.go
index aa52f9e08..c4f024a69 100644
--- a/api/golang/core/lib/shared_utils/parsed_git_url_test.go
+++ b/api/golang/core/lib/shared_utils/parsed_git_url_test.go
@@ -70,8 +70,8 @@ func TestParsedGitURL_ParsingGetsRidOfAnyPathEscapes(t *testing.T) {
 	escapedURLWithStartosisFile := "github.com/../../etc/passwd/startosis.star"
 	parsedURL, err = ParseGitURL(escapedURLWithStartosisFile)
 	require.Nil(t, err)
-	require.Equal(t, parsedURL.moduleAuthor, "etc")
-	require.Equal(t, parsedURL.moduleName, "passwd")
+	require.Equal(t, parsedURL.repositoryAuthor, "etc")
+	require.Equal(t, parsedURL.repositoryName, "passwd")
 	require.Equal(t, parsedURL.gitURL, "https://github.com/etc/passwd.git")
 	require.Equal(t, parsedURL.relativeFilePath, "etc/passwd/startosis.star")
 	require.Equal(t, parsedURL.relativeRepoPath, "etc/passwd")
@@ -79,8 +79,8 @@ func TestParsedGitURL_ParsingGetsRidOfAnyPathEscapes(t *testing.T) {
 	escapedURLWithStartosisFile = "github.com/foo/../etc/passwd/startosis.star"
 	parsedURL, err = ParseGitURL(escapedURLWithStartosisFile)
 	require.Nil(t, err)
-	require.Equal(t, parsedURL.moduleAuthor, "etc")
-	require.Equal(t, parsedURL.moduleName, "passwd")
+	require.Equal(t, parsedURL.repositoryAuthor, "etc")
+	require.Equal(t, parsedURL.repositoryName, "passwd")
 	require.Equal(t, parsedURL.gitURL, "https://github.com/etc/passwd.git")
 	require.Equal(t, parsedURL.relativeFilePath, "etc/passwd/startosis.star")
 	require.Equal(t, parsedURL.relativeRepoPath, "etc/passwd")
diff --git a/core/server/api_container/server/docker_compose_transpiler/docker_compose_transpiler_test.go b/core/server/api_container/server/docker_compose_transpiler/docker_compose_transpiler_test.go
index fa6e5142a..e9e123032 100644
--- a/core/server/api_container/server/docker_compose_transpiler/docker_compose_transpiler_test.go
+++ b/core/server/api_container/server/docker_compose_transpiler/docker_compose_transpiler_test.go
@@ -72,10 +72,10 @@ services:
     ports: 
       - '80:80'
     volumes:
-     - ~/data:/data
+     - ./data:/data
 `)
 	expectedResult := fmt.Sprintf(`def run(plan):
-    plan.upload_files(src = "~/data", name = "web--volume0")
+    plan.upload_files(src = "./data", name = "web--volume0")
     plan.add_service(name = "web", config = ServiceConfig(image=ImageBuildSpec(image_name="web%s", build_context_dir="app", target_stage="builder"), ports={"port0": PortSpec(number=80, transport_protocol="TCP")}, files={"/data": "web--volume0"}, env_vars={}))
 `, builtImageSuffix)
 
@@ -139,7 +139,7 @@ services:
     environment:
      NODE_ENV: "development"
     volumes:
-     - ~/data:/data
+     - ./data:/data
      - /project/node_modules:/node_modules
     entrypoint:
      - /bin/echo
@@ -148,7 +148,7 @@ services:
     command: ["echo", "Hello,", "World!"]
 `)
 	expectedResult := fmt.Sprintf(`def run(plan):
-    plan.upload_files(src = "~/data", name = "web--volume0")
+    plan.upload_files(src = "./data", name = "web--volume0")
     plan.add_service(name = "web", config = ServiceConfig(image=ImageBuildSpec(image_name="web%s", build_context_dir="app", target_stage="builder"), ports={"port0": PortSpec(number=80, transport_protocol="TCP")}, files={"/data": "web--volume0", "/node_modules": Directory(persistent_key="volume1")}, entrypoint=["/bin/echo", "-c", "echo \"Hello\""], cmd=["echo", "Hello,", "World!"], env_vars={"NODE_ENV": "development"}))
 `, builtImageSuffix)
 
@@ -368,14 +368,10 @@ services:
    volumes:
      - "~/minecraft_data:/data"
 `)
-	expectedResult := `def run(plan):
-    plan.upload_files(src = "~/minecraft_data", name = "minecraft--volume0")
-    plan.add_service(name = "minecraft", config = ServiceConfig(image="itzg/minecraft-server", ports={"port0": PortSpec(number=25565, transport_protocol="TCP")}, files={"/data": "minecraft--volume0"}, env_vars={"EULA": "TRUE"}, min_cpu=0, min_memory=0))
-`
 
-	result, err := convertComposeToStarlarkScript(composeBytes, map[string]string{})
-	require.NoError(t, err)
-	require.Equal(t, expectedResult, result)
+	// Returns error because '~' indicates the user is trying to reference their home path which is outside the package
+	_, err := convertComposeToStarlarkScript(composeBytes, map[string]string{})
+	require.Error(t, err)
 }
 
 // https://github.com/docker/awesome-compose/tree/master/angular
@@ -509,13 +505,13 @@ services:
     ports:
       - '8000:8000'
     volumes:
-      - .:/code
+      - ./code:/code
     depends_on:
       - redis
 `)
 	expectedResult := fmt.Sprintf(`def run(plan):
     plan.add_service(name = "redis", config = ServiceConfig(image="redislabs/redismod", ports={"port0": PortSpec(number=6379, transport_protocol="TCP")}, env_vars={}))
-    plan.upload_files(src = ".", name = "web--volume0")
+    plan.upload_files(src = "./code", name = "web--volume0")
     plan.add_service(name = "web", config = ServiceConfig(image=ImageBuildSpec(image_name="web%v", build_context_dir=".", target_stage="builder"), ports={"port0": PortSpec(number=8000, transport_protocol="TCP")}, files={"/code": "web--volume0"}, env_vars={}))
 `, builtImageSuffix)
 
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/image_build_spec_framework_test.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/image_build_spec_framework_test.go
index a47dafed1..f2f5ab548 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/image_build_spec_framework_test.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/image_build_spec_framework_test.go
@@ -22,7 +22,7 @@ func (suite *KurtosisTypeConstructorTestSuite) TestImageBuildSpecTest() {
 		Return(testBuildContextLocator, nil)
 
 	suite.packageContentProvider.EXPECT().
-		GetOnDiskAbsoluteFilePath(testContainerImageLocator).
+		GetOnDiskAbsolutePackageFilePath(testContainerImageLocator).
 		Times(1).
 		Return(testOnDiskContainerImagePath, nil)
 
diff --git a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_build_spec_test.go b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_build_spec_test.go
index 3a7932369..ade8edebe 100644
--- a/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_build_spec_test.go
+++ b/core/server/api_container/server/startosis_engine/kurtosis_starlark_framework/test_engine/service_config_image_build_spec_test.go
@@ -26,7 +26,7 @@ func (suite *KurtosisTypeConstructorTestSuite) TestServiceConfigWithImageBuildSp
 		Return(testBuildContextLocator, nil)
 
 	suite.packageContentProvider.EXPECT().
-		GetOnDiskAbsoluteFilePath(testContainerImageLocator).
+		GetOnDiskAbsolutePackageFilePath(testContainerImageLocator).
 		Times(1).
 		Return(testOnDiskContainerImagePath, nil)
 
diff --git a/core/server/api_container/server/startosis_engine/startosis_packages/git_package_content_provider/git_package_content_provider_test.go b/core/server/api_container/server/startosis_engine/startosis_packages/git_package_content_provider/git_package_content_provider_test.go
index 047c0e2e1..33d221124 100644
--- a/core/server/api_container/server/startosis_engine/startosis_packages/git_package_content_provider/git_package_content_provider_test.go
+++ b/core/server/api_container/server/startosis_engine/startosis_packages/git_package_content_provider/git_package_content_provider_test.go
@@ -17,7 +17,8 @@ import (
 
 const (
 	packagesDirRelPath                = "startosis-packages"
-	packagesTmpDirRelPath             = "tmp-startosis-packages"
+	repositoriesTmpDirRelPath         = "tmp-repositories"
+	genericRepositoriesDirRelPath     = "generic-repositories"
 	packageDescriptionForTest         = "package description test"
 	localAbsoluteLocatorNotAllowedMsg = "is referencing a file within the same package using absolute import syntax"
 )
@@ -28,7 +29,7 @@ func TestGitPackageProvider_SucceedsForValidPackage(t *testing.T) {
 	packageDir, err := os.MkdirTemp("", packagesDirRelPath)
 	require.Nil(t, err)
 	defer os.RemoveAll(packageDir)
-	packageTmpDir, err := os.MkdirTemp("", packagesTmpDirRelPath)
+	packageTmpDir, err := os.MkdirTemp("", repositoriesTmpDirRelPath)
 	require.Nil(t, err)
 	defer os.RemoveAll(packageTmpDir)
 
@@ -44,7 +45,7 @@ func TestGitPackageProvider_SucceedsForValidPackageWithExplicitMasterSet(t *test
 	packageDir, err := os.MkdirTemp("", packagesDirRelPath)
 	require.Nil(t, err)
 	defer os.RemoveAll(packageDir)
-	packageTmpDir, err := os.MkdirTemp("", packagesTmpDirRelPath)
+	packageTmpDir, err := os.MkdirTemp("", repositoriesTmpDirRelPath)
 	require.Nil(t, err)
 	defer os.RemoveAll(packageTmpDir)
 
@@ -60,7 +61,7 @@ func TestGitPackageProvider_SucceedsForValidPackageWithBranch(t *testing.T) {
 	packageDir, err := os.MkdirTemp("", packagesDirRelPath)
 	require.Nil(t, err)
 	defer os.RemoveAll(packageDir)
-	packageTmpDir, err := os.MkdirTemp("", packagesTmpDirRelPath)
+	packageTmpDir, err := os.MkdirTemp("", repositoriesTmpDirRelPath)
 	require.Nil(t, err)
 	defer os.RemoveAll(packageTmpDir)
 
@@ -76,7 +77,7 @@ func TestGitPackageProvider_FailsForInvalidBranch(t *testing.T) {
 	packageDir, err := os.MkdirTemp("", packagesDirRelPath)
 	require.Nil(t, err)
 	defer os.RemoveAll(packageDir)
-	packageTmpDir, err := os.MkdirTemp("", packagesTmpDirRelPath)
+	packageTmpDir, err := os.MkdirTemp("", repositoriesTmpDirRelPath)
 	require.Nil(t, err)
 	defer os.RemoveAll(packageTmpDir)
 
@@ -91,7 +92,7 @@ func TestGitPackageProvider_SucceedsForValidPackageWithTag(t *testing.T) {
 	packageDir, err := os.MkdirTemp("", packagesDirRelPath)
 	require.Nil(t, err)
 	defer os.RemoveAll(packageDir)
-	packageTmpDir, err := os.MkdirTemp("", packagesTmpDirRelPath)
+	packageTmpDir, err := os.MkdirTemp("", repositoriesTmpDirRelPath)
 	require.Nil(t, err)
 	defer os.RemoveAll(packageTmpDir)
 
@@ -107,7 +108,7 @@ func TestGitPackageProvider_SucceedsForValidPackageWithCommit(t *testing.T) {
 	packageDir, err := os.MkdirTemp("", packagesDirRelPath)
 	require.Nil(t, err)
 	defer os.RemoveAll(packageDir)
-	packageTmpDir, err := os.MkdirTemp("", packagesTmpDirRelPath)
+	packageTmpDir, err := os.MkdirTemp("", repositoriesTmpDirRelPath)
 	require.Nil(t, err)
 	defer os.RemoveAll(packageTmpDir)
 
@@ -123,7 +124,7 @@ func TestGitPackageProvider_SucceedsForValidPackageWithCommitOnABranch(t *testin
 	packageDir, err := os.MkdirTemp("", packagesDirRelPath)
 	require.Nil(t, err)
 	defer os.RemoveAll(packageDir)
-	packageTmpDir, err := os.MkdirTemp("", packagesTmpDirRelPath)
+	packageTmpDir, err := os.MkdirTemp("", repositoriesTmpDirRelPath)
 	require.Nil(t, err)
 	defer os.RemoveAll(packageTmpDir)
 
@@ -139,7 +140,7 @@ func TestGitPackageProvider_SucceedsForNonStarlarkFile(t *testing.T) {
 	packageDir, err := os.MkdirTemp("", packagesDirRelPath)
 	require.Nil(t, err)
 	defer os.RemoveAll(packageDir)
-	packageTmpDir, err := os.MkdirTemp("", packagesTmpDirRelPath)
+	packageTmpDir, err := os.MkdirTemp("", repositoriesTmpDirRelPath)
 	require.Nil(t, err)
 	defer os.RemoveAll(packageTmpDir)
 
@@ -156,7 +157,7 @@ func TestGitPackageProvider_FailsForNonExistentPackage(t *testing.T) {
 	oackageDir, err := os.MkdirTemp("", packagesDirRelPath)
 	require.Nil(t, err)
 	defer os.RemoveAll(oackageDir)
-	packageTmpDir, err := os.MkdirTemp("", packagesTmpDirRelPath)
+	packageTmpDir, err := os.MkdirTemp("", repositoriesTmpDirRelPath)
 	require.Nil(t, err)
 	defer os.RemoveAll(packageTmpDir)
 
@@ -171,14 +172,14 @@ func TestGetAbsolutePathOnDisk_WorksForPureDirectories(t *testing.T) {
 	packageDir, err := os.MkdirTemp("", packagesDirRelPath)
 	require.Nil(t, err)
 	defer os.RemoveAll(packageDir)
-	packageTmpDir, err := os.MkdirTemp("", packagesTmpDirRelPath)
+	packageTmpDir, err := os.MkdirTemp("", repositoriesTmpDirRelPath)
 	require.Nil(t, err)
 	defer os.RemoveAll(packageTmpDir)
 
 	provider := NewGitPackageContentProvider(packageDir, packageTmpDir, nil)
 
 	packagePath := "github.com/kurtosis-tech/datastore-army-package/src/helpers.star"
-	pathOnDisk, err := provider.GetOnDiskAbsoluteFilePath(packagePath)
+	pathOnDisk, err := provider.getOnDiskAbsolutePath(packagePath, true)
 
 	require.Nil(t, err, "This test depends on your internet working and the kurtosis-tech/datastore-army-package existing")
 	require.Equal(t, path.Join(packageDir, "kurtosis-tech", "datastore-army-package", "src/helpers.star"), pathOnDisk)
@@ -188,19 +189,55 @@ func TestGetAbsolutePathOnDisk_WorksForNonInMainBranchLocators(t *testing.T) {
 	packageDir, err := os.MkdirTemp("", packagesDirRelPath)
 	require.Nil(t, err)
 	defer os.RemoveAll(packageDir)
-	packageTmpDir, err := os.MkdirTemp("", packagesTmpDirRelPath)
+	packageTmpDir, err := os.MkdirTemp("", repositoriesTmpDirRelPath)
 	require.Nil(t, err)
 	defer os.RemoveAll(packageTmpDir)
 
 	provider := NewGitPackageContentProvider(packageDir, packageTmpDir, nil)
 
 	absoluteFileLocator := "github.com/kurtosis-tech/sample-dependency-package@test-branch/main.star"
-	pathOnDisk, err := provider.GetOnDiskAbsoluteFilePath(absoluteFileLocator)
+	pathOnDisk, err := provider.getOnDiskAbsolutePath(absoluteFileLocator, true)
 
 	require.Nil(t, err, "This test depends on your internet working and the kurtosis-tech/datastore-army-package existing")
 	require.Equal(t, path.Join(packageDir, "kurtosis-tech", "sample-dependency-package", "main.star"), pathOnDisk)
 }
 
+func TestGetAbsolutePathOnDisk_GenericRepositoryDir(t *testing.T) {
+	repositoriesDir, err := os.MkdirTemp("", packagesDirRelPath)
+	require.Nil(t, err)
+	defer os.RemoveAll(repositoriesDir)
+
+	repositoriesTmpDir, err := os.MkdirTemp("", repositoriesTmpDirRelPath)
+	require.Nil(t, err)
+	defer os.RemoveAll(repositoriesTmpDir)
+
+	provider := NewGitPackageContentProvider(repositoriesDir, repositoriesTmpDir, nil)
+
+	repositoryPathURL := "github.com/kurtosis-tech/minimal-grpc-server/golang/scripts"
+	pathOnDisk, err := provider.GetOnDiskAbsolutePath(repositoryPathURL)
+
+	require.Nil(t, err, "This test depends on your internet working and the kurtosis-tech/minimal-grpc-server existing")
+	require.Equal(t, path.Join(repositoriesDir, "kurtosis-tech", "minimal-grpc-server", "golang", "scripts"), pathOnDisk)
+}
+
+func TestGetAbsolutePathOnDisk_GenericRepositoryFile(t *testing.T) {
+	repositoriesDir, err := os.MkdirTemp("", packagesDirRelPath)
+	require.Nil(t, err)
+	defer os.RemoveAll(repositoriesDir)
+
+	repositoriesTmpDir, err := os.MkdirTemp("", repositoriesTmpDirRelPath)
+	require.Nil(t, err)
+	defer os.RemoveAll(repositoriesTmpDir)
+
+	provider := NewGitPackageContentProvider(repositoriesDir, repositoriesTmpDir, nil)
+
+	repositoryPathURL := "github.com/kurtosis-tech/minimal-grpc-server/golang/scripts/build.sh"
+	pathOnDisk, err := provider.GetOnDiskAbsolutePath(repositoryPathURL)
+
+	require.Nil(t, err, "This test depends on your internet working and the kurtosis-tech/minimal-grpc-server existing")
+	require.Equal(t, path.Join(repositoriesDir, "kurtosis-tech", "minimal-grpc-server", "golang", "scripts", "build.sh"), pathOnDisk)
+}
+
 func TestGetAbsoluteLocator_SucceedsForRelativeFile(t *testing.T) {
 	provider := NewGitPackageContentProvider("", "", nil)
 
@@ -557,7 +594,7 @@ func TestCloneReplacedPackagesIfNeeded_Succeeds(t *testing.T) {
 	packageDir, err := os.MkdirTemp("", packagesDirRelPath)
 	require.Nil(t, err)
 	defer os.RemoveAll(packageDir)
-	packageTmpDir, err := os.MkdirTemp("", packagesTmpDirRelPath)
+	packageTmpDir, err := os.MkdirTemp("", repositoriesTmpDirRelPath)
 	require.Nil(t, err)
 	defer os.RemoveAll(packageTmpDir)
 
diff --git a/enclave-manager/web/cypress/e2e/catalog.cy.ts b/enclave-manager/web/cypress/e2e/catalog.cy.ts
new file mode 100644
index 000000000..2c8376dc2
--- /dev/null
+++ b/enclave-manager/web/cypress/e2e/catalog.cy.ts
@@ -0,0 +1,36 @@
+describe("Catalog", () => {
+  it("Lists the catalog", () => {
+    cy.goToCatalog();
+    cy.contains("Package Catalog");
+    cy.contains("Most Ran");
+    cy.contains("Most starred");
+    cy.contains("All");
+    cy.contains("Postgres");
+  });
+
+  it("Searches with ctrl+f", () => {
+    cy.goToCatalog();
+    cy.contains("Package Catalog");
+    cy.get("body").type("{ctrl}f");
+    cy.focused().type("postgres");
+    cy.contains("1 Matches");
+  });
+
+  it("Opens packages details", () => {
+    cy.goToCatalog();
+    cy.contains("Postgres").click();
+    cy.url().should("match", /catalog\/.*postgres/);
+    cy.contains("button", "Run").click();
+    cy.contains("[role='dialog']", "Postgres Package").contains("Run");
+  });
+
+  it("Can save packages", () => {
+    cy.goToCatalog();
+    // Trigger click rather than use `click` so that it is exactly the button that is clicked
+    cy.contains("a", "Postgres").find("button[aria-label*='save']").trigger("click");
+
+    // Now, remove it from favourites
+    cy.contains(".chakra-card", "Saved").contains("a", "Postgres").find("button[aria-label*='save']").trigger("click");
+    cy.contains(".chakra-card", "Saved").should("not.exist");
+  });
+});
diff --git a/enclave-manager/web/cypress/e2e/enclave/service/logging.cy.ts b/enclave-manager/web/cypress/e2e/enclave/service/logging.cy.ts
new file mode 100644
index 000000000..ef019cccb
--- /dev/null
+++ b/enclave-manager/web/cypress/e2e/enclave/service/logging.cy.ts
@@ -0,0 +1,17 @@
+describe("Service Logging", () => {
+  const enclaveName = `cyrpress-test-${Math.floor(Math.random() * 100000)}`;
+  before(() => {
+    cy.createAndGoToEnclave(enclaveName);
+  });
+  after(() => {
+    cy.deleteEnclave(enclaveName);
+  });
+
+  it("Should buffer logs", () => {
+    cy.contains("button", "postgres").click();
+    cy.contains("Logs", { matchCase: false }).click();
+
+    // TODO: Implement this test once the WSS logging is used - cypress blocks the streamed
+    // response until the request is closed.
+  });
+});
diff --git a/enclave-manager/web/cypress/e2e/enclaveList.cy.ts b/enclave-manager/web/cypress/e2e/enclaveList.cy.ts
new file mode 100644
index 000000000..99a7a993b
--- /dev/null
+++ b/enclave-manager/web/cypress/e2e/enclaveList.cy.ts
@@ -0,0 +1,41 @@
+describe("Enclave List", () => {
+  let enclaveName: string = "unknown";
+
+  beforeEach(() => {
+    enclaveName = `cyrpress-test-${Math.floor(Math.random() * 100000)}`;
+  });
+
+  afterEach(() => {
+    cy.deleteEnclave(enclaveName);
+  });
+
+  it("Can create and update an enclave", () => {
+    cy.createAndGoToEnclave(enclaveName);
+    cy.findCardWithName("Name").contains(enclaveName);
+    cy.contains("table", "postgres");
+    cy.get("table").contains("button", "postgres").click();
+
+    cy.url().should("match", /enclave\/[^/]+\/service\/[^/]+/);
+    cy.findCardWithName("Name").contains("postgres");
+    cy.findCardWithName("Status").contains("Running", { matchCase: false });
+
+    // Update the postgres instance
+    cy.contains("button", "Edit").click();
+    cy.focusInputWithLabel("Max CPU").type("1024");
+    cy.contains("button", "Update").click();
+    cy.contains("Script completed", { timeout: 10 * 1000 });
+  });
+
+  it.only("Shows a new enclave in the list", () => {
+    cy.goToEnclaveList();
+    cy.contains("tr", enclaveName).should("not.exist");
+
+    cy.createAndGoToEnclave(enclaveName);
+
+    cy.goToEnclaveList();
+
+    cy.contains("tr", enclaveName).should("exist");
+    cy.contains("tr", enclaveName).contains("Running");
+    cy.contains("tr", enclaveName).contains("Postgres Package");
+  });
+});
diff --git a/enclave-manager/web/cypress/support/e2e.ts b/enclave-manager/web/cypress/support/e2e.ts
new file mode 100644
index 000000000..6a173d6fc
--- /dev/null
+++ b/enclave-manager/web/cypress/support/e2e.ts
@@ -0,0 +1,20 @@
+// ***********************************************************
+// This example support/e2e.ts is processed and
+// loaded automatically before your test files.
+//
+// This is a great place to put global configuration and
+// behavior that modifies Cypress.
+//
+// You can change the location of this file or turn off
+// automatically serving support files with the
+// 'supportFile' configuration option.
+//
+// You can read more here:
+// https://on.cypress.io/configuration
+// ***********************************************************
+
+// Import commands.js using ES2015 syntax:
+import "./commands";
+
+// Alternatively you can use CommonJS syntax:
+// require('./commands')
diff --git a/internal_testsuites/golang/testsuite/startosis_package_test/startosis_compose_package_test.go b/internal_testsuites/golang/testsuite/startosis_package_test/startosis_compose_package_test.go
new file mode 100644
index 000000000..b80883806
--- /dev/null
+++ b/internal_testsuites/golang/testsuite/startosis_package_test/startosis_compose_package_test.go
@@ -0,0 +1,28 @@
+package startosis_package_test
+
+import (
+	"context"
+	"github.com/stretchr/testify/require"
+)
+
+const (
+	simpleDockerComposePackageRelPath = "../../../starlark/docker-compose-package"
+)
+
+func (suite *StartosisPackageTestSuite) TestStartosisPackage_DockerComposePackage() {
+	ctx := context.Background()
+	runResult, err := suite.RunPackage(ctx, simpleDockerComposePackageRelPath)
+
+	t := suite.T()
+	require.Nil(t, err, "Unexpected error executing Starlark package")
+
+	require.NotNil(t, runResult)
+	require.Nil(t, runResult.InterpretationError)
+	require.Empty(t, runResult.ValidationErrors)
+	require.Nil(t, runResult.ExecutionError)
+
+	expectedScriptOutputSubstring := `Service 'pgadmin' added with service UUID `
+
+	require.Contains(t, string(runResult.RunOutput), expectedScriptOutputSubstring)
+	require.Len(t, runResult.Instructions, 3)
+}
diff --git a/internal_testsuites/golang/testsuite/startosis_package_test/startosis_image_build_test.go b/internal_testsuites/golang/testsuite/startosis_package_test/startosis_image_build_test.go
index c26fcedc0..fc6fce045 100644
--- a/internal_testsuites/golang/testsuite/startosis_package_test/startosis_image_build_test.go
+++ b/internal_testsuites/golang/testsuite/startosis_package_test/startosis_image_build_test.go
@@ -12,7 +12,8 @@ import (
 )
 
 const (
-	imageBuildSpecPackageRelPath = "../../../starlark/image-build-package"
+	imageBuildSpecPackageRelPath              = "../../../starlark/image-build-package"
+	imageBuildSpecDockerComposePackageRelPath = "../../../starlark/docker-compose-package-img-build"
 )
 
 func (suite *StartosisPackageTestSuite) TestStartosisPackage_ImageBuildSpec() {
@@ -35,3 +36,21 @@ func (suite *StartosisPackageTestSuite) TestStartosisPackage_ImageBuildSpec() {
 	// TODO: Figure out a way to clean image
 	logrus.Warnf("THIS TEST GENERATES A SMALL DOCKER IMAGE. IF YOU ARE RUNNING TESTSUITE LOCALLY(NOT IN CI), YOU MUST MANUALLY REMOVE IT!")
 }
+
+func (suite *StartosisPackageTestSuite) TestStartosisPackage_DockerComposePackageWithImageBuildSpec() {
+	ctx := context.Background()
+	runResult, err := suite.RunPackage(ctx, imageBuildSpecDockerComposePackageRelPath)
+
+	t := suite.T()
+	require.Nil(t, err, "Unexpected error executing Starlark package")
+
+	require.NotNil(t, runResult)
+	require.Nil(t, runResult.InterpretationError)
+	require.Empty(t, runResult.ValidationErrors)
+	require.Nil(t, runResult.ExecutionError)
+
+	expectedScriptOutputSubstring := `Service 'web' added with service UUID `
+
+	require.Contains(t, string(runResult.RunOutput), expectedScriptOutputSubstring)
+	require.Len(t, runResult.Instructions, 2)
+}
diff --git a/internal_testsuites/golang/testsuite/startosis_package_test/startosis_no_main_file_test.go b/internal_testsuites/golang/testsuite/startosis_package_test/startosis_no_main_file_test.go
index 7474fe13e..5b291048e 100644
--- a/internal_testsuites/golang/testsuite/startosis_package_test/startosis_no_main_file_test.go
+++ b/internal_testsuites/golang/testsuite/startosis_package_test/startosis_no_main_file_test.go
@@ -18,8 +18,8 @@ func (suite *StartosisPackageTestSuite) TestStartosisPackage_NoMainFile() {
 	require.Error(t, err)
 	require.NotNil(t, runResult)
 
-	expectedErrorContents := `An error occurred while verifying that 'main.star' exists in the package 'github.com/kurtosis-tech/kurtosis/internal_testsuites/starlark/no-main-star' at '/kurtosis-data/startosis-packages/kurtosis-tech/kurtosis/internal_testsuites/starlark/no-main-star/main.star'
-	Caused by: stat /kurtosis-data/startosis-packages/kurtosis-tech/kurtosis/internal_testsuites/starlark/no-main-star/main.star: no such file or directory`
+	expectedErrorContents := `An error occurred while verifying that 'main.star' exists in the package 'github.com/kurtosis-tech/kurtosis/internal_testsuites/starlark/no-main-star' at '/kurtosis-data/repositories/kurtosis-tech/kurtosis/internal_testsuites/starlark/no-main-star/main.star'
+	Caused by: stat /kurtosis-data/repositories/kurtosis-tech/kurtosis/internal_testsuites/starlark/no-main-star/main.star: no such file or directory`
 	require.NotNil(t, runResult.InterpretationError)
 	require.Equal(t, expectedErrorContents, runResult.InterpretationError.GetErrorMessage())
 	require.Empty(t, runResult.ValidationErrors)
diff --git a/internal_testsuites/golang/testsuite/startosis_upload_file_test/startosis_upload_file_generic_package_test.go b/internal_testsuites/golang/testsuite/startosis_upload_file_test/startosis_upload_file_generic_package_test.go
new file mode 100644
index 000000000..917bda418
--- /dev/null
+++ b/internal_testsuites/golang/testsuite/startosis_upload_file_test/startosis_upload_file_generic_package_test.go
@@ -0,0 +1,44 @@
+package startosis_upload_file_test
+
+import (
+	"context"
+	"github.com/sirupsen/logrus"
+	"github.com/stretchr/testify/require"
+	"os"
+	"path"
+)
+
+const (
+	packageWithUploadFileGenericRelPath = "../../../starlark/upload-file/upload-file-generic-package"
+)
+
+func (suite *StartosisUploadFileTestSuite) TestStartosisPackage_UploadFileGenericPackage() {
+	ctx := context.Background()
+	t := suite.T()
+	// ------------------------------------- ENGINE SETUP ----------------------------------------------
+
+	// ------------------------------------- TEST RUN ----------------------------------------------
+	currentWorkingDirectory, err := os.Getwd()
+	require.Nil(t, err)
+	packageDirpath := path.Join(currentWorkingDirectory, packageWithUploadFileGenericRelPath)
+
+	// ------------------------------------- TEST RUN ----------------------------------------------
+	logrus.Info("Executing Startosis Package...")
+
+	// Note: the result extracted from the recipe inside Starlark contains a newline char at the end.
+	// We need to add it here manually to have matching hashes
+	isRemotePackage := false
+	runResult, err := suite.RunPackage(ctx, packageDirpath, nil, isRemotePackage)
+	require.NoError(t, err, "Unexpected error executing Starlark package")
+
+	// the package itself runs the assertion here. If the file hash computed withing the enclave with md5sum differs
+	// from the one passed as parameter to the package (computed in Go above), an Execution Error will be returned
+	require.Nil(t, runResult.InterpretationError, "Unexpected interpretation error")
+	require.Empty(t, runResult.ValidationErrors, "Unexpected validation error")
+	require.Nil(t, runResult.ExecutionError, "Unexpected execution error")
+
+	expectedScriptOutputSubstring := `uploaded with artifact UUID`
+	require.Contains(t, string(runResult.RunOutput), expectedScriptOutputSubstring)
+
+	logrus.Info("Successfully ran Startosis module")
+}
diff --git a/internal_testsuites/golang/testsuite/startosis_upload_file_test/startosis_upload_file_test.go b/internal_testsuites/golang/testsuite/startosis_upload_file_test/startosis_upload_file_test.go
index ce0110e68..b75a5d075 100644
--- a/internal_testsuites/golang/testsuite/startosis_upload_file_test/startosis_upload_file_test.go
+++ b/internal_testsuites/golang/testsuite/startosis_upload_file_test/startosis_upload_file_test.go
@@ -1,4 +1,4 @@
-package startosis_upload_files_test
+package startosis_upload_file_test
 
 import (
 	"context"
@@ -11,33 +11,27 @@ import (
 	"github.com/stretchr/testify/require"
 	"os"
 	"path"
-	"testing"
 )
 
 const (
-	validPackageWithInputTestName = "upload-file-package"
-	validPackageWithInputRelPath  = "../../../starlark/upload-file-package"
+	packageWithUploadFileRelPath = "../../../starlark/upload-file/upload-file-package"
 
 	fileName = "large-file.bin"
 	fileSize = 25 * 1024 * 1024 // 25MB
 )
 
-func TestStartosisPackage_UploadFileAndCheckFileHash(t *testing.T) {
-	t.Parallel()
+func (suite *StartosisUploadFileTestSuite) TestStartosisPackage_UploadFileAndCheckFileHash() {
 	ctx := context.Background()
-
+	t := suite.T()
 	// ------------------------------------- ENGINE SETUP ----------------------------------------------
-	enclaveCtx, _, destroyEnclaveFunc, err := test_helpers.CreateEnclave(t, ctx, validPackageWithInputTestName)
-	require.NoError(t, err, "An error occurred creating an enclave")
-	defer func() { _ = destroyEnclaveFunc() }()
 
+	// ------------------------------------- TEST RUN ----------------------------------------------
 	currentWorkingDirectory, err := os.Getwd()
 	require.Nil(t, err)
-	packageDirpath := path.Join(currentWorkingDirectory, validPackageWithInputRelPath)
+	packageDirpath := path.Join(currentWorkingDirectory, packageWithUploadFileRelPath)
 
-	// ------------------------------------- TEST RUN ----------------------------------------------
 	// we generate a file and place it into the package - as it is a big file, we don't want to check it in GitHub
-	fileRelPath := path.Join(validPackageWithInputRelPath, fileName)
+	fileRelPath := path.Join(packageWithUploadFileRelPath, fileName)
 	randomFilePath, deleteFile, err := test_helpers.GenerateRandomTempFile(fileSize, fileRelPath)
 	require.NoError(t, err)
 	defer deleteFile()
@@ -57,7 +51,9 @@ func TestStartosisPackage_UploadFileAndCheckFileHash(t *testing.T) {
 	// Note: the result extracted from the recipe inside Starlark contains a newline char at the end.
 	// We need to add it here manually to have matching hashes
 	params := fmt.Sprintf(`{"file_hash": "%s\n"}`, randomFileHexHash)
-	runResult, err := enclaveCtx.RunStarlarkPackageBlocking(ctx, packageDirpath, starlark_run_config.NewRunStarlarkConfig(starlark_run_config.WithSerializedParams(params)))
+	runConfig := starlark_run_config.NewRunStarlarkConfig(starlark_run_config.WithSerializedParams(params))
+	isRemotePackage := false
+	runResult, err := suite.RunPackage(ctx, packageDirpath, runConfig, isRemotePackage)
 	require.NoError(t, err, "Unexpected error executing Starlark package")
 
 	// the package itself runs the assertion here. If the file hash computed withing the enclave with md5sum differs
diff --git a/internal_testsuites/golang/testsuite/startosis_upload_file_test/startosis_upload_file_testsuite_test.go b/internal_testsuites/golang/testsuite/startosis_upload_file_test/startosis_upload_file_testsuite_test.go
new file mode 100644
index 000000000..8b67ac552
--- /dev/null
+++ b/internal_testsuites/golang/testsuite/startosis_upload_file_test/startosis_upload_file_testsuite_test.go
@@ -0,0 +1,69 @@
+package startosis_upload_file_test
+
+import (
+	"context"
+	"github.com/kurtosis-tech/kurtosis-cli/golang_internal_testsuite/test_helpers"
+	"github.com/kurtosis-tech/kurtosis/api/golang/core/lib/enclaves"
+	"github.com/kurtosis-tech/kurtosis/api/golang/core/lib/starlark_run_config"
+	"github.com/sirupsen/logrus"
+	"github.com/stretchr/testify/require"
+	"github.com/stretchr/testify/suite"
+	"testing"
+)
+
+const (
+	name = "startosis-upload-file"
+)
+
+type StartosisUploadFileTestSuite struct {
+	suite.Suite
+	enclaveCtx         *enclaves.EnclaveContext
+	destroyEnclaveFunc func() error
+}
+
+func TestStartosisUploadFileTestSuite(t *testing.T) {
+	suite.Run(t, new(StartosisUploadFileTestSuite))
+}
+
+func (suite *StartosisUploadFileTestSuite) SetupTest() {
+	ctx := context.Background()
+	t := suite.T()
+	enclaveCtx, _, destroyEnclaveFunc, err := test_helpers.CreateEnclave(t, ctx, name)
+	require.NoError(t, err, "An error occurred creating an enclave")
+	suite.enclaveCtx = enclaveCtx
+	suite.destroyEnclaveFunc = destroyEnclaveFunc
+}
+
+func (suite *StartosisUploadFileTestSuite) TearDownTest() {
+	err := suite.destroyEnclaveFunc()
+	require.NoError(suite.T(), err, "Destroying the test suite's enclave process has failed, you will have to remove it manually")
+}
+
+func (suite *StartosisUploadFileTestSuite) RunPackage(
+	ctx context.Context,
+	packageLocation string,
+	runConfig *starlark_run_config.StarlarkRunConfig,
+	remote bool,
+) (*enclaves.StarlarkRunResult, error) {
+	logrus.Infof("Executing Startosis package...")
+
+	logrus.Debugf("Startosis package location: %v", packageLocation)
+
+	if runConfig == nil {
+		runConfig = starlark_run_config.NewRunStarlarkConfig()
+	}
+
+	if remote {
+		return suite.enclaveCtx.RunStarlarkRemotePackageBlocking(
+			ctx,
+			packageLocation,
+			runConfig,
+		)
+	}
+
+	return suite.enclaveCtx.RunStarlarkPackageBlocking(
+		ctx,
+		packageLocation,
+		runConfig,
+	)
+}
diff --git a/internal_testsuites/resources/docker_import_cli_test/.env b/internal_testsuites/resources/docker_import_cli_test/.env
index 961e9abe0..ecb2fa892 100644
--- a/internal_testsuites/resources/docker_import_cli_test/.env
+++ b/internal_testsuites/resources/docker_import_cli_test/.env
@@ -2,4 +2,4 @@ POSTGRES_USER=yourUser
 POSTGRES_PW=changeit
 POSTGRES_DB=postgres
 PGADMIN_MAIL=your@email.com
-PGADMIN_PW=changeit
\ No newline at end of file
+PGADMIN_PW=changeit
diff --git a/internal_testsuites/resources/docker_import_cli_test/volume/file.txt b/internal_testsuites/resources/docker_import_cli_test/volume/file.txt
index 40816a2b5..b14df6442 100644
--- a/internal_testsuites/resources/docker_import_cli_test/volume/file.txt
+++ b/internal_testsuites/resources/docker_import_cli_test/volume/file.txt
@@ -1,1 +1,1 @@
-Hi
\ No newline at end of file
+Hi
diff --git a/internal_testsuites/starlark/docker-compose-package-img-build/app/Dockerfile b/internal_testsuites/starlark/docker-compose-package-img-build/app/Dockerfile
new file mode 100644
index 000000000..eac3dfd21
--- /dev/null
+++ b/internal_testsuites/starlark/docker-compose-package-img-build/app/Dockerfile
@@ -0,0 +1,22 @@
+# syntax=docker/dockerfile:1.4
+
+FROM --platform=$BUILDPLATFORM php:8.0.9-apache as builder
+
+CMD ["apache2-foreground"]
+
+FROM builder as dev-envs
+
+RUN <<EOF
+apt-get update
+apt-get install -y --no-install-recommends git
+EOF
+
+RUN <<EOF
+useradd -s /bin/bash -m vscode
+groupadd docker
+usermod -aG docker vscode
+EOF
+# install Docker tools (cli, buildx, compose)
+COPY --from=gloursdocker/docker / /
+
+CMD ["apache2-foreground"]
\ No newline at end of file
diff --git a/internal_testsuites/starlark/docker-compose-package-img-build/app/index.php b/internal_testsuites/starlark/docker-compose-package-img-build/app/index.php
new file mode 100644
index 000000000..04c6aca9b
--- /dev/null
+++ b/internal_testsuites/starlark/docker-compose-package-img-build/app/index.php
@@ -0,0 +1,3 @@
+<?php
+    echo '<h1>Hello World!</h1>';
+?>
\ No newline at end of file
diff --git a/internal_testsuites/starlark/docker-compose-package-img-build/compose.yaml b/internal_testsuites/starlark/docker-compose-package-img-build/compose.yaml
new file mode 100644
index 000000000..b9e16ef28
--- /dev/null
+++ b/internal_testsuites/starlark/docker-compose-package-img-build/compose.yaml
@@ -0,0 +1,9 @@
+services:
+  web:
+    build:
+      context: app
+      target: builder
+    ports:
+      - '80:80'
+    volumes:
+      - ./app:/var/www/html/
\ No newline at end of file
diff --git a/internal_testsuites/starlark/docker-compose-package/.env b/internal_testsuites/starlark/docker-compose-package/.env
new file mode 100644
index 000000000..961e9abe0
--- /dev/null
+++ b/internal_testsuites/starlark/docker-compose-package/.env
@@ -0,0 +1,5 @@
+POSTGRES_USER=yourUser
+POSTGRES_PW=changeit
+POSTGRES_DB=postgres
+PGADMIN_MAIL=your@email.com
+PGADMIN_PW=changeit
\ No newline at end of file
diff --git a/internal_testsuites/starlark/docker-compose-package/docker-compose.yml b/internal_testsuites/starlark/docker-compose-package/docker-compose.yml
new file mode 100644
index 000000000..718d2f7de
--- /dev/null
+++ b/internal_testsuites/starlark/docker-compose-package/docker-compose.yml
@@ -0,0 +1,19 @@
+services:
+  postgres:
+    image: postgres:latest
+    environment:
+      - POSTGRES_USER=${POSTGRES_USER}
+      - POSTGRES_PASSWORD=${POSTGRES_PW}
+      - POSTGRES_DB=${POSTGRES_DB} #optional (specify default database instead of $POSTGRES_DB)
+    ports:
+      - "5432:5432"
+    volumes:
+      - "./volume:/tmp"
+
+  pgadmin:
+    image: dpage/pgadmin4:latest
+    environment:
+      - PGADMIN_DEFAULT_EMAIL=${PGADMIN_MAIL}
+      - PGADMIN_DEFAULT_PASSWORD=${PGADMIN_PW}
+    ports:
+      - "5050:80"
diff --git a/internal_testsuites/starlark/docker-compose-package/volume/file.txt b/internal_testsuites/starlark/docker-compose-package/volume/file.txt
new file mode 100644
index 000000000..40816a2b5
--- /dev/null
+++ b/internal_testsuites/starlark/docker-compose-package/volume/file.txt
@@ -0,0 +1,1 @@
+Hi
\ No newline at end of file
diff --git a/internal_testsuites/starlark/upload-file/upload-file-generic-package/kurtosis.yml b/internal_testsuites/starlark/upload-file/upload-file-generic-package/kurtosis.yml
new file mode 100644
index 000000000..923e558c1
--- /dev/null
+++ b/internal_testsuites/starlark/upload-file/upload-file-generic-package/kurtosis.yml
@@ -0,0 +1,1 @@
+name: "github.com/kurtosis-tech/kurtosis/internal_testsuites/starlark/upload-file/upload-file-generic-package"
diff --git a/internal_testsuites/starlark/upload-file/upload-file-generic-package/main.star b/internal_testsuites/starlark/upload-file/upload-file-generic-package/main.star
new file mode 100644
index 000000000..03550f152
--- /dev/null
+++ b/internal_testsuites/starlark/upload-file/upload-file-generic-package/main.star
@@ -0,0 +1,21 @@
+SERVICE_NAME = "service-example"
+
+def run(plan, args={}):
+    url = "github.com/kurtosis-tech/minimal-grpc-server/golang/scripts/build.sh"
+    artifact_name_1 = plan.upload_files(url)
+
+    service = plan.add_service(
+        SERVICE_NAME,
+        config=ServiceConfig(
+            image="alpine:latest",
+            cmd=["/bin/sh",
+                 "-c","ls /files && sleep 9999"],
+            files={
+                "/files": Directory(
+                    artifact_names=[artifact_name_1]
+                )
+            },
+        ),
+    )
+
+
diff --git a/internal_testsuites/starlark/upload-file-package/.gitignore b/internal_testsuites/starlark/upload-file/upload-file-package/.gitignore
similarity index 100%
rename from internal_testsuites/starlark/upload-file-package/.gitignore
rename to internal_testsuites/starlark/upload-file/upload-file-package/.gitignore
diff --git a/internal_testsuites/starlark/upload-file-package/kurtosis.yml b/internal_testsuites/starlark/upload-file/upload-file-package/kurtosis.yml
similarity index 62%
rename from internal_testsuites/starlark/upload-file-package/kurtosis.yml
rename to internal_testsuites/starlark/upload-file/upload-file-package/kurtosis.yml
index 957dc7ab4..97a23143f 100644
--- a/internal_testsuites/starlark/upload-file-package/kurtosis.yml
+++ b/internal_testsuites/starlark/upload-file/upload-file-package/kurtosis.yml
@@ -1,1 +1,1 @@
-name: "github.com/kurtosis-tech/kurtosis/internal_testsuites/starlark/upload-file-package"
+name: "github.com/kurtosis-tech/kurtosis/internal_testsuites/starlark/upload-file/upload-file-package"
diff --git a/internal_testsuites/starlark/upload-file-package/main.star b/internal_testsuites/starlark/upload-file/upload-file-package/main.star
similarity index 100%
rename from internal_testsuites/starlark/upload-file-package/main.star
rename to internal_testsuites/starlark/upload-file/upload-file-package/main.star
diff --git a/internal_testsuites/typescript/src/testsuite/startosis_package_test/startosis_package.test.ts b/internal_testsuites/typescript/src/testsuite/startosis_package_test/startosis_package.test.ts
index 32b7387fa..df0656db1 100644
--- a/internal_testsuites/typescript/src/testsuite/startosis_package_test/startosis_package.test.ts
+++ b/internal_testsuites/typescript/src/testsuite/startosis_package_test/startosis_package.test.ts
@@ -9,6 +9,9 @@ import {StarlarkRunConfig} from "kurtosis-sdk";
 const VALID_PACKAGE_WITH_PACKAGE_INPUT_TEST_NAME = "valid-package-with-input"
 const VALID_PACKAGE_WITH_PACKAGE_INPUT_REL_PATH = "../../../../starlark/valid-kurtosis-package-with-input"
 
+const DOCKER_COMPOSE_PACKAGE_TEST_NAME = "docker-compose-package"
+const DOCKER_COMPOSE_PACKAGE_REL_PATH = "../../../../starlark/docker-compose-package"
+
 jest.setTimeout(JEST_TIMEOUT_MS)
 
 test("Test valid Starlark package with input", async () => {
@@ -88,3 +91,38 @@ test("Test valid Starlark package with input - missing key in params", async ()
         stopEnclaveFunction()
     }
 })
+
+test("Test valid Docker Compose package", async () => {
+    // ------------------------------------- ENGINE SETUP ----------------------------------------------
+    const createEnclaveResult = await createEnclave(DOCKER_COMPOSE_PACKAGE_TEST_NAME + "-test")
+
+    if (createEnclaveResult.isErr()) {
+        throw createEnclaveResult.error
+    }
+
+    const {enclaveContext, stopEnclaveFunction} = createEnclaveResult.value
+
+    try {
+        // ------------------------------------- TEST SETUP ----------------------------------------------
+        const packageRootPath = path.join(__dirname, DOCKER_COMPOSE_PACKAGE_REL_PATH)
+
+        log.info(`Loading package at path '${packageRootPath}'`)
+
+        const runResult = await enclaveContext.runStarlarkPackageBlocking(
+            packageRootPath,
+            new StarlarkRunConfig(),
+        )
+
+        if (runResult.isErr()) {
+            log.error(`An error occurred execute Starlark package '${packageRootPath}'`);
+            throw runResult.error
+        }
+
+        expect(runResult.value.interpretationError).toBeUndefined()
+        expect(runResult.value.validationErrors).toEqual([])
+        expect(runResult.value.executionError).toBeUndefined()
+        expect(runResult.value.instructions).toHaveLength(3)
+    } finally {
+        stopEnclaveFunction()
+    }
+})
\ No newline at end of file