(
sessionId: string,
options?: { portMappings?: Record<number, number> }
)
| 48 | * Create a new container for a chat session |
| 49 | */ |
| 50 | export async function createContainer( |
| 51 | sessionId: string, |
| 52 | options?: { portMappings?: Record<number, number> } |
| 53 | ): Promise<Docker.Container> { |
| 54 | try { |
| 55 | logger.info('Creating Docker container', { sessionId, portMappings: options?.portMappings }); |
| 56 | |
| 57 | await ensureImageExists(); |
| 58 | |
| 59 | // Build port bindings if provided |
| 60 | const portBindings: Record<string, Array<{ HostPort: string }>> = {}; |
| 61 | const exposedPorts: Record<string, object> = {}; |
| 62 | |
| 63 | if (options?.portMappings) { |
| 64 | for (const [containerPort, hostPort] of Object.entries(options.portMappings)) { |
| 65 | const portKey = `${containerPort}/tcp`; |
| 66 | portBindings[portKey] = [{ HostPort: String(hostPort) }]; |
| 67 | exposedPorts[portKey] = {}; |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | const container = await docker.createContainer({ |
| 72 | Image: 'pilot-agent:latest', |
| 73 | name: `pilot-session-${sessionId}`, |
| 74 | Env: [ |
| 75 | `GITHUB_TOKEN=${process.env.GITHUB_TOKEN || ''}`, |
| 76 | ], |
| 77 | WorkingDir: '/workspace', |
| 78 | ExposedPorts: Object.keys(exposedPorts).length > 0 ? exposedPorts : undefined, |
| 79 | HostConfig: { |
| 80 | AutoRemove: false, |
| 81 | Memory: 2 * 1024 * 1024 * 1024, // 2GB memory limit |
| 82 | MemorySwap: 2 * 1024 * 1024 * 1024, |
| 83 | CpuPeriod: 100000, |
| 84 | CpuQuota: 200000, // 2 CPU cores max |
| 85 | NetworkMode: 'bridge', |
| 86 | PortBindings: Object.keys(portBindings).length > 0 ? portBindings : undefined, |
| 87 | }, |
| 88 | AttachStdout: true, |
| 89 | AttachStderr: true, |
| 90 | Tty: false, |
| 91 | }); |
| 92 | |
| 93 | await container.start(); |
| 94 | |
| 95 | // Verify container is actually running |
| 96 | const inspection = await container.inspect(); |
| 97 | if (inspection.State.Status !== 'running') { |
| 98 | throw new Error( |
| 99 | `Container failed to start properly. Status: ${inspection.State.Status}, ` + |
| 100 | `Error: ${inspection.State.Error || 'unknown'}. This may indicate Docker is out of disk space.` |
| 101 | ); |
| 102 | } |
| 103 | |
| 104 | containerCache.set(sessionId, container); |
| 105 | |
| 106 | logger.info('Container created and started', { sessionId, containerId: container.id }); |
| 107 | return container; |
no test coverage detected