Download app/empacotamento/setup.sh from OpenTARS/openTARS: direct link, hf CLI and curl.
- Browser
- Download file 15.9 kB
-
https://huggingface.co/OpenTARS/openTARS/resolve/main/app/empacotamento/setup.sh
- Command line
-
hf download hf://OpenTARS/openTARS/app/empacotamento/setup.sh
-
curl -L -o setup.sh https://huggingface.co/OpenTARS/openTARS/resolve/main/app/empacotamento/setup.sh
15.9 kB
| # Configuração do openTARS: instala o que falta pra ele rodar (Ollama, | |
| # ambiente Python isolado com as dependências, modelos do Ollama; nenhum | |
| # ajudante desde a 3.0.5: a helper Murph vem no pacote; desde a 3.1, usa as | |
| # IAs que outros programas já têm; desde a 4.0, baixa o Gargantua, a IA | |
| # oficial; na Endurance Neo, nada de modelo de embeddings) e NUNCA reinstala | |
| # algo que já está presente no PC. | |
| # Roda automaticamente depois do "apt install" (chamado pelo postinst) | |
| # e pode ser rodado de novo a qualquer momento com "opentars --setup". | |
| # | |
| # De propósito, NUNCA sai com erro: se o dpkg visse esse script falhar, | |
| # deixaria o pacote marcado como "quebrado" mesmo com o openTARS | |
| # utilizável. Cada etapa arriscada (rede, permissões) avisa e segue. | |
| set -uo pipefail | |
| # Ajudantes das versões antigas (não são mais baixados nem usados): se já | |
| # estiverem instalados, só não contam como modelo de conversa. | |
| MODELO_AJUDANTE="qwen2.5:0.5b" | |
| MODELO_AJUDANTE_ANTIGO="gemma3:270m" | |
| DIR_OPENTARS="/usr/share/opentars" | |
| VENV="$DIR_OPENTARS/venv" | |
| DEPENDENCIAS=("requests:requests" "psutil:psutil" "pyautogui:pyautogui" "PIL:pillow" "PySide6:PySide6-Essentials" "numpy:numpy" "onnxruntime:onnxruntime") | |
| # Textos no idioma do sistema: vêm de idiomas/*.json, pelo mesmo módulo | |
| # que o openTARS usa (tars_i18n.py). Se algo falhar, aparecem as chaves | |
| # em vez das frases — a instalação nunca quebra por causa disso. | |
| eval "$(python3 "$DIR_OPENTARS/tars_i18n.py" --shell setup. 2>/dev/null)" 2>/dev/null || true | |
| # t chave [campo=valor ...] -> frase do idioma, com os {campos} preenchidos | |
| t() { | |
| local var="T_setup_$1" texto par | |
| texto="${!var:-$1}" | |
| shift | |
| for par in "$@"; do | |
| texto="${texto//"{${par%%=*}}"/${par#*=}}" | |
| done | |
| printf '%s' "$texto" | |
| } | |
| msg() { echo "[openTARS] $*"; } | |
| aviso() { echo "[openTARS] $(t aviso): $*"; } | |
| echo "" | |
| echo "$(t titulo)" | |
| echo "" | |
| OLLAMA_URL="http://127.0.0.1:11434" | |
| # Respeita OLLAMA_HOST (o mesmo do próprio Ollama): "0.0.0.0", "host:porta" ou URL | |
| if [ -n "${OLLAMA_HOST:-}" ]; then | |
| OLLAMA_URL="$OLLAMA_HOST" | |
| case "$OLLAMA_URL" in *://*) ;; *) OLLAMA_URL="http://$OLLAMA_URL" ;; esac | |
| case "${OLLAMA_URL#*://}" in *:*) ;; *) OLLAMA_URL="$OLLAMA_URL:11434" ;; esac | |
| OLLAMA_URL="${OLLAMA_URL/\/\/0.0.0.0/\/\/127.0.0.1}" | |
| fi | |
| ollama_responde() { | |
| curl -fsS --max-time 3 "$OLLAMA_URL/api/version" >/dev/null 2>&1 | |
| } | |
| # 4.5: WSL. O Ollama costuma ficar no Windows, que o WSL2 alcança pelo IP dele | |
| # (servidor DNS e gateway do WSL), não pelo 127.0.0.1. | |
| em_wsl() { | |
| grep -qi microsoft /proc/version 2>/dev/null || [ -n "${WSL_DISTRO_NAME:-}" ] | |
| } | |
| ollama_do_windows() { | |
| local ip | |
| for ip in $(awk '/^nameserver/ {print $2}' /etc/resolv.conf 2>/dev/null) $(ip route 2>/dev/null | awk '/^default/ {print $3}'); do | |
| case "$ip" in 127.*|"") continue ;; esac | |
| if curl -fsS --max-time 2 "http://$ip:11434/api/version" >/dev/null 2>&1; then | |
| echo "http://$ip:11434" | |
| return 0 | |
| fi | |
| done | |
| return 1 | |
| } | |
| if em_wsl; then | |
| msg "$(t wsl_detectado)" | |
| if [ -z "${OLLAMA_HOST:-}" ] && ! ollama_responde && windows_url="$(ollama_do_windows)"; then | |
| OLLAMA_URL="$windows_url" | |
| msg "$(t ollama_windows url="$OLLAMA_URL")" | |
| fi | |
| fi | |
| # O comando "ollama" (pull) fala com o mesmo servidor que o resto deste script | |
| export OLLAMA_HOST="${OLLAMA_URL#*://}" | |
| # Nomes dos modelos baixados, um por linha (via API: funciona mesmo com | |
| # o Ollama rodando em Docker, sem o comando "ollama" no PC). | |
| modelos_instalados() { | |
| curl -fsS --max-time 5 "$OLLAMA_URL/api/tags" 2>/dev/null \ | |
| | grep -o '"name"[[:space:]]*:[[:space:]]*"[^"]*"' | cut -d'"' -f4 | |
| } | |
| # ------------------------------------------------------------------ | |
| # 1. Ollama — só instala se ainda não existir. | |
| # | |
| # Um Ollama que já responde na porta padrão (instalado de outro jeito, | |
| # em Docker, via Snap...) conta como instalado: instalar outro por cima | |
| # criaria um segundo serviço brigando pela mesma porta. | |
| # ------------------------------------------------------------------ | |
| if command -v ollama >/dev/null 2>&1; then | |
| msg "$(t ollama_ja)" | |
| elif ollama_responde; then | |
| msg "$(t ollama_existente url="$OLLAMA_URL")" | |
| else | |
| msg "$(t ollama_instalando)" | |
| # Baixa pro disco e só então executa: com "curl | sh" o status do | |
| # pipe é o do "sh", que "funciona" mesmo se o download falhar. | |
| instalador="$(mktemp)" | |
| if curl -fsSL https://ollama.com/install.sh -o "$instalador" && sh "$instalador"; then | |
| msg "$(t ollama_ok)" | |
| else | |
| aviso "$(t ollama_falhou)" | |
| echo " $(t ollama_manual)" | |
| fi | |
| rm -f "$instalador" | |
| fi | |
| # ------------------------------------------------------------------ | |
| # 2. Ambiente Python isolado. | |
| # | |
| # Um venv próprio evita o conflito do pyautogui com o setuptools do | |
| # sistema em distros recentes (Ubuntu 24.04+, Zorin 17+). | |
| # --system-site-packages deixa o venv enxergar o tkinter, que só existe | |
| # como pacote do sistema (python3-tk), nunca via pip. | |
| # | |
| # Se o venv existe mas o Python dele não roda mais (ex: atualização da | |
| # distro trocou a versão do Python), ele é recriado do zero. | |
| # ------------------------------------------------------------------ | |
| # Escolhe o Python base: o "python3" padrão, a menos que ele não tenha | |
| # tkinter e outro python3.X instalado tenha (acontece quando o python3 | |
| # do PATH foi trocado e o pacote python3-tk é da versão padrão da distro). | |
| escolher_python() { | |
| local candidato | |
| for candidato in python3 /usr/bin/python3 $(ls /usr/bin/python3.[0-9]* 2>/dev/null | grep -E 'python3\.[0-9]+$' | sort -t. -k2 -n -r); do | |
| command -v "$candidato" >/dev/null 2>&1 || continue | |
| "$candidato" -c "import sys; sys.exit(0 if sys.version_info >= (3, 8) else 1)" 2>/dev/null || continue | |
| "$candidato" -c "import venv, tkinter" >/dev/null 2>&1 && { echo "$candidato"; return; } | |
| done | |
| echo python3 | |
| } | |
| venv_ok() { | |
| [ -x "$VENV/bin/python3" ] && "$VENV/bin/python3" -c "pass" >/dev/null 2>&1 | |
| } | |
| # Venv criado com um Python sem tkinter enquanto existe outro com tkinter | |
| # (ex: python3-tk instalado depois): recria pra liberar a interface gráfica. | |
| PYTHON_BASE="$(escolher_python)" | |
| if venv_ok && ! "$VENV/bin/python3" -c "import tkinter" >/dev/null 2>&1 \ | |
| && "$PYTHON_BASE" -c "import tkinter" >/dev/null 2>&1; then | |
| msg "$(t venv_sem_tk python="$PYTHON_BASE")" | |
| rm -rf "$VENV" | |
| fi | |
| if venv_ok; then | |
| msg "$(t venv_ja)" | |
| else | |
| [ -e "$VENV" ] && msg "$(t venv_quebrado)" | |
| rm -rf "$VENV" | |
| msg "$(t venv_criando versao="$("$PYTHON_BASE" --version 2>&1)")" | |
| if "$PYTHON_BASE" -m venv --system-site-packages "$VENV"; then | |
| "$VENV/bin/pip" install --quiet --upgrade pip setuptools wheel \ | |
| || aviso "$(t pip_falhou)" | |
| msg "$(t venv_ok)" | |
| else | |
| aviso "$(t venv_falhou)" | |
| rm -rf "$VENV" | |
| fi | |
| fi | |
| if [ -x "$VENV/bin/python3" ]; then | |
| msg "$(t deps_verificando)" | |
| faltando=() | |
| for par in "${DEPENDENCIAS[@]}"; do | |
| modulo="${par%%:*}" | |
| pacote="${par##*:}" | |
| # find_spec só checa se o módulo existe, sem executá-lo. Um | |
| # "import" real do pyautogui lê $DISPLAY na hora e dá KeyError | |
| # sem sessão gráfica (como durante o próprio postinst). | |
| if ! "$VENV/bin/python3" -c "import importlib.util,sys; sys.exit(0 if importlib.util.find_spec('$modulo') else 1)" >/dev/null 2>&1; then | |
| faltando+=("$pacote") | |
| fi | |
| done | |
| if [ "${#faltando[@]}" -eq 0 ]; then | |
| msg "$(t deps_ok)" | |
| else | |
| msg "$(t deps_instalando pacotes="${faltando[*]}")" | |
| if ! "$VENV/bin/pip" install --quiet "${faltando[@]}"; then | |
| aviso "$(t deps_falhou)" | |
| fi | |
| fi | |
| if "$VENV/bin/python3" -c "import tkinter" >/dev/null 2>&1; then | |
| msg "$(t tk_ok)" | |
| else | |
| aviso "$(t tk_falta)" | |
| echo " $(t tk_instale)" | |
| echo " $(t tk_terminal)" | |
| fi | |
| # Clicar nos botões pelo nome (árvore de acessibilidade, AT-SPI). | |
| if "$VENV/bin/python3" -c "import gi; gi.require_version('Atspi', '2.0'); from gi.repository import Atspi" >/dev/null 2>&1; then | |
| msg "$(t acess_ok)" | |
| else | |
| aviso "$(t acess_falta)" | |
| echo " $(t acess_instale)" | |
| fi | |
| else | |
| aviso "$(t sem_venv)" | |
| fi | |
| # ------------------------------------------------------------------ | |
| # 3. Serviço do Ollama pronto (a helper Murph já vem no pacote). | |
| # ------------------------------------------------------------------ | |
| servico_pronto=0 | |
| # Recém-instalado, o serviço leva um instante pra subir. Se o Ollama | |
| # existe mas o serviço está parado (sem systemd, por exemplo), tenta | |
| # iniciar. | |
| if ! ollama_responde && command -v systemctl >/dev/null 2>&1 \ | |
| && systemctl list-unit-files ollama.service >/dev/null 2>&1; then | |
| systemctl start ollama >/dev/null 2>&1 || true | |
| fi | |
| for _ in $(seq 1 10); do | |
| if ollama_responde; then | |
| servico_pronto=1 | |
| break | |
| fi | |
| sleep 2 | |
| done | |
| # 3.0.5: nenhum modelo ajudante é baixado. Quem decide o tipo de cada | |
| # pedido é a helper Murph, que já vem no pacote (modelos/murph.json). | |
| msg "$(t helper_murph versao="1.0")" | |
| if [ "$servico_pronto" -eq 0 ]; then | |
| aviso "$(t ollama_nao_respondeu url="$OLLAMA_URL")" | |
| # 4.5: WSL (e contêiner) costuma não ter systemd: o serviço não sobe sozinho | |
| if em_wsl || ! command -v systemctl >/dev/null 2>&1; then | |
| echo " $(t ollama_serve_manual)" | |
| fi | |
| fi | |
| # ------------------------------------------------------------------ | |
| # 3d. Gargantua (4.0): a IA oficial do openTARS, treinada pra usar as ferramentas | |
| # dele. Desde 18/10 é o Gargantua 2 (Qwen3.5 2B, ~1,6 GB, enxerga a tela: o Cooper, | |
| # modelo à parte, foi descontinuado). Baixa só se ainda não tiver. | |
| # Com ele instalado, o passo 4 não baixa outro modelo de conversa. | |
| # TARS_SEM_GARGANTUA=1 pula; TARS_MODELO_GARGANTUA=<nome> troca. | |
| # ------------------------------------------------------------------ | |
| MODELO_GARGANTUA="${TARS_MODELO_GARGANTUA:-ProjectOpenTARS/Gargantua-2}" | |
| # 5.1: máquina fraca (sem placa NVIDIA com 4 GB E com até 6 GB de RAM OU menos de 4 threads) recebe o Gargantua Lite | |
| # (Qwen3.5 0,8B com visão, 0,6 GB, Gargantua-2:lite). TARS_GARGANTUA=lite|completo força. A mesma regra do primeiro uso (tars_primeiro_uso.py). | |
| maquina_fraca() { | |
| case "${TARS_GARGANTUA:-}" in lite) return 0 ;; completo) return 1 ;; esac | |
| local vram=0 ram threads | |
| if command -v nvidia-smi >/dev/null 2>&1; then | |
| vram="$(nvidia-smi --query-gpu=memory.total --format=csv,noheader,nounits 2>/dev/null | sort -n | tail -1 | cut -d. -f1)" | |
| fi | |
| [ "${vram:-0}" -ge 4000 ] 2>/dev/null && return 1 | |
| ram="$(awk '/^MemTotal:/ {print int($2/1024)}' /proc/meminfo 2>/dev/null)" | |
| threads="$(nproc 2>/dev/null || echo 4)" | |
| [ "${ram:-99999}" -le 6200 ] || [ "${threads:-4}" -lt 4 ] | |
| } | |
| if [ -z "${TARS_MODELO_GARGANTUA:-}" ] && maquina_fraca; then | |
| MODELO_GARGANTUA="ProjectOpenTARS/Gargantua-2:lite" | |
| fi | |
| gargantua_instalado() { | |
| # Qualquer "gargantua" conta (o oficial ou um treinado no próprio PC). | |
| modelos_instalados | awk -F/ '{print tolower($NF)}' | grep -q '^gargantua' | |
| } | |
| if [ "$servico_pronto" -eq 1 ]; then | |
| if [ "${TARS_SEM_GARGANTUA:-0}" = "1" ]; then | |
| msg "$(t gargantua_pulado)" | |
| elif gargantua_instalado; then | |
| msg "$(t gargantua_ja modelo="$(modelos_instalados | grep -i 'gargantua' | head -1)")" | |
| else | |
| msg "$(t gargantua_baixando modelo="$MODELO_GARGANTUA")" | |
| if [ -t 1 ]; then | |
| # num terminal ("dpkg -i", "apt install", "opentars --setup"): baixa aqui, com a barra do Ollama | |
| msg "$(t pode_demorar)" | |
| if command -v ollama >/dev/null 2>&1; then | |
| ollama pull "$MODELO_GARGANTUA" || true | |
| else | |
| curl -fsS --max-time 7200 "$OLLAMA_URL/api/pull" \ | |
| -d "{\"model\":\"$MODELO_GARGANTUA\",\"stream\":false}" >/dev/null || true | |
| fi | |
| if gargantua_instalado; then | |
| msg "$(t baixado modelo="$MODELO_GARGANTUA")" | |
| else | |
| aviso "$(t nao_baixou modelo="$MODELO_GARGANTUA")" | |
| fi | |
| else | |
| # SEM terminal (loja de aplicativos, instalação automática, atualização pelo próprio openTARS): um pull aqui | |
| # segura a instalação por minutos sem mostrar nada, e a barra da loja fica parada em 0% enquanto o | |
| # Ollama já está em 50%. Baixa em segundo plano, solto da instalação; o assistente de primeiro uso do | |
| # openTARS acompanha o mesmo download (o Ollama junta os pedidos) e mostra a barra de verdade. | |
| msg "$(t gargantua_segundo_plano)" | |
| if command -v ollama >/dev/null 2>&1; then | |
| ( setsid nohup ollama pull "$MODELO_GARGANTUA" >/dev/null 2>&1 < /dev/null & ) | |
| else | |
| ( setsid nohup curl -fsS --max-time 7200 "$OLLAMA_URL/api/pull" \ | |
| -d "{\"model\":\"$MODELO_GARGANTUA\",\"stream\":false}" >/dev/null 2>&1 < /dev/null & ) | |
| fi | |
| fi | |
| fi | |
| fi | |
| # ------------------------------------------------------------------ | |
| # 4. Modelo de conversa: sem nenhum, o openTARS abre mas não tem quem | |
| # responda. Baixa UM, do tamanho certo pro PC, só se não houver | |
| # nenhum ainda (quem já tem modelos escolheu os seus). | |
| # TARS_MODELO_CONVERSA=<modelo> escolhe outro; TARS_SEM_MODELO=1 pula. | |
| # ------------------------------------------------------------------ | |
| vram_gb() { | |
| local total=0 | |
| if command -v nvidia-smi >/dev/null 2>&1; then | |
| total=$(nvidia-smi --query-gpu=memory.total --format=csv,noheader,nounits 2>/dev/null \ | |
| | awk '{s+=$1} END {printf "%d", s/1024}') | |
| fi | |
| if [ "${total:-0}" -eq 0 ]; then | |
| for d in /sys/class/drm/card[0-9]*/device; do | |
| [ "$(cat "$d/vendor" 2>/dev/null)" = "0x1002" ] || continue | |
| local b; b=$(cat "$d/mem_info_vram_total" 2>/dev/null || echo 0) | |
| [ "$b" -ge 2147483648 ] && total=$((total + b / 1073741824)) | |
| done | |
| fi | |
| echo "${total:-0}" | |
| } | |
| ram_gb() { | |
| awk '/MemTotal/ {printf "%d", $2/1048576}' /proc/meminfo 2>/dev/null || echo 0 | |
| } | |
| modelo_de_conversa_sugerido() { | |
| local vram ram | |
| vram=$(vram_gb); ram=$(ram_gb) | |
| if [ "$vram" -ge 6 ]; then | |
| echo "qwen3:8b" # ~5 GB, cabe na placa | |
| elif [ "$ram" -ge 12 ]; then | |
| echo "qwen3:4b" # ~2.5 GB, roda bem na CPU | |
| else | |
| echo "qwen3:1.7b" # ~1.4 GB, pra PC com pouca memória | |
| fi | |
| } | |
| contar_modelos_de_conversa() { | |
| modelos_instalados | grep -vx "$MODELO_AJUDANTE" | grep -vx "$MODELO_AJUDANTE_ANTIGO" \ | |
| | grep -viE 'embed|minilm|bge-|cooper' | wc -l | |
| } | |
| if [ "$servico_pronto" -eq 1 ]; then | |
| outros=$(contar_modelos_de_conversa) | |
| if [ "$outros" -gt 0 ]; then | |
| msg "$(t ja_tem_modelos n="$outros")" | |
| elif [ "${TARS_SEM_MODELO:-0}" = "1" ]; then | |
| aviso "$(t sem_modelo_pedido)" | |
| else | |
| MODELO_CONVERSA="${TARS_MODELO_CONVERSA:-$(modelo_de_conversa_sugerido)}" | |
| msg "$(t baixando_conversa modelo="$MODELO_CONVERSA" vram="$(vram_gb)" ram="$(ram_gb)")" | |
| msg "$(t pode_demorar)" | |
| if command -v ollama >/dev/null 2>&1; then | |
| ollama pull "$MODELO_CONVERSA" || true | |
| else | |
| curl -fsS --max-time 7200 "$OLLAMA_URL/api/pull" \ | |
| -d "{\"model\":\"$MODELO_CONVERSA\",\"stream\":false}" >/dev/null || true | |
| fi | |
| if modelos_instalados | grep -qx "$MODELO_CONVERSA"; then | |
| msg "$(t baixado modelo="$MODELO_CONVERSA")" | |
| else | |
| aviso "$(t nao_baixou modelo="$MODELO_CONVERSA")" | |
| fi | |
| fi | |
| else | |
| aviso "$(t sem_ollama_modelo)" | |
| fi | |
| echo "" | |
| echo "$(t pronto)" | |
| echo "$(t dica_atalho)" | |
| echo "" | |
| exit 0 | |