UNPKG

@vibe-kit/dagger

Version:

Local sandbox provider for Vibekit using Dagger

940 lines (935 loc) 31.1 kB
// src/dagger/vibekit-dagger.ts import { connect } from "@dagger.io/dagger"; import { exec } from "child_process"; import { promisify } from "util"; import { join } from "path"; import { EventEmitter } from "events"; import { homedir } from "os"; // src/dagger/registry-integration.ts var DockerClient = null; var ConfigManager = null; var DockerHubRegistry = null; var RegistryManager = null; async function getDockerClient() { if (!DockerClient) { const servicesPath = "@vibe-kit/sdk/services"; const servicesModule = await import(servicesPath).catch(() => null); if (servicesModule) { DockerClient = servicesModule.DockerClient; } } if (!DockerClient) { throw new Error("DockerClient not available. Please ensure @vibe-kit/sdk is properly installed."); } const config = Configuration.getInstance().get(); const logger = Configuration.getInstance().getLogger(); return new DockerClient({ retryAttempts: config.retryAttempts, retryDelayMs: config.retryDelayMs, logger }); } async function getConfigManager() { if (!ConfigManager) { const servicesPath = "@vibe-kit/sdk/services"; const servicesModule = await import(servicesPath).catch(() => null); if (servicesModule) { ConfigManager = servicesModule.ConfigManager; } } if (!ConfigManager) { throw new Error("ConfigManager not available. Please ensure @vibe-kit/sdk is properly installed."); } const config = Configuration.getInstance().get(); const logger = Configuration.getInstance().getLogger(); return new ConfigManager({ configPath: config.configPath, logger }); } async function getRegistryManager() { if (!DockerHubRegistry || !RegistryManager) { const registryPath = "@vibe-kit/sdk/registry"; const registryModule = await import(registryPath).catch(() => null); if (registryModule) { DockerHubRegistry = registryModule.DockerHubRegistry; RegistryManager = registryModule.RegistryManager; } } if (!DockerHubRegistry || !RegistryManager) { throw new Error("Registry modules not available. Please ensure @vibe-kit/sdk is properly installed."); } const logger = Configuration.getInstance().getLogger(); const dockerHubRegistry = new DockerHubRegistry({ logger }); const registryManager = new RegistryManager({ defaultRegistry: "dockerhub", logger }); registryManager.registerRegistry("dockerhub", dockerHubRegistry); return registryManager; } async function checkDockerLogin() { const dockerClient = await getDockerClient(); return await dockerClient.checkDockerLogin(); } async function getVibeKitConfig() { const configManager = await getConfigManager(); return await configManager.getConfig(); } async function saveVibeKitConfig(config) { const configManager = await getConfigManager(); await configManager.saveConfig(config); } async function uploadImagesToUserAccount(dockerHubUser, selectedAgents) { const registryManager = await getRegistryManager(); return await registryManager.uploadImages(dockerHubUser, selectedAgents, "dockerhub"); } async function setupUserDockerRegistry(selectedAgents) { const registryManager = await getRegistryManager(); return await registryManager.setupRegistry(selectedAgents, "dockerhub"); } // src/dagger/vibekit-dagger.ts var execAsync = promisify(exec); var ConsoleLogger = class { context; constructor(context = "VibeKitDagger") { this.context = context; } log(level, message, meta) { const timestamp = (/* @__PURE__ */ new Date()).toISOString(); const logMessage = `[${timestamp}] [${level}] [${this.context}] ${message}`; if (meta) { console.log(logMessage, meta); } else { console.log(logMessage); } } debug(message, meta) { if (process.env.VIBEKIT_LOG_LEVEL === "debug") { this.log("DEBUG", message, meta); } } info(message, meta) { this.log("INFO", message, meta); } warn(message, meta) { this.log("WARN", message, meta); } error(message, error, meta) { const errorMeta = error instanceof Error ? { ...meta, error: { message: error.message, stack: error.stack, name: error.name } } : meta; this.log("ERROR", message, errorMeta); } }; var VibeKitError = class extends Error { constructor(message, code, cause) { super(message); this.code = code; this.cause = cause; this.name = "VibeKitError"; } }; var ContainerExecutionError = class extends VibeKitError { constructor(message, exitCode, cause) { super(message, "CONTAINER_EXECUTION_ERROR", cause); this.exitCode = exitCode; this.name = "ContainerExecutionError"; } }; var Configuration = class _Configuration { static instance; config; logger; constructor(config = {}) { const registryUser = process.env.VIBEKIT_REGISTRY_USER || config.registryUser || process.env.VIBEKIT_DOCKER_USER || config.dockerHubUser; this.config = { preferRegistryImages: this.getEnvBoolean("VIBEKIT_PREFER_REGISTRY", config.preferRegistryImages ?? true), dockerHubUser: registryUser, // Keep for backward compatibility registryUser, registryName: process.env.VIBEKIT_REGISTRY_NAME || config.registryName || "dockerhub", pushImages: this.getEnvBoolean("VIBEKIT_PUSH_IMAGES", config.pushImages ?? true), privateRegistry: process.env.VIBEKIT_REGISTRY || config.privateRegistry, autoInstall: this.getEnvBoolean("VIBEKIT_AUTO_INSTALL", config.autoInstall ?? false), retryAttempts: this.getEnvNumber("VIBEKIT_RETRY_ATTEMPTS", config.retryAttempts ?? 3), retryDelayMs: this.getEnvNumber("VIBEKIT_RETRY_DELAY", config.retryDelayMs ?? 1e3), connectionTimeout: this.getEnvNumber("VIBEKIT_CONNECTION_TIMEOUT", config.connectionTimeout ?? 3e4), configPath: process.env.VIBEKIT_CONFIG_PATH || config.configPath || join(homedir(), ".vibekit"), logger: config.logger || new ConsoleLogger() }; this.logger = this.config.logger; } static getInstance(config) { if (!_Configuration.instance) { _Configuration.instance = new _Configuration(config); } return _Configuration.instance; } getEnvBoolean(key, defaultValue) { const value = process.env[key]; if (value === void 0) return defaultValue; return value.toLowerCase() === "true"; } getEnvNumber(key, defaultValue) { const value = process.env[key]; if (value === void 0) return defaultValue; const num = parseInt(value, 10); return isNaN(num) ? defaultValue : num; } get() { return this.config; } getLogger() { return this.logger; } }; function sanitizeCommand(command) { const veryDangerous = [ "rm -rf /", "rm -rf /*", ":(){ :|:& };:", // Fork bomb "dd if=/dev/zero" // Disk fill ]; for (const pattern of veryDangerous) { if (command.includes(pattern)) { throw new Error(`Command contains dangerous pattern: ${pattern}`); } } return command; } async function createRegistryManager(config, logger) { const modulePath = "@vibe-kit/sdk/registry"; const registryModule = await import(modulePath).catch(() => null); if (!registryModule) { logger.warn("Registry module not available"); return null; } const { RegistryManager: RegistryManager2, DockerHubRegistry: DockerHubRegistry2, GitHubContainerRegistry, AWSECRRegistry } = registryModule; const registryName = config.registryName || "dockerhub"; const registryManager = new RegistryManager2({ defaultRegistry: registryName, logger }); switch (registryName) { case "ghcr": const ghcrRegistry = new GitHubContainerRegistry({ logger, githubToken: process.env.GITHUB_TOKEN }); registryManager.registerRegistry("ghcr", ghcrRegistry); break; case "ecr": const ecrRegistry = new AWSECRRegistry({ logger, awsRegion: process.env.AWS_REGION, awsAccountId: process.env.AWS_ACCOUNT_ID }); registryManager.registerRegistry("ecr", ecrRegistry); break; case "dockerhub": default: const dockerHubRegistry = new DockerHubRegistry2({ logger }); registryManager.registerRegistry("dockerhub", dockerHubRegistry); break; } return registryManager; } var ImageResolver = class { sharedResolver; config; constructor(config, logger) { this.config = config; const registryUser = config.registryUser || config.dockerHubUser; const sharedConfig = { preferRegistryImages: config.preferRegistryImages, pushImages: config.pushImages, privateRegistry: config.privateRegistry, dockerHubUser: registryUser, // For backward compatibility registryUser, registryName: config.registryName, retryAttempts: config.retryAttempts, retryDelayMs: config.retryDelayMs, logger }; this.initializeSharedResolver(sharedConfig); } async initializeSharedResolver(config) { try { const modulePath = "@vibe-kit/sdk/registry"; const registryModule = await import(modulePath).catch(() => null); if (!registryModule) { config.logger.warn("Registry module not available, using fallback image resolution"); return; } const { ImageResolver: SharedImageResolver } = registryModule; const registryManager = await createRegistryManager(this.config, config.logger); if (!registryManager) { config.logger.warn("Failed to create registry manager, using fallback"); return; } this.sharedResolver = new SharedImageResolver(config, registryManager); } catch (error) { config.logger.warn("Failed to initialize shared resolver:", error); } } async resolveImage(agentType) { if (!this.sharedResolver) { const registryUser = this.config.registryUser || this.config.dockerHubUser; if (agentType && registryUser) { return `${registryUser}/vibekit-${agentType}:latest`; } return agentType ? `vibekit-${agentType}:latest` : "ubuntu:24.04"; } return await this.sharedResolver.resolveImage(agentType); } }; var LocalSandboxInstance = class extends EventEmitter { constructor(sandboxId, envs, workDir, agentType, config) { super(); this.sandboxId = sandboxId; this.envs = envs; this.workDir = workDir; this.agentType = agentType; this.config = Configuration.getInstance(config).get(); this.logger = Configuration.getInstance().getLogger(); this.imageResolver = new ImageResolver(this.config, this.logger); } isRunning = true; workspaceDirectory = null; logger; imageResolver; config; get commands() { return { run: async (command, options) => { if (!this.isRunning) { throw new ContainerExecutionError("Sandbox instance is not running", -1); } let sanitizedCommand; try { sanitizedCommand = sanitizeCommand(command); } catch (error) { throw new ContainerExecutionError( `Invalid command: ${error instanceof Error ? error.message : String(error)}`, -1 ); } this.emit("update", JSON.stringify({ type: "start", command: sanitizedCommand, timestamp: Date.now() })); try { return await this.executeCommand(sanitizedCommand, options); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); this.emit("error", errorMessage); if (error instanceof ContainerExecutionError) { throw error; } throw new ContainerExecutionError( `Command execution failed: ${errorMessage}`, -1, error instanceof Error ? error : void 0 ); } finally { this.emit("update", JSON.stringify({ type: "end", command: sanitizedCommand, timestamp: Date.now() })); } } }; } async executeCommand(command, options) { let result = null; await connect(async (client) => { const image = await this.imageResolver.resolveImage(this.agentType); let container = client.container().from(image).withWorkdir(this.workDir || "/vibe0"); if (this.envs) { for (const [key, value] of Object.entries(this.envs)) { container = container.withEnvVariable(key, value); } } if (this.workspaceDirectory) { container = container.withDirectory( this.workDir || "/vibe0", this.workspaceDirectory ); } if (options?.background) { container = container.withExec(["sh", "-c", command], { experimentalPrivilegedNesting: true }); this.workspaceDirectory = await container.directory(this.workDir || "/vibe0"); result = { exitCode: 0, stdout: `Background process started: ${command}`, stderr: "" }; } else { const timeout = options?.timeoutMs || 12e4; const execContainer = container.withExec(["sh", "-c", command]); try { const [stdout, stderr, exitCode] = await Promise.race([ Promise.all([ execContainer.stdout(), execContainer.stderr(), execContainer.exitCode() ]), new Promise( (_, reject) => setTimeout(() => reject(new Error("Command execution timeout")), timeout) ) ]); this.workspaceDirectory = await execContainer.directory(this.workDir || "/vibe0"); if (stdout && options?.onStdout) { this.emitOutput("stdout", stdout, options.onStdout); } if (stderr && options?.onStderr) { this.emitOutput("stderr", stderr, options.onStderr); } result = { exitCode, stdout, stderr }; } catch (error) { if (error instanceof Error && error.message === "Command execution timeout") { throw new ContainerExecutionError("Command execution timeout", -1, error); } throw error; } } }); if (!result) { throw new ContainerExecutionError("Command execution failed - no result returned", -1); } return result; } emitOutput(type, output, callback) { const lines = output.split("\n").filter((line) => line.trim()); for (const line of lines) { this.emit("update", `${type.toUpperCase()}: ${line}`); if (callback) callback(line); } } async kill() { this.isRunning = false; this.workspaceDirectory = null; this.logger.debug(`Killed sandbox instance: ${this.sandboxId}`); } async pause() { this.logger.debug(`Pause requested for sandbox: ${this.sandboxId} (no-op)`); } async getHost(_port) { return "localhost"; } }; var LocalSandboxProvider = class { logger; config; constructor(config = {}) { this.config = Configuration.getInstance(config).get(); this.logger = Configuration.getInstance().getLogger(); } async create(envs, agentType, workingDirectory) { const timestamp = Date.now().toString(36); const randomSuffix = Math.random().toString(36).substring(2, 8); const sandboxId = `dagger-${agentType || "default"}-${timestamp}-${randomSuffix}`; const workDir = workingDirectory || "/vibe0"; this.logger.info(`Creating sandbox instance`, { sandboxId, agentType, workDir }); const instance = new LocalSandboxInstance( sandboxId, envs, workDir, agentType, this.config ); return instance; } async resume(sandboxId) { this.logger.info(`Resuming sandbox instance: ${sandboxId}`); return await this.create(); } async listEnvironments() { return []; } }; function createLocalProvider(config = {}) { return new LocalSandboxProvider(config); } async function prebuildAgentImages(selectedAgents) { const config = Configuration.getInstance().get(); const logger = Configuration.getInstance().getLogger(); try { const modulePath = "@vibe-kit/sdk/registry"; const registryModule = await import(modulePath).catch(() => null); if (registryModule) { const { ImageResolver: SharedImageResolver } = registryModule; const registryManager = await createRegistryManager(config, logger); if (registryManager) { const registryUser = config.registryUser || config.dockerHubUser; const imageResolver = new SharedImageResolver({ preferRegistryImages: config.preferRegistryImages, pushImages: config.pushImages, privateRegistry: config.privateRegistry, dockerHubUser: registryUser, // For backward compatibility registryUser, registryName: config.registryName, retryAttempts: config.retryAttempts, retryDelayMs: config.retryDelayMs, logger }, registryManager); return await imageResolver.prebuildImages(selectedAgents); } } } catch (error) { logger.warn("Failed to use shared image resolver, falling back to basic prebuilding:", error); } const allAgentTypes = ["claude", "codex", "opencode", "gemini", "grok"]; const agentTypes = selectedAgents?.length ? selectedAgents : allAgentTypes; const results = []; logger.info("Pre-caching agent images for faster startup (fallback mode)"); for (const agentType of agentTypes) { try { const imageResolver = new ImageResolver(config, logger); await imageResolver.resolveImage(agentType); results.push({ agentType, success: true, source: "cached" }); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); logger.error(`Failed to cache image for ${agentType}`, error); results.push({ agentType, success: false, error: errorMessage, source: "dockerfile" }); } } const successCount = results.filter((r) => r.success).length; logger.info(`Pre-cache complete: ${successCount}/${agentTypes.length} images ready`); return { success: successCount > 0, results }; } // src/setup/installer.ts import { exec as exec2 } from "child_process"; import { promisify as promisify2 } from "util"; var execAsync2 = promisify2(exec2); async function validateDependencies() { const issues = []; try { await execAsync2("docker --version"); try { await execAsync2("docker info"); } catch (error) { issues.push("Docker is installed but not running. Please start Docker."); } } catch (error) { issues.push("Docker is not installed. Please install Docker from https://docs.docker.com/get-docker/"); } try { await execAsync2("dagger version"); } catch (error) { issues.push("Dagger CLI is not installed. Please install from https://docs.dagger.io/install/"); } try { const { stdout } = await execAsync2("node --version"); const version = stdout.trim(); const majorVersion = parseInt(version.substring(1).split(".")[0]); if (majorVersion < 18) { issues.push(`Node.js ${version} detected. Node.js 18+ is recommended for optimal performance.`); } } catch (error) { issues.push("Node.js is not available. This may affect MCP server functionality."); } return { valid: issues.length === 0, issues }; } async function setupLocalProvider(options = {}) { const { skipPreBuild = false, selectedAgents, verbose = false } = options; const warnings = []; try { if (verbose) { console.log("\u{1F50D} Validating system dependencies..."); } const validation = await validateDependencies(); if (!validation.valid) { return { success: false, message: `Setup failed due to missing dependencies: ${validation.issues.map((issue) => ` \u274C ${issue}`).join("\n")}`, warnings }; } if (verbose) { console.log("\u2705 System dependencies validated"); } if (verbose) { console.log("\u{1F517} Testing Dagger engine connectivity..."); } try { await execAsync2("dagger query --help", { timeout: 1e4 }); if (verbose) { console.log("\u2705 Dagger engine connectivity verified"); } } catch (error) { warnings.push("Dagger engine test skipped (may start on first use)"); if (verbose) { console.log("\u26A0\uFE0F Dagger engine will start automatically on first use"); } } let preBuildResults; if (!skipPreBuild) { if (verbose) { console.log("\u{1F3D7}\uFE0F Pre-building agent images for faster startup..."); } try { const buildResult = await prebuildAgentImages(selectedAgents); preBuildResults = buildResult.results; if (buildResult.success) { const successCount = buildResult.results.filter((r) => r.success).length; if (verbose) { console.log(`\u2705 Pre-build completed: ${successCount}/${buildResult.results.length} images ready`); } } else { warnings.push("Some agent images failed to pre-build but can be built on first use"); } } catch (error) { warnings.push(`Pre-building failed: ${error instanceof Error ? error.message : String(error)}`); if (verbose) { console.log("\u26A0\uFE0F Agent images will be built on first use instead"); } } } else if (verbose) { console.log("\u23ED\uFE0F Skipping pre-build as requested"); } const successMessage = [ "Local provider setup completed successfully!", "", "\u{1F4CB} What's available:", " \u2022 Create sandboxes with agent-specific environments", " \u2022 Automatic Docker image caching for fast startup", " \u2022 Local development with containerized isolation", " \u2022 Git operations and PR creation support", "", "\u{1F680} Quick start:", " const provider = createLocalProvider();", ' const sandbox = await provider.create({}, "claude");', "" ]; if (preBuildResults) { const successfulBuilds = preBuildResults.filter((r) => r.success).map((r) => r.agentType); if (successfulBuilds.length > 0) { successMessage.push(`\u{1F3AF} Pre-built agents: ${successfulBuilds.join(", ")}`); } } if (warnings.length > 0) { successMessage.push(""); successMessage.push("\u26A0\uFE0F Warnings:"); warnings.forEach((warning) => successMessage.push(` \u2022 ${warning}`)); } return { success: true, message: successMessage.join("\n"), preBuildResults, warnings }; } catch (error) { return { success: false, message: `Setup failed: ${error instanceof Error ? error.message : String(error)}`, warnings }; } } async function prebuildSpecificAgents(agentTypes) { try { console.log(`\u{1F3D7}\uFE0F Pre-building images for: ${agentTypes.join(", ")}`); const buildResult = await prebuildAgentImages(); const requestedResults = buildResult.results.filter((r) => agentTypes.includes(r.agentType)); const successCount = requestedResults.filter((r) => r.success).length; const failedAgents = requestedResults.filter((r) => !r.success).map((r) => `${r.agentType}: ${r.error}`); let message = `Pre-build completed: ${successCount}/${agentTypes.length} requested images ready`; if (failedAgents.length > 0) { message += ` Failed builds: ${failedAgents.map((f) => ` \u274C ${f}`).join("\n")}`; } return { success: successCount > 0, message, preBuildResults: requestedResults }; } catch (error) { return { success: false, message: `Pre-build failed: ${error instanceof Error ? error.message : String(error)}` }; } } async function checkSetupStatus() { const issues = []; const recommendations = []; const validation = await validateDependencies(); issues.push(...validation.issues); try { const agentTypes = ["claude", "codex", "opencode", "gemini"]; let hasPreBuiltImages = false; for (const agentType of agentTypes) { try { const { stdout } = await execAsync2(`docker images -q vibekit-${agentType}:latest`); if (stdout.trim()) { hasPreBuiltImages = true; break; } } catch (error) { } } if (!hasPreBuiltImages) { recommendations.push("Consider pre-building agent images for faster startup: prebuildAgentImages()"); } } catch (error) { recommendations.push("Unable to check pre-built images status"); } return { isSetup: issues.length === 0, issues, recommendations }; } async function cleanupPreBuiltImages() { const removed = []; const errors = []; const agentTypes = ["claude", "codex", "opencode", "gemini"]; for (const agentType of agentTypes) { const imageTag = `vibekit-${agentType}:latest`; try { const { stdout } = await execAsync2(`docker images -q ${imageTag}`); if (stdout.trim()) { await execAsync2(`docker rmi ${imageTag}`); removed.push(imageTag); } } catch (error) { errors.push(`Failed to remove ${imageTag}: ${error instanceof Error ? error.message : String(error)}`); } } return { success: errors.length === 0, removed, errors }; } // src/storage/environment-store.ts import { readFile, writeFile, mkdir } from "fs/promises"; import { existsSync } from "fs"; import { join as join2, dirname } from "path"; import { homedir as homedir2 } from "os"; var EnvironmentStore = class { storePath; lockPath; constructor(customPath) { const basePath = customPath || join2(homedir2(), ".vibekit"); this.storePath = join2(basePath, "environments.json"); this.lockPath = join2(basePath, "environments.lock"); } /** * Ensure storage directory exists */ async ensureStorageDir() { const dir = dirname(this.storePath); if (!existsSync(dir)) { await mkdir(dir, { recursive: true }); } } /** * Simple file-based locking for concurrent access */ async acquireLock() { await this.ensureStorageDir(); let attempts = 0; const maxAttempts = 50; while (attempts < maxAttempts) { try { if (!existsSync(this.lockPath)) { await writeFile(this.lockPath, process.pid.toString()); return; } await new Promise((resolve) => setTimeout(resolve, 100)); attempts++; } catch (error) { attempts++; } } throw new Error("Could not acquire lock for environment storage"); } /** * Release the file lock */ async releaseLock() { try { if (existsSync(this.lockPath)) { const lockContent = await readFile(this.lockPath, "utf-8"); if (lockContent.trim() === process.pid.toString()) { await writeFile(this.lockPath, ""); } } } catch (error) { } } /** * Load all environments from storage */ async load() { await this.acquireLock(); try { await this.ensureStorageDir(); if (!existsSync(this.storePath)) { return []; } const content = await readFile(this.storePath, "utf-8"); const data = JSON.parse(content); return data.map((env) => ({ ...env, created: new Date(env.created), lastUsed: new Date(env.lastUsed) })); } catch (error) { if (error instanceof SyntaxError) { return []; } throw error; } finally { await this.releaseLock(); } } /** * Save all environments to storage */ async saveAll(environments) { await this.ensureStorageDir(); await writeFile(this.storePath, JSON.stringify(environments, null, 2)); } /** * Save a new environment */ async save(env) { await this.acquireLock(); try { const environments = await this.load(); const existing = environments.find((e) => e.name === env.name); if (existing) { throw new Error(`Environment with name '${env.name}' already exists`); } environments.push(env); await this.saveAll(environments); } finally { await this.releaseLock(); } } /** * Update an existing environment */ async update(id, updates) { await this.acquireLock(); try { const environments = await this.load(); const index = environments.findIndex((env) => env.id === id); if (index === -1) { throw new Error(`Environment with id '${id}' not found`); } environments[index] = { ...environments[index], ...updates }; await this.saveAll(environments); } finally { await this.releaseLock(); } } /** * Delete an environment by ID */ async delete(id) { await this.acquireLock(); try { const environments = await this.load(); const filteredEnvironments = environments.filter((env) => env.id !== id); if (filteredEnvironments.length === environments.length) { throw new Error(`Environment with id '${id}' not found`); } await this.saveAll(filteredEnvironments); } finally { await this.releaseLock(); } } /** * Find environment by ID */ async findById(id) { const environments = await this.load(); return environments.find((env) => env.id === id) || null; } /** * Find environment by name */ async findByName(name) { const environments = await this.load(); return environments.find((env) => env.name === name) || null; } /** * Get environments filtered by status */ async getByStatus(status) { const environments = await this.load(); return environments.filter((env) => env.status === status); } /** * Get environments filtered by agent type */ async getByAgentType(agentType) { const environments = await this.load(); return environments.filter((env) => env.agentType === agentType); } /** * Clean up old environments (older than specified days) */ async cleanup(olderThanDays = 30) { await this.acquireLock(); try { const environments = await this.load(); const cutoffDate = /* @__PURE__ */ new Date(); cutoffDate.setDate(cutoffDate.getDate() - olderThanDays); const toRemove = environments.filter( (env) => env.lastUsed < cutoffDate && env.status !== "running" ); const remaining = environments.filter( (env) => env.lastUsed >= cutoffDate || env.status === "running" ); await this.saveAll(remaining); return toRemove.map((env) => env.name); } finally { await this.releaseLock(); } } }; export { EnvironmentStore, LocalSandboxProvider as LocalDaggerSandboxProvider, LocalSandboxProvider, checkDockerLogin, checkSetupStatus, cleanupPreBuiltImages, createLocalProvider, getVibeKitConfig, prebuildAgentImages, prebuildSpecificAgents, saveVibeKitConfig, setupLocalProvider, setupUserDockerRegistry, uploadImagesToUserAccount, validateDependencies }; //# sourceMappingURL=index.js.map