MCPcopy
hub / github.com/stephengpope/thepopebot / runHeadlessContainer

Function runHeadlessContainer

lib/tools/docker.js:417–493  ·  view source on GitHub ↗

* Create and start a headless coding agent container. * Runs a task via the agent, commits, and merges back. Ephemeral — exits when done. * @param {object} options * @param {string} options.containerName - Docker container name * @param {string} options.repo - GitHub repo full name * @param {st

({ containerName, repo, branch, featureBranch, workspaceId, taskPrompt, mode = 'plan', codingAgent, systemPrompt, continueSession = true, injectSecrets, scope, userId })

Source from the content-addressed store, hash-verified

415 * @returns {Promise<{containerId: string, containerName: string}>}
416 */
417async function runHeadlessContainer({ containerName, repo, branch, featureBranch, workspaceId, taskPrompt, mode = 'plan', codingAgent, systemPrompt, continueSession = true, injectSecrets, scope, userId }) {
418 const agent = codingAgent || getConfig('CODING_AGENT') || 'claude-code';
419 const version = process.env.THEPOPEBOT_VERSION;
420 const image = `stephengpope/thepopebot:coding-agent-${agent}-${version}`;
421
422 const env = [
423 `RUNTIME=headless`,
424 `REPO=${repo}`,
425 `BRANCH=${branch}`,
426 `PROMPT=${taskPrompt}`,
427 `USER_ID=${userId}`,
428 ];
429 if (featureBranch) {
430 env.push(`FEATURE_BRANCH=${featureBranch}`);
431 }
432 if (mode) {
433 // Map 'dangerous' to 'code', keep 'plan' as-is
434 const permission = mode === 'dangerous' ? 'code' : mode;
435 env.push(`PERMISSION=${permission}`);
436 }
437 if (continueSession) {
438 env.push(`CONTINUE_SESSION=1`);
439 }
440 if (scope) {
441 env.push(`SCOPE=${scope}`);
442 }
443
444 // Auth env vars based on agent type
445 const { env: authEnv, backendApi } = buildAgentAuthEnv(agent);
446 env.push(...authEnv);
447
448 const ghToken = getConfig('GH_TOKEN');
449 if (ghToken) {
450 env.push(`GH_TOKEN=${ghToken}`);
451 }
452
453 // Inject agent job secrets when running in agent chat mode
454 if (injectSecrets) {
455 const { getAllAgentJobSecrets } = await import('../db/config.js');
456 const jobSecrets = getAllAgentJobSecrets();
457 for (const { key, value } of jobSecrets) {
458 if (value !== null && !env.some(e => e.startsWith(`${key}=`))) {
459 env.push(`${key}=${value}`);
460 }
461 }
462 // Create per-container API key for agent-secrets access
463 const { createAgentJobApiKey } = await import('../db/api-keys.js');
464 const { key: agentJobToken } = createAgentJobApiKey(containerName);
465 env.push(`AGENT_JOB_TOKEN=${agentJobToken}`);
466 const appUrl = getConfig('APP_URL');
467 if (appUrl) env.push(`APP_URL=${appUrl}`);
468 }
469
470 const hostConfig = {};
471 if (workspaceId) {
472 const dir = workspaceDir(workspaceId);
473 const wsDir = path.join(dir, 'workspace');
474 fs.mkdirSync(wsDir, { recursive: true });

Callers 1

streamViaContainerFunction · 0.85

Calls 8

getConfigFunction · 0.90
buildAgentAuthEnvFunction · 0.85
getAllAgentJobSecretsFunction · 0.85
createAgentJobApiKeyFunction · 0.85
workspaceDirFunction · 0.85
resolveHostPathFunction · 0.85
runContainerFunction · 0.85
pushMethod · 0.80

Tested by

no test coverage detected