feat(sandbox/v2): enhance connector integration and VNC configuration
- Refactored the sandbox initialization process to resolve the connector before obtaining the Computer, allowing for the injection of OPENAI_PROXY_* environment variables. - Updated the GetComputer and BuildCreateOptions functions to accept an optional connector parameter for improved environment variable management. - Standardized the VNC configuration by replacing SANDBOX_VNC_ENABLED with VNC_ENABLED across Dockerfiles and related scripts for consistency. - Enhanced the VNC service startup script to check the new VNC_ENABLED variable, ensuring proper service initialization. Made-with: Cursor
This commit is contained in:
parent
6407b8531f
commit
5ec7801689
11 changed files with 94 additions and 82 deletions
|
|
@ -39,15 +39,22 @@ func (ast *Assistant) initSandboxV2(ctx *context.Context, opts *context.Options)
|
||||||
|
|
||||||
stdCtx := ctx.Context
|
stdCtx := ctx.Context
|
||||||
|
|
||||||
// 1. Obtain Computer.
|
// 1. Resolve connector (before Computer so proxy env vars can be injected).
|
||||||
computer, identifier, err := sandboxv2.GetComputer(ctx, cfg, manager)
|
conn, _, err := ast.GetConnector(ctx, opts)
|
||||||
|
if err != nil && cfg.Runner.Name != "yao" {
|
||||||
|
closeLoadingV2(ctx, loadingMsgID, "sandbox.failed")
|
||||||
|
return nil, nil, nil, "", fmt.Errorf("get connector: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Obtain Computer (passes connector for OPENAI_PROXY_* env injection).
|
||||||
|
computer, identifier, err := sandboxv2.GetComputer(ctx, cfg, manager, conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
closeLoadingV2(ctx, loadingMsgID, "sandbox.failed")
|
closeLoadingV2(ctx, loadingMsgID, "sandbox.failed")
|
||||||
return nil, nil, nil, "", fmt.Errorf("getComputer failed: %w", err)
|
return nil, nil, nil, "", fmt.Errorf("getComputer failed: %w", err)
|
||||||
}
|
}
|
||||||
_ = identifier
|
_ = identifier
|
||||||
|
|
||||||
// 2. Get Runner.
|
// 3. Get Runner.
|
||||||
runner, err := sandboxv2.Get(cfg.Runner.Name)
|
runner, err := sandboxv2.Get(cfg.Runner.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sandboxv2.LifecycleAction(stdCtx, cfg, computer, manager)
|
sandboxv2.LifecycleAction(stdCtx, cfg, computer, manager)
|
||||||
|
|
@ -55,14 +62,6 @@ func (ast *Assistant) initSandboxV2(ctx *context.Context, opts *context.Options)
|
||||||
return nil, nil, nil, "", fmt.Errorf("get runner %q: %w", cfg.Runner.Name, err)
|
return nil, nil, nil, "", fmt.Errorf("get runner %q: %w", cfg.Runner.Name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Resolve connector.
|
|
||||||
conn, _, err := ast.GetConnector(ctx, opts)
|
|
||||||
if err != nil && cfg.Runner.Name != "yao" {
|
|
||||||
sandboxv2.LifecycleAction(stdCtx, cfg, computer, manager)
|
|
||||||
closeLoadingV2(ctx, loadingMsgID, "sandbox.failed")
|
|
||||||
return nil, nil, nil, "", fmt.Errorf("get connector: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Resolve skills directory.
|
// 4. Resolve skills directory.
|
||||||
skillsDir := ""
|
skillsDir := ""
|
||||||
if ast.Path != "" {
|
if ast.Path != "" {
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ const (
|
||||||
// ClaudeRunner implements the Runner interface for Claude CLI (mode=cli).
|
// ClaudeRunner implements the Runner interface for Claude CLI (mode=cli).
|
||||||
type ClaudeRunner struct {
|
type ClaudeRunner struct {
|
||||||
mode string
|
mode string
|
||||||
proxyReady bool
|
|
||||||
hasMCP bool
|
hasMCP bool
|
||||||
mcpToolPattern string // e.g. "mcp__yao__*,mcp__github__*"
|
mcpToolPattern string // e.g. "mcp__yao__*,mcp__github__*"
|
||||||
servicePort int
|
servicePort int
|
||||||
|
|
@ -60,30 +59,6 @@ func (r *ClaudeRunner) Prepare(ctx context.Context, req *types.PrepareRequest) e
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Runner-specific: write proxy config and start proxy (for non-anthropic connectors).
|
|
||||||
if req.Connector != nil && !req.Connector.Is(connector.ANTHROPIC) {
|
|
||||||
setting := req.Connector.Setting()
|
|
||||||
host, _ := setting["host"].(string)
|
|
||||||
key, _ := setting["key"].(string)
|
|
||||||
model, _ := setting["model"].(string)
|
|
||||||
if host != "" && key != "" {
|
|
||||||
proxyJSON := buildProxyConfig(host, key, model, setting)
|
|
||||||
steps = append(steps, types.PrepareStep{
|
|
||||||
Action: "file",
|
|
||||||
Path: ".yao/proxy.json",
|
|
||||||
Content: proxyJSON,
|
|
||||||
Once: true,
|
|
||||||
})
|
|
||||||
steps = append(steps, types.PrepareStep{
|
|
||||||
Action: "exec",
|
|
||||||
Cmd: "which start-claude-proxy && start-claude-proxy || true",
|
|
||||||
Once: true,
|
|
||||||
IgnoreError: true,
|
|
||||||
})
|
|
||||||
r.proxyReady = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Runner-specific: write MCP config.
|
// Runner-specific: write MCP config.
|
||||||
if len(req.MCPServers) > 0 {
|
if len(req.MCPServers) > 0 {
|
||||||
r.hasMCP = true
|
r.hasMCP = true
|
||||||
|
|
@ -174,7 +149,6 @@ func (r *ClaudeRunner) Stream(ctx context.Context, req *types.StreamRequest, han
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup kills any remaining claude processes.
|
// Cleanup kills any remaining claude processes.
|
||||||
// mode=service: don't kill the service daemon (lifecycle manages it), only clean proxy.
|
|
||||||
// mode=cli: kill all claude CLI processes.
|
// mode=cli: kill all claude CLI processes.
|
||||||
func (r *ClaudeRunner) Cleanup(ctx context.Context, computer infra.Computer) error {
|
func (r *ClaudeRunner) Cleanup(ctx context.Context, computer infra.Computer) error {
|
||||||
if computer == nil {
|
if computer == nil {
|
||||||
|
|
@ -185,10 +159,6 @@ func (r *ClaudeRunner) Cleanup(ctx context.Context, computer infra.Computer) err
|
||||||
computer.Exec(ctx, []string{"sh", "-c", "pkill -f 'claude' || true"})
|
computer.Exec(ctx, []string{"sh", "-c", "pkill -f 'claude' || true"})
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.proxyReady {
|
|
||||||
computer.Exec(ctx, []string{"sh", "-c", "pkill -f 'claude-proxy' || true"})
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -320,32 +290,9 @@ func (r *ClaudeRunner) buildCLICommand(req *types.StreamRequest, isContinuation
|
||||||
return []string{"bash", "-c", bash.String()}, env
|
return []string{"bash", "-c", bash.String()}, env
|
||||||
}
|
}
|
||||||
|
|
||||||
// buildProxyConfig creates the claude-proxy configuration JSON.
|
|
||||||
func buildProxyConfig(host, key, model string, setting map[string]any) []byte {
|
|
||||||
backendURL := connector.BuildAPIURL(host, "/chat/completions")
|
|
||||||
config := map[string]any{
|
|
||||||
"backend": backendURL,
|
|
||||||
"api_key": key,
|
|
||||||
"model": model,
|
|
||||||
}
|
|
||||||
opts := make(map[string]any)
|
|
||||||
for k, v := range setting {
|
|
||||||
switch k {
|
|
||||||
case "host", "key", "model", "azure", "capabilities":
|
|
||||||
continue
|
|
||||||
default:
|
|
||||||
opts[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(opts) > 0 {
|
|
||||||
config["options"] = opts
|
|
||||||
}
|
|
||||||
data, _ := json.MarshalIndent(config, "", " ")
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
// buildMCPConfig creates the .mcp.json for Claude CLI based on declared servers.
|
// buildMCPConfig creates the .mcp.json for Claude CLI based on declared servers.
|
||||||
// Each server delegates to "tai call" which bridges stdio JSON-RPC to Yao gRPC.
|
// Each server delegates to "tai mcp" which implements the standard MCP protocol
|
||||||
|
// over stdio and bridges to Yao gRPC with authentication.
|
||||||
// Connection is configured via env vars (YAO_GRPC_ADDR, YAO_TOKEN, etc.)
|
// Connection is configured via env vars (YAO_GRPC_ADDR, YAO_TOKEN, etc.)
|
||||||
// injected by the sandbox infrastructure at container start.
|
// injected by the sandbox infrastructure at container start.
|
||||||
func buildMCPConfig(servers []types.MCPServer) []byte {
|
func buildMCPConfig(servers []types.MCPServer) []byte {
|
||||||
|
|
@ -357,13 +304,13 @@ func buildMCPConfig(servers []types.MCPServer) []byte {
|
||||||
}
|
}
|
||||||
mcpServers[name] = map[string]any{
|
mcpServers[name] = map[string]any{
|
||||||
"command": "tai",
|
"command": "tai",
|
||||||
"args": []string{"call"},
|
"args": []string{"mcp"},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(mcpServers) == 0 {
|
if len(mcpServers) == 0 {
|
||||||
mcpServers["yao"] = map[string]any{
|
mcpServers["yao"] = map[string]any{
|
||||||
"command": "tai",
|
"command": "tai",
|
||||||
"args": []string{"call"},
|
"args": []string{"mcp"},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
config := map[string]any{"mcpServers": mcpServers}
|
config := map[string]any{"mcpServers": mcpServers}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
|
"github.com/yaoapp/gou/connector"
|
||||||
agentContext "github.com/yaoapp/yao/agent/context"
|
agentContext "github.com/yaoapp/yao/agent/context"
|
||||||
"github.com/yaoapp/yao/agent/sandbox/v2/types"
|
"github.com/yaoapp/yao/agent/sandbox/v2/types"
|
||||||
infra "github.com/yaoapp/yao/sandbox/v2"
|
infra "github.com/yaoapp/yao/sandbox/v2"
|
||||||
|
|
@ -37,8 +38,9 @@ func BuildIdentifier(cfg *types.SandboxConfig, ownerID, chatID, assistantID stri
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetComputer obtains or creates a Computer for the current request.
|
// GetComputer obtains or creates a Computer for the current request.
|
||||||
|
// An optional connector may be passed to inject OPENAI_PROXY_* env vars.
|
||||||
// Returns the Computer, the resolved identifier, and any error.
|
// Returns the Computer, the resolved identifier, and any error.
|
||||||
func GetComputer(ctx *agentContext.Context, cfg *types.SandboxConfig, manager *infra.Manager) (infra.Computer, string, error) {
|
func GetComputer(ctx *agentContext.Context, cfg *types.SandboxConfig, manager *infra.Manager, conn ...connector.Connector) (infra.Computer, string, error) {
|
||||||
ownerID := resolveOwnerID(ctx)
|
ownerID := resolveOwnerID(ctx)
|
||||||
identifier := BuildIdentifier(cfg, ownerID, ctx.ChatID, ctx.AssistantID, ctx.Metadata)
|
identifier := BuildIdentifier(cfg, ownerID, ctx.ChatID, ctx.AssistantID, ctx.Metadata)
|
||||||
|
|
||||||
|
|
@ -84,7 +86,11 @@ func GetComputer(ctx *agentContext.Context, cfg *types.SandboxConfig, manager *i
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new box.
|
// Create new box.
|
||||||
createOpts, err := BuildCreateOptions(cfg, identifier, ownerID, workspaceID)
|
var c connector.Connector
|
||||||
|
if len(conn) > 0 {
|
||||||
|
c = conn[0]
|
||||||
|
}
|
||||||
|
createOpts, err := BuildCreateOptions(cfg, identifier, ownerID, workspaceID, c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, identifier, fmt.Errorf("build create options: %w", err)
|
return nil, identifier, fmt.Errorf("build create options: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
package sandboxv2
|
package sandboxv2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/yaoapp/gou/connector"
|
||||||
"github.com/yaoapp/yao/agent/sandbox/v2/types"
|
"github.com/yaoapp/yao/agent/sandbox/v2/types"
|
||||||
infra "github.com/yaoapp/yao/sandbox/v2"
|
infra "github.com/yaoapp/yao/sandbox/v2"
|
||||||
)
|
)
|
||||||
|
|
@ -19,8 +21,9 @@ func resolveEnvRef(value string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildCreateOptions converts a SandboxConfig into the V2 infrastructure
|
// BuildCreateOptions converts a SandboxConfig into the V2 infrastructure
|
||||||
// CreateOptions. Pure runtime mapping — no file-system or DSL access.
|
// CreateOptions. An optional connector is used to inject OPENAI_PROXY_*
|
||||||
func BuildCreateOptions(cfg *types.SandboxConfig, identifier, ownerID, workspaceID string) (infra.CreateOptions, error) {
|
// environment variables when the connector is OpenAI-compatible (non-Anthropic).
|
||||||
|
func BuildCreateOptions(cfg *types.SandboxConfig, identifier, ownerID, workspaceID string, conn ...connector.Connector) (infra.CreateOptions, error) {
|
||||||
opts := infra.CreateOptions{
|
opts := infra.CreateOptions{
|
||||||
ID: identifier,
|
ID: identifier,
|
||||||
Owner: ownerID,
|
Owner: ownerID,
|
||||||
|
|
@ -116,9 +119,69 @@ func BuildCreateOptions(cfg *types.SandboxConfig, identifier, ownerID, workspace
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.Env == nil {
|
||||||
|
opts.Env = make(map[string]string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inject OPENAI_PROXY_* when connector is OpenAI-compatible (non-Anthropic).
|
||||||
|
// The a2o proxy inside the container translates Anthropic API → OpenAI API.
|
||||||
|
if len(conn) > 0 && conn[0] != nil && !conn[0].Is(connector.ANTHROPIC) {
|
||||||
|
injectProxyEnv(opts.Env, conn[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inject VNC_* environment variables from config.
|
||||||
|
if cfg.Computer.VNC.Enabled {
|
||||||
|
opts.Env["VNC_ENABLED"] = "true"
|
||||||
|
if cfg.Computer.VNC.Password != "" {
|
||||||
|
opts.Env["VNC_PASSWORD"] = resolveEnvRef(cfg.Computer.VNC.Password)
|
||||||
|
}
|
||||||
|
if cfg.Computer.VNC.Resolution != "" {
|
||||||
|
opts.Env["VNC_RESOLUTION"] = cfg.Computer.VNC.Resolution
|
||||||
|
}
|
||||||
|
if cfg.Computer.VNC.ViewOnly {
|
||||||
|
opts.Env["VNC_VIEW_ONLY"] = "true"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return opts, nil
|
return opts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// injectProxyEnv extracts backend URL, model, and API key from an
|
||||||
|
// OpenAI-compatible connector's settings and writes them as OPENAI_PROXY_*
|
||||||
|
// environment variables into env.
|
||||||
|
func injectProxyEnv(env map[string]string, conn connector.Connector) {
|
||||||
|
settings := conn.Setting()
|
||||||
|
if settings == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if host, ok := settings["host"].(string); ok && host != "" {
|
||||||
|
env["OPENAI_PROXY_BACKEND"] = host
|
||||||
|
}
|
||||||
|
if model, ok := settings["model"].(string); ok && model != "" {
|
||||||
|
env["OPENAI_PROXY_MODEL"] = model
|
||||||
|
}
|
||||||
|
if key, ok := settings["key"].(string); ok && key != "" {
|
||||||
|
env["OPENAI_PROXY_API_KEY"] = key
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forward extra options as JSON.
|
||||||
|
extra := make(map[string]interface{})
|
||||||
|
for k, v := range settings {
|
||||||
|
switch k {
|
||||||
|
case "host", "model", "key", "proxy", "type":
|
||||||
|
continue
|
||||||
|
default:
|
||||||
|
extra[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(extra) > 0 {
|
||||||
|
if data, err := json.Marshal(extra); err == nil {
|
||||||
|
env["OPENAI_PROXY_OPTIONS"] = string(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// parseMemory converts a human-readable memory string to bytes.
|
// parseMemory converts a human-readable memory string to bytes.
|
||||||
// Supported formats: "4GB", "4G", "4g", "512MB", "512M", "512m", "1024KB", "1024K", "1024".
|
// Supported formats: "4GB", "4G", "4g", "512MB", "512M", "512m", "1024KB", "1024K", "1024".
|
||||||
func parseMemory(s string) (int64, error) {
|
func parseMemory(s string) (int64, error) {
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ ENV DISPLAY=:99
|
||||||
ENV VNC_PORT=5900
|
ENV VNC_PORT=5900
|
||||||
ENV NOVNC_PORT=6080
|
ENV NOVNC_PORT=6080
|
||||||
ENV RESOLUTION=1920x1080x24
|
ENV RESOLUTION=1920x1080x24
|
||||||
ENV SANDBOX_VNC_ENABLED=true
|
ENV VNC_ENABLED=true
|
||||||
ENV SANDBOX_DESKTOP=fluxbox
|
ENV SANDBOX_DESKTOP=fluxbox
|
||||||
|
|
||||||
# Node.js environment - ensure global modules are accessible
|
# Node.js environment - ensure global modules are accessible
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,7 @@ ENV DISPLAY=:99
|
||||||
ENV VNC_PORT=5900
|
ENV VNC_PORT=5900
|
||||||
ENV NOVNC_PORT=6080
|
ENV NOVNC_PORT=6080
|
||||||
ENV RESOLUTION=1920x1080x24
|
ENV RESOLUTION=1920x1080x24
|
||||||
ENV SANDBOX_VNC_ENABLED=true
|
ENV VNC_ENABLED=true
|
||||||
ENV SANDBOX_DESKTOP=fluxbox
|
ENV SANDBOX_DESKTOP=fluxbox
|
||||||
|
|
||||||
# Node.js environment
|
# Node.js environment
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ ENV DISPLAY=:99
|
||||||
ENV VNC_PORT=5900
|
ENV VNC_PORT=5900
|
||||||
ENV NOVNC_PORT=6080
|
ENV NOVNC_PORT=6080
|
||||||
ENV RESOLUTION=1920x1080x24
|
ENV RESOLUTION=1920x1080x24
|
||||||
ENV SANDBOX_VNC_ENABLED=true
|
ENV VNC_ENABLED=true
|
||||||
ENV SANDBOX_DESKTOP=xfce
|
ENV SANDBOX_DESKTOP=xfce
|
||||||
# Set hostname for XFCE panel display
|
# Set hostname for XFCE panel display
|
||||||
ENV HOSTNAME="Yao Sandbox"
|
ENV HOSTNAME="Yao Sandbox"
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
# ============================================
|
# ============================================
|
||||||
# VNC Services Startup
|
# VNC Services Startup
|
||||||
# ============================================
|
# ============================================
|
||||||
if [ "$SANDBOX_VNC_ENABLED" = "true" ]; then
|
if [ "$VNC_ENABLED" = "true" ]; then
|
||||||
echo "[Entrypoint] Starting VNC services..."
|
echo "[Entrypoint] Starting VNC services..."
|
||||||
/usr/local/bin/start-vnc.sh &
|
/usr/local/bin/start-vnc.sh &
|
||||||
# Wait for VNC to initialize
|
# Wait for VNC to initialize
|
||||||
|
|
|
||||||
|
|
@ -350,8 +350,7 @@ func (m *Manager) createContainer(ctx context.Context, opts CreateOptions) (*Con
|
||||||
"6080/tcp": struct{}{}, // noVNC websockify
|
"6080/tcp": struct{}{}, // noVNC websockify
|
||||||
"5900/tcp": struct{}{}, // VNC
|
"5900/tcp": struct{}{}, // VNC
|
||||||
}
|
}
|
||||||
// Enable SANDBOX_VNC_ENABLED environment variable
|
containerConfig.Env = append(containerConfig.Env, "VNC_ENABLED=true")
|
||||||
containerConfig.Env = append(containerConfig.Env, "SANDBOX_VNC_ENABLED=true")
|
|
||||||
|
|
||||||
// Map to random available ports on 127.0.0.1
|
// Map to random available ports on 127.0.0.1
|
||||||
hostConfig.PortBindings = nat.PortMap{
|
hostConfig.PortBindings = nat.PortMap{
|
||||||
|
|
|
||||||
|
|
@ -323,10 +323,8 @@ func (p *Proxy) checkVNCEnabled(ctx context.Context, containerName string) bool
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check environment variables for VNC_ENABLED or SANDBOX_VNC_ENABLED
|
|
||||||
for _, env := range info.Config.Env {
|
for _, env := range info.Config.Env {
|
||||||
if strings.HasPrefix(env, "SANDBOX_VNC_ENABLED=true") ||
|
if strings.HasPrefix(env, "VNC_ENABLED=true") {
|
||||||
strings.HasPrefix(env, "VNC_ENABLED=true") {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ func (d *dockerCore) create(ctx context.Context, opts CreateOptions, addVNCPorts
|
||||||
shmSize = 256 * 1024 * 1024
|
shmSize = 256 * 1024 * 1024
|
||||||
}
|
}
|
||||||
hostCfg.ShmSize = shmSize
|
hostCfg.ShmSize = shmSize
|
||||||
cfg.Env = append(cfg.Env, "SANDBOX_VNC_ENABLED=true")
|
cfg.Env = append(cfg.Env, "VNC_ENABLED=true")
|
||||||
|
|
||||||
if addVNCPorts {
|
if addVNCPorts {
|
||||||
for _, p := range []int{6080, 5900} {
|
for _, p := range []int{6080, 5900} {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue