From 47d532e869d7e2435fd3d87173acb677dc9f70bd Mon Sep 17 00:00:00 2001 From: Eyejoker Date: Tue, 31 Mar 2026 13:34:21 +0900 Subject: [PATCH] fix: sync refreshed OAuth tokens to process.env for container exec Token refresh updated internal array and .env file but not process.env, so docker exec -e kept injecting the stale startup token. Now syncs to process.env after refresh. Container kill on refresh is no longer needed since docker exec picks up the latest token each turn. --- src/container-runner.ts | 44 +++++------------------------------------ src/token-refresh.ts | 10 ++++++++++ 2 files changed, 15 insertions(+), 39 deletions(-) diff --git a/src/container-runner.ts b/src/container-runner.ts index e30dd9d..de25084 100644 --- a/src/container-runner.ts +++ b/src/container-runner.ts @@ -192,47 +192,13 @@ export function stopReviewerContainer(groupFolder: string): void { } /** - * Remove all running reviewer containers so they are recreated on the next - * reviewer turn with fresh credentials. Called after token rotation/refresh - * because the persistent container's Claude Code SDK process holds the token - * that was baked in at `docker run` time — `docker exec -e` only updates the - * env for newly spawned processes, not the already-running SDK. + * No-op — container recreation is no longer needed after token refresh. + * Tokens are now synced to process.env, and `docker exec -e` injects + * the latest token at each turn. Kept as a no-op to avoid breaking callers. */ export function recreateAllReviewerContainers(): void { - try { - const output = execFileSync( - CONTAINER_RUNTIME_BIN, - ['ps', '--filter', 'name=ejclaw-reviewer-', '--format', '{{.Names}}'], - { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 10000 }, - ).trim(); - - const containers = output.split('\n').filter(Boolean); - if (containers.length === 0) { - logger.debug('No reviewer containers to recreate after token refresh'); - return; - } - - for (const name of containers) { - try { - execFileSync(CONTAINER_RUNTIME_BIN, ['rm', '-f', name], { - stdio: 'pipe', - timeout: 10000, - }); - } catch { - /* already gone */ - } - } - - logger.info( - { count: containers.length, names: containers }, - 'Removed reviewer containers after token refresh — will recreate on next turn', - ); - } catch (err) { - logger.warn( - { err }, - 'Failed to list/remove reviewer containers after token refresh', - ); - } + // Intentionally empty — docker exec -e picks up refreshed tokens + // from process.env without needing to recreate the container. } // ── Mount builder ───────────────────────────────────────────────── diff --git a/src/token-refresh.ts b/src/token-refresh.ts index 4d2fde6..020fb41 100644 --- a/src/token-refresh.ts +++ b/src/token-refresh.ts @@ -316,6 +316,16 @@ async function checkAndRefreshAll(): Promise { } if (anyRefreshed) { + // Sync refreshed tokens to process.env so docker exec picks them up + const refreshedTokens = getAllTokens(); + if (refreshedTokens.length > 0) { + process.env.CLAUDE_CODE_OAUTH_TOKEN = refreshedTokens[0].token; + if (refreshedTokens.length > 1) { + process.env.CLAUDE_CODE_OAUTH_TOKENS = refreshedTokens + .map((t) => t.token) + .join(','); + } + } updateEnvTokens(); if (onTokenRefreshedCallback) { try {