diff --git a/.changeset/cold-sites-hear.md b/.changeset/cold-sites-hear.md
new file mode 100644
index 000000000..a93e45e0f
--- /dev/null
+++ b/.changeset/cold-sites-hear.md
@@ -0,0 +1,5 @@
+---
+"@sei-js/mcp-server": minor
+---
+
+Add local docker tools to create and start local sei chains
diff --git a/docs/mcp-server/environment-variables.mdx b/docs/mcp-server/environment-variables.mdx
index fe316a1f1..d97cb0ba0 100644
--- a/docs/mcp-server/environment-variables.mdx
+++ b/docs/mcp-server/environment-variables.mdx
@@ -125,6 +125,21 @@ The Sei MCP Server supports configuration through environment variables, which c
- **Description**: Base path for HTTP endpoints
- **Format**: Must start with `/` (automatically normalized)
+### Docker Configuration
+
+#### `SEI_DOCKER_ENABLED`
+- **Type**: `string`
+- **Default**: `"false"`
+- **Description**: Enable Docker tools for local Sei blockchain development
+- **Options**:
+ - `"false"` - Docker tools disabled (default)
+ - `"true"` - Enable Docker container management tools
+- **Prerequisites**: Docker must be installed and running on your system
+
+
+**Docker Tools**: When enabled, provides 6 additional tools for managing local Sei blockchain containers including start, stop, restart, and cleanup operations.
+
+
### System Configuration
#### `PATH`
@@ -248,6 +263,48 @@ The Sei MCP Server supports configuration through environment variables, which c
**Status**: Deprecated - migrate to streamable-http for better performance
+
+
+ Enable Docker tools for local blockchain development:
+
+
+
+ ```json
+ {
+ "mcpServers": {
+ "sei": {
+ "command": "npx",
+ "args": ["-y", "@sei-js/mcp-server"],
+ "env": {
+ "SEI_DOCKER_ENABLED": "true",
+ "WALLET_MODE": "private-key",
+ "PRIVATE_KEY": "0x123..."
+ }
+ }
+ }
+ }
+ ```
+
+
+ ```bash
+ # Enable Docker tools with wallet support
+ SEI_DOCKER_ENABLED=true \
+ WALLET_MODE=private-key \
+ PRIVATE_KEY=0x123... \
+ npx @sei-js/mcp-server
+
+ # Or with Claude CLI
+ claude mcp add sei-docker npx @sei-js/mcp-server \
+ --env SEI_DOCKER_ENABLED=true \
+ --env WALLET_MODE=private-key \
+ --env PRIVATE_KEY=0x123...
+ ```
+
+
+
+ **Use Case**: Local development, testing, and blockchain experimentation
+ **Tools Added**: get_sei_releases, start_sei_chain, get_running_chains, restart_sei_container, stop_sei_chain, delete_sei_container
+
## Security Best Practices
@@ -286,6 +343,12 @@ The Sei MCP Server supports configuration through environment variables, which c
- Verify JSON syntax in MCP configuration
- Check for typos in variable names
+**Docker Tools Not Available**
+- Ensure Docker is installed and running: `docker --version`
+- Verify `SEI_DOCKER_ENABLED=true` is set correctly
+- Check Docker daemon is accessible: `docker ps`
+- Restart MCP server after enabling Docker tools
+
Check our comprehensive troubleshooting guide for solutions to specific problems.
diff --git a/docs/mcp-server/introduction.mdx b/docs/mcp-server/introduction.mdx
index 90ddb6724..066a7449a 100644
--- a/docs/mcp-server/introduction.mdx
+++ b/docs/mcp-server/introduction.mdx
@@ -97,8 +97,9 @@ icon: "robot"
"SERVER_PATH": "/mcp",
"MAINNET_RPC_URL": "https://evm-rpc.sei-apis.com",
"TESTNET_RPC_URL": "https://evm-rpc-testnet.sei-apis.com",
- "DEVNET_RPC_URL": "https://evm-rpc-arctic-1.sei-apis.com"
- }
+ "DEVNET_RPC_URL": "https://evm-rpc-arctic-1.sei-apis.com",
+ "SEI_DOCKER_ENABLED": "true",
+ }
}
}
}
@@ -113,6 +114,7 @@ icon: "robot"
The Model Context Protocol is an open standard that connects AI systems with custom prompts, tools and data sources (context). It enables things like:
- **Real-time blockchain data access** - Get current balances, transaction history, and network status directly from Sei
+- **Local development environment** - Spin up complete Sei blockchain containers for testing and development
- **Full execution and write operations** - Deploy contracts, execute transactions, and interact with smart contracts (with wallet configured)
- **Up-to-date documentation access** - Search both main Sei docs and Sei-JS package documentation for comprehensive guidance
- **Specialized blockchain capabilities** - Access Sei-specific features like precompiles and native token operations
@@ -136,7 +138,7 @@ The Sei MCP Server leverages this protocol to bring all of this and more directl
- Access 29 blockchain tools covering token management, NFT operations, smart contract interactions, network monitoring, and documentation search.
+ Access 35+ blockchain tools covering token management, NFT operations, smart contract interactions, network monitoring, local development with Docker, and documentation search.
@@ -148,6 +150,9 @@ The Sei MCP Server leverages this protocol to bring all of this and more directl
"Generate a boilerplate dApp with Sei Global Wallet connection"
"What's my SEI balance?"
"Send 1 SEI to this address"
+"Start a local Sei blockchain for testing"
+"Show me all running Sei containers"
+"Stop my local blockchain and clean up"
```
## Next Steps
diff --git a/docs/mcp-server/quickstart.mdx b/docs/mcp-server/quickstart.mdx
index 0e845ef8b..e222fb2af 100644
--- a/docs/mcp-server/quickstart.mdx
+++ b/docs/mcp-server/quickstart.mdx
@@ -126,6 +126,9 @@ icon: "download"
# Wallet-enabled mode
claude mcp add sei-mcp-server-wallet npx @sei-js/mcp-server --env WALLET_MODE=private-key PRIVATE_KEY=0x123...
+
+ # Docker-enabled mode for local development
+ claude mcp add sei-mcp-server-docker npx @sei-js/mcp-server --env SEI_DOCKER_ENABLED=true WALLET_MODE=private-key PRIVATE_KEY=0x123...
```
@@ -233,6 +236,16 @@ After setup, verify the server is working by asking your AI assistant:
This confirms wallet tools are enabled.
+
+
+ If you enabled Docker tools with `SEI_DOCKER_ENABLED=true`:
+
+ **Ask:** "What Sei chain versions are available?"
+
+ **Ask:** "Start a local Sei blockchain"
+
+ This confirms Docker tools are working.
+
## Next Steps
@@ -242,7 +255,7 @@ After setup, verify the server is working by asking your AI assistant:
Configure wallet access and custom RPC endpoints
- Explore 50+ blockchain tools and capabilities
+ Explore 35+ blockchain tools and capabilities
Solutions to common setup and configuration issues
diff --git a/docs/mcp-server/tools.mdx b/docs/mcp-server/tools.mdx
index eed895699..70e066184 100644
--- a/docs/mcp-server/tools.mdx
+++ b/docs/mcp-server/tools.mdx
@@ -4,7 +4,7 @@ description: "Complete reference of all Sei MCP server tools"
icon: "wrench"
---
-The Sei MCP Server provides various tools for blockchain operations, queries, documentation lookup, and more. **By default, wallet tools are disabled** and only read-only tools are available. [Enable wallet tools](/mcp-server/quickstart#wallet-connection) to unlock transaction capabilities.
+The Sei MCP Server provides various tools for blockchain operations, queries, documentation lookup, local development, and more. **By default, wallet tools are disabled** and only read-only tools are available. [Enable wallet tools](/mcp-server/quickstart#wallet-connection) to unlock transaction capabilities.
**Wallet Tools Disabled by Default**
@@ -116,6 +116,49 @@ These tools require [wallet configuration](/mcp-server/quickstart#wallet-connect
| `search_docs` | Search the main Sei docs for general chain information, ecosystem providers, and user onboarding guides | "How do I bridge tokens to Sei?" |
| `search_sei_js_docs` | Search Sei-JS documentation | "How do I use precompiles with Viem?" |
+## Local Development (Docker Tools) 🐳
+
+
+**Docker Required**: These tools require Docker to be installed and running on your system. Enable with `SEI_DOCKER_ENABLED=true`.
+
+
+Manage local Sei blockchain containers for development and testing:
+
+| Tool | Purpose | Example Usage |
+|------|---------|---------------|
+| `get_sei_releases` | Get available Sei chain releases | "What Sei versions are available?" |
+| `start_sei_chain` | Start local Sei blockchain container | "Start a local Sei chain with version v6.1.0" |
+| `get_running_chains` | List running Sei containers | "Show me all running Sei containers" |
+| `restart_sei_container` | Restart stopped Sei container | "Restart my sei-chain-latest container" |
+| `stop_sei_chain` | Stop running Sei container | "Stop my local blockchain" |
+| `delete_sei_container` | Delete Sei container | "Delete the sei-chain-v6.1.0 container" |
+
+### Docker Tool Features
+
+- **Multi-version support**: Run different Sei chain versions simultaneously
+- **Pre-configured accounts**: Test accounts with funds automatically created
+- **Port management**: Configurable RPC, REST, EVM, and gRPC ports
+- **Container lifecycle**: Full start, stop, restart, and cleanup operations
+- **GitHub integration**: Fetch latest releases directly from sei-protocol/sei-chain
+
+### Enabling Docker Tools
+
+To use Docker tools, add the environment variable to your MCP configuration:
+
+```json
+{
+ "mcpServers": {
+ "sei": {
+ "command": "npx",
+ "args": ["-y", "@sei-js/mcp-server"],
+ "env": {
+ "SEI_DOCKER_ENABLED": "true"
+ }
+ }
+ }
+}
+```
+
## Enabling Wallet Tools
To use wallet-required tools (🔐), you must configure a wallet connection:
diff --git a/docs/mcp-server/troubleshooting.mdx b/docs/mcp-server/troubleshooting.mdx
index 70640bd68..79b2abb3b 100644
--- a/docs/mcp-server/troubleshooting.mdx
+++ b/docs/mcp-server/troubleshooting.mdx
@@ -147,7 +147,92 @@ Many issues stem from incorrect environment variable configuration.
+### Docker Issues
+Problems with Docker tools and local blockchain containers.
+
+
+
+ If Docker tools aren't showing up or you get "Docker not available" errors:
+
+ ```bash
+ # Check if Docker is installed
+ docker --version
+
+ # Check if Docker daemon is running
+ docker ps
+
+ # Verify SEI_DOCKER_ENABLED is set
+ echo $SEI_DOCKER_ENABLED
+ ```
+
+ **Solutions:**
+ - Install Docker from [docker.com](https://docker.com)
+ - Start Docker Desktop or Docker daemon
+ - Ensure `SEI_DOCKER_ENABLED=true` in your MCP configuration
+ - Restart your AI assistant after enabling Docker tools
+
+
+
+ If Sei containers fail to start or crash immediately:
+
+ ```bash
+ # Check Docker logs
+ docker logs sei-chain-latest
+
+ # Check available system resources
+ docker system df
+
+ # Check port conflicts
+ lsof -i :26657 -i :1317 -i :8545 -i :9090
+ ```
+
+ **Common causes:**
+ - Port conflicts (26657, 1317, 8545, 9090 already in use)
+ - Insufficient disk space or memory
+ - Docker image corruption
+
+ **Solutions:**
+ - Stop conflicting services or use different ports
+ - Free up disk space (`docker system prune`)
+ - Pull fresh Docker image (`docker pull ghcr.io/sei-protocol/sei:latest`)
+
+
+
+ If Docker images fail to download:
+
+ ```bash
+ # Test Docker Hub connectivity
+ docker pull hello-world
+
+ # Try pulling Sei image manually
+ docker pull ghcr.io/sei-protocol/sei:latest
+ ```
+
+ **Solutions:**
+ - Check internet connection
+ - Verify Docker Hub/GitHub Container Registry access
+ - Try different network (corporate firewalls may block)
+ - Use VPN if in restricted region
+
+
+
+ If containers won't stop, restart, or delete:
+
+ ```bash
+ # Force stop container
+ docker stop sei-chain-latest --time 0
+
+ # Force remove container
+ docker rm sei-chain-latest --force
+
+ # Clean up all stopped containers
+ docker container prune
+ ```
+
+ **Use with caution:** Force operations may cause data loss.
+
+
## Error Reference
@@ -168,6 +253,18 @@ Many issues stem from incorrect environment variable configuration.
**Cause**: Invalid RPC URL or network connectivity issues
**Solution**: Check your RPC URLs and internet connection
+
+ **Cause**: Docker not installed, not running, or SEI_DOCKER_ENABLED not set
+ **Solution**: Install Docker, start daemon, and set SEI_DOCKER_ENABLED=true
+
+
+ **Cause**: Required ports (26657, 1317, 8545, 9090) are occupied by other services
+ **Solution**: Stop conflicting services or configure different ports
+
+
+ **Cause**: Docker image issues, resource constraints, or configuration problems
+ **Solution**: Check Docker logs, free up resources, or pull fresh image
+
## Getting Help
@@ -188,6 +285,7 @@ If you're still experiencing problems, please [create an issue on GitHub](https:
Include:
- Operating system and version
- Node.js version (`node --version`)
+ - Docker version (`docker --version`) if using Docker tools
- MCP client (Cursor, Claude Desktop, etc.)
- Your PATH (`echo $PATH`)
diff --git a/package.json b/package.json
index f839277c5..0cf05b7d2 100644
--- a/package.json
+++ b/package.json
@@ -13,7 +13,9 @@
},
"dependencies": {
"@biomejs/biome": "^1.9.4",
- "@changesets/cli": "^2.28.1"
+ "@changesets/cli": "^2.28.1",
+ "optional": "^0.1.4",
+ "sharp": "^0.34.3"
},
"devDependencies": {
"@types/jest": "^29.5.14",
diff --git a/packages/mcp-server/.env.example b/packages/mcp-server/.env.example
index a768e2d50..6da6147c1 100644
--- a/packages/mcp-server/.env.example
+++ b/packages/mcp-server/.env.example
@@ -17,6 +17,11 @@ SERVER_PORT=8080
SERVER_HOST=localhost
SERVER_PATH=/mcp
+# Docker Configuration
+# Enable Docker tools for local Sei chain development
+# Set to 'true' to enable Docker container management tools
+SEI_DOCKER_ENABLED=false
+
# RPC URLs
# Optional, only needed if you want to use a custom RPC URL
MAINNET_RPC_URL=your_mainnet_rpc_url_here
diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json
index d64427d9f..f8a0b0882 100644
--- a/packages/mcp-server/package.json
+++ b/packages/mcp-server/package.json
@@ -24,6 +24,7 @@
"devDependencies": {
"@jest/globals": "^30.0.3",
"@types/cors": "^2.8.17",
+ "@types/dockerode": "^3.3.31",
"@types/express": "^5.0.0",
"typescript": "^5.8.3"
},
@@ -32,6 +33,7 @@
"@noble/hashes": "^1.8.0",
"commander": "^14.0.0",
"cors": "^2.8.5",
+ "dockerode": "^4.0.2",
"dotenv": "^16.5.0",
"express": "^4.21.2",
"jest": "^30.0.3",
diff --git a/packages/mcp-server/src/core/services/chain.ts b/packages/mcp-server/src/core/services/chain.ts
new file mode 100644
index 000000000..f57c4e8f4
--- /dev/null
+++ b/packages/mcp-server/src/core/services/chain.ts
@@ -0,0 +1,288 @@
+import Docker from 'dockerode';
+
+const docker = new Docker();
+
+/**
+ * Interface for Sei chain version information
+ */
+export interface SeiChainVersion {
+ tag: string;
+ digest: string;
+ created: string;
+ size: string;
+}
+
+/**
+ * Interface for Docker container information
+ */
+export interface DockerContainer {
+ id: string;
+ name: string;
+ image: string;
+ status: string;
+ ports: string;
+ created: string;
+}
+
+/**
+ * Get available Sei chain versions from GitHub Container Registry
+ * Uses GitHub Container Registry API to query available tags
+ */
+export async function getAvailableSeiVersions(): Promise {
+ try {
+ // Use GitHub Container Registry API directly
+ return await getVersionsFromRegistryAPI();
+ } catch (error) {
+ throw new Error(`Failed to get available Sei versions: ${error instanceof Error ? error.message : String(error)}`);
+ }
+}
+
+/**
+ * Get versions using GitHub Container Registry API with fetch
+ */
+async function getVersionsFromRegistryAPI(): Promise {
+ try {
+ // Use fetch to query the GitHub Container Registry API
+ const response = await fetch('https://ghcr.io/v2/sei-protocol/sei/tags/list', {
+ headers: {
+ 'Accept': 'application/vnd.docker.distribution.manifest.v2+json'
+ }
+ });
+
+ if (!response.ok) {
+ throw new Error(`Registry API returned ${response.status}: ${response.statusText}`);
+ }
+
+ const data = await response.json() as { tags?: string[] };
+
+ if (!data.tags || !Array.isArray(data.tags)) {
+ throw new Error('No tags found in registry response');
+ }
+
+ // Convert to version objects (without detailed metadata)
+ const versions: SeiChainVersion[] = data.tags.map((tag: string) => ({
+ tag,
+ digest: 'unknown',
+ created: 'unknown',
+ size: 'unknown'
+ }));
+
+ return versions.sort((a, b) => b.tag.localeCompare(a.tag, undefined, { numeric: true }));
+ } catch (error) {
+ throw new Error(`Failed to query registry API: ${error instanceof Error ? error.message : String(error)}`);
+ }
+}
+
+/**
+ * Check if a specific Sei version exists in the registry
+ */
+export async function checkSeiVersionExists(version: string): Promise {
+ try {
+ const versions = await getAvailableSeiVersions();
+ return versions.some((v) => v.tag === version || v.tag === `v${version}` || v.tag === version.replace('v', ''));
+ } catch (error) {
+ console.error('Error checking version existence:', error);
+ return false;
+ }
+}
+
+/**
+ * Get the latest Sei version
+ */
+export async function getLatestSeiVersion(): Promise {
+ try {
+ const versions = await getAvailableSeiVersions();
+
+ // Filter out non-semantic versions and find the latest
+ const semanticVersions = versions.filter((v) => /^v?\d+\.\d+\.\d+$/.test(v.tag));
+
+ if (semanticVersions.length === 0) {
+ // Fallback to first available version
+ return versions[0]?.tag || 'latest';
+ }
+
+ return semanticVersions[0].tag;
+ } catch (error) {
+ throw new Error(`Failed to get latest Sei version: ${error instanceof Error ? error.message : String(error)}`);
+ }
+}
+
+/**
+ * Start a Sei chain Docker container
+ */
+export async function startSeiChain(
+ version = 'latest',
+ options: {
+ containerName?: string;
+ rpcPort?: number;
+ restPort?: number;
+ grpcPort?: number;
+ evmRpcPort?: number;
+ p2pPort?: number;
+ dataDir?: string;
+ chainId?: string;
+ moniker?: string;
+ } = {}
+): Promise<{ containerId: string; containerName: string; ports: Record }> {
+ const {
+ containerName = `sei-chain-${version.replace(/[^a-zA-Z0-9]/g, '-')}`,
+ rpcPort = 26657,
+ restPort = 1317,
+ grpcPort = 9090,
+ evmRpcPort = 8545,
+ p2pPort = 26656,
+ dataDir = `./sei-data-${version}`,
+ chainId = 'sei-local',
+ moniker = 'sei-local-node'
+ } = options;
+
+ try {
+ // Check if version exists
+ if (version !== 'latest') {
+ const exists = await checkSeiVersionExists(version);
+ if (!exists) {
+ throw new Error(`Version ${version} not found in registry`);
+ }
+ }
+
+ // Stop existing container if it exists
+ try {
+ const existingContainer = docker.getContainer(containerName);
+ await existingContainer.stop();
+ await existingContainer.remove();
+ } catch (error) {
+ // Ignore errors when stopping/removing non-existent containers
+ }
+
+ // Pull the image
+ const imageTag = `ghcr.io/sei-protocol/sei:${version}`;
+
+ await new Promise((resolve, reject) => {
+ docker.pull(imageTag, (err: Error | null, stream: NodeJS.ReadableStream) => {
+ if (err) {
+ reject(err);
+ return;
+ }
+
+ docker.modem.followProgress(stream, (err: Error | null) => {
+ if (err) {
+ reject(err);
+ } else {
+ resolve();
+ }
+ });
+ });
+ });
+
+ // Create and start the container using dockerode
+ const container = await docker.createContainer({
+ Image: imageTag,
+ name: containerName,
+ Cmd: [
+ 'seid', 'start',
+ '--chain-id', chainId,
+ '--moniker', moniker,
+ '--rpc.laddr', 'tcp://0.0.0.0:26657',
+ '--api.enable', 'true',
+ '--api.address', 'tcp://0.0.0.0:1317',
+ '--grpc.address', '0.0.0.0:9090',
+ '--evm-rpc.address', '0.0.0.0:8545'
+ ],
+ ExposedPorts: {
+ '26657/tcp': {},
+ '1317/tcp': {},
+ '9090/tcp': {},
+ '8545/tcp': {},
+ '26656/tcp': {}
+ },
+ HostConfig: {
+ PortBindings: {
+ '26657/tcp': [{ HostPort: rpcPort.toString() }],
+ '1317/tcp': [{ HostPort: restPort.toString() }],
+ '9090/tcp': [{ HostPort: grpcPort.toString() }],
+ '8545/tcp': [{ HostPort: evmRpcPort.toString() }],
+ '26656/tcp': [{ HostPort: p2pPort.toString() }]
+ },
+ Binds: [`${dataDir}:/root/.sei`]
+ }
+ });
+
+ await container.start();
+ const containerInfo = await container.inspect();
+ const containerId = containerInfo.Id;
+
+ return {
+ containerId,
+ containerName,
+ ports: {
+ rpc: rpcPort,
+ rest: restPort,
+ grpc: grpcPort,
+ evmRpc: evmRpcPort,
+ p2p: p2pPort
+ }
+ };
+ } catch (error) {
+ throw new Error(`Failed to start Sei chain: ${error instanceof Error ? error.message : String(error)}`);
+ }
+}
+
+/**
+ * Stop a Sei chain Docker container
+ */
+export async function stopSeiChain(containerName: string): Promise {
+ try {
+ const container = docker.getContainer(containerName);
+ await container.stop();
+ await container.remove();
+ } catch (error) {
+ throw new Error(`Failed to stop Sei chain: ${error instanceof Error ? error.message : String(error)}`);
+ }
+}
+
+/**
+ * Get status of running Sei chain containers
+ */
+export async function getSeiChainStatus(): Promise {
+ try {
+ const containers = await docker.listContainers({ all: true });
+
+ // Filter for Sei chain containers
+ const seiContainers = containers.filter(container =>
+ container.Image.includes('ghcr.io/sei-protocol/sei') ||
+ container.Names.some(name => name.includes('sei-chain'))
+ );
+
+ return seiContainers.map(container => ({
+ id: container.Id.substring(0, 12),
+ name: container.Names[0]?.replace('/', '') || '',
+ image: container.Image,
+ status: container.Status,
+ ports: container.Ports.map(port =>
+ port.PublicPort ? `${port.PublicPort}:${port.PrivatePort}` : `${port.PrivatePort}`
+ ).join(', '),
+ created: new Date(container.Created * 1000).toISOString()
+ }));
+ } catch (error) {
+ throw new Error(`Failed to get Sei chain status: ${error instanceof Error ? error.message : String(error)}`);
+ }
+}
+
+/**
+ * Get logs from a Sei chain container
+ */
+export async function getSeiChainLogs(containerName: string, lines = 100): Promise {
+ try {
+ const container = docker.getContainer(containerName);
+ const logStream = await container.logs({
+ stdout: true,
+ stderr: true,
+ tail: lines,
+ follow: false
+ });
+
+ return logStream.toString();
+ } catch (error) {
+ throw new Error(`Failed to get Sei chain logs: ${error instanceof Error ? error.message : String(error)}`);
+ }
+}
diff --git a/packages/mcp-server/src/docker/index.ts b/packages/mcp-server/src/docker/index.ts
new file mode 100644
index 000000000..99512117e
--- /dev/null
+++ b/packages/mcp-server/src/docker/index.ts
@@ -0,0 +1,2 @@
+// Export Docker chain management functionality
+export { registerDockerTools } from './tools.js';
diff --git a/packages/mcp-server/src/docker/initialize.ts b/packages/mcp-server/src/docker/initialize.ts
new file mode 100644
index 000000000..823d0f4f6
--- /dev/null
+++ b/packages/mcp-server/src/docker/initialize.ts
@@ -0,0 +1,86 @@
+import type Docker from 'dockerode';
+
+/**
+ * Creates and starts a Sei blockchain Docker container with comprehensive initialization
+ */
+export async function createAndStartSeiContainer(
+ docker: Docker,
+ imageTag: string,
+ finalContainerName: string,
+ rpcPort: number,
+ restPort: number,
+ evmRpcPort: number,
+ grpcPort: number
+) {
+ // Pull the Docker image if it doesn't exist
+ await new Promise((resolve, reject) => {
+ docker.pull(imageTag, (err: Error | null, stream: NodeJS.ReadableStream) => {
+ if (err) {
+ reject(err);
+ return;
+ }
+
+ docker.modem.followProgress(stream, (err: Error | null) => {
+ if (err) {
+ reject(err);
+ } else {
+ resolve();
+ }
+ });
+ });
+ });
+
+ // Create and start the container with comprehensive initialization
+ const container = await docker.createContainer({
+ Image: imageTag,
+ name: finalContainerName,
+ Cmd: [
+ 'sh',
+ '-c',
+ `# Check if blockchain is already initialized
+if [ ! -f ~/.sei/config/genesis.json ]; then
+ echo "Initializing new Sei blockchain..."
+ seid init demo --chain-id sei-chain && \
+ seid keys add admin --keyring-backend test && \
+ seid add-genesis-account $(seid keys show admin -a --keyring-backend test) 100000000000000000000usei,100000000000000000000uusdc,100000000000000000000uatom --keyring-backend test && \
+ seid gentx admin 7000000000000000usei --chain-id sei-chain --keyring-backend test && \
+ seid collect-gentxs && \
+ sed -i 's/"max_deposit_period": "172800s"/"max_deposit_period": "60s"/g' ~/.sei/config/genesis.json && \
+ sed -i 's/"voting_period": "172800s"/"voting_period": "30s"/g' ~/.sei/config/genesis.json && \
+ sed -i 's/"expedited_voting_period": "86400s"/"expedited_voting_period": "10s"/g' ~/.sei/config/genesis.json && \
+ sed -i 's/"vote_period": "5"/"vote_period": "2"/g' ~/.sei/config/genesis.json && \
+ sed -i 's/"community_tax": "0.020000000000000000"/"community_tax": "0.000000000000000000"/g' ~/.sei/config/genesis.json && \
+ sed -i 's/"max_gas": "-1"/"max_gas": "35000000"/g' ~/.sei/config/genesis.json && \
+ seid config keyring-backend test
+ echo "Blockchain initialization complete."
+else
+ echo "Blockchain already initialized, skipping setup..."
+ seid config keyring-backend test
+fi
+echo "Starting Sei blockchain..."
+seid start --chain-id sei-chain`
+ ],
+ ExposedPorts: {
+ '26657/tcp': {},
+ '1317/tcp': {},
+ '8545/tcp': {},
+ '9090/tcp': {}
+ },
+ HostConfig: {
+ PortBindings: {
+ '26657/tcp': [{ HostPort: rpcPort.toString() }],
+ '1317/tcp': [{ HostPort: restPort.toString() }],
+ '8545/tcp': [{ HostPort: evmRpcPort.toString() }],
+ '9090/tcp': [{ HostPort: grpcPort.toString() }]
+ }
+ }
+ });
+
+ await container.start();
+ const containerInfo = await container.inspect();
+
+ return {
+ container,
+ containerId: containerInfo.Id
+ };
+}
diff --git a/packages/mcp-server/src/docker/releases.ts b/packages/mcp-server/src/docker/releases.ts
new file mode 100644
index 000000000..2bc7c9264
--- /dev/null
+++ b/packages/mcp-server/src/docker/releases.ts
@@ -0,0 +1,41 @@
+/**
+ * Sei Chain Release Management
+ *
+ * This module provides functionality to fetch available Sei chain releases
+ * from the GitHub repository.
+ */
+
+export interface SeiRelease {
+ tag: string;
+ name: string;
+ published_at: string;
+ prerelease: boolean;
+ draft: boolean;
+ html_url: string;
+ tarball_url: string;
+ zipball_url: string;
+}
+
+/**
+ * Get available Sei chain releases from GitHub
+ * Uses the GitHub API to fetch release information
+ */
+export async function getSeiReleases(): Promise {
+ try {
+ const response = await fetch('https://api.github.com/repos/sei-protocol/sei-chain/releases');
+
+ if (!response.ok) {
+ throw new Error(`GitHub API error: ${response.status} ${response.statusText}`);
+ }
+
+ const releases = (await response.json()) as SeiRelease[];
+
+ // Sort by published date (newest first)
+ return releases.sort((a, b) => new Date(b.published_at).getTime() - new Date(a.published_at).getTime());
+ } catch (error) {
+ // No fallback versions are provided; no releases will be available if API fails
+ console.warn('Failed to fetch releases from GitHub API; no releases will be available:', error);
+
+ return [];
+ }
+}
diff --git a/packages/mcp-server/src/docker/tools.ts b/packages/mcp-server/src/docker/tools.ts
new file mode 100644
index 000000000..02fd907e6
--- /dev/null
+++ b/packages/mcp-server/src/docker/tools.ts
@@ -0,0 +1,678 @@
+import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
+import { z } from 'zod';
+import Docker from 'dockerode';
+import * as releases from './releases.js';
+import { createAndStartSeiContainer } from './initialize.js';
+
+const docker = new Docker();
+
+/**
+ * Register Sei chain release tools with the MCP server
+ * Only registers tools if SEI_DOCKER_ENABLED environment variable is set to 'true'
+ *
+ * @param server The MCP server instance
+ */
+export function registerDockerTools(server: McpServer) {
+ // Check if Docker tools are enabled via environment variable
+ if (process.env.SEI_DOCKER_ENABLED !== 'true') {
+ return;
+ }
+ // Get available Sei chain releases
+ server.tool(
+ 'get_sei_releases',
+ 'Get a list of available Sei chain releases from GitHub. Returns official releases with metadata like publication date and download links.',
+ {},
+ async () => {
+ try {
+ const seiReleases = await releases.getSeiReleases();
+
+ return {
+ content: [
+ {
+ type: 'text',
+ text: JSON.stringify(
+ {
+ releases: seiReleases,
+ totalReleases: seiReleases.length,
+ latestRelease: seiReleases[0]?.tag || 'unknown',
+ repository: 'https://github.com/sei-protocol/sei-chain'
+ },
+ null,
+ 2
+ )
+ }
+ ]
+ };
+ } catch (error) {
+ return {
+ content: [
+ {
+ type: 'text',
+ text: `Error fetching Sei releases: ${error instanceof Error ? error.message : String(error)}`
+ }
+ ],
+ isError: true
+ };
+ }
+ }
+ );
+
+ // Start Sei chain container
+ server.tool(
+ 'start_sei_chain',
+ 'Start a Sei chain Docker container for a specific version. This will pull the image if needed and start a local Sei blockchain node.',
+ {
+ version: z.string().optional().describe("The Sei version to run (e.g., 'v6.1.0', 'latest'). Defaults to 'latest'"),
+ containerName: z.string().optional().describe("Custom name for the Docker container. Defaults to 'sei-chain-{version}'"),
+ rpcPort: z.number().optional().describe('Port for RPC endpoint (default: 26657)'),
+ restPort: z.number().optional().describe('Port for REST API endpoint (default: 1317)'),
+ evmRpcPort: z.number().optional().describe('Port for EVM RPC endpoint (default: 8545)'),
+ grpcPort: z.number().optional().describe('Port for gRPC endpoint (default: 9090)')
+ },
+ async ({ version = 'latest', containerName, rpcPort = 26657, restPort = 1317, evmRpcPort = 8545, grpcPort = 9090 }) => {
+ try {
+ // Generate container name if not provided
+ const finalContainerName = containerName || `sei-chain-${version.replace(/[^a-zA-Z0-9]/g, '-')}`;
+
+ // Stop and remove existing container if it exists
+ try {
+ const existingContainer = docker.getContainer(finalContainerName);
+ await existingContainer.stop();
+ await existingContainer.remove();
+ } catch (error) {
+ // Ignore errors when stopping/removing non-existent containers
+ }
+
+ // Pull the image and create/start the container
+ const imageTag = `ghcr.io/sei-protocol/sei:${version}`;
+ const { container, containerId } = await createAndStartSeiContainer(
+ docker,
+ imageTag,
+ finalContainerName,
+ rpcPort,
+ restPort,
+ evmRpcPort,
+ grpcPort
+ );
+
+ return {
+ content: [
+ {
+ type: 'text',
+ text: JSON.stringify(
+ {
+ success: true,
+ version,
+ containerId,
+ containerName: finalContainerName,
+ ports: {
+ rpc: rpcPort,
+ rest: restPort,
+ evmRpc: evmRpcPort,
+ grpc: grpcPort
+ },
+ endpoints: {
+ rpc: `http://localhost:${rpcPort}`,
+ rest: `http://localhost:${restPort}`,
+ evmRpc: `http://localhost:${evmRpcPort}`,
+ grpc: `http://localhost:${grpcPort}`
+ },
+ dockerImage: imageTag,
+ message: 'Sei chain started successfully'
+ },
+ null,
+ 2
+ )
+ }
+ ]
+ };
+ } catch (error) {
+ return {
+ content: [
+ {
+ type: 'text',
+ text: `Error starting Sei chain: ${error instanceof Error ? error.message : String(error)}`
+ }
+ ],
+ isError: true
+ };
+ }
+ }
+ );
+
+ // Get running Sei chain containers
+ server.tool(
+ 'get_running_chains',
+ 'Get a list of currently running Sei chain Docker containers. Shows container names, status, ports, and runtime information.',
+ {},
+ async () => {
+ try {
+ const containers = await docker.listContainers({ all: false }); // Only running containers
+
+ // Filter for Sei chain containers
+ const seiContainers = containers.filter(container =>
+ container.Names.some(name => name.includes('sei-chain')) ||
+ container.Image.includes('sei-protocol/sei') ||
+ container.Image.includes('ghcr.io/sei-protocol/sei')
+ );
+
+ const containerDetails = await Promise.all(
+ seiContainers.map(async (container) => {
+ try {
+ const containerObj = docker.getContainer(container.Id);
+ const inspectData = await containerObj.inspect();
+
+ // Extract port mappings
+ const portMappings: Record = {};
+ if (inspectData.NetworkSettings.Ports) {
+ for (const [containerPort, hostBindings] of Object.entries(inspectData.NetworkSettings.Ports)) {
+ if (hostBindings && hostBindings.length > 0) {
+ const hostPort = hostBindings[0].HostPort;
+ const portName = containerPort.replace('/tcp', '');
+
+ // Map common Sei ports to their purposes
+ if (portName === '26657') portMappings.rpc = `http://localhost:${hostPort}`;
+ else if (portName === '1317') portMappings.rest = `http://localhost:${hostPort}`;
+ else if (portName === '8545') portMappings.evmRpc = `http://localhost:${hostPort}`;
+ else portMappings[portName] = `http://localhost:${hostPort}`;
+ }
+ }
+ }
+
+ return {
+ id: container.Id.substring(0, 12),
+ name: container.Names[0].replace('/', ''),
+ image: container.Image,
+ status: container.Status,
+ state: container.State,
+ created: new Date(container.Created * 1000).toISOString(),
+ ports: portMappings,
+ uptime: inspectData.State.StartedAt ?
+ Math.floor((Date.now() - new Date(inspectData.State.StartedAt).getTime()) / 1000) : 0
+ };
+ } catch (error) {
+ return {
+ id: container.Id.substring(0, 12),
+ name: container.Names[0].replace('/', ''),
+ image: container.Image,
+ status: container.Status,
+ state: container.State,
+ created: new Date(container.Created * 1000).toISOString(),
+ ports: {},
+ uptime: 0,
+ error: error instanceof Error ? error.message : String(error)
+ };
+ }
+ })
+ );
+
+ return {
+ content: [
+ {
+ type: 'text',
+ text: JSON.stringify({
+ success: true,
+ totalContainers: containerDetails.length,
+ containers: containerDetails,
+ message: containerDetails.length > 0
+ ? `Found ${containerDetails.length} running Sei chain container(s)`
+ : 'No running Sei chain containers found'
+ }, null, 2)
+ }
+ ]
+ };
+ } catch (error) {
+ return {
+ content: [
+ {
+ type: 'text',
+ text: `Error getting running chains: ${error instanceof Error ? error.message : String(error)}`
+ }
+ ],
+ isError: true
+ };
+ }
+ }
+ );
+
+ // Restart Sei chain container
+ server.tool(
+ 'restart_sei_container',
+ 'Restart a stopped Sei chain Docker container. This will start an existing container without re-initializing the blockchain data.',
+ {
+ containerName: z.string().optional().describe("Name of the container to restart. If not provided, will show all available stopped Sei containers")
+ },
+ async ({ containerName }) => {
+ try {
+ const containers = await docker.listContainers({ all: true });
+ let targetContainers: Array<{ id: string; name: string; status: string; state: string }> = [];
+
+ if (containerName) {
+ // Restart specific container
+ const container = containers.find(c =>
+ c.Names.some(name => name.replace('/', '') === containerName)
+ );
+
+ if (!container) {
+ return {
+ content: [
+ {
+ type: 'text',
+ text: JSON.stringify({
+ success: false,
+ message: `Container '${containerName}' not found`
+ }, null, 2)
+ }
+ ]
+ };
+ }
+
+ targetContainers = [{
+ id: container.Id,
+ name: containerName,
+ status: container.Status,
+ state: container.State
+ }];
+ } else {
+ // Find all stopped Sei chain containers
+ const seiContainers = containers.filter(container =>
+ (container.Names.some(name => name.includes('sei-chain')) ||
+ container.Image.includes('sei-protocol/sei') ||
+ container.Image.includes('ghcr.io/sei-protocol/sei')) &&
+ container.State !== 'running'
+ );
+
+ if (seiContainers.length === 0) {
+ return {
+ content: [
+ {
+ type: 'text',
+ text: JSON.stringify({
+ success: false,
+ message: 'No stopped Sei chain containers found to restart'
+ }, null, 2)
+ }
+ ]
+ };
+ }
+
+ targetContainers = seiContainers.map(container => ({
+ id: container.Id,
+ name: container.Names[0].replace('/', ''),
+ status: container.Status,
+ state: container.State
+ }));
+ }
+
+ const results: Array<{
+ container: string;
+ restarted: boolean;
+ status: 'success' | 'error';
+ error?: string;
+ previousState?: string;
+ ports?: Record;
+ }> = [];
+
+ for (const containerInfo of targetContainers) {
+ try {
+ const container = docker.getContainer(containerInfo.id);
+ const inspectData = await container.inspect();
+
+ // Check if container is already running
+ if (inspectData.State.Running) {
+ results.push({
+ container: containerInfo.name,
+ restarted: false,
+ status: 'error',
+ error: 'Container is already running',
+ previousState: containerInfo.state
+ });
+ continue;
+ }
+
+ // Start the container
+ await container.start();
+
+ // Get updated container info including port mappings
+ const updatedInspectData = await container.inspect();
+ const portMappings: Record = {};
+
+ if (updatedInspectData.NetworkSettings.Ports) {
+ for (const [containerPort, hostBindings] of Object.entries(updatedInspectData.NetworkSettings.Ports)) {
+ if (hostBindings && hostBindings.length > 0) {
+ const hostPort = hostBindings[0].HostPort;
+ const portName = containerPort.replace('/tcp', '');
+
+ // Map common Sei ports to their purposes
+ if (portName === '26657') portMappings.rpc = `http://localhost:${hostPort}`;
+ else if (portName === '1317') portMappings.rest = `http://localhost:${hostPort}`;
+ else if (portName === '8545') portMappings.evmRpc = `http://localhost:${hostPort}`;
+ else if (portName === '9090') portMappings.grpc = `http://localhost:${hostPort}`;
+ else portMappings[portName] = `http://localhost:${hostPort}`;
+ }
+ }
+ }
+
+ results.push({
+ container: containerInfo.name,
+ restarted: true,
+ status: 'success',
+ previousState: containerInfo.state,
+ ports: portMappings
+ });
+ } catch (error) {
+ results.push({
+ container: containerInfo.name,
+ restarted: false,
+ status: 'error',
+ error: error instanceof Error ? error.message : String(error),
+ previousState: containerInfo.state
+ });
+ }
+ }
+
+ const successCount = results.filter(r => r.status === 'success').length;
+ const errorCount = results.filter(r => r.status === 'error').length;
+
+ return {
+ content: [
+ {
+ type: 'text',
+ text: JSON.stringify({
+ success: errorCount === 0,
+ message: `Processed ${results.length} container(s): ${successCount} restarted, ${errorCount} failed`,
+ containers: results,
+ summary: {
+ total: results.length,
+ restarted: successCount,
+ failed: errorCount
+ }
+ }, null, 2)
+ }
+ ]
+ };
+ } catch (error) {
+ return {
+ content: [
+ {
+ type: 'text',
+ text: `Error restarting containers: ${error instanceof Error ? error.message : String(error)}`
+ }
+ ],
+ isError: true
+ };
+ }
+ }
+ );
+
+ // Delete Sei chain container
+ server.tool(
+ 'delete_sei_container',
+ 'Delete (remove) one or more Sei chain Docker containers. This will permanently remove the container and its data. The container must be stopped first.',
+ {
+ containerName: z.string().optional().describe("Name of the specific container to delete. If not provided, will show all available Sei containers for selection"),
+ force: z.boolean().optional().describe('Force removal of running containers (stops them first, default: false)'),
+ removeVolumes: z.boolean().optional().describe('Remove associated volumes with the container (default: false)')
+ },
+ async ({ containerName, force = false, removeVolumes = false }) => {
+ try {
+ const containers = await docker.listContainers({ all: true });
+ let targetContainers: Array<{ id: string; name: string; status: string; state: string }> = [];
+
+ if (containerName) {
+ // Delete specific container
+ const container = containers.find(c =>
+ c.Names.some(name => name.replace('/', '') === containerName)
+ );
+
+ if (!container) {
+ return {
+ content: [
+ {
+ type: 'text',
+ text: JSON.stringify({
+ success: false,
+ message: `Container '${containerName}' not found`
+ }, null, 2)
+ }
+ ]
+ };
+ }
+
+ targetContainers = [{
+ id: container.Id,
+ name: containerName,
+ status: container.Status,
+ state: container.State
+ }];
+ } else {
+ // Find all sei-chain containers
+ const seiContainers = containers.filter(container =>
+ container.Names.some(name => name.includes('sei-chain')) ||
+ container.Image.includes('sei-protocol/sei') ||
+ container.Image.includes('ghcr.io/sei-protocol/sei')
+ );
+
+ if (seiContainers.length === 0) {
+ return {
+ content: [
+ {
+ type: 'text',
+ text: JSON.stringify({
+ success: false,
+ message: 'No Sei chain containers found to delete'
+ }, null, 2)
+ }
+ ]
+ };
+ }
+
+ targetContainers = seiContainers.map(container => ({
+ id: container.Id,
+ name: container.Names[0].replace('/', ''),
+ status: container.Status,
+ state: container.State
+ }));
+ }
+
+ const results: Array<{
+ container: string;
+ deleted: boolean;
+ status: 'success' | 'error';
+ error?: string;
+ volumesRemoved?: boolean;
+ wasRunning?: boolean;
+ }> = [];
+ for (const containerInfo of targetContainers) {
+ try {
+ const container = docker.getContainer(containerInfo.id);
+ const inspectData = await container.inspect();
+
+ // Stop container if it's running and force is enabled
+ if (inspectData.State.Running) {
+ if (force) {
+ await container.stop();
+ } else {
+ results.push({
+ container: containerInfo.name,
+ deleted: false,
+ status: 'error',
+ error: 'Container is running. Use force=true to stop and remove, or stop it first.'
+ });
+ continue;
+ }
+ }
+
+ // Remove the container
+ await container.remove({
+ v: removeVolumes, // Remove volumes if requested
+ force: force
+ });
+
+ results.push({
+ container: containerInfo.name,
+ deleted: true,
+ status: 'success',
+ volumesRemoved: removeVolumes,
+ wasRunning: inspectData.State.Running
+ });
+ } catch (error) {
+ results.push({
+ container: containerInfo.name,
+ deleted: false,
+ status: 'error',
+ error: error instanceof Error ? error.message : String(error)
+ });
+ }
+ }
+
+ const successCount = results.filter(r => r.status === 'success').length;
+ const errorCount = results.filter(r => r.status === 'error').length;
+
+ return {
+ content: [
+ {
+ type: 'text',
+ text: JSON.stringify({
+ success: errorCount === 0,
+ message: `Processed ${results.length} container(s): ${successCount} deleted, ${errorCount} failed`,
+ containers: results,
+ summary: {
+ total: results.length,
+ deleted: successCount,
+ failed: errorCount,
+ options: {
+ force,
+ removeVolumes
+ }
+ }
+ }, null, 2)
+ }
+ ]
+ };
+ } catch (error) {
+ return {
+ content: [
+ {
+ type: 'text',
+ text: `Error deleting containers: ${error instanceof Error ? error.message : String(error)}`
+ }
+ ],
+ isError: true
+ };
+ }
+ }
+ );
+
+ // Stop Sei chain container
+ server.tool(
+ 'stop_sei_chain',
+ 'Stop and remove a running Sei chain Docker container. This will gracefully stop the blockchain node and clean up the container.',
+ {
+ containerName: z.string().optional().describe("Name of the container to stop. If not provided, will attempt to stop containers matching 'sei-chain-*' pattern"),
+ removeContainer: z.boolean().optional().describe('Whether to remove the container after stopping (default: false)')
+ },
+ async ({ containerName, removeContainer = false }) => {
+ try {
+ const containers = await docker.listContainers({ all: true });
+ let targetContainers: string[] = [];
+
+ if (containerName) {
+ // Stop specific container
+ targetContainers = [containerName];
+ } else {
+ // Find all sei-chain containers
+ targetContainers = containers
+ .filter(container =>
+ container.Names.some(name => name.includes('sei-chain'))
+ )
+ .map(container => container.Names[0].replace('/', ''));
+ }
+
+ if (targetContainers.length === 0) {
+ return {
+ content: [
+ {
+ type: 'text',
+ text: JSON.stringify({
+ success: false,
+ message: containerName
+ ? `Container '${containerName}' not found`
+ : 'No Sei chain containers found'
+ }, null, 2)
+ }
+ ]
+ };
+ }
+
+ const results: Array<{
+ container: string;
+ stopped: boolean;
+ removed: boolean;
+ status: 'success' | 'error';
+ error?: string;
+ }> = [];
+ for (const name of targetContainers) {
+ try {
+ const container = docker.getContainer(name);
+ const containerInfo = await container.inspect();
+
+ if (containerInfo.State.Running) {
+ await container.stop();
+ }
+
+ if (removeContainer) {
+ await container.remove();
+ }
+
+ results.push({
+ container: name,
+ stopped: true,
+ removed: removeContainer,
+ status: 'success'
+ });
+ } catch (error) {
+ results.push({
+ container: name,
+ stopped: false,
+ removed: false,
+ status: 'error',
+ error: error instanceof Error ? error.message : String(error)
+ });
+ }
+ }
+
+ const successCount = results.filter(r => r.status === 'success').length;
+ const errorCount = results.filter(r => r.status === 'error').length;
+
+ return {
+ content: [
+ {
+ type: 'text',
+ text: JSON.stringify({
+ success: errorCount === 0,
+ message: `Processed ${results.length} container(s): ${successCount} successful, ${errorCount} failed`,
+ containers: results,
+ summary: {
+ total: results.length,
+ successful: successCount,
+ failed: errorCount
+ }
+ }, null, 2)
+ }
+ ]
+ };
+ } catch (error) {
+ return {
+ content: [
+ {
+ type: 'text',
+ text: `Error stopping Sei chain: ${error instanceof Error ? error.message : String(error)}`
+ }
+ ],
+ isError: true
+ };
+ }
+ }
+ );
+}
diff --git a/packages/mcp-server/src/server/server.ts b/packages/mcp-server/src/server/server.ts
index de6c0f0d1..72d491a6b 100644
--- a/packages/mcp-server/src/server/server.ts
+++ b/packages/mcp-server/src/server/server.ts
@@ -6,6 +6,7 @@ import { createSeiJSDocsSearchTool } from '../mintlify/search.js';
import { getPackageInfo } from './package-info.js';
import { getSupportedNetworks } from '../core/chains.js';
import { createDocsSearchTool } from '../docs/index.js';
+import { registerDockerTools } from '../docker/index.js';
export const getServer = async () => {
try {
@@ -18,6 +19,7 @@ export const getServer = async () => {
registerEVMResources(server);
registerEVMTools(server);
registerEVMPrompts(server);
+ registerDockerTools(server);
await createSeiJSDocsSearchTool(server);
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0e5523b7a..7c47d9d1e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -14,6 +14,12 @@ importers:
'@changesets/cli':
specifier: ^2.28.1
version: 2.29.6(@types/node@22.18.0)
+ optional:
+ specifier: ^0.1.4
+ version: 0.1.4
+ sharp:
+ specifier: ^0.34.3
+ version: 0.34.3
devDependencies:
'@types/jest':
specifier: ^29.5.14
@@ -112,6 +118,9 @@ importers:
cors:
specifier: ^2.8.5
version: 2.8.5
+ dockerode:
+ specifier: ^4.0.2
+ version: 4.0.7
dotenv:
specifier: ^16.5.0
version: 16.6.1
@@ -140,6 +149,9 @@ importers:
'@types/cors':
specifier: ^2.8.17
version: 2.8.19
+ '@types/dockerode':
+ specifier: ^3.3.31
+ version: 3.3.43
'@types/express':
specifier: ^5.0.0
version: 5.0.3
@@ -387,6 +399,9 @@ packages:
resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==}
engines: {node: '>=6.9.0'}
+ '@balena/dockerignore@1.0.2':
+ resolution: {integrity: sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==}
+
'@bcoe/v8-coverage@0.2.3':
resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
@@ -844,6 +859,15 @@ packages:
'@ethersproject/web@5.8.0':
resolution: {integrity: sha512-j7+Ksi/9KfGviws6Qtf9Q7KCqRhpwrYKQPs+JBA/rKVFF/yaWLHJEH3zfVP2plVu+eys0d2DlFmhoQJayFewcw==}
+ '@grpc/grpc-js@1.13.4':
+ resolution: {integrity: sha512-GsFaMXCkMqkKIvwCQjCrwH+GHbPKBjhwo/8ZuUkWHqbI73Kky9I+pQltrlT0+MWpedCoosda53lgjYfyEPgxBg==}
+ engines: {node: '>=12.10.0'}
+
+ '@grpc/proto-loader@0.7.15':
+ resolution: {integrity: sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==}
+ engines: {node: '>=6'}
+ hasBin: true
+
'@hexagon/base64@1.1.28':
resolution: {integrity: sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw==}
@@ -853,105 +877,227 @@ packages:
cpu: [arm64]
os: [darwin]
+ '@img/sharp-darwin-arm64@0.34.3':
+ resolution: {integrity: sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [darwin]
+
'@img/sharp-darwin-x64@0.33.5':
resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [darwin]
+ '@img/sharp-darwin-x64@0.34.3':
+ resolution: {integrity: sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [darwin]
+
'@img/sharp-libvips-darwin-arm64@1.0.4':
resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==}
cpu: [arm64]
os: [darwin]
+ '@img/sharp-libvips-darwin-arm64@1.2.0':
+ resolution: {integrity: sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==}
+ cpu: [arm64]
+ os: [darwin]
+
'@img/sharp-libvips-darwin-x64@1.0.4':
resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==}
cpu: [x64]
os: [darwin]
+ '@img/sharp-libvips-darwin-x64@1.2.0':
+ resolution: {integrity: sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==}
+ cpu: [x64]
+ os: [darwin]
+
'@img/sharp-libvips-linux-arm64@1.0.4':
resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==}
cpu: [arm64]
os: [linux]
+ '@img/sharp-libvips-linux-arm64@1.2.0':
+ resolution: {integrity: sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==}
+ cpu: [arm64]
+ os: [linux]
+
'@img/sharp-libvips-linux-arm@1.0.5':
resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==}
cpu: [arm]
os: [linux]
+ '@img/sharp-libvips-linux-arm@1.2.0':
+ resolution: {integrity: sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==}
+ cpu: [arm]
+ os: [linux]
+
+ '@img/sharp-libvips-linux-ppc64@1.2.0':
+ resolution: {integrity: sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==}
+ cpu: [ppc64]
+ os: [linux]
+
'@img/sharp-libvips-linux-s390x@1.0.4':
resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==}
cpu: [s390x]
os: [linux]
+ '@img/sharp-libvips-linux-s390x@1.2.0':
+ resolution: {integrity: sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==}
+ cpu: [s390x]
+ os: [linux]
+
'@img/sharp-libvips-linux-x64@1.0.4':
resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==}
cpu: [x64]
os: [linux]
+ '@img/sharp-libvips-linux-x64@1.2.0':
+ resolution: {integrity: sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==}
+ cpu: [x64]
+ os: [linux]
+
'@img/sharp-libvips-linuxmusl-arm64@1.0.4':
resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==}
cpu: [arm64]
os: [linux]
+ '@img/sharp-libvips-linuxmusl-arm64@1.2.0':
+ resolution: {integrity: sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==}
+ cpu: [arm64]
+ os: [linux]
+
'@img/sharp-libvips-linuxmusl-x64@1.0.4':
resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==}
cpu: [x64]
os: [linux]
+ '@img/sharp-libvips-linuxmusl-x64@1.2.0':
+ resolution: {integrity: sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==}
+ cpu: [x64]
+ os: [linux]
+
'@img/sharp-linux-arm64@0.33.5':
resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
+ '@img/sharp-linux-arm64@0.34.3':
+ resolution: {integrity: sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [linux]
+
'@img/sharp-linux-arm@0.33.5':
resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm]
os: [linux]
+ '@img/sharp-linux-arm@0.34.3':
+ resolution: {integrity: sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm]
+ os: [linux]
+
+ '@img/sharp-linux-ppc64@0.34.3':
+ resolution: {integrity: sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [ppc64]
+ os: [linux]
+
'@img/sharp-linux-s390x@0.33.5':
resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [s390x]
os: [linux]
+ '@img/sharp-linux-s390x@0.34.3':
+ resolution: {integrity: sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [s390x]
+ os: [linux]
+
'@img/sharp-linux-x64@0.33.5':
resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
+ '@img/sharp-linux-x64@0.34.3':
+ resolution: {integrity: sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [linux]
+
'@img/sharp-linuxmusl-arm64@0.33.5':
resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [arm64]
os: [linux]
+ '@img/sharp-linuxmusl-arm64@0.34.3':
+ resolution: {integrity: sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [linux]
+
'@img/sharp-linuxmusl-x64@0.33.5':
resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [linux]
+ '@img/sharp-linuxmusl-x64@0.34.3':
+ resolution: {integrity: sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [linux]
+
'@img/sharp-wasm32@0.33.5':
resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [wasm32]
+ '@img/sharp-wasm32@0.34.3':
+ resolution: {integrity: sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [wasm32]
+
+ '@img/sharp-win32-arm64@0.34.3':
+ resolution: {integrity: sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [win32]
+
'@img/sharp-win32-ia32@0.33.5':
resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [ia32]
os: [win32]
+ '@img/sharp-win32-ia32@0.34.3':
+ resolution: {integrity: sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [ia32]
+ os: [win32]
+
'@img/sharp-win32-x64@0.33.5':
resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
cpu: [x64]
os: [win32]
+ '@img/sharp-win32-x64@0.34.3':
+ resolution: {integrity: sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [win32]
+
'@inquirer/checkbox@4.2.2':
resolution: {integrity: sha512-E+KExNurKcUJJdxmjglTl141EwxWyAHplvsYJQgSwXf8qiNWkTxTuCCqmhFEmbIXd4zLaGMfQFJ6WrZ7fSeV3g==}
engines: {node: '>=18'}
@@ -1258,6 +1404,9 @@ packages:
'@jridgewell/trace-mapping@0.3.9':
resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==}
+ '@js-sdsl/ordered-map@4.4.2':
+ resolution: {integrity: sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==}
+
'@jsep-plugin/assignment@1.3.0':
resolution: {integrity: sha512-VVgV+CXrhbMI3aSusQyclHkenWSAm95WaiKrMxRFam3JSUiIaQjoMIw2sEs/OX4XifnqeQUN4DYbJjlA8EfktQ==}
engines: {node: '>= 10.16.0'}
@@ -1704,6 +1853,12 @@ packages:
'@types/debug@4.1.12':
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
+ '@types/docker-modem@3.0.6':
+ resolution: {integrity: sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg==}
+
+ '@types/dockerode@3.3.43':
+ resolution: {integrity: sha512-YCi0aKKpKeC9dhKTbuglvsWDnAyuIITd6CCJSTKiAdbDzPH4RWu0P9IK2XkJHdyplH6mzYtDYO+gB06JlzcPxg==}
+
'@types/es-aggregate-error@1.0.6':
resolution: {integrity: sha512-qJ7LIFp06h1QE1aVxbVd+zJP2wdaugYXYfd6JxsyRMrYHaxb6itXPogW2tz+ylUJ1n1b+JF1PHyYCfYHm0dvUg==}
@@ -1770,6 +1925,9 @@ packages:
'@types/node@12.20.55':
resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==}
+ '@types/node@18.19.124':
+ resolution: {integrity: sha512-hY4YWZFLs3ku6D2Gqo3RchTd9VRCcrjqp/I0mmohYeUVA5Y8eCXKJEasHxLAJVZRJuQogfd1GiJ9lgogBgKeuQ==}
+
'@types/node@20.19.12':
resolution: {integrity: sha512-lSOjyS6vdO2G2g2CWrETTV3Jz2zlCXHpu1rcubLKpz9oj+z/1CceHlj+yq53W+9zgb98nSov/wjEKYDNauD+Hw==}
@@ -1794,6 +1952,9 @@ packages:
'@types/serve-static@1.15.8':
resolution: {integrity: sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==}
+ '@types/ssh2@1.15.5':
+ resolution: {integrity: sha512-N1ASjp/nXH3ovBHddRJpli4ozpk6UdDYIX4RJWFa9L1YKnzdhTlVmiGHm4DZnj/jLbqZpes4aeR30EFGQtvhQQ==}
+
'@types/stack-utils@2.0.3':
resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==}
@@ -2139,6 +2300,9 @@ packages:
resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==}
engines: {node: '>=0.10.0'}
+ asn1@0.2.6:
+ resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==}
+
asn1js@3.0.6:
resolution: {integrity: sha512-UOCGPYbl0tv8+006qks/dTgV9ajs97X2p0FAbyS2iyCRrmLSRolDaHdp+v/CLgnzHc3fVB+CwYiUmei7ndFcgA==}
engines: {node: '>=12.0.0'}
@@ -2274,6 +2438,9 @@ packages:
resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==}
engines: {node: '>=10.0.0'}
+ bcrypt-pbkdf@1.0.2:
+ resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==}
+
bech32@1.1.4:
resolution: {integrity: sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==}
@@ -2366,6 +2533,10 @@ packages:
resolution: {integrity: sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==}
engines: {node: '>=6.14.2'}
+ buildcheck@0.0.6:
+ resolution: {integrity: sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A==}
+ engines: {node: '>=10.0.0'}
+
bytes@3.1.2:
resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
engines: {node: '>= 0.8'}
@@ -2627,6 +2798,10 @@ packages:
cosmjs-types@0.9.0:
resolution: {integrity: sha512-MN/yUe6mkJwHnCFfsNPeCfXVhyxHYW6c/xDUzrSbBycYzw++XvWDMJArXp2pLdgD6FQ8DW79vkPjeNKVrXaHeQ==}
+ cpu-features@0.0.10:
+ resolution: {integrity: sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==}
+ engines: {node: '>=10.0.0'}
+
create-jest@29.7.0:
resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -2815,6 +2990,14 @@ packages:
resolution: {integrity: sha512-BDeBd8najI4/lS00HSKpdFia+OvUMytaVjfzR9n5Lq8MlZRSvtbI+uLtx1+XmQFls5wFU9dssccTmQQ6nfpjdg==}
engines: {node: '>=6'}
+ docker-modem@5.0.6:
+ resolution: {integrity: sha512-ens7BiayssQz/uAxGzH8zGXCtiV24rRWXdjNha5V4zSOcxmAZsfGVm/PPFbwQdqEkDnhG+SyR9E3zSHUbOKXBQ==}
+ engines: {node: '>= 8.0'}
+
+ dockerode@4.0.7:
+ resolution: {integrity: sha512-R+rgrSRTRdU5mH14PZTCPZtW/zw3HDWNTS/1ZAQpL/5Upe/ye5K9WQkIysu4wBoiMwKynsz0a8qWuGsHgEvSAA==}
+ engines: {node: '>= 8.0'}
+
dotenv@16.6.1:
resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==}
engines: {node: '>=12'}
@@ -4149,6 +4332,9 @@ packages:
resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
engines: {node: '>=8'}
+ lodash.camelcase@4.3.0:
+ resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==}
+
lodash.memoize@4.1.2:
resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==}
@@ -4168,6 +4354,9 @@ packages:
long@4.0.0:
resolution: {integrity: sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==}
+ long@5.3.2:
+ resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==}
+
longest-streak@3.1.0:
resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==}
@@ -4527,6 +4716,9 @@ packages:
mz@2.7.0:
resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
+ nan@2.23.0:
+ resolution: {integrity: sha512-1UxuyYGdoQHcGg87Lkqm3FzefucTa0NAiOcuRsDmysep3c1LVCRK2krrUDafMWtjSG04htvAmvg96+SDknOmgQ==}
+
nanoid@3.3.11:
resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
@@ -4678,6 +4870,9 @@ packages:
openapi-types@12.1.3:
resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==}
+ optional@0.1.4:
+ resolution: {integrity: sha512-gtvrrCfkE08wKcgXaVwQVgwEQ8vel2dc5DDBn9RLQZ3YtmtkBss6A2HY6BnJH4N/4Ku97Ri/SF8sNWE2225WJw==}
+
ora@5.4.1:
resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==}
engines: {node: '>=10'}
@@ -4933,6 +5128,10 @@ packages:
resolution: {integrity: sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==}
hasBin: true
+ protobufjs@7.5.4:
+ resolution: {integrity: sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==}
+ engines: {node: '>=12.0.0'}
+
proxy-addr@2.0.7:
resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
engines: {node: '>= 0.10'}
@@ -5296,6 +5495,10 @@ packages:
resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==}
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ sharp@0.34.3:
+ resolution: {integrity: sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+
shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
@@ -5409,9 +5612,16 @@ packages:
spawndamnit@3.0.1:
resolution: {integrity: sha512-MmnduQUuHCoFckZoWnXsTg7JaiLBJrKFj9UI2MbRPGaJeVpsLcVBu6P/IGZovziM/YBsellCmsprgNA+w0CzVg==}
+ split-ca@1.0.1:
+ resolution: {integrity: sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==}
+
sprintf-js@1.0.3:
resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
+ ssh2@1.17.0:
+ resolution: {integrity: sha512-wPldCk3asibAjQ/kziWQQt1Wh3PgDFpC0XpwclzKcdT1vql6KeYxf5LIt4nlFkUeR8WuphYMKqUA56X4rjbfgQ==}
+ engines: {node: '>=10.16.0'}
+
stack-utils@2.0.6:
resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==}
engines: {node: '>=10'}
@@ -5702,6 +5912,9 @@ packages:
tunnel-agent@0.6.0:
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
+ tweetnacl@0.14.5:
+ resolution: {integrity: sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==}
+
type-detect@4.0.8:
resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==}
engines: {node: '>=4'}
@@ -5759,6 +5972,9 @@ packages:
unbzip2-stream@1.4.3:
resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==}
+ undici-types@5.26.5:
+ resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
+
undici-types@6.19.8:
resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==}
@@ -5873,6 +6089,10 @@ packages:
resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
engines: {node: '>= 0.4.0'}
+ uuid@10.0.0:
+ resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==}
+ hasBin: true
+
uuid@8.3.2:
resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
hasBin: true
@@ -6372,6 +6592,8 @@ snapshots:
'@babel/helper-string-parser': 7.27.1
'@babel/helper-validator-identifier': 7.27.1
+ '@balena/dockerignore@1.0.2': {}
+
'@bcoe/v8-coverage@0.2.3': {}
'@biomejs/biome@1.9.4':
@@ -7024,6 +7246,18 @@ snapshots:
'@ethersproject/properties': 5.8.0
'@ethersproject/strings': 5.8.0
+ '@grpc/grpc-js@1.13.4':
+ dependencies:
+ '@grpc/proto-loader': 0.7.15
+ '@js-sdsl/ordered-map': 4.4.2
+
+ '@grpc/proto-loader@0.7.15':
+ dependencies:
+ lodash.camelcase: 4.3.0
+ long: 5.3.2
+ protobufjs: 7.5.4
+ yargs: 17.7.2
+
'@hexagon/base64@1.1.28': {}
'@img/sharp-darwin-arm64@0.33.5':
@@ -7031,76 +7265,162 @@ snapshots:
'@img/sharp-libvips-darwin-arm64': 1.0.4
optional: true
+ '@img/sharp-darwin-arm64@0.34.3':
+ optionalDependencies:
+ '@img/sharp-libvips-darwin-arm64': 1.2.0
+ optional: true
+
'@img/sharp-darwin-x64@0.33.5':
optionalDependencies:
'@img/sharp-libvips-darwin-x64': 1.0.4
optional: true
+ '@img/sharp-darwin-x64@0.34.3':
+ optionalDependencies:
+ '@img/sharp-libvips-darwin-x64': 1.2.0
+ optional: true
+
'@img/sharp-libvips-darwin-arm64@1.0.4':
optional: true
+ '@img/sharp-libvips-darwin-arm64@1.2.0':
+ optional: true
+
'@img/sharp-libvips-darwin-x64@1.0.4':
optional: true
+ '@img/sharp-libvips-darwin-x64@1.2.0':
+ optional: true
+
'@img/sharp-libvips-linux-arm64@1.0.4':
optional: true
+ '@img/sharp-libvips-linux-arm64@1.2.0':
+ optional: true
+
'@img/sharp-libvips-linux-arm@1.0.5':
optional: true
+ '@img/sharp-libvips-linux-arm@1.2.0':
+ optional: true
+
+ '@img/sharp-libvips-linux-ppc64@1.2.0':
+ optional: true
+
'@img/sharp-libvips-linux-s390x@1.0.4':
optional: true
+ '@img/sharp-libvips-linux-s390x@1.2.0':
+ optional: true
+
'@img/sharp-libvips-linux-x64@1.0.4':
optional: true
+ '@img/sharp-libvips-linux-x64@1.2.0':
+ optional: true
+
'@img/sharp-libvips-linuxmusl-arm64@1.0.4':
optional: true
+ '@img/sharp-libvips-linuxmusl-arm64@1.2.0':
+ optional: true
+
'@img/sharp-libvips-linuxmusl-x64@1.0.4':
optional: true
+ '@img/sharp-libvips-linuxmusl-x64@1.2.0':
+ optional: true
+
'@img/sharp-linux-arm64@0.33.5':
optionalDependencies:
'@img/sharp-libvips-linux-arm64': 1.0.4
optional: true
+ '@img/sharp-linux-arm64@0.34.3':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-arm64': 1.2.0
+ optional: true
+
'@img/sharp-linux-arm@0.33.5':
optionalDependencies:
'@img/sharp-libvips-linux-arm': 1.0.5
optional: true
+ '@img/sharp-linux-arm@0.34.3':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-arm': 1.2.0
+ optional: true
+
+ '@img/sharp-linux-ppc64@0.34.3':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-ppc64': 1.2.0
+ optional: true
+
'@img/sharp-linux-s390x@0.33.5':
optionalDependencies:
'@img/sharp-libvips-linux-s390x': 1.0.4
optional: true
+ '@img/sharp-linux-s390x@0.34.3':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-s390x': 1.2.0
+ optional: true
+
'@img/sharp-linux-x64@0.33.5':
optionalDependencies:
'@img/sharp-libvips-linux-x64': 1.0.4
optional: true
+ '@img/sharp-linux-x64@0.34.3':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-x64': 1.2.0
+ optional: true
+
'@img/sharp-linuxmusl-arm64@0.33.5':
optionalDependencies:
'@img/sharp-libvips-linuxmusl-arm64': 1.0.4
optional: true
+ '@img/sharp-linuxmusl-arm64@0.34.3':
+ optionalDependencies:
+ '@img/sharp-libvips-linuxmusl-arm64': 1.2.0
+ optional: true
+
'@img/sharp-linuxmusl-x64@0.33.5':
optionalDependencies:
'@img/sharp-libvips-linuxmusl-x64': 1.0.4
optional: true
+ '@img/sharp-linuxmusl-x64@0.34.3':
+ optionalDependencies:
+ '@img/sharp-libvips-linuxmusl-x64': 1.2.0
+ optional: true
+
'@img/sharp-wasm32@0.33.5':
dependencies:
'@emnapi/runtime': 1.5.0
optional: true
+ '@img/sharp-wasm32@0.34.3':
+ dependencies:
+ '@emnapi/runtime': 1.5.0
+ optional: true
+
+ '@img/sharp-win32-arm64@0.34.3':
+ optional: true
+
'@img/sharp-win32-ia32@0.33.5':
optional: true
+ '@img/sharp-win32-ia32@0.34.3':
+ optional: true
+
'@img/sharp-win32-x64@0.33.5':
optional: true
+ '@img/sharp-win32-x64@0.34.3':
+ optional: true
+
'@inquirer/checkbox@4.2.2(@types/node@22.18.0)':
dependencies:
'@inquirer/core': 10.2.0(@types/node@22.18.0)
@@ -7638,6 +7958,8 @@ snapshots:
'@jridgewell/resolve-uri': 3.1.2
'@jridgewell/sourcemap-codec': 1.5.5
+ '@js-sdsl/ordered-map@4.4.2': {}
+
'@jsep-plugin/assignment@1.3.0(jsep@1.4.0)':
dependencies:
jsep: 1.4.0
@@ -8543,6 +8865,17 @@ snapshots:
dependencies:
'@types/ms': 2.1.0
+ '@types/docker-modem@3.0.6':
+ dependencies:
+ '@types/node': 22.18.0
+ '@types/ssh2': 1.15.5
+
+ '@types/dockerode@3.3.43':
+ dependencies:
+ '@types/docker-modem': 3.0.6
+ '@types/node': 22.18.0
+ '@types/ssh2': 1.15.5
+
'@types/es-aggregate-error@1.0.6':
dependencies:
'@types/node': 22.18.0
@@ -8615,6 +8948,10 @@ snapshots:
'@types/node@12.20.55': {}
+ '@types/node@18.19.124':
+ dependencies:
+ undici-types: 5.26.5
+
'@types/node@20.19.12':
dependencies:
undici-types: 6.21.0
@@ -8646,6 +8983,10 @@ snapshots:
'@types/node': 22.18.0
'@types/send': 0.17.5
+ '@types/ssh2@1.15.5':
+ dependencies:
+ '@types/node': 18.19.124
+
'@types/stack-utils@2.0.3': {}
'@types/unist@2.0.11': {}
@@ -8929,6 +9270,10 @@ snapshots:
arrify@1.0.1: {}
+ asn1@0.2.6:
+ dependencies:
+ safer-buffer: 2.1.2
+
asn1js@3.0.6:
dependencies:
pvtsutils: 1.3.6
@@ -9100,6 +9445,10 @@ snapshots:
basic-ftp@5.0.5: {}
+ bcrypt-pbkdf@1.0.2:
+ dependencies:
+ tweetnacl: 0.14.5
+
bech32@1.1.4: {}
better-opn@3.0.2:
@@ -9233,6 +9582,9 @@ snapshots:
node-gyp-build: 4.8.4
optional: true
+ buildcheck@0.0.6:
+ optional: true
+
bytes@3.1.2: {}
cacheable-lookup@7.0.0: {}
@@ -9453,6 +9805,12 @@ snapshots:
cosmjs-types@0.9.0: {}
+ cpu-features@0.0.10:
+ dependencies:
+ buildcheck: 0.0.6
+ nan: 2.23.0
+ optional: true
+
create-jest@29.7.0(@types/node@20.19.12)(ts-node@10.9.2(@types/node@20.19.12)(typescript@5.9.2)):
dependencies:
'@jest/types': 29.6.3
@@ -9622,6 +9980,27 @@ snapshots:
dependencies:
dns-packet: 5.6.1
+ docker-modem@5.0.6:
+ dependencies:
+ debug: 4.4.1
+ readable-stream: 3.6.2
+ split-ca: 1.0.1
+ ssh2: 1.17.0
+ transitivePeerDependencies:
+ - supports-color
+
+ dockerode@4.0.7:
+ dependencies:
+ '@balena/dockerignore': 1.0.2
+ '@grpc/grpc-js': 1.13.4
+ '@grpc/proto-loader': 0.7.15
+ docker-modem: 5.0.6
+ protobufjs: 7.5.4
+ tar-fs: 2.1.3
+ uuid: 10.0.0
+ transitivePeerDependencies:
+ - supports-color
+
dotenv@16.6.1: {}
dunder-proto@1.0.1:
@@ -11773,6 +12152,8 @@ snapshots:
dependencies:
p-locate: 4.1.0
+ lodash.camelcase@4.3.0: {}
+
lodash.memoize@4.1.2: {}
lodash.startcase@4.4.0: {}
@@ -11788,6 +12169,8 @@ snapshots:
long@4.0.0: {}
+ long@5.3.2: {}
+
longest-streak@3.1.0: {}
loose-envify@1.4.0:
@@ -12404,6 +12787,9 @@ snapshots:
object-assign: 4.1.1
thenify-all: 1.6.0
+ nan@2.23.0:
+ optional: true
+
nanoid@3.3.11: {}
napi-build-utils@2.0.0: {}
@@ -12535,6 +12921,8 @@ snapshots:
openapi-types@12.1.3: {}
+ optional@0.1.4: {}
+
ora@5.4.1:
dependencies:
bl: 4.1.0
@@ -12809,6 +13197,21 @@ snapshots:
'@types/node': 22.18.0
long: 4.0.0
+ protobufjs@7.5.4:
+ dependencies:
+ '@protobufjs/aspromise': 1.1.2
+ '@protobufjs/base64': 1.1.2
+ '@protobufjs/codegen': 2.0.4
+ '@protobufjs/eventemitter': 1.1.0
+ '@protobufjs/fetch': 1.1.0
+ '@protobufjs/float': 1.0.2
+ '@protobufjs/inquire': 1.1.0
+ '@protobufjs/path': 1.1.2
+ '@protobufjs/pool': 1.1.0
+ '@protobufjs/utf8': 1.1.0
+ '@types/node': 22.18.0
+ long: 5.3.2
+
proxy-addr@2.0.7:
dependencies:
forwarded: 0.2.0
@@ -13380,6 +13783,35 @@ snapshots:
'@img/sharp-win32-ia32': 0.33.5
'@img/sharp-win32-x64': 0.33.5
+ sharp@0.34.3:
+ dependencies:
+ color: 4.2.3
+ detect-libc: 2.0.4
+ semver: 7.7.2
+ optionalDependencies:
+ '@img/sharp-darwin-arm64': 0.34.3
+ '@img/sharp-darwin-x64': 0.34.3
+ '@img/sharp-libvips-darwin-arm64': 1.2.0
+ '@img/sharp-libvips-darwin-x64': 1.2.0
+ '@img/sharp-libvips-linux-arm': 1.2.0
+ '@img/sharp-libvips-linux-arm64': 1.2.0
+ '@img/sharp-libvips-linux-ppc64': 1.2.0
+ '@img/sharp-libvips-linux-s390x': 1.2.0
+ '@img/sharp-libvips-linux-x64': 1.2.0
+ '@img/sharp-libvips-linuxmusl-arm64': 1.2.0
+ '@img/sharp-libvips-linuxmusl-x64': 1.2.0
+ '@img/sharp-linux-arm': 0.34.3
+ '@img/sharp-linux-arm64': 0.34.3
+ '@img/sharp-linux-ppc64': 0.34.3
+ '@img/sharp-linux-s390x': 0.34.3
+ '@img/sharp-linux-x64': 0.34.3
+ '@img/sharp-linuxmusl-arm64': 0.34.3
+ '@img/sharp-linuxmusl-x64': 0.34.3
+ '@img/sharp-wasm32': 0.34.3
+ '@img/sharp-win32-arm64': 0.34.3
+ '@img/sharp-win32-ia32': 0.34.3
+ '@img/sharp-win32-x64': 0.34.3
+
shebang-command@2.0.0:
dependencies:
shebang-regex: 3.0.0
@@ -13528,8 +13960,18 @@ snapshots:
cross-spawn: 7.0.6
signal-exit: 4.1.0
+ split-ca@1.0.1: {}
+
sprintf-js@1.0.3: {}
+ ssh2@1.17.0:
+ dependencies:
+ asn1: 0.2.6
+ bcrypt-pbkdf: 1.0.2
+ optionalDependencies:
+ cpu-features: 0.0.10
+ nan: 2.23.0
+
stack-utils@2.0.6:
dependencies:
escape-string-regexp: 2.0.0
@@ -13918,6 +14360,8 @@ snapshots:
dependencies:
safe-buffer: 5.2.1
+ tweetnacl@0.14.5: {}
+
type-detect@4.0.8: {}
type-fest@0.21.3: {}
@@ -13987,6 +14431,8 @@ snapshots:
buffer: 5.7.1
through: 2.3.8
+ undici-types@5.26.5: {}
+
undici-types@6.19.8: {}
undici-types@6.21.0: {}
@@ -14147,6 +14593,8 @@ snapshots:
utils-merge@1.0.1: {}
+ uuid@10.0.0: {}
+
uuid@8.3.2: {}
v8-compile-cache-lib@3.0.1: {}