mirror of
https://github.com/OpenRouterTeam/spawn.git
synced 2026-04-28 03:49:31 +00:00
fix(ux): suppress duplicate install message and set UTF-8 locale (#2950)
1. Suppress Claude Code curl installer stdout — the remote installer prints its own "Installation complete!" which duplicated the local "Claude Code agent installed successfully" message. 2. Export LANG=C.UTF-8 in both the interactive SSH session command and the .spawnrc env config. Fresh cloud VMs often default to the C locale which cannot render Unicode properly, causing garbled ANSI output in agent TUIs (e.g. "⏵⏵bypasspermissionson" instead of properly spaced text). Fixes #2946 Agent: ux-engineer Co-authored-by: B <6723574+louisgv@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
0f3cb8b2eb
commit
f93c799db8
7 changed files with 8 additions and 6 deletions
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@openrouter/spawn",
|
||||
"version": "0.25.25",
|
||||
"version": "0.25.26",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
"spawn": "cli.js"
|
||||
|
|
|
|||
|
|
@ -1213,7 +1213,7 @@ export async function interactiveSession(cmd: string): Promise<number> {
|
|||
throw new Error("Invalid command: must be non-empty and must not contain null bytes");
|
||||
}
|
||||
const term = sanitizeTermValue(process.env.TERM || "xterm-256color");
|
||||
const fullCmd = `export TERM='${term}' PATH="$HOME/.npm-global/bin:$HOME/.claude/local/bin:$HOME/.local/bin:$HOME/.bun/bin:$PATH" && exec bash -l -c ${shellQuote(cmd)}`;
|
||||
const fullCmd = `export TERM='${term}' LANG='C.UTF-8' PATH="$HOME/.npm-global/bin:$HOME/.claude/local/bin:$HOME/.local/bin:$HOME/.bun/bin:$PATH" && exec bash -l -c ${shellQuote(cmd)}`;
|
||||
const keyOpts = getSshKeyOpts(await ensureSshKeys());
|
||||
const exitCode = spawnInteractive([
|
||||
"ssh",
|
||||
|
|
|
|||
|
|
@ -1471,7 +1471,7 @@ export async function interactiveSession(cmd: string, ip?: string): Promise<numb
|
|||
}
|
||||
const serverIp = ip || _state.serverIp;
|
||||
const term = sanitizeTermValue(process.env.TERM || "xterm-256color");
|
||||
const fullCmd = `export TERM='${term}' PATH="$HOME/.npm-global/bin:$HOME/.claude/local/bin:$HOME/.local/bin:$HOME/.bun/bin:$PATH" && exec bash -l -c ${shellQuote(cmd)}`;
|
||||
const fullCmd = `export TERM='${term}' LANG='C.UTF-8' PATH="$HOME/.npm-global/bin:$HOME/.claude/local/bin:$HOME/.local/bin:$HOME/.bun/bin:$PATH" && exec bash -l -c ${shellQuote(cmd)}`;
|
||||
const keyOpts = getSshKeyOpts(await ensureSshKeys());
|
||||
|
||||
const exitCode = spawnInteractive([
|
||||
|
|
|
|||
|
|
@ -1107,7 +1107,7 @@ export async function interactiveSession(cmd: string): Promise<number> {
|
|||
const username = resolveUsername();
|
||||
const term = sanitizeTermValue(process.env.TERM || "xterm-256color");
|
||||
// Use shellQuote for consistent single-quote escaping (prevents shell expansion of $variables in cmd)
|
||||
const fullCmd = `export TERM='${term}' PATH="$HOME/.npm-global/bin:$HOME/.claude/local/bin:$HOME/.local/bin:$HOME/.bun/bin:$PATH" && exec bash -l -c ${shellQuote(cmd)}`;
|
||||
const fullCmd = `export TERM='${term}' LANG='C.UTF-8' PATH="$HOME/.npm-global/bin:$HOME/.claude/local/bin:$HOME/.local/bin:$HOME/.bun/bin:$PATH" && exec bash -l -c ${shellQuote(cmd)}`;
|
||||
const keyOpts = getSshKeyOpts(await ensureSshKeys());
|
||||
|
||||
const exitCode = spawnInteractive([
|
||||
|
|
|
|||
|
|
@ -929,7 +929,7 @@ export async function interactiveSession(cmd: string, ip?: string): Promise<numb
|
|||
}
|
||||
const serverIp = ip || _state.serverIp;
|
||||
const term = sanitizeTermValue(process.env.TERM || "xterm-256color");
|
||||
const fullCmd = `export TERM='${term}' PATH="$HOME/.npm-global/bin:$HOME/.claude/local/bin:$HOME/.local/bin:$HOME/.bun/bin:$PATH" && exec bash -l -c ${shellQuote(cmd)}`;
|
||||
const fullCmd = `export TERM='${term}' LANG='C.UTF-8' PATH="$HOME/.npm-global/bin:$HOME/.claude/local/bin:$HOME/.local/bin:$HOME/.bun/bin:$PATH" && exec bash -l -c ${shellQuote(cmd)}`;
|
||||
|
||||
const keyOpts = getSshKeyOpts(await ensureSshKeys());
|
||||
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ async function installClaudeCode(runner: CloudRunner): Promise<void> {
|
|||
`if [ -f ~/.bash_profile ] && grep -q 'spawn:env\\|Claude Code PATH\\|spawn:path' ~/.bash_profile 2>/dev/null; then rm -f ~/.bash_profile; fi`,
|
||||
`if command -v claude >/dev/null 2>&1; then ${finalize}; exit 0; fi`,
|
||||
`echo "==> Installing Claude Code (method 1/2: curl installer)..."`,
|
||||
"curl --proto '=https' -fsSL https://claude.ai/install.sh | bash || true",
|
||||
"curl --proto '=https' -fsSL https://claude.ai/install.sh | bash >/dev/null 2>&1 || true",
|
||||
`export PATH="${claudePath}:$PATH"`,
|
||||
`if command -v claude >/dev/null 2>&1; then ${finalize}; exit 0; fi`,
|
||||
"if ! command -v node >/dev/null 2>&1; then export N_PREFIX=$HOME/.n; curl --proto '=https' -fsSL https://raw.githubusercontent.com/tj/n/master/bin/n | bash -s install 22 || true; export PATH=$N_PREFIX/bin:$PATH; fi",
|
||||
|
|
|
|||
|
|
@ -185,6 +185,8 @@ export function generateEnvConfig(pairs: string[]): string {
|
|||
"",
|
||||
"# [spawn:env]",
|
||||
"export IS_SANDBOX='1'",
|
||||
"# UTF-8 locale — required for agent TUIs that use Unicode (e.g. Claude Code)",
|
||||
"export LANG='C.UTF-8'",
|
||||
"# Ensure agent binaries are in PATH on reconnect",
|
||||
'export PATH="$HOME/.npm-global/bin:$HOME/.bun/bin:$HOME/.local/bin:$HOME/.cargo/bin:$HOME/.claude/local/bin:/usr/local/bin:$PATH"',
|
||||
];
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue