diff --git a/packages/cli/src/shared/agent-setup.ts b/packages/cli/src/shared/agent-setup.ts index 9412dd0cd..a0c8cf0b0 100644 --- a/packages/cli/src/shared/agent-setup.ts +++ b/packages/cli/src/shared/agent-setup.ts @@ -266,14 +266,33 @@ export async function offerGithubAuth(runner: CloudRunner, explicitlyRequested?: } let ghCmd = "curl --proto '=https' -fsSL https://openrouter.ai/labs/spawn/shared/github-auth.sh | bash"; + // Upload the token to a remote temp file so it never appears in `ps auxe` + // process listings. We use runner.uploadFile() (SCP) — the same proven + // pattern as uploadConfigFile(). A heredoc won't work here because all + // cloud runners wrap commands in `bash -c ${shellQuote(cmd)}`, and + // heredocs are not valid inside single-quoted `bash -c '...'` strings. + let remoteTokenPath = ""; if (githubToken) { - const tokenB64 = Buffer.from(githubToken).toString("base64"); - ghCmd = `export GITHUB_TOKEN=$(printf '%s' ${shellQuote(tokenB64)} | base64 -d) && ${ghCmd}`; + const localTmpFile = join(getTmpDir(), `spawn_gh_token_${Date.now()}_${Math.random().toString(36).slice(2)}`); + remoteTokenPath = `/tmp/spawn_gh_token_${Date.now()}`; + writeFileSync(localTmpFile, githubToken, { + mode: 0o600, + }); + const uploadResult = await asyncTryCatch(() => runner.uploadFile(localTmpFile, remoteTokenPath)); + tryCatchIf(isOperationalError, () => unlinkSync(localTmpFile)); + if (!uploadResult.ok) { + throw uploadResult.error; + } + ghCmd = `export GITHUB_TOKEN=$(cat ${shellQuote(remoteTokenPath)}) && rm -f ${shellQuote(remoteTokenPath)} && ${ghCmd}`; } logStep("Installing and authenticating GitHub CLI on the remote server..."); const ghSetup = await asyncTryCatchIf(isOperationalError, () => runner.runServer(ghCmd)); if (!ghSetup.ok) { + // Best-effort cleanup of remote token file if the command failed before rm ran + if (remoteTokenPath) { + await asyncTryCatchIf(isOperationalError, () => runner.runServer(`rm -f ${shellQuote(remoteTokenPath)}`)); + } logWarn("GitHub CLI setup failed (non-fatal, continuing)"); }