#!/usr/bin/env bash # Leaga OpenCode la Qwen3.8-Flash-Next de pe PC2 si la uneltele # web search / web fetch / bash / ssh / powershell / fisiere Office. # # curl -fsSL http://10.11.90.169:8088/setup-fn.sh | bash # # **Ruleaza-l din nou oricand ca sa actualizezi**: aduce ultima versiune a regulilor # si a uneltelor, si actualizeaza OpenCode. Nu strica nimic daca rulezi de zece ori. # # Variabile optionale: # FN_SRV=http://alt-ip:8099 alt server de model # SKIP_SSH=1 / SKIP_PWSH=1 / SKIP_WEB=1 / SKIP_OFFICE=1 sari peste unealta set -u SRV="${FN_SRV:-http://10.11.90.169:8099}" PROV="flashnext" MODEL="qwen38-flash-next" CFG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/opencode" CFG="$CFG_DIR/opencode.json" MCP_DIR="$CFG_DIR/mcp" ok() { printf " \033[32m[ok]\033[0m %s\n" "$1"; } rau() { printf " \033[31m[nu]\033[0m %s\n" "$1"; } info() { printf " %s\n" "$1"; } echo "== OpenCode + Qwen3.8-Flash-Next ==" # ---- 1. raspunde serverul de model? ---- if curl -fsS -m 10 "$SRV/v1/models" -o /tmp/.fn-models.$$ 2>/dev/null; then ok "serverul de model raspunde: $SRV" grep -q "$MODEL" /tmp/.fn-models.$$ || { rau "serverul nu serveste modelul '$MODEL'" info "gasit in schimb: $(tr -d '\n' < /tmp/.fn-models.$$ | head -c 200)"; } rm -f /tmp/.fn-models.$$ else rau "nu ajung la $SRV" info "esti pe VPN? masina e pornita? incearca: curl -v $SRV/v1/models" exit 1 fi # ---- 2. OpenCode ---- OC="$(command -v opencode 2>/dev/null || true)" [ -z "$OC" ] && [ -x "$HOME/.opencode/bin/opencode" ] && OC="$HOME/.opencode/bin/opencode" if [ -z "$OC" ]; then info "instalez OpenCode..." curl -fsSL https://opencode.ai/install | bash >/dev/null 2>&1 [ -x "$HOME/.opencode/bin/opencode" ] && OC="$HOME/.opencode/bin/opencode" else # la re-rulare: actualizam, dar nu ne oprim daca nu merge info "actualizez OpenCode..." "$OC" upgrade >/dev/null 2>&1 || true fi if [ -z "$OC" ]; then rau "instalarea OpenCode a esuat"; exit 1; fi ok "opencode: $OC ($($OC --version 2>&1 | tail -1))" # directorul de configurare: intrebam opencode, nu ghicim D="$($OC debug paths 2>/dev/null | awk '/^[[:space:]]*config[[:space:]]/{print $2; exit}')" [ -n "$D" ] && CFG_DIR="$D" && CFG="$CFG_DIR/opencode.json" && MCP_DIR="$CFG_DIR/mcp" mkdir -p "$CFG_DIR" "$MCP_DIR" # ---- 3. uvx, pentru uneltele de cautare web si ssh ---- UVX="$(command -v uvx 2>/dev/null || true)" [ -z "$UVX" ] && [ -x "$HOME/.local/bin/uvx" ] && UVX="$HOME/.local/bin/uvx" if [ -z "$UVX" ] && { [ "${SKIP_SSH:-0}" != "1" ] || [ "${SKIP_WEB:-0}" != "1" ]; }; then info "instalez uv (pentru uneltele ssh si de cautare web)..." curl -LsSf https://astral.sh/uv/install.sh | sh >/dev/null 2>&1 [ -x "$HOME/.local/bin/uvx" ] && UVX="$HOME/.local/bin/uvx" fi UV="${UVX%uvx}uv" [ -x "$UV" ] || UV="$(command -v uv 2>/dev/null || true)" [ -n "$UVX" ] && ok "uvx: $UVX" || rau "uvx lipseste: sar peste ssh si cautare web" # ---- 4. unealta PowerShell (server MCP local, fara dependinte) ---- PWSH_MCP="" if [ "${SKIP_PWSH:-0}" != "1" ]; then if curl -fsS -m 20 "${SETUP_SRV:-http://10.11.90.169:8088}/pwsh_mcp.py" -o "$MCP_DIR/pwsh_mcp.py" 2>/dev/null; then PWSH_MCP="$MCP_DIR/pwsh_mcp.py" if command -v pwsh >/dev/null 2>&1; then ok "powershell: pwsh gasit" else info "powershell: pwsh nu e instalat local (unealta va spune asta la folosire)"; fi else rau "nu am putut lua pwsh_mcp.py" fi fi # ---- 4b. unealta pentru fisiere Office (.xlsx, .docx, .pptx) ---- # Serverele gata facute au 29 de unelte (8360 de tokene, ~33 s de prefill in plus # la fiecare cerere), din care 13 de scriere. Astea doua doar citesc. OFFICE_MCP="" if [ "${SKIP_OFFICE:-0}" != "1" ] && [ -n "$UV" ]; then if curl -fsS -m 20 "${SETUP_SRV:-http://10.11.90.169:8088}/office_mcp.py" -o "$MCP_DIR/office_mcp.py" 2>/dev/null; then OFFICE_MCP="$MCP_DIR/office_mcp.py" ok "unealta Office (Excel, Word, PowerPoint)" else rau "nu am putut lua office_mcp.py" fi elif [ "${SKIP_OFFICE:-0}" != "1" ]; then rau "uv lipseste: sar peste unealta Office" fi # ---- 5. regulile de lucru (cauta pe internet intai) ---- # Se aduc de fiecare data: asa, o re-rulare a scriptului le actualizeaza. REGULI="$CFG_DIR/reguli-flashnext.md" if curl -fsS -m 20 "${SETUP_SRV:-http://10.11.90.169:8088}/reguli-flashnext.md" -o "$REGULI.nou" 2>/dev/null; then if [ -f "$REGULI" ] && cmp -s "$REGULI" "$REGULI.nou"; then rm -f "$REGULI.nou"; ok "reguli: deja la zi ($(grep -m1 -oE 'versiune: *[0-9.-]+' "$REGULI" | tr -d ' ' | cut -d: -f2))" else mv "$REGULI.nou" "$REGULI"; ok "reguli actualizate ($(grep -m1 -oE 'versiune: *[0-9.-]+' "$REGULI" | tr -d ' ' | cut -d: -f2))" fi else rau "nu am putut lua regulile"; REGULI="" fi # ---- 6. configuratia ---- # copia de siguranta se face abia dupa ce stim ca fisierul chiar se schimba, # altfel fiecare re-rulare ar lasa in urma inca un .bak PY="$(command -v python3 || command -v python)" [ -z "$PY" ] && { rau "nu am python3, nu pot scrie configuratia"; exit 1; } SRV="$SRV" PROV="$PROV" MODEL="$MODEL" CFG="$CFG" UVX="$UVX" PWSH_MCP="$PWSH_MCP" \ SKIP_SSH="${SKIP_SSH:-0}" SKIP_WEB="${SKIP_WEB:-0}" REGULI="$REGULI" OFFICE_MCP="$OFFICE_MCP" UV="$UV" "$PY" - <<'PYEOF' import json, os, pathlib cfg = pathlib.Path(os.environ["CFG"]) c = {} if cfg.exists(): try: c = json.loads(cfg.read_text()) except Exception: c = {} prov, model, srv = os.environ["PROV"], os.environ["MODEL"], os.environ["SRV"] c.setdefault("$schema", "https://opencode.ai/config.json") c.setdefault("provider", {})[prov] = { "npm": "@ai-sdk/openai-compatible", "name": "Qwen3.8-Flash-Next (PC2)", "options": {"baseURL": srv + "/v1"}, "models": {model: { "name": "Qwen3.8-Flash-Next 125B-A6B", # fara tool_call:true OpenCode nu trimite deloc uneltele catre model "tool_call": True, "limit": {"context": 131072, "output": 8192}, }}, } # doar modelul asta sa fie vizibil in /models c["enabled_providers"] = [prov] c["model"] = "%s/%s" % (prov, model) # regulile de lucru: cheia "instructions" accepta cai locale si se combina cu AGENTS.md reguli = os.environ.get("REGULI", "") inst = [x for x in c.get("instructions", []) if "reguli-flashnext" not in str(x)] if reguli: inst.append(reguli) if inst: c["instructions"] = inst c.setdefault("permission", {}) c["permission"].update({"bash": "allow", "edit": "allow", "read": "allow", "webfetch": "allow"}) mcp = c.setdefault("mcp", {}) # pastram doar uneltele cerute; scoatem ce a mai ramas din configuratii vechi for vechi in ("homelab", "toolsmith", "duckduckgo", "morpheus", "ssh", "powershell", "websearch", "office"): mcp.pop(vechi, None) uvx = os.environ.get("UVX", "") if uvx and os.environ.get("SKIP_WEB") != "1": mcp["websearch"] = {"type": "local", "enabled": True, "timeout": 120000, "command": [uvx, "duckduckgo-mcp-server", "--transport", "stdio"]} if uvx and os.environ.get("SKIP_SSH") != "1": mcp["ssh"] = {"type": "local", "enabled": True, "timeout": 300000, "command": [uvx, "mcp-ssh"]} off = os.environ.get("OFFICE_MCP", "") if off: # `uv run` citeste dependintele declarate in capul scriptului (PEP 723) si le # tine in cache singur -- fara mediu virtual de intretinut mcp["office"] = {"type": "local", "enabled": True, "timeout": 300000, "command": [os.environ["UV"], "run", "--quiet", off]} pw = os.environ.get("PWSH_MCP", "") if pw: import sys mcp["powershell"] = {"type": "local", "enabled": True, "timeout": 300000, "command": [sys.executable, pw]} nou = json.dumps(c, indent=2, ensure_ascii=False) + "\n" # citit aici, nu pasat din bash: $(cat) taie liniile noi finale si comparatia # ar iesi mereu "diferit", lasand in urma cate un .bak la fiecare rulare vechi = cfg.read_text() if cfg.exists() else "" if vechi and vechi != nou: import time copie = str(cfg) + ".bak-" + time.strftime("%Y%m%d-%H%M%S") pathlib.Path(copie).write_text(vechi) print(" configuratia veche salvata: %s" % copie) if vechi == nou: print(" configuratie: neschimbata") else: cfg.write_text(nou) print(" scris: %s" % cfg) print(" model implicit: %s" % c["model"]) print(" unelte MCP: %s" % ", ".join(sorted(mcp)) or "(niciuna)") print(" incorporate: bash, read, edit, webfetch") if reguli: print(" reguli : %s" % reguli) PYEOF # ---- 7. verificare ---- echo echo "== verificare ==" LISTA="$($OC models 2>&1 | grep "$PROV/" || true)" if [ -n "$LISTA" ]; then ok "OpenCode vede doar:"; echo "$LISTA" | sed 's/^/ /' else rau "OpenCode nu listeaza modelul" info "ruleaza 'opencode --print-logs models' ca sa vezi eroarea" fi if [ -n "$REGULI" ]; then ok "reguli de lucru incarcate" fi echo echo "Porneste cu: opencode" echo "Modelul se alege cu /models (e singurul din lista)." echo "Actualizare: ruleaza din nou aceeasi comanda curl."