install.sh
Rama main · réplica de lectura del repositorio original.
#!/usr/bin/env bash
# OpenCompany Installer
# Usage: curl -fsSL https://opencompany.sh/install.sh | bash
#
# Served from opencompany.sh (not the GitHub raw URL) so installs are
# trackable. The release to install comes from https://opencompany.sh/version
# unless OPENCOMPANY_VERSION is set.
#
# This script installs OpenCompany and its dependencies:
# - bun (the JavaScript runtime and package manager; no Node.js, no npm)
# - Python 3.12+ (via brew/apt/dnf/pacman)
# - uv (Python package manager)
#
# Prefer the desktop app if you just want to use OpenCompany on a laptop; this
# path is for terminal and server use.
set -e
MIN_PYTHON_VERSION_MINOR=12
PKG_NAME="@zeenie-ai/opencompany"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'
info() { echo -e "${CYAN}[INFO]${NC} $*"; }
success() { echo -e "${GREEN}[OK]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
error_exit() { echo -e "${RED}[ERROR]${NC} $*"; exit 1; }
# Banner
echo ""
echo -e "${CYAN} OpenCompany${NC}"
echo ""
echo "Self-improving AI employees, running on your own computer"
echo ""
# Detect OS
detect_os() {
if [[ "$OSTYPE" == "darwin"* ]]; then
echo "macos"
elif [[ -f /etc/debian_version ]]; then
echo "debian"
elif [[ -f /etc/redhat-release ]]; then
echo "redhat"
elif [[ -f /etc/arch-release ]]; then
echo "arch"
else
echo "unknown"
fi
}
OS=$(detect_os)
# bun installs to $BUN_INSTALL (default ~/.bun) and puts global bin shims in
# its bin dir. Where it stores the global package itself varies by platform
# and configuration, so nothing below depends on that: the `company` shim
# knows its own package root and `company provision` does the rest.
BUN_HOME="${BUN_INSTALL:-$HOME/.bun}"
BUN_BIN_DIR="$BUN_HOME/bin"
# A previous npm-based install (or the pre-rebrand `machinaos` package) leaves
# a `company` / `machina` shim on PATH that would shadow the bun one. Remove it
# when npm happens to be around; skip silently otherwise.
remove_legacy_npm_installs() {
command -v npm &> /dev/null || return 0
for pkg in "$PKG_NAME" machinaos; do
if npm list -g --depth=0 "$pkg" &> /dev/null; then
info "Removing legacy npm install of $pkg..."
npm uninstall -g "$pkg" &> /dev/null || warn "Could not remove $pkg; run: npm uninstall -g $pkg"
fi
done
}
# =============================================================================
# Dependency Checks and Installation
# =============================================================================
check_bun() {
hash -r 2>/dev/null || true
if command -v bun &> /dev/null; then
success "bun $(bun --version)"
return 0
fi
return 1
}
install_bun() {
info "Installing bun (JavaScript runtime and package manager)..."
curl -fsSL https://bun.sh/install | bash
export PATH="$BUN_BIN_DIR:$PATH"
if ! check_bun; then
error_exit "Failed to install bun. Install it manually from https://bun.sh and re-run."
fi
}
check_python() {
for cmd in python3 python; do
if command -v "$cmd" &> /dev/null; then
version=$($cmd --version 2>&1 | sed -n 's/.*Python \([0-9]*\.[0-9]*\).*/\1/p')
major=$(echo "$version" | cut -d. -f1)
minor=$(echo "$version" | cut -d. -f2)
if [ "$major" -ge 3 ] && [ "$minor" -ge "$MIN_PYTHON_VERSION_MINOR" ]; then
success "Python $version ($cmd)"
PYTHON_CMD="$cmd"
return 0
fi
fi
done
warn "Python 3.$MIN_PYTHON_VERSION_MINOR+ not found"
return 1
}
install_python() {
info "Installing Python 3.$MIN_PYTHON_VERSION_MINOR..."
case "$OS" in
macos)
if command -v brew &> /dev/null; then
brew install python@3.12
else
error_exit "Please install Homebrew first: https://brew.sh/"
fi
;;
debian)
sudo apt-get update
sudo apt-get install -y python3.12 python3.12-venv python3-pip
;;
redhat)
sudo dnf install -y python3.12
;;
arch)
sudo pacman -S --noconfirm python python-pip
;;
*)
error_exit "Please install Python 3.12+ manually from https://python.org/"
;;
esac
if ! check_python; then
error_exit "Failed to install Python. Please install manually."
fi
}
check_uv() {
if command -v uv &> /dev/null; then
version=$(uv --version | tr -d 'uv ')
success "uv $version"
return 0
fi
return 1
}
install_uv() {
info "Installing uv (Python package manager)..."
# Try pip first
if [ -n "$PYTHON_CMD" ]; then
if $PYTHON_CMD -m pip install uv 2>/dev/null; then
export PATH="$HOME/.local/bin:$PATH"
if check_uv; then return 0; fi
fi
fi
# Fallback to official installer
curl -LsSf https://astral.sh/uv/install.sh | sh
export PATH="$HOME/.local/bin:$PATH"
if ! check_uv; then
error_exit "Failed to install uv"
fi
}
# bun finds its global "project" by walking up from $BUN_INSTALL/install/global
# until it meets a package.json. A stray package.json or package-lock.json in
# $HOME (an old `npm init` / `npm install` run in the home dir) therefore
# hijacks every global install: packages land in $HOME/node_modules and
# `bun add -g` dies with "InvalidNPMLockfile: failed to migrate lockfile"
# (docs-internal/errors.md #23). Giving the global dir its own manifest stops
# the walk-up before it reaches $HOME. Harmless on a clean machine: it is the
# same file bun would create there itself.
seed_bun_global_dir() {
local global_dir="$BUN_HOME/install/global"
mkdir -p "$global_dir"
if [ ! -f "$global_dir/package.json" ]; then
printf '{\n "private": true\n}\n' > "$global_dir/package.json"
fi
}
ensure_bun_on_path() {
# The bun installer adds ~/.bun/bin to the shell profile; make sure the
# `company` shim is reachable from new shells even when bun was already
# present but its bin dir was not exported.
case ":$PATH:" in
*":$BUN_BIN_DIR:"*) ;;
*)
export PATH="$BUN_BIN_DIR:$PATH"
if ! grep -q '\.bun/bin' "$HOME/.bashrc" 2>/dev/null; then
echo '' >> "$HOME/.bashrc"
echo '# bun global packages (OpenCompany installer)' >> "$HOME/.bashrc"
echo 'export PATH="$HOME/.bun/bin:$PATH"' >> "$HOME/.bashrc"
fi
;;
esac
}
# =============================================================================
# Main Installation Flow
# =============================================================================
main() {
echo ""
info "Checking dependencies..."
echo ""
# Check and install dependencies
check_bun || install_bun
check_python || install_python
check_uv || install_uv
ensure_bun_on_path
seed_bun_global_dir
remove_legacy_npm_installs
echo ""
info "Installing OpenCompany..."
echo ""
# OPENCOMPANY_VERSION pins a release (the cli/terraform startup scripts set
# it). Otherwise ask opencompany.sh which release is current; if that is
# unreachable, fall back to the registry's latest tag.
VERSION="${OPENCOMPANY_VERSION:-$(curl -fsSL --max-time 10 https://opencompany.sh/version 2>/dev/null | tr -d '[:space:]' || true)}"
PKG="${PKG_NAME}${VERSION:+@$VERSION}"
# Global install as the current user: bun's global root is user-owned, so
# no sudo and no root-owned venvs (docs-internal/errors.md #16).
bun add -g "$PKG" || error_exit "bun add -g $PKG failed."
# bun runs no lifecycle scripts for a global package, so provision the Python
# side now rather than on the first `company` command (which would do it too).
# The shim is addressed through bun's own answer for the global bin dir.
COMPANY="$(bun pm bin -g)/company"
info "Provisioning the Python environment..."
"$COMPANY" provision || error_exit "Provisioning failed. Re-run with: company provision"
echo ""
echo -e "${GREEN}============================================${NC}"
echo -e "${GREEN} OpenCompany installed successfully!${NC}"
echo -e "${GREEN}============================================${NC}"
echo ""
echo " Start OpenCompany:"
echo " company start"
echo ""
echo " Optional: Enable JS-rendered web scraping:"
echo " playwright install chromium"
echo ""
echo " Run diagnostics:"
echo " company doctor"
echo ""
echo " New shells pick up the bun bin dir from your profile; in this one run:"
echo " export PATH=\"\$HOME/.bun/bin:\$PATH\""
echo ""
}
# Run main
main