Paper2Code / lib /sandbox /sandbox-manager.ts
AUXteam's picture
Upload folder using huggingface_hub
d530f14 verified
import { SandboxProvider } from './types';
import { SandboxFactory } from './factory';
interface SandboxInfo {
sandboxId: string;
provider: SandboxProvider;
createdAt: Date;
lastAccessed: Date;
}
class SandboxManager {
private sandboxes: Map<string, SandboxInfo> = new Map();
private activeSandboxId: string | null = null;
/**
* Get or create a sandbox provider for the given sandbox ID
*/
async getOrCreateProvider(sandboxId: string): Promise<SandboxProvider> {
// Check if we already have this sandbox
const existing = this.sandboxes.get(sandboxId);
if (existing) {
existing.lastAccessed = new Date();
return existing.provider;
}
// Try to reconnect to existing sandbox
try {
const provider = SandboxFactory.create();
// For E2B provider, try to reconnect
if (provider.constructor.name === 'E2BProvider') {
// E2B sandboxes can be reconnected using the sandbox ID
const reconnected = await (provider as any).reconnect(sandboxId);
if (reconnected) {
this.sandboxes.set(sandboxId, {
sandboxId,
provider,
createdAt: new Date(),
lastAccessed: new Date()
});
this.activeSandboxId = sandboxId;
return provider;
}
}
// For Vercel or if reconnection failed, return the new provider
// The caller will need to handle creating a new sandbox
return provider;
} catch (error) {
console.error(`[SandboxManager] Error reconnecting to sandbox ${sandboxId}:`, error);
throw error;
}
}
/**
* Register a new sandbox
*/
registerSandbox(sandboxId: string, provider: SandboxProvider): void {
this.sandboxes.set(sandboxId, {
sandboxId,
provider,
createdAt: new Date(),
lastAccessed: new Date()
});
this.activeSandboxId = sandboxId;
}
/**
* Get the active sandbox provider
*/
getActiveProvider(): SandboxProvider | null {
if (!this.activeSandboxId) {
return null;
}
const sandbox = this.sandboxes.get(this.activeSandboxId);
if (sandbox) {
sandbox.lastAccessed = new Date();
return sandbox.provider;
}
return null;
}
/**
* Get a specific sandbox provider
*/
getProvider(sandboxId: string): SandboxProvider | null {
const sandbox = this.sandboxes.get(sandboxId);
if (sandbox) {
sandbox.lastAccessed = new Date();
return sandbox.provider;
}
return null;
}
/**
* Set the active sandbox
*/
setActiveSandbox(sandboxId: string): boolean {
if (this.sandboxes.has(sandboxId)) {
this.activeSandboxId = sandboxId;
return true;
}
return false;
}
/**
* Terminate a sandbox
*/
async terminateSandbox(sandboxId: string): Promise<void> {
const sandbox = this.sandboxes.get(sandboxId);
if (sandbox) {
try {
await sandbox.provider.terminate();
} catch (error) {
console.error(`[SandboxManager] Error terminating sandbox ${sandboxId}:`, error);
}
this.sandboxes.delete(sandboxId);
if (this.activeSandboxId === sandboxId) {
this.activeSandboxId = null;
}
}
}
/**
* Terminate all sandboxes
*/
async terminateAll(): Promise<void> {
const promises = Array.from(this.sandboxes.values()).map(sandbox =>
sandbox.provider.terminate().catch(err =>
console.error(`[SandboxManager] Error terminating sandbox ${sandbox.sandboxId}:`, err)
)
);
await Promise.all(promises);
this.sandboxes.clear();
this.activeSandboxId = null;
}
/**
* Clean up old sandboxes (older than maxAge milliseconds)
*/
async cleanup(maxAge: number = 3600000): Promise<void> {
const now = new Date();
const toDelete: string[] = [];
for (const [id, info] of this.sandboxes.entries()) {
const age = now.getTime() - info.lastAccessed.getTime();
if (age > maxAge) {
toDelete.push(id);
}
}
for (const id of toDelete) {
await this.terminateSandbox(id);
}
}
}
// Export singleton instance
export const sandboxManager = new SandboxManager();
// Also maintain backward compatibility with global state
declare global {
var sandboxManager: SandboxManager;
}
// Ensure the global reference points to our singleton
global.sandboxManager = sandboxManager;