# @apiclient.xyz/docker A fully typed TypeScript client for the Docker Engine API. Talk to Docker from Node.js or Deno with a clean, object-oriented interface β€” containers, images, networks, services, secrets, and image storage all in one package. 🐳 ## Issue Reporting and Security For reporting bugs, issues, or security vulnerabilities, please visit [community.foss.global/](https://community.foss.global/). This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a [code.foss.global/](https://code.foss.global/) account to submit Pull Requests directly. ## Install ```shell pnpm install @apiclient.xyz/docker # or npm install @apiclient.xyz/docker ``` ## Usage `DockerHost` is the single entry point. Every Docker resource β€” containers, images, networks, services, secrets β€” is managed through it. ```typescript import { DockerHost } from '@apiclient.xyz/docker'; ``` ### πŸ”Œ Setting Up the Docker Host ```typescript // Default: auto-detects Docker socket from common locations const docker = new DockerHost({}); // Custom socket path const docker = new DockerHost({ socketPath: 'http://unix:/var/run/docker.sock:', }); // Custom image store directory for local image caching const docker = new DockerHost({ imageStoreDir: '/tmp/my-image-store', }); // Start the host (initializes the image store) await docker.start(); // When done, stop the host await docker.stop(); ``` Socket path resolution order: 1. Explicit `socketPath` constructor option (highest priority) 2. `DOCKER_HOST` environment variable 3. `http://docker:2375/` when running in CI (the `CI` env var is set) 4. `http://unix:/var/run/docker.sock:` as the default ### πŸ₯ Health Check and Version ```typescript // Ping the Docker daemon to verify it is accessible await docker.ping(); // Get Docker daemon version information const version = await docker.getVersion(); console.log(`Docker ${version.Version} (API ${version.ApiVersion})`); console.log(`Platform: ${version.Os}/${version.Arch}`); console.log(`Kernel: ${version.KernelVersion}`); ``` | Field | Type | Description | | ---------------- | ------ | ---------------------------------- | | `Version` | string | Docker engine version | | `ApiVersion` | string | API version | | `MinAPIVersion` | string | Minimum supported API version | | `GitCommit` | string | Git commit of the build | | `GoVersion` | string | Go compiler version | | `Os` | string | Operating system (e.g., `linux`) | | `Arch` | string | Architecture (e.g., `amd64`) | | `KernelVersion` | string | Host kernel version | | `BuildTime` | string | Build timestamp | ### πŸ”‘ Authentication ```typescript // Authenticate with explicit credentials await docker.auth({ serveraddress: 'https://index.docker.io/v1/', username: 'myuser', password: 'mypassword', }); // Authenticate using credentials stored in ~/.docker/config.json await docker.getAuthTokenFromDockerConfig('https://registry.gitlab.com'); ``` ### 🐝 Docker Swarm ```typescript // Activate swarm with automatic IP detection await docker.activateSwarm(); // Activate swarm with a specific advertisement address await docker.activateSwarm('192.168.1.100'); ``` ### πŸ“‘ Docker Events Subscribe to real-time Docker daemon events using an RxJS Observable. ```typescript const eventObservable = await docker.getEventObservable(); const subscription = eventObservable.subscribe((event) => { console.log(`Event: ${event.Type} ${event.Action}`); console.log(`Actor: ${event.Actor?.ID}`); }); // Later: unsubscribe to stop listening subscription.unsubscribe(); ``` --- ## πŸ“¦ Containers ### Listing Containers ```typescript const containers = await docker.listContainers(); for (const container of containers) { console.log(`${container.Names[0]} - ${container.State} (${container.Status})`); console.log(` Image: ${container.Image}`); console.log(` ID: ${container.Id}`); } ``` ### Getting a Container by ID Returns `undefined` if the container does not exist. ```typescript const container = await docker.getContainerById('abc123def456'); if (container) { console.log(`Found container: ${container.Names[0]}`); } else { console.log('Container not found'); } ``` ### Creating a Container ```typescript const container = await docker.createContainer({ Hostname: 'my-container', Domainname: 'example.com', networks: ['my-network'], }); ``` ### Container Lifecycle ```typescript // Start a container await container.start(); // Stop a container (with optional timeout in seconds) await container.stop(); await container.stop({ t: 30 }); // Remove a container await container.remove(); await container.remove({ force: true, v: true }); // Force removal and delete volumes ``` ### Inspecting a Container ```typescript const details = await container.inspect(); console.log(`State: ${details.State.Status}`); console.log(`PID: ${details.State.Pid}`); ``` ### Refreshing Container State Reload the container's properties from the Docker daemon. ```typescript await container.refresh(); console.log(`Current state: ${container.State}`); ``` ### Container Logs ```typescript // Get logs as a string (one-shot) const logs = await container.logs({ stdout: true, stderr: true, timestamps: true, tail: 100, // Last 100 lines since: 1609459200, // Unix timestamp }); console.log(logs); // Stream logs continuously (follow mode) const logStream = await container.streamLogs({ stdout: true, stderr: true, timestamps: true, tail: 50, }); logStream.on('data', (chunk) => { process.stdout.write(chunk.toString()); }); logStream.on('end', () => { console.log('Log stream ended'); }); ``` ### Container Stats ```typescript // Get a single stats snapshot const stats = await container.stats({ stream: false }); console.log(`CPU usage: ${stats.cpu_stats.cpu_usage.total_usage}`); console.log(`Memory usage: ${stats.memory_stats.usage}`); ``` ### Attaching to a Container Attach to the container's main process (PID 1) for bidirectional communication. ```typescript const { stream, close } = await container.attach({ stdin: true, stdout: true, stderr: true, stream: true, logs: true, // Include previous logs }); // Read output from the container stream.on('data', (chunk) => { process.stdout.write(chunk.toString()); }); // Send input to the container stream.write('echo hello\n'); // Detach when done await close(); ``` ### Executing Commands in a Container Run a command inside a running container with full streaming support. The command argument can be a string (wrapped in `/bin/sh -c`) or an array of strings. ```typescript // Simple command execution const { stream, close, inspect } = await container.exec('ls -la /app', { tty: true, }); let output = ''; stream.on('data', (chunk) => { output += chunk.toString(); }); stream.on('end', async () => { // Check the exit code after the command finishes const info = await inspect(); console.log(`Exit code: ${info.ExitCode}`); console.log(`Output:\n${output}`); await close(); }); ``` ```typescript // Execute with advanced options const { stream, close, inspect } = await container.exec( ['python', '-c', 'print("hello from python")'], { tty: false, env: ['MY_VAR=hello', 'DEBUG=1'], workingDir: '/app', user: 'appuser', attachStdin: true, attachStdout: true, attachStderr: true, } ); stream.on('data', (chunk) => { console.log(chunk.toString()); }); stream.on('end', async () => { const info = await inspect(); if (info.ExitCode === 0) { console.log('Command succeeded'); } else { console.log(`Command failed with exit code ${info.ExitCode}`); } await close(); }); ``` The `inspect()` method on the exec result returns an `IExecInspectInfo` object: | Field | Type | Description | | --------------- | ------- | ---------------------------------------------- | | `ExitCode` | number | Exit code of the process (0 = success) | | `Running` | boolean | Whether the exec process is still running | | `Pid` | number | Process ID | | `ContainerID` | string | Container where the exec ran | | `ID` | string | Exec instance ID | | `OpenStderr` | boolean | Whether stderr is open | | `OpenStdin` | boolean | Whether stdin is open | | `OpenStdout` | boolean | Whether stdout is open | | `CanRemove` | boolean | Whether the exec instance can be removed | | `DetachKeys` | string | Detach keys | | `ProcessConfig` | object | Process config (tty, entrypoint, arguments, privileged) | ### Container Properties Each `DockerContainer` instance exposes these properties: | Property | Type | Description | | ----------------- | -------- | ---------------------------------------- | | `Id` | string | Container ID | | `Names` | string[] | Container names | | `Image` | string | Image name | | `ImageID` | string | Image ID | | `Command` | string | Command used to start the container | | `Created` | number | Creation timestamp | | `Ports` | array | Port mappings | | `Labels` | object | Key-value label pairs | | `State` | string | Container state (running, exited, etc.) | | `Status` | string | Human-readable status string | | `HostConfig` | object | Host configuration | | `NetworkSettings` | object | Network configuration and IP addresses | | `Mounts` | any | Volume mounts | --- ## πŸ–ΌοΈ Images ### Listing Images ```typescript const images = await docker.listImages(); for (const image of images) { console.log(`Tags: ${image.RepoTags?.join(', ')}`); console.log(` Size: ${(image.Size / 1024 / 1024).toFixed(2)} MB`); console.log(` ID: ${image.Id}`); } ``` ### Getting an Image by Name ```typescript const image = await docker.getImageByName('nginx:latest'); if (image) { console.log(`Found image: ${image.RepoTags[0]}`); console.log(`Size: ${image.Size} bytes`); } ``` ### Pulling an Image from a Registry ```typescript // Pull with explicit tag const image = await docker.createImageFromRegistry({ imageUrl: 'nginx', imageTag: 'latest', }); // Pull with tag embedded in the URL const image = await docker.createImageFromRegistry({ imageUrl: 'node:20-alpine', }); // Pull from a private registry (authenticate first) await docker.auth({ serveraddress: 'https://registry.gitlab.com', username: 'deploy-token', password: 'my-token', }); const image = await docker.createImageFromRegistry({ imageUrl: 'registry.gitlab.com/myorg/myapp:v1.2.3', }); ``` ### Importing an Image from a Tar Stream ```typescript import * as fs from 'node:fs'; const tarStream = fs.createReadStream('/path/to/image.tar'); const image = await docker.createImageFromTarStream(tarStream, { imageUrl: 'myapp:imported', }); console.log(`Imported: ${image.RepoTags[0]}`); ``` ### Exporting an Image to a Tar Stream ```typescript import * as fs from 'node:fs'; const image = await docker.getImageByName('myapp:latest'); const tarStream = await image.exportToTarStream(); const writeStream = fs.createWriteStream('/path/to/output.tar'); tarStream.pipe(writeStream); writeStream.on('finish', () => { console.log('Image exported successfully'); }); ``` ### Pulling the Latest Version of an Image ```typescript const image = await docker.getImageByName('nginx:latest'); await image.pullLatestImageFromRegistry(); ``` ### Removing an Image ```typescript const image = await docker.getImageByName('old-image:v1'); await image.remove(); // Force remove (even if referenced by containers) await image.remove({ force: true }); // Remove without deleting untagged parent images await image.remove({ noprune: true }); ``` ### Pruning Unused Images ```typescript // Prune dangling (untagged) images const result = await docker.pruneImages({ dangling: true }); console.log(`Deleted: ${result.ImagesDeleted?.length || 0} image layers`); console.log(`Reclaimed: ${(result.SpaceReclaimed / 1024 / 1024).toFixed(2)} MB`); // Prune images older than 7 days const result = await docker.pruneImages({ filters: { until: ['168h'], }, }); // Prune images matching specific labels const result = await docker.pruneImages({ filters: { label: ['environment=staging'], }, }); ``` ### Getting the Image Version Label ```typescript const version = await image.getVersion(); console.log(`Image version: ${version}`); // Returns the "version" label value, or "0.0.0" ``` ### Image Properties Each `DockerImage` instance exposes these properties: | Property | Type | Description | | ------------- | -------- | ------------------------------------ | | `Id` | string | Image ID | | `RepoTags` | string[] | Repository tags | | `RepoDigests` | string[] | Repository digests | | `Created` | number | Creation timestamp | | `Size` | number | Image size in bytes | | `VirtualSize` | number | Virtual size in bytes | | `SharedSize` | number | Shared size in bytes | | `Labels` | object | Key-value label pairs | | `ParentId` | string | Parent image ID | | `Containers` | number | Number of containers using the image | --- ## 🌐 Networks ### Listing Networks ```typescript const networks = await docker.listNetworks(); for (const network of networks) { console.log(`${network.Name} (${network.Driver}) - ${network.Scope}`); console.log(` ID: ${network.Id}`); console.log(` Attachable: ${network.Attachable}`); } ``` ### Getting a Network by Name ```typescript const network = await docker.getNetworkByName('my-overlay-network'); if (network) { console.log(`Network: ${network.Name}`); console.log(`Driver: ${network.Driver}`); console.log(`Scope: ${network.Scope}`); } ``` ### Creating a Network ```typescript // Simple overlay network (default driver) const network = await docker.createNetwork({ Name: 'my-overlay', }); // Bridge network with custom IPAM configuration const network = await docker.createNetwork({ Name: 'custom-bridge', Driver: 'bridge', IPAM: { Config: [{ Subnet: '172.20.0.0/16', Gateway: '172.20.0.1', IPRange: '172.20.10.0/24', }], }, Labels: { environment: 'production' }, }); // Internal network (no external access) const network = await docker.createNetwork({ Name: 'internal-net', Driver: 'overlay', Internal: true, Attachable: true, EnableIPv6: false, }); ``` Network creation options: | Field | Type | Default | Description | | ------------ | ------- | ----------- | -------------------------------------------------------------- | | `Name` | string | (required) | Network name | | `Driver` | string | `'overlay'` | Network driver: `bridge`, `overlay`, `host`, `none`, `macvlan` | | `Attachable` | boolean | `true` | Whether non-service containers can attach | | `Labels` | object | -- | Key-value label pairs | | `IPAM` | object | -- | IP Address Management configuration | | `Internal` | boolean | `false` | Restrict external access to the network | | `EnableIPv6` | boolean | `false` | Enable IPv6 | IPAM Config entries support: `Subnet` (CIDR), `Gateway`, `IPRange`, `AuxiliaryAddresses`. ### Listing Containers on a Network ```typescript const network = await docker.getNetworkByName('my-overlay'); const containers = await network.listContainersOnNetwork(); for (const container of containers) { console.log(`${container.Name}: ${container.IPv4Address}`); } ``` ### Getting Containers for a Specific Service on a Network ```typescript const network = await docker.getNetworkByName('my-overlay'); const service = await docker.getServiceByName('web'); const serviceContainers = await network.getContainersOnNetworkForService(service); for (const container of serviceContainers) { console.log(`${container.Name}: ${container.IPv4Address}`); } ``` ### Removing a Network ```typescript const network = await docker.getNetworkByName('old-network'); await network.remove(); ``` ### Network Properties | Property | Type | Description | | ------------ | ------- | ------------------------------------- | | `Id` | string | Network ID | | `Name` | string | Network name | | `Created` | string | Creation timestamp | | `Scope` | string | Network scope (local, swarm, global) | | `Driver` | string | Network driver | | `EnableIPv6` | boolean | Whether IPv6 is enabled | | `Internal` | boolean | Whether the network is internal-only | | `Attachable` | boolean | Whether non-service containers can attach | | `Ingress` | boolean | Whether the network is an ingress network | | `IPAM` | object | IP Address Management configuration | --- ## βš™οΈ Services (Swarm Mode) Services are only available when the Docker daemon is running in Swarm mode. ### Listing Services ```typescript const services = await docker.listServices(); for (const service of services) { console.log(`${service.Spec.Name} - ${service.Spec.TaskTemplate.ContainerSpec.Image}`); console.log(` ID: ${service.ID}`); } ``` ### Getting a Service by Name ```typescript const service = await docker.getServiceByName('my-web-service'); console.log(`Service: ${service.Spec.Name}`); console.log(`Image: ${service.Spec.TaskTemplate.ContainerSpec.Image}`); ``` ### Creating a Service Services support both string references and resource instances for images, networks, and secrets. ```typescript // Using string references const service = await docker.createService({ name: 'web-app', image: 'nginx:latest', labels: { environment: 'production', team: 'platform' }, networks: ['frontend-network'], networkAlias: 'web', secrets: ['tls-certificate'], ports: ['80:80', '443:443'], resources: { memorySizeMB: 512, }, }); // Using resource instances directly const image = await docker.getImageByName('nginx:latest'); const network = await docker.getNetworkByName('frontend-network'); const secret = await docker.getSecretByName('tls-certificate'); const service = await docker.createService({ name: 'web-app', image: image, labels: { environment: 'production' }, networks: [network], networkAlias: 'web', secrets: [secret], ports: ['80:80'], accessHostDockerSock: false, resources: { memorySizeMB: 1024, volumeMounts: [ { containerFsPath: '/data', hostFsPath: '/mnt/storage/data', }, ], }, }); ``` Service creation descriptor fields: | Field | Type | Description | | -------------------------- | ----------------------------- | -------------------------------------------------------- | | `name` | string | Service name | | `image` | string \| DockerImage | Image tag string or DockerImage instance | | `labels` | object | Key-value label pairs | | `networks` | (string \| DockerNetwork)[] | Network names or DockerNetwork instances | | `networkAlias` | string | DNS alias for the service on the network | | `secrets` | (string \| DockerSecret)[] | Secret names or DockerSecret instances | | `ports` | string[] | Port mappings in `"hostPort:containerPort"` format | | `accessHostDockerSock` | boolean | Mount the Docker socket inside the service container | | `resources.memorySizeMB` | number | Memory limit in megabytes (default: 1000) | | `resources.volumeMounts` | array | Array of `{ containerFsPath, hostFsPath }` mounts | ### Checking if a Service Needs an Update ```typescript const service = await docker.getServiceByName('web-app'); const needsUpdate = await service.needsUpdate(); if (needsUpdate) { console.log('A newer image version is available'); } ``` ### Removing a Service ```typescript const service = await docker.getServiceByName('old-service'); await service.remove(); ``` ### Service Properties | Property | Type | Description | | ----------- | ------ | --------------------------------------- | | `ID` | string | Service ID | | `Version` | object | Version info with `Index` number | | `CreatedAt` | string | Creation timestamp | | `UpdatedAt` | string | Last update timestamp | | `Spec` | object | Full service specification | | `Endpoint` | object | Endpoint specification and VirtualIPs | --- ## πŸ” Secrets (Swarm Mode) Secrets are only available when the Docker daemon is running in Swarm mode. ### Listing Secrets ```typescript const secrets = await docker.listSecrets(); for (const secret of secrets) { console.log(`${secret.Spec.Name} (ID: ${secret.ID})`); } ``` ### Getting a Secret ```typescript // By name const secret = await docker.getSecretByName('my-api-key'); // By ID const secret = await docker.getSecretById('abc123secretid'); ``` ### Creating a Secret ```typescript const secret = await docker.createSecret({ name: 'database-credentials', version: '1.0.0', contentArg: JSON.stringify({ host: 'db.example.com', username: 'admin', password: 'secret-password', }), labels: { environment: 'production', team: 'backend' }, }); console.log(`Secret created: ${secret.Spec.Name} (ID: ${secret.ID})`); ``` | Field | Type | Description | | ------------ | ------ | ------------------------------------------- | | `name` | string | Secret name | | `version` | string | Version label for the secret | | `contentArg` | any | Secret content (will be base64-encoded) | | `labels` | object | Key-value label pairs | ### Updating a Secret ```typescript const secret = await docker.getSecretByName('database-credentials'); await secret.update(JSON.stringify({ host: 'new-db.example.com', username: 'admin', password: 'new-secret-password', })); ``` ### Removing a Secret ```typescript const secret = await docker.getSecretByName('old-secret'); await secret.remove(); ``` ### Secret Properties | Property | Type | Description | | --------- | ------ | -------------------------------------- | | `ID` | string | Secret ID | | `Spec` | object | Contains `Name` and `Labels` | | `Version` | object | Version info with `Index` string | --- ## πŸ’Ύ Image Store Built-in image storage for caching Docker images locally or in S3-compatible object storage. Useful for backup, environment transfer, or air-gapped deployments. ### Storing an Image ```typescript const image = await docker.getImageByName('myapp:latest'); const tarStream = await image.exportToTarStream(); await docker.storeImage('myapp:latest', tarStream); ``` ### Retrieving an Image ```typescript const tarStream = await docker.retrieveImage('myapp:latest'); // Import the retrieved image back into Docker const image = await docker.createImageFromTarStream(tarStream, { imageUrl: 'myapp:latest', }); ``` ### S3 Storage Backend Add S3-compatible object storage for longer-term image persistence. ```typescript await docker.addS3Storage({ endpoint: 's3.amazonaws.com', accessKey: 'YOUR_ACCESS_KEY', accessSecret: 'YOUR_SECRET_KEY', bucketName: 'docker-image-backups', directoryPath: 'images', }); // Now storeImage() persists images to S3 const image = await docker.getImageByName('myapp:v2.0.0'); const tarStream = await image.exportToTarStream(); await docker.storeImage('myapp:v2.0.0', tarStream); ``` --- ## πŸ—ΊοΈ Complete DockerHost API Reference ### Lifecycle and Daemon | Method | Signature | Description | | ------ | --------- | ----------- | | `start` | `() => Promise` | Initialize the host and image store | | `stop` | `() => Promise` | Shut down the host and image store | | `ping` | `() => Promise` | Ping the Docker daemon; throws if unavailable | | `getVersion` | `() => Promise` | Get Docker daemon version information | | `auth` | `(authData) => Promise` | Authenticate against a Docker registry | | `getAuthTokenFromDockerConfig` | `(registryUrl: string) => Promise` | Authenticate using ~/.docker/config.json | | `activateSwarm` | `(advertiseIp?: string) => Promise` | Initialize Docker Swarm mode | | `getEventObservable` | `() => Promise>` | Subscribe to real-time Docker events | ### Containers | Method | Signature | Description | | ------ | --------- | ----------- | | `listContainers` | `() => Promise` | List all containers | | `getContainerById` | `(id: string) => Promise` | Get a container by ID | | `createContainer` | `(descriptor) => Promise` | Create a new container | ### Images | Method | Signature | Description | | ------ | --------- | ----------- | | `listImages` | `() => Promise` | List all images | | `getImageByName` | `(name: string) => Promise` | Get an image by tag name | | `createImageFromRegistry` | `(descriptor) => Promise` | Pull an image from a registry | | `createImageFromTarStream` | `(stream, descriptor) => Promise` | Import an image from a tar stream | | `pruneImages` | `(options?) => Promise` | Remove unused images | ### Networks | Method | Signature | Description | | ------ | --------- | ----------- | | `listNetworks` | `() => Promise` | List all networks | | `getNetworkByName` | `(name: string) => Promise` | Get a network by name | | `createNetwork` | `(descriptor) => Promise` | Create a new network | ### Services (Swarm) | Method | Signature | Description | | ------ | --------- | ----------- | | `listServices` | `() => Promise` | List all services | | `getServiceByName` | `(name: string) => Promise` | Get a service by name | | `createService` | `(descriptor) => Promise` | Create a new service | ### Secrets (Swarm) | Method | Signature | Description | | ------ | --------- | ----------- | | `listSecrets` | `() => Promise` | List all secrets | | `getSecretByName` | `(name: string) => Promise` | Get a secret by name | | `getSecretById` | `(id: string) => Promise` | Get a secret by ID | | `createSecret` | `(descriptor) => Promise` | Create a new secret | ### Image Store | Method | Signature | Description | | ------ | --------- | ----------- | | `storeImage` | `(name: string, tarStream: Readable) => Promise` | Store an image tar stream | | `retrieveImage` | `(name: string) => Promise` | Retrieve a stored image as a tar stream | | `addS3Storage` | `(options) => Promise` | Configure S3 backend for image storage | --- ## πŸš€ Complete Example A full workflow: connect to Docker, pull an image, create a network and service, then clean up. ```typescript import { DockerHost } from '@apiclient.xyz/docker'; async function main() { // Connect to Docker const docker = new DockerHost({}); await docker.start(); // Check Docker availability await docker.ping(); const version = await docker.getVersion(); console.log(`Connected to Docker ${version.Version}`); // Initialize Swarm (if not already active) await docker.activateSwarm(); // Pull an image const image = await docker.createImageFromRegistry({ imageUrl: 'nginx', imageTag: 'latest', }); console.log(`Pulled image: ${image.RepoTags[0]}`); // Create a network const network = await docker.createNetwork({ Name: 'web-network', Driver: 'overlay', Attachable: true, }); console.log(`Created network: ${network.Name}`); // Create a secret const secret = await docker.createSecret({ name: 'web-config', version: '1.0.0', contentArg: JSON.stringify({ port: 8080 }), labels: {}, }); console.log(`Created secret: ${secret.Spec.Name}`); // Create a service const service = await docker.createService({ name: 'web-server', image: image, labels: { app: 'web' }, networks: [network], networkAlias: 'web', secrets: [secret], ports: ['8080:80'], resources: { memorySizeMB: 256, }, }); console.log(`Created service: ${service.Spec.Name}`); // List running containers const containers = await docker.listContainers(); for (const container of containers) { console.log(`Container: ${container.Names[0]} - ${container.State}`); } // Clean up await service.remove(); await secret.remove(); await network.remove(); // Prune unused images const pruneResult = await docker.pruneImages({ dangling: true }); console.log(`Reclaimed ${(pruneResult.SpaceReclaimed / 1024 / 1024).toFixed(2)} MB`); await docker.stop(); } main().catch(console.error); ``` ## License and Legal Information This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [LICENSE](./license) file. **Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file. ### Trademarks This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar. ### Company Information Task Venture Capital GmbH Registered at District Court Bremen HRB 35230 HB, Germany For any legal inquiries or further information, please contact us via email at hello@task.vc. By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.