Spaces:
Paused
Paused
File size: 6,386 Bytes
6ed02d2 7fa01c3 6ed02d2 29f7b7f f3b4dc5 29f7b7f 6ed02d2 7fa01c3 6ed02d2 7fa01c3 6ed02d2 f3b4dc5 6ed02d2 29f7b7f 6ed02d2 29f7b7f 6ed02d2 85620fa 6ed02d2 f3b4dc5 6ed02d2 29f7b7f 6ed02d2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | #!/usr/bin/env bash
# testzerogpu2 預設環境 bootstrap。
# app.py 啟動時會在背景執行這支腳本,做兩件事:
# 1. 安裝 HelloSun/storage 的 s5p(oshopencode 等)到工作目錄並設定 PATH
# 2. 安裝「甲」:flint Q4_1 llama-server(port 20000)並設定 pi coding agent 使用它
#
# 所有輸出都寫到 /tmp/webterm-bootstrap.log,狀態可看 GET /api/bootstrap/status。
set -uo pipefail
LOG="${WEBTERM_BOOTSTRAP_LOG:-/tmp/webterm-bootstrap.log}"
mkdir -p "$(dirname "$LOG")" 2>/dev/null || true
exec >>"$LOG" 2>&1
HOME_DIR="${HOME:-/home/user}"
# repo/工作目錄 = 這支腳本所在目錄(HF Space 是 /app,不是 $HOME/app)
APP_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
S5P_DIR="$APP_DIR/s5p"
FLINT_DIR="$HOME_DIR/flint_cpu_opt"
STORAGE_BASE="${S5P_STORAGE_BASE:-https://huggingface.co/spaces/HelloSun/storage/resolve/main}"
FLINT_BASE="${FLINT_BASE:-https://huggingface.co/HelloSun/flint_cpu_opt/resolve/main}"
S5P_FILES=(
"a.sh"
"o.sh"
"oshopencode.py"
"oshopencode/HANDOFF.md"
"oshopencode/o.sh"
"oshopencode/oshopencode.py"
"oshopencode/tests/local_http_proxy.py"
"oshopencode/tests/test_oshopencode.py"
)
log() { echo "[bootstrap $(date -u +%Y-%m-%dT%H:%M:%SZ)] $*"; }
tor_port_open() { (exec 3<>/dev/tcp/127.0.0.1/9050) 2>/dev/null; }
# oshopencode.py 預設走 Tor;Space 沒有 systemd,所以自己設定 torrc 並用 init script 啟動。
install_tor_env() {
log "設定 Tor 環境(SocksPort 9050 + ControlSocket /run/tor/control)"
local rc=/etc/tor/torrc
if [ -f "$rc" ]; then
grep -qE '^[[:space:]]*SocksPort[[:space:]]+9050' "$rc" 2>/dev/null || \
grep -qE '^[[:space:]]*SocksPort[[:space:]]' "$rc" 2>/dev/null || \
echo 'SocksPort 9050' >>"$rc"
grep -qE '^[[:space:]]*ControlSocket[[:space:]]+/run/tor/control' "$rc" 2>/dev/null || \
echo 'ControlSocket /run/tor/control' >>"$rc"
grep -qE '^[[:space:]]*CookieAuthentication[[:space:]]+1' "$rc" 2>/dev/null || \
echo 'CookieAuthentication 1' >>"$rc"
fi
if ! tor_port_open; then
mkdir -p /run/tor
if id debian-tor >/dev/null 2>&1; then
chown -R debian-tor:debian-tor /run/tor 2>/dev/null || true
fi
chmod 700 /run/tor 2>/dev/null || true
if [ -x /etc/init.d/tor ]; then
/etc/init.d/tor start >>"$LOG" 2>&1 || true
fi
if ! tor_port_open && command -v tor >/dev/null 2>&1; then
if id debian-tor >/dev/null 2>&1; then
chown -R debian-tor:debian-tor /run/tor 2>/dev/null || true
su -s /bin/sh debian-tor -c 'tor --RunAsDaemon 1' >>"$LOG" 2>&1 || true
fi
fi
fi
for _ in $(seq 1 15); do
tor_port_open && break
sleep 1
done
if tor_port_open; then
log "Tor 就緒(127.0.0.1:9050)"
return 0
fi
log "WARN Tor 未就緒;oshopencode 會自動退回 HTTP proxy 模式"
return 1
}
add_path_line() {
local rc="$1" line="$2"
[ -n "$rc" ] || return 0
touch "$rc" 2>/dev/null || return 0
grep -qF "$line" "$rc" 2>/dev/null || echo "$line" >>"$rc"
}
install_s5p() {
log "install s5p -> $S5P_DIR"
mkdir -p "$S5P_DIR/oshopencode/tests" "$HOME_DIR/.local/bin"
local f failed=0
for f in "${S5P_FILES[@]}"; do
mkdir -p "$S5P_DIR/$(dirname "$f")"
if ! curl -fL --retry 3 --retry-delay 3 --max-time 300 \
"$STORAGE_BASE/s5p/$f" -o "$S5P_DIR/$f"; then
log "WARN 下載失敗:$f"
failed=1
fi
done
chmod +x "$S5P_DIR"/*.sh "$S5P_DIR/oshopencode.py" 2>/dev/null || true
chmod +x "$S5P_DIR/oshopencode"/*.sh "$S5P_DIR/oshopencode/oshopencode.py" 2>/dev/null || true
# 讓 `oshopencode.py` 直接可用(不論 shell 有沒有讀到 rc)。
cat >"$HOME_DIR/.local/bin/oshopencode.py" <<EOS
#!/usr/bin/env bash
exec python3 "$S5P_DIR/oshopencode.py" "\$@"
EOS
chmod +x "$HOME_DIR/.local/bin/oshopencode.py"
ln -sf "$HOME_DIR/.local/bin/oshopencode.py" /usr/local/bin/oshopencode.py 2>/dev/null || true
# opencode 由 app.py 安裝在 ~/.opencode/bin;做一個固定路徑的連結,登入 shell 一定找得到。
if [ -x "$HOME_DIR/.opencode/bin/opencode" ]; then
ln -sf "$HOME_DIR/.opencode/bin/opencode" /usr/local/bin/opencode 2>/dev/null || true
fi
local path_line="export PATH=\"$S5P_DIR:$S5P_DIR/oshopencode:$HOME_DIR/.opencode/bin:$HOME_DIR/.local/bin:\$PATH\""
add_path_line "$HOME_DIR/.bashrc" "$path_line"
add_path_line "$HOME_DIR/.profile" "$path_line"
add_path_line "$HOME_DIR/.bash_profile" "$path_line"
export PATH="$S5P_DIR:$S5P_DIR/oshopencode:$HOME_DIR/.opencode/bin:$HOME_DIR/.local/bin:$PATH"
if [ "$failed" -ne 0 ]; then
log "s5p 部分檔案下載失敗"
return 1
fi
log "s5p 安裝完成;oshopencode.py -> $(command -v oshopencode.py 2>/dev/null || echo 未找到)"
return 0
}
install_jia() {
log "install 甲:flint Q4_1 llama-server :20000 + pi coding agent 設定"
mkdir -p "$FLINT_DIR/patches"
local f
for f in "build.sh" "patches/BASE_COMMIT" "patches/flint-cpu-opt.patch"; do
mkdir -p "$FLINT_DIR/$(dirname "$f")"
if ! curl -fL --retry 3 --retry-delay 3 --max-time 300 \
"$FLINT_BASE/$f" -o "$FLINT_DIR/$f"; then
log "WARN 無法取得 $f"
return 1
fi
done
chmod +x "$FLINT_DIR/build.sh"
local avail_kb
avail_kb=$(df -Pk "$HOME_DIR" 2>/dev/null | awk 'NR==2{print $4}')
avail_kb=${avail_kb:-0}
log "HOME 可用磁碟:$((avail_kb / 1024 / 1024)) GiB"
if [ "$avail_kb" -lt 30000000 ]; then
log "WARN 可用磁碟 < 30 GiB;24 GiB 模型下載可能失敗,仍嘗試"
fi
# build.sh 會:編譯 patched llama.cpp(若尚未編譯)→ 下載 Q4_1 → 設定 pi → 啟動 server:20000
# pi 預設 context 128k(131072);build.sh 的預設是 32768,這裡用環境變數覆寫。
( cd "$FLINT_DIR" && CONTEXT_SIZE="${PI_CONTEXT_SIZE:-131072}" bash ./build.sh )
local rc=$?
# pi 裝在 ~/.local/bin;連結到 /usr/local/bin,任何 shell 都直接可用。
local b
for b in pi pi-local; do
if [ -x "$HOME_DIR/.local/bin/$b" ]; then
ln -sf "$HOME_DIR/.local/bin/$b" "/usr/local/bin/$b" 2>/dev/null || true
fi
done
log "build.sh 結束,exit=$rc"
return "$rc"
}
log "===== bootstrap 開始 ====="
install_tor_env || true
install_s5p || log "WARN s5p 安裝未完全成功"
install_jia || log "WARN 甲 安裝未完全成功"
log "===== bootstrap 結束 ====="
|