mirror of
https://github.com/OpenRouterTeam/spawn.git
synced 2026-05-19 16:39:50 +00:00
fix: persist npm-global PATH to .profile/.bash_profile/.bashrc for SSH reconnect (#2399)
After SSH reconnect, agent commands (openclaw, codex, kilocode, junie) were not found because PATH was only written to ~/.bashrc, which is not sourced by login shells. Login shells (used by SSH) source ~/.profile or ~/.bash_profile instead. Changes: - Write .spawnrc sourcing to ~/.profile and ~/.bash_profile in addition to ~/.bashrc and ~/.zshrc (orchestrate.ts) - Write npm-global PATH export to ~/.profile and ~/.bash_profile for all npm-installed agents: OpenClaw, Codex, Kilo Code, Junie (agent-setup.ts) - Write Claude Code PATH to ~/.profile and ~/.bash_profile (agent-setup.ts) - Write OpenCode PATH to ~/.profile and ~/.bash_profile (agent-setup.ts) - Extract NPM_GLOBAL_PATH_PERSIST constant to DRY up repeated shell snippets - Fix e2e provision.sh to also write .spawnrc sourcing to login shell configs - Bump CLI version to 0.15.32 Fixes #2394 Agent: code-health Co-authored-by: B <6723574+louisgv@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
5b47ad8da9
commit
705687de17
3 changed files with 24 additions and 17 deletions
|
|
@ -97,7 +97,7 @@ async function installClaudeCode(runner: CloudRunner): Promise<void> {
|
|||
logStep("Installing Claude Code...");
|
||||
|
||||
const claudePath = "$HOME/.npm-global/bin:$HOME/.claude/local/bin:$HOME/.local/bin:$HOME/.bun/bin:$HOME/.n/bin";
|
||||
const pathSetup = `for rc in ~/.bashrc ~/.zshrc; do grep -q '.claude/local/bin' "$rc" 2>/dev/null || printf '\\n# Claude Code PATH\\nexport PATH="$HOME/.claude/local/bin:$HOME/.local/bin:$HOME/.bun/bin:$PATH"\\n' >> "$rc"; done`;
|
||||
const pathSetup = `for rc in ~/.bashrc ~/.profile ~/.bash_profile ~/.zshrc; do grep -q '.claude/local/bin' "$rc" 2>/dev/null || printf '\\n# Claude Code PATH\\nexport PATH="$HOME/.claude/local/bin:$HOME/.local/bin:$HOME/.bun/bin:$PATH"\\n' >> "$rc"; done`;
|
||||
const finalize = `claude install --force 2>/dev/null || true; ${pathSetup}`;
|
||||
|
||||
const script = [
|
||||
|
|
@ -534,7 +534,7 @@ async function ensureSwapSpace(runner: CloudRunner, sizeMb = 1024): Promise<void
|
|||
// ─── OpenCode Install Command ────────────────────────────────────────────────
|
||||
|
||||
function openCodeInstallCmd(): string {
|
||||
return 'OC_ARCH=$(uname -m); case "$OC_ARCH" in aarch64) OC_ARCH=arm64;; x86_64) OC_ARCH=x64;; esac; OC_OS=$(uname -s | tr A-Z a-z); mkdir -p /tmp/opencode-install "$HOME/.opencode/bin" && curl --proto \'=https\' -fsSL -o /tmp/opencode-install/oc.tar.gz "https://github.com/sst/opencode/releases/latest/download/opencode-${OC_OS}-${OC_ARCH}.tar.gz" && if tar -tzf /tmp/opencode-install/oc.tar.gz | grep -qE \'(^/|\\.\\.)\'; then echo "Tarball contains unsafe paths" >&2; exit 1; fi && tar xzf /tmp/opencode-install/oc.tar.gz -C /tmp/opencode-install && mv /tmp/opencode-install/opencode "$HOME/.opencode/bin/" && rm -rf /tmp/opencode-install && grep -q ".opencode/bin" "$HOME/.bashrc" 2>/dev/null || echo \'export PATH="$HOME/.opencode/bin:$PATH"\' >> "$HOME/.bashrc"; grep -q ".opencode/bin" "$HOME/.zshrc" 2>/dev/null || echo \'export PATH="$HOME/.opencode/bin:$PATH"\' >> "$HOME/.zshrc" 2>/dev/null; export PATH="$HOME/.opencode/bin:$PATH"';
|
||||
return 'OC_ARCH=$(uname -m); case "$OC_ARCH" in aarch64) OC_ARCH=arm64;; x86_64) OC_ARCH=x64;; esac; OC_OS=$(uname -s | tr A-Z a-z); mkdir -p /tmp/opencode-install "$HOME/.opencode/bin" && curl --proto \'=https\' -fsSL -o /tmp/opencode-install/oc.tar.gz "https://github.com/sst/opencode/releases/latest/download/opencode-${OC_OS}-${OC_ARCH}.tar.gz" && if tar -tzf /tmp/opencode-install/oc.tar.gz | grep -qE \'(^/|\\.\\.)\'; then echo "Tarball contains unsafe paths" >&2; exit 1; fi && tar xzf /tmp/opencode-install/oc.tar.gz -C /tmp/opencode-install && mv /tmp/opencode-install/opencode "$HOME/.opencode/bin/" && rm -rf /tmp/opencode-install && for _rc in "$HOME/.bashrc" "$HOME/.profile" "$HOME/.bash_profile"; do grep -q ".opencode/bin" "$_rc" 2>/dev/null || echo \'export PATH="$HOME/.opencode/bin:$PATH"\' >> "$_rc"; done; { [ ! -f "$HOME/.zshrc" ] || grep -q ".opencode/bin" "$HOME/.zshrc" 2>/dev/null || echo \'export PATH="$HOME/.opencode/bin:$PATH"\' >> "$HOME/.zshrc"; }; export PATH="$HOME/.opencode/bin:$PATH"';
|
||||
}
|
||||
|
||||
// ─── npm prefix helper ────────────────────────────────────────────────────────
|
||||
|
|
@ -557,6 +557,19 @@ const NPM_PREFIX_SETUP =
|
|||
'mkdir -p ~/.npm-global/bin; _NPM_G_FLAGS="--prefix $HOME/.npm-global"; fi; ' +
|
||||
'export PATH="$HOME/.npm-global/bin:$PATH"';
|
||||
|
||||
/**
|
||||
* Shell snippet that persists ~/.npm-global/bin in PATH across all shell config
|
||||
* files: ~/.bashrc, ~/.profile, ~/.bash_profile, and ~/.zshrc.
|
||||
* Login shells (SSH reconnect) source ~/.profile or ~/.bash_profile, not ~/.bashrc,
|
||||
* so writing to ~/.bashrc alone is insufficient.
|
||||
*/
|
||||
const NPM_GLOBAL_PATH_PERSIST =
|
||||
"for _rc in ~/.bashrc ~/.profile ~/.bash_profile; do " +
|
||||
"grep -qF '.npm-global/bin' \"$_rc\" 2>/dev/null || " +
|
||||
'echo \'export PATH="$HOME/.npm-global/bin:$PATH"\' >> "$_rc"; done; ' +
|
||||
"{ [ ! -f ~/.zshrc ] || grep -qF '.npm-global/bin' ~/.zshrc 2>/dev/null || " +
|
||||
"echo 'export PATH=\"$HOME/.npm-global/bin:$PATH\"' >> ~/.zshrc; }";
|
||||
|
||||
// ─── Default Agent Definitions ───────────────────────────────────────────────
|
||||
|
||||
const ZEROCLAW_INSTALL_URL =
|
||||
|
|
@ -590,9 +603,7 @@ function createAgents(runner: CloudRunner): Record<string, AgentConfig> {
|
|||
installAgent(
|
||||
runner,
|
||||
"Codex CLI",
|
||||
`${NPM_PREFIX_SETUP} && npm install -g \${_NPM_G_FLAGS} @openai/codex && ` +
|
||||
"{ grep -qF '.npm-global/bin' ~/.bashrc 2>/dev/null || echo 'export PATH=\"$HOME/.npm-global/bin:$PATH\"' >> ~/.bashrc; } && " +
|
||||
"{ [ ! -f ~/.zshrc ] || grep -qF '.npm-global/bin' ~/.zshrc 2>/dev/null || echo 'export PATH=\"$HOME/.npm-global/bin:$PATH\"' >> ~/.zshrc; }",
|
||||
`${NPM_PREFIX_SETUP} && npm install -g \${_NPM_G_FLAGS} @openai/codex && ${NPM_GLOBAL_PATH_PERSIST}`,
|
||||
),
|
||||
envVars: (apiKey) => [
|
||||
`OPENROUTER_API_KEY=${apiKey}`,
|
||||
|
|
@ -610,9 +621,7 @@ function createAgents(runner: CloudRunner): Record<string, AgentConfig> {
|
|||
await installAgent(
|
||||
runner,
|
||||
"openclaw",
|
||||
`source ~/.bashrc 2>/dev/null; ${NPM_PREFIX_SETUP} && npm install -g \${_NPM_G_FLAGS} openclaw && ` +
|
||||
"{ grep -qF '.npm-global/bin' ~/.bashrc 2>/dev/null || echo 'export PATH=\"$HOME/.npm-global/bin:$PATH\"' >> ~/.bashrc; } && " +
|
||||
"{ [ ! -f ~/.zshrc ] || grep -qF '.npm-global/bin' ~/.zshrc 2>/dev/null || echo 'export PATH=\"$HOME/.npm-global/bin:$PATH\"' >> ~/.zshrc; }",
|
||||
`source ~/.bashrc 2>/dev/null; ${NPM_PREFIX_SETUP} && npm install -g \${_NPM_G_FLAGS} openclaw && ${NPM_GLOBAL_PATH_PERSIST}`,
|
||||
);
|
||||
},
|
||||
envVars: (apiKey) => [
|
||||
|
|
@ -647,9 +656,7 @@ function createAgents(runner: CloudRunner): Record<string, AgentConfig> {
|
|||
installAgent(
|
||||
runner,
|
||||
"Kilo Code",
|
||||
`${NPM_PREFIX_SETUP} && npm install -g \${_NPM_G_FLAGS} @kilocode/cli && ` +
|
||||
"{ grep -qF '.npm-global/bin' ~/.bashrc 2>/dev/null || echo 'export PATH=\"$HOME/.npm-global/bin:$PATH\"' >> ~/.bashrc; } && " +
|
||||
"{ [ ! -f ~/.zshrc ] || grep -qF '.npm-global/bin' ~/.zshrc 2>/dev/null || echo 'export PATH=\"$HOME/.npm-global/bin:$PATH\"' >> ~/.zshrc; }",
|
||||
`${NPM_PREFIX_SETUP} && npm install -g \${_NPM_G_FLAGS} @kilocode/cli && ${NPM_GLOBAL_PATH_PERSIST}`,
|
||||
),
|
||||
envVars: (apiKey) => [
|
||||
`OPENROUTER_API_KEY=${apiKey}`,
|
||||
|
|
@ -711,9 +718,7 @@ function createAgents(runner: CloudRunner): Record<string, AgentConfig> {
|
|||
installAgent(
|
||||
runner,
|
||||
"Junie",
|
||||
`${NPM_PREFIX_SETUP} && npm install -g \${_NPM_G_FLAGS} @jetbrains/junie-cli && ` +
|
||||
"{ grep -qF '.npm-global/bin' ~/.bashrc 2>/dev/null || echo 'export PATH=\"$HOME/.npm-global/bin:$PATH\"' >> ~/.bashrc; } && " +
|
||||
"{ [ ! -f ~/.zshrc ] || grep -qF '.npm-global/bin' ~/.zshrc 2>/dev/null || echo 'export PATH=\"$HOME/.npm-global/bin:$PATH\"' >> ~/.zshrc; }",
|
||||
`${NPM_PREFIX_SETUP} && npm install -g \${_NPM_G_FLAGS} @jetbrains/junie-cli && ${NPM_GLOBAL_PATH_PERSIST}`,
|
||||
),
|
||||
envVars: (apiKey) => [
|
||||
`JUNIE_OPENROUTER_API_KEY=${apiKey}`,
|
||||
|
|
|
|||
|
|
@ -147,8 +147,9 @@ export async function runOrchestration(
|
|||
wrapSshCall(
|
||||
cloud.runner.runServer(
|
||||
`printf '%s' '${envB64}' | base64 -d > ~/.spawnrc && chmod 600 ~/.spawnrc; ` +
|
||||
`grep -q 'source ~/.spawnrc' ~/.bashrc 2>/dev/null || echo '[ -f ~/.spawnrc ] && source ~/.spawnrc' >> ~/.bashrc; ` +
|
||||
`grep -q 'source ~/.spawnrc' ~/.zshrc 2>/dev/null || echo '[ -f ~/.spawnrc ] && source ~/.spawnrc' >> ~/.zshrc`,
|
||||
"for _rc in ~/.bashrc ~/.profile ~/.bash_profile ~/.zshrc; do " +
|
||||
`grep -q 'source ~/.spawnrc' "$_rc" 2>/dev/null || echo '[ -f ~/.spawnrc ] && source ~/.spawnrc' >> "$_rc"; ` +
|
||||
"done",
|
||||
),
|
||||
),
|
||||
2,
|
||||
|
|
|
|||
|
|
@ -224,7 +224,8 @@ CLOUD_ENV
|
|||
fi
|
||||
|
||||
if printf '%s' "${env_b64}" | cloud_exec "${app_name}" "base64 -d > ~/.spawnrc && chmod 600 ~/.spawnrc && \
|
||||
grep -q 'source ~/.spawnrc' ~/.bashrc 2>/dev/null || printf '%s\n' '[ -f ~/.spawnrc ] && source ~/.spawnrc' >> ~/.bashrc" >/dev/null 2>&1; then
|
||||
for _rc in ~/.bashrc ~/.profile ~/.bash_profile; do \
|
||||
grep -q 'source ~/.spawnrc' \"\$_rc\" 2>/dev/null || printf '%s\n' '[ -f ~/.spawnrc ] && source ~/.spawnrc' >> \"\$_rc\"; done" >/dev/null 2>&1; then
|
||||
log_ok "Manual .spawnrc created successfully"
|
||||
else
|
||||
log_err "Failed to create manual .spawnrc"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue