Загрузить файлы в «/»
This commit is contained in:
+409
@@ -0,0 +1,409 @@
|
||||
#!/usr/bin/env bash
|
||||
# installagent — install CLI AI coding agents inside asandbox
|
||||
# Usage: installagent <name> | installagent list | installagent status
|
||||
set -euo pipefail
|
||||
|
||||
VERSION="1.1.0"
|
||||
|
||||
if [[ -t 2 ]]; then
|
||||
C_RED=$'\033[31m'; C_GRN=$'\033[32m'; C_YLW=$'\033[33m'
|
||||
C_BLU=$'\033[34m'; C_DIM=$'\033[2m'; C_RST=$'\033[0m'; C_BLD=$'\033[1m'
|
||||
else
|
||||
C_RED=; C_GRN=; C_YLW=; C_BLU=; C_DIM=; C_RST=; C_BLD=
|
||||
fi
|
||||
|
||||
die() { echo "${C_RED}installagent:${C_RST} $*" >&2; exit 1; }
|
||||
info() { echo "${C_BLU}installagent:${C_RST} $*" >&2; }
|
||||
ok() { echo "${C_GRN}installagent:${C_RST} $*" >&2; }
|
||||
warn() { echo "${C_YLW}installagent:${C_RST} $*" >&2; }
|
||||
|
||||
ensure_local_path() {
|
||||
mkdir -p "${HOME}/.local/bin" "${HOME}/.local/lib" "${HOME}/.npm-global"
|
||||
# Prefer user-local npm so installs survive container recreate (via persist mount)
|
||||
if command -v npm >/dev/null 2>&1; then
|
||||
npm config set prefix "${HOME}/.npm-global" >/dev/null 2>&1 || true
|
||||
fi
|
||||
export PATH="${HOME}/.local/bin:${HOME}/.npm-global/bin:${PATH}"
|
||||
}
|
||||
|
||||
npm_g() {
|
||||
ensure_local_path
|
||||
npm install -g --prefix "${HOME}/.npm-global" "$@"
|
||||
}
|
||||
|
||||
have_cmd() { command -v "$1" >/dev/null 2>&1; }
|
||||
|
||||
# ── catalog ─────────────────────────────────────────────────────────
|
||||
# id|aliases|binary|description
|
||||
catalog() {
|
||||
cat <<'EOF'
|
||||
antigravity|agy,antigravity-cli|agy|Google Antigravity CLI
|
||||
cursor|cursor-cli,cursor-agent|agent|Cursor Agent CLI
|
||||
opencode|open-code|opencode|OpenCode open-source coding agent
|
||||
claude|claude-code,claude-cli|claude|Anthropic Claude Code
|
||||
gemini|gemini-cli,google-gemini|gemini|Google Gemini CLI
|
||||
codex|openai-codex|codex|OpenAI Codex CLI
|
||||
aider|aider-chat|aider|Aider Python coding agent
|
||||
qwen|qwen-code,qwen-cli|qwen|Qwen Code CLI
|
||||
amp|sourcegraph-amp|amp|Sourcegraph Amp CLI
|
||||
goose|block-goose|goose|Block Goose agent
|
||||
continue|cn|cn|Continue CLI
|
||||
EOF
|
||||
}
|
||||
|
||||
normalize_name() {
|
||||
echo "$1" | tr '[:upper:]' '[:lower:]' | tr '_' '-'
|
||||
}
|
||||
|
||||
resolve_id() {
|
||||
local want
|
||||
want=$(normalize_name "$1")
|
||||
local line id aliases
|
||||
while IFS= read -r line; do
|
||||
[[ -z "$line" || "$line" == \#* ]] && continue
|
||||
id="${line%%|*}"
|
||||
aliases="${line#*|}"
|
||||
aliases="${aliases%%|*}"
|
||||
if [[ "$want" == "$id" ]]; then
|
||||
echo "$id"
|
||||
return 0
|
||||
fi
|
||||
local a
|
||||
IFS=',' read -ra _als <<< "$aliases"
|
||||
for a in "${_als[@]}"; do
|
||||
a=$(echo "$a" | tr -d ' ')
|
||||
if [[ "$want" == "$a" ]]; then
|
||||
echo "$id"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
done < <(catalog)
|
||||
return 1
|
||||
}
|
||||
|
||||
agent_binary() {
|
||||
local id="$1" line
|
||||
while IFS= read -r line; do
|
||||
[[ "${line%%|*}" == "$id" ]] || continue
|
||||
# id|aliases|binary|desc
|
||||
local rest="${line#*|}"
|
||||
rest="${rest#*|}"
|
||||
echo "${rest%%|*}"
|
||||
return 0
|
||||
done < <(catalog)
|
||||
}
|
||||
|
||||
agent_desc() {
|
||||
local id="$1" line
|
||||
while IFS= read -r line; do
|
||||
[[ "${line%%|*}" == "$id" ]] || continue
|
||||
echo "${line##*|}"
|
||||
return 0
|
||||
done < <(catalog)
|
||||
}
|
||||
|
||||
# ── installers ──────────────────────────────────────────────────────
|
||||
install_antigravity() {
|
||||
info "installing Antigravity CLI (agy)..."
|
||||
ensure_local_path
|
||||
curl -fsSL https://antigravity.google/cli/install.sh | bash
|
||||
# Some installers put binary only in a custom dir — ensure on PATH
|
||||
if ! have_cmd agy; then
|
||||
# common locations
|
||||
for p in \
|
||||
"${HOME}/.local/bin/agy" \
|
||||
"${HOME}/.antigravity/bin/agy" \
|
||||
"${HOME}/.agy/bin/agy" \
|
||||
/usr/local/bin/agy; do
|
||||
if [[ -x "$p" ]]; then
|
||||
mkdir -p "${HOME}/.local/bin"
|
||||
ln -sfn "$p" "${HOME}/.local/bin/agy"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
have_cmd agy || die "agy not found after install — check installer output"
|
||||
ok "Antigravity CLI ready: $(command -v agy)"
|
||||
agy --version 2>/dev/null || agy version 2>/dev/null || true
|
||||
}
|
||||
|
||||
install_cursor() {
|
||||
info "installing Cursor Agent CLI..."
|
||||
ensure_local_path
|
||||
curl -fsSL https://cursor.com/install | bash
|
||||
if ! have_cmd agent; then
|
||||
for p in \
|
||||
"${HOME}/.local/bin/agent" \
|
||||
"${HOME}/.cursor/bin/agent" \
|
||||
"${HOME}/.cursor-agent/bin/agent"; do
|
||||
if [[ -x "$p" ]]; then
|
||||
mkdir -p "${HOME}/.local/bin"
|
||||
ln -sfn "$p" "${HOME}/.local/bin/agent"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
have_cmd agent || die "agent not found after install"
|
||||
ok "Cursor CLI ready: $(command -v agent)"
|
||||
agent --version 2>/dev/null || true
|
||||
}
|
||||
|
||||
install_opencode() {
|
||||
info "installing OpenCode..."
|
||||
ensure_local_path
|
||||
if have_cmd npm; then
|
||||
npm_g opencode-ai
|
||||
else
|
||||
curl -fsSL https://opencode.ai/install | bash
|
||||
fi
|
||||
if ! have_cmd opencode; then
|
||||
for p in "${HOME}/.npm-global/bin/opencode" "${HOME}/.local/bin/opencode" /usr/local/bin/opencode; do
|
||||
[[ -x "$p" ]] && ln -sfn "$p" "${HOME}/.local/bin/opencode" && break
|
||||
done
|
||||
fi
|
||||
have_cmd opencode || die "opencode not found after install"
|
||||
ok "OpenCode ready: $(command -v opencode)"
|
||||
opencode --version 2>/dev/null || true
|
||||
}
|
||||
|
||||
install_claude() {
|
||||
info "installing Claude Code..."
|
||||
have_cmd npm || die "npm required (Node.js)"
|
||||
npm_g @anthropic-ai/claude-code
|
||||
have_cmd claude || die "claude not found after install"
|
||||
ok "Claude Code ready: $(command -v claude)"
|
||||
claude --version 2>/dev/null || true
|
||||
}
|
||||
|
||||
install_gemini() {
|
||||
info "installing Gemini CLI..."
|
||||
have_cmd npm || die "npm required (Node.js)"
|
||||
npm_g @google/gemini-cli
|
||||
have_cmd gemini || die "gemini not found after install"
|
||||
ok "Gemini CLI ready: $(command -v gemini)"
|
||||
gemini --version 2>/dev/null || true
|
||||
}
|
||||
|
||||
install_codex() {
|
||||
info "installing OpenAI Codex CLI..."
|
||||
have_cmd npm || die "npm required (Node.js)"
|
||||
npm_g @openai/codex
|
||||
have_cmd codex || die "codex not found after install"
|
||||
ok "Codex ready: $(command -v codex)"
|
||||
codex --version 2>/dev/null || true
|
||||
}
|
||||
|
||||
install_aider() {
|
||||
info "installing Aider..."
|
||||
ensure_local_path
|
||||
if have_cmd uv; then
|
||||
uv tool install aider-chat
|
||||
elif have_cmd pip3; then
|
||||
pip3 install --user --break-system-packages aider-chat \
|
||||
|| pip3 install --user aider-chat
|
||||
else
|
||||
die "need uv or pip3 to install aider"
|
||||
fi
|
||||
# uv tools go to ~/.local/bin
|
||||
have_cmd aider || die "aider not found after install — ensure ~/.local/bin is on PATH"
|
||||
ok "Aider ready: $(command -v aider)"
|
||||
aider --version 2>/dev/null || true
|
||||
}
|
||||
|
||||
install_qwen() {
|
||||
info "installing Qwen Code..."
|
||||
have_cmd npm || die "npm required (Node.js)"
|
||||
npm_g @qwen-code/qwen-code
|
||||
# binary name may be qwen or qwen-code
|
||||
if ! have_cmd qwen && have_cmd qwen-code; then
|
||||
ln -sfn "$(command -v qwen-code)" "${HOME}/.local/bin/qwen"
|
||||
fi
|
||||
if have_cmd qwen || have_cmd qwen-code; then
|
||||
ok "Qwen Code ready: $(command -v qwen 2>/dev/null || command -v qwen-code)"
|
||||
else
|
||||
die "qwen binary not found after install"
|
||||
fi
|
||||
}
|
||||
|
||||
install_amp() {
|
||||
info "installing Amp (Sourcegraph)..."
|
||||
ensure_local_path
|
||||
# Official installer when available
|
||||
if curl -fsSL https://ampcode.com/install.sh 2>/dev/null | bash; then
|
||||
:
|
||||
elif have_cmd npm; then
|
||||
npm_g @sourcegraph/amp 2>/dev/null || npm_g amp 2>/dev/null || true
|
||||
fi
|
||||
have_cmd amp || warn "amp may need manual install — see https://ampcode.com"
|
||||
have_cmd amp && ok "Amp ready: $(command -v amp)"
|
||||
}
|
||||
|
||||
install_goose() {
|
||||
info "installing Goose..."
|
||||
ensure_local_path
|
||||
curl -fsSL https://github.com/block/goose/releases/download/stable/download_cli.sh 2>/dev/null | bash \
|
||||
|| curl -fsSL https://raw.githubusercontent.com/block/goose/main/download_cli.sh 2>/dev/null | bash \
|
||||
|| true
|
||||
if ! have_cmd goose; then
|
||||
# fallback cargo if present
|
||||
if have_cmd cargo; then
|
||||
cargo install goose-cli 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
have_cmd goose || die "goose not found after install — see https://github.com/block/goose"
|
||||
ok "Goose ready: $(command -v goose)"
|
||||
}
|
||||
|
||||
install_continue() {
|
||||
info "installing Continue CLI..."
|
||||
ensure_local_path
|
||||
have_cmd npm || die "npm required"
|
||||
npm_g @continuedev/cli 2>/dev/null || npm_g continue-cli 2>/dev/null || true
|
||||
if have_cmd cn || have_cmd continue; then
|
||||
ok "Continue ready: $(command -v cn 2>/dev/null || command -v continue)"
|
||||
else
|
||||
warn "Continue CLI package name may have changed — try: npm install -g @continuedev/cli"
|
||||
fi
|
||||
}
|
||||
|
||||
do_install() {
|
||||
local id="$1"
|
||||
case "$id" in
|
||||
antigravity) install_antigravity ;;
|
||||
cursor) install_cursor ;;
|
||||
opencode) install_opencode ;;
|
||||
claude) install_claude ;;
|
||||
gemini) install_gemini ;;
|
||||
codex) install_codex ;;
|
||||
aider) install_aider ;;
|
||||
qwen) install_qwen ;;
|
||||
amp) install_amp ;;
|
||||
goose) install_goose ;;
|
||||
continue) install_continue ;;
|
||||
*) die "no installer for: $id" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
cmd_list() {
|
||||
echo "${C_BLD}Available agents${C_RST} (installagent <name>)"
|
||||
echo
|
||||
printf " ${C_BLD}%-14s${C_RST} %-12s %s\n" "NAME" "BINARY" "DESCRIPTION"
|
||||
printf " %-14s %-12s %s\n" "----" "------" "-----------"
|
||||
local line id aliases bin desc
|
||||
while IFS= read -r line; do
|
||||
id="${line%%|*}"
|
||||
rest="${line#*|}"
|
||||
aliases="${rest%%|*}"
|
||||
rest="${rest#*|}"
|
||||
bin="${rest%%|*}"
|
||||
desc="${rest#*|}"
|
||||
local mark=" "
|
||||
if have_cmd "$bin"; then
|
||||
mark="${C_GRN}✓${C_RST}"
|
||||
else
|
||||
mark="${C_DIM}·${C_RST}"
|
||||
fi
|
||||
printf " %s %-12s %-12s %s\n" "$mark" "$id" "$bin" "$desc"
|
||||
if [[ -n "$aliases" ]]; then
|
||||
printf " ${C_DIM}aliases: %s${C_RST}\n" "$aliases"
|
||||
fi
|
||||
done < <(catalog)
|
||||
echo
|
||||
echo "Examples:"
|
||||
echo " installagent claude"
|
||||
echo " installagent antigravity"
|
||||
echo " installagent cursor"
|
||||
echo " installagent opencode"
|
||||
echo " installagent all # install core set"
|
||||
}
|
||||
|
||||
cmd_status() {
|
||||
ensure_local_path
|
||||
echo "${C_BLD}Installed agents${C_RST}"
|
||||
local line id bin
|
||||
while IFS= read -r line; do
|
||||
id="${line%%|*}"
|
||||
rest="${line#*|}"
|
||||
rest="${rest#*|}"
|
||||
bin="${rest%%|*}"
|
||||
if have_cmd "$bin"; then
|
||||
printf " ${C_GRN}✓${C_RST} %-14s %s\n" "$id" "$(command -v "$bin")"
|
||||
else
|
||||
printf " ${C_DIM}·${C_RST} %-14s (not installed)\n" "$id"
|
||||
fi
|
||||
done < <(catalog)
|
||||
echo
|
||||
echo "PATH includes:"
|
||||
echo " ${HOME}/.local/bin"
|
||||
echo " ${HOME}/.npm-global/bin"
|
||||
}
|
||||
|
||||
cmd_all() {
|
||||
# Core set commonly used
|
||||
local core=(claude gemini codex opencode cursor antigravity aider)
|
||||
local name
|
||||
for name in "${core[@]}"; do
|
||||
info "── $name ──"
|
||||
if do_install "$name"; then
|
||||
ok "$name done"
|
||||
else
|
||||
warn "$name failed (continuing)"
|
||||
fi
|
||||
done
|
||||
cmd_status
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
installagent v${VERSION} — install CLI AI agents inside asandbox
|
||||
|
||||
USAGE
|
||||
installagent <name> Install agent by name or alias
|
||||
installagent list Show catalog
|
||||
installagent status Show what is installed
|
||||
installagent all Install core agents
|
||||
installagent help This help
|
||||
|
||||
AGENTS
|
||||
antigravity (agy) Google Antigravity CLI
|
||||
cursor (agent) Cursor Agent CLI
|
||||
opencode OpenCode
|
||||
claude Claude Code
|
||||
gemini Gemini CLI
|
||||
codex OpenAI Codex
|
||||
aider Aider
|
||||
qwen Qwen Code
|
||||
amp Sourcegraph Amp
|
||||
goose Block Goose
|
||||
continue Continue CLI
|
||||
|
||||
Installs go to ~/.local and ~/.npm-global (persisted by asandbox).
|
||||
EOF
|
||||
}
|
||||
|
||||
main() {
|
||||
ensure_local_path
|
||||
|
||||
local cmd="${1:-list}"
|
||||
shift || true
|
||||
|
||||
case "$cmd" in
|
||||
list|ls|-l) cmd_list ;;
|
||||
status|st) cmd_status ;;
|
||||
all) cmd_all ;;
|
||||
help|-h|--help) usage ;;
|
||||
version|-V|--version) echo "installagent $VERSION" ;;
|
||||
*)
|
||||
local id
|
||||
if ! id=$(resolve_id "$cmd"); then
|
||||
die "unknown agent: $cmd (try: installagent list)"
|
||||
fi
|
||||
info "resolved '$cmd' → $id ($(agent_desc "$id"))"
|
||||
do_install "$id"
|
||||
echo
|
||||
ok "run: $(agent_binary "$id")"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user