@vibe-kit/dagger
Version:
Local sandbox provider for Vibekit using Dagger
981 lines (974 loc) • 33.3 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
EnvironmentStore: () => EnvironmentStore,
LocalDaggerSandboxProvider: () => LocalSandboxProvider,
LocalSandboxProvider: () => LocalSandboxProvider,
checkDockerLogin: () => checkDockerLogin,
checkSetupStatus: () => checkSetupStatus,
cleanupPreBuiltImages: () => cleanupPreBuiltImages,
createLocalProvider: () => createLocalProvider,
getVibeKitConfig: () => getVibeKitConfig,
prebuildAgentImages: () => prebuildAgentImages,
prebuildSpecificAgents: () => prebuildSpecificAgents,
saveVibeKitConfig: () => saveVibeKitConfig,
setupLocalProvider: () => setupLocalProvider,
setupUserDockerRegistry: () => setupUserDockerRegistry,
uploadImagesToUserAccount: () => uploadImagesToUserAccount,
validateDependencies: () => validateDependencies
});
module.exports = __toCommonJS(index_exports);
// src/dagger/vibekit-dagger.ts
var import_dagger = require("@dagger.io/dagger");
var import_child_process = require("child_process");
var import_util = require("util");
var import_path = require("path");
var import_events = require("events");
var import_os = require("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 = (0, import_util.promisify)(import_child_process.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 || (0, import_path.join)((0, import_os.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 import_events.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 (0, import_dagger.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
var import_child_process2 = require("child_process");
var import_util2 = require("util");
var execAsync2 = (0, import_util2.promisify)(import_child_process2.exec);
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
var import_promises = require("fs/promises");
var import_fs = require("fs");
var import_path2 = require("path");
var import_os2 = require("os");
var EnvironmentStore = class {
storePath;
lockPath;
constructor(customPath) {
const basePath = customPath || (0, import_path2.join)((0, import_os2.homedir)(), ".vibekit");
this.storePath = (0, import_path2.join)(basePath, "environments.json");
this.lockPath = (0, import_path2.join)(basePath, "environments.lock");
}
/**
* Ensure storage directory exists
*/
async ensureStorageDir() {
const dir = (0, import_path2.dirname)(this.storePath);
if (!(0, import_fs.existsSync)(dir)) {
await (0, import_promises.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 (!(0, import_fs.existsSync)(this.lockPath)) {
await (0, import_promises.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 ((0, import_fs.existsSync)(this.lockPath)) {
const lockContent = await (0, import_promises.readFile)(this.lockPath, "utf-8");
if (lockContent.trim() === process.pid.toString()) {
await (0, import_promises.writeFile)(this.lockPath, "");
}
}
} catch (error) {
}
}
/**
* Load all environments from storage
*/
async load() {
await this.acquireLock();
try {
await this.ensureStorageDir();
if (!(0, import_fs.existsSync)(this.storePath)) {
return [];
}
const content = await (0, import_promises.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 (0, import_promises.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();
}
}
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
EnvironmentStore,
LocalDaggerSandboxProvider,
LocalSandboxProvider,
checkDockerLogin,
checkSetupStatus,
cleanupPreBuiltImages,
createLocalProvider,
getVibeKitConfig,
prebuildAgentImages,
prebuildSpecificAgents,
saveVibeKitConfig,
setupLocalProvider,
setupUserDockerRegistry,
uploadImagesToUserAccount,
validateDependencies
});
//# sourceMappingURL=index.cjs.map