port the agent CLIs, using Anthropic's installer rather than npm
Claude Code goes in through https://claude.ai/install.sh, matching what the platform already does for members in os-user-claude.ts and chosen there for the auto-update npm does not give. The comment at os-user-claude.ts:28 claiming setup.sh already did this was simply wrong — setup-ubuntu.sh used `npm install -g @anthropic-ai/claude-code`. Two things that installer insists on, both of which a naive port gets wrong and both of which os-user-claude.ts had already found: It REFUSES to run under sudo from a regular user's shell — it checks for uid 0 with SUDO_USER set, because everything it writes goes under $HOME and under sudo that is root's. This script runs as root, so the install has to be done AS the account. It declares #!/bin/bash and uses [[ … =~ … ]], so it must be piped to bash. On Ubuntu /bin/sh is dash and `| sh` fails. Two bugs found by running it rather than reading it: opencode does not install to ~/.local/bin. It goes to ~/.opencode/bin, which is what sidecar/opencode/index.ts:22 hardcodes. The first version looked in the wrong place, reported a working install as missing, and installed it again — the run said "did not complete" while the installer had plainly succeeded. claude on this machine came from npm, so `command -v claude` found it and the section would have left a copy that never updates. It now detects an npm install by resolving the binary into node_modules, says so, and offers to reinstall through the official installer — naming the npm copy and how to remove it rather than deleting something it did not put there. ~/.local/bin and ~/.opencode/bin are both added to the account's PATH. The sidecars do not need it — they check the exact paths — but a user who cannot run `claude` in their own terminal reasonably concludes it was never installed. PI stays optional and says outright that nothing in the platform spawns it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -269,3 +269,81 @@ install_deno() {
|
||||
(cd / && sudo -H -u "$USERNAME" bash -c 'curl -fsSL https://deno.land/install.sh | sh') >/dev/null 2>&1
|
||||
[[ -x "${USER_HOME}/.deno/bin/deno" ]]
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Agent CLIs
|
||||
# -----------------------------------------------------------------------------
|
||||
#
|
||||
# Claude Code goes in through Anthropic's own installer rather than npm, matching
|
||||
# what the platform does for members (os-user-claude.ts) and chosen there for the
|
||||
# auto-update the npm package does not do.
|
||||
#
|
||||
# Two things that installer insists on, both of which a naive port gets wrong:
|
||||
#
|
||||
# It REFUSES to run under sudo from a regular user's shell — it checks for uid 0
|
||||
# with SUDO_USER set, because everything it installs goes under $HOME and under
|
||||
# sudo that is root's home. So it must run AS the account, not as root.
|
||||
#
|
||||
# It declares #!/bin/bash and uses [[ … =~ … ]], so it must be piped to bash.
|
||||
# `| sh` fails on a dash-based /bin/sh, which is Ubuntu's.
|
||||
#
|
||||
# Both are recorded in os-user-claude.ts too, which found them first.
|
||||
|
||||
CLAUDE_INSTALL_URL="https://claude.ai/install.sh"
|
||||
OPENCODE_INSTALL_URL="https://opencode.ai/install"
|
||||
|
||||
# Where each installer actually puts its binary. They disagree, and the platform
|
||||
# depends on the difference:
|
||||
#
|
||||
# claude ~/.local/bin/claude — claude-manager.ts tries Bun.which then
|
||||
# that exact path
|
||||
# opencode ~/.opencode/bin/opencode — sidecar/opencode/index.ts:22 hardcodes
|
||||
# join(homedir(), '.opencode', 'bin', …)
|
||||
#
|
||||
# Looking for opencode in ~/.local/bin, as an earlier version of this did,
|
||||
# reports a perfectly good install as missing and then installs it again.
|
||||
agent_bin() {
|
||||
case "$1" in
|
||||
claude) echo "${USER_HOME}/.local/bin/claude" ;;
|
||||
opencode) echo "${USER_HOME}/.opencode/bin/opencode" ;;
|
||||
*) echo "${USER_HOME}/.local/bin/$1" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# The directories those live in, for the account's PATH.
|
||||
agent_bin_dirs() { echo "${USER_HOME}/.local/bin" "${USER_HOME}/.opencode/bin"; }
|
||||
|
||||
agent_installed() { [[ -x "$(agent_bin "$1")" ]] || command -v "$1" &>/dev/null; }
|
||||
|
||||
# Which copy answers, so the run can say where it came from. Claude installed
|
||||
# from npm sits in /usr/local/lib/node_modules and does NOT auto-update, which is
|
||||
# the whole reason the platform prefers Anthropic's installer.
|
||||
agent_path() {
|
||||
local name="$1" bin
|
||||
bin="$(agent_bin "$name")"
|
||||
[[ -x "$bin" ]] && {
|
||||
echo "$bin"
|
||||
return
|
||||
}
|
||||
command -v "$name" 2>/dev/null
|
||||
}
|
||||
|
||||
agent_is_npm_install() { [[ "$(readlink -f "$(agent_path "$1")" 2>/dev/null)" == */node_modules/* ]]; }
|
||||
|
||||
agent_version() {
|
||||
local bin
|
||||
bin="$(agent_path "$1")"
|
||||
[[ -n "$bin" ]] && (cd / && sudo -H -u "$USERNAME" "$bin" --version 2>/dev/null | head -1)
|
||||
}
|
||||
|
||||
install_claude_code() {
|
||||
(cd / && sudo -H -u "$USERNAME" bash -c "set -e; curl -fsSL ${CLAUDE_INSTALL_URL} | bash") >/dev/null 2>&1
|
||||
[[ -x "$(agent_bin claude)" ]]
|
||||
}
|
||||
|
||||
install_opencode() {
|
||||
(cd / && sudo -H -u "$USERNAME" bash -c "set -e; curl -fsSL ${OPENCODE_INSTALL_URL} | bash") >/dev/null 2>&1
|
||||
[[ -x "$(agent_bin opencode)" ]]
|
||||
}
|
||||
|
||||
install_pi() { npm install -g @mariozechner/pi-coding-agent >/dev/null 2>&1; }
|
||||
|
||||
Reference in New Issue
Block a user