File size: 4,789 Bytes
c5dde9d | 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 | import os
import platform
import re
import shutil
import subprocess
import tarfile
import urllib.request
from pathlib import Path
import gradio as gr
import spaces
# ============================================================
# ZeroGPU requirement: at least one module-level @spaces.GPU
# function, or the Space fails startup with
# "No @spaces.GPU function detected during startup".
# Never called; the app serving port 7860 is the bansos daemon.
# ============================================================
@spaces.GPU
def _zerogpu_startup_stub():
return None
# A launched Gradio server is what triggers the ZeroGPU startup report
# (spaces hooks gr.Blocks.launch). It serves only this stub page on 7861;
# bansos-router itself serves port 7860.
demo = gr.Blocks()
with demo:
gr.Markdown(
"bansos-router runs on this Space's port 7860. "
"Open the root URL once the Space is Running."
)
def launch_gradio_stub():
demo.queue().launch(
server_name="0.0.0.0",
server_port=7861,
prevent_thread_lock=True,
show_error=True,
)
# ============================================================
# bansos-router launcher (npm package, prebuilt — no build)
# ============================================================
NODE_ROOT = Path("/tmp/node22")
PORT = "7860"
def log(message):
print(f"\n[bansos HF] {message}", flush=True)
def run(cmd, cwd=None, env=None):
log("+ " + " ".join(str(x) for x in cmd))
subprocess.run(cmd, cwd=str(cwd) if cwd else None, env=env, check=True)
def node_arch():
arch = platform.machine().lower()
if arch in ("x86_64", "amd64"):
return "x64"
if arch in ("aarch64", "arm64"):
return "arm64"
raise RuntimeError(f"Unsupported architecture: {arch}")
def install_node22():
node_bin = NODE_ROOT / "bin" / "node"
npm_bin = NODE_ROOT / "bin" / "npm"
if node_bin.exists() and npm_bin.exists():
log("Node.js already available.")
return node_bin, npm_bin
log("Finding latest Node.js 22...")
arch = node_arch()
with urllib.request.urlopen(
"https://nodejs.org/dist/latest-v22.x/SHASUMS256.txt", timeout=60
) as response:
shasums = response.read().decode("utf-8")
match = re.search(rf"([a-f0-9]{{64}})\s+(node-v[\d.]+-linux-{arch}\.tar\.xz)", shasums)
if not match:
raise RuntimeError("Node.js 22 binary was not found.")
expected_hash, filename = match.group(1), match.group(2)
archive = Path("/tmp") / filename
log(f"Downloading {filename}...")
urllib.request.urlretrieve(f"https://nodejs.org/dist/latest-v22.x/{filename}", archive)
import hashlib
digest = hashlib.sha256()
with open(archive, "rb") as f:
for chunk in iter(lambda: f.read(1024 * 1024), b""):
digest.update(chunk)
if digest.hexdigest() != expected_hash:
raise RuntimeError("Node.js SHA256 verification failed.")
extract_root = Path("/tmp/node22-extract")
shutil.rmtree(extract_root, ignore_errors=True)
extract_root.mkdir(parents=True, exist_ok=True)
log("Extracting Node.js...")
with tarfile.open(archive, "r:xz") as tar:
tar.extractall(extract_root)
extracted = next(p for p in extract_root.iterdir() if p.is_dir())
shutil.rmtree(NODE_ROOT, ignore_errors=True)
shutil.move(str(extracted), str(NODE_ROOT))
log("Node.js 22 installed.")
return node_bin, npm_bin
def main():
node_bin, npm_bin = install_node22()
env = os.environ.copy()
env["PATH"] = str(NODE_ROOT / "bin") + os.pathsep + env.get("PATH", "")
# State dir (~/.bansos). Free-tier containers reset on restart.
env["HOME"] = "/tmp"
env["PORT"] = PORT
run([str(node_bin), "--version"], env=env)
bansos_bin = NODE_ROOT / "bin" / "bansos"
if not bansos_bin.exists():
log("Installing bansos-router (prebuilt, npm)...")
run([str(npm_bin), "install", "--global", "--no-audit", "--no-fund", "bansos-router"], env=env)
else:
log("bansos-router already installed.")
run([str(bansos_bin), "--version"], env=env)
log("Launching Gradio stub on 7861 (ZeroGPU startup report)...")
launch_gradio_stub()
log(f"Starting bansos daemon on 0.0.0.0:{PORT}")
# Foreground (no --bg): this process IS the Space. HF proxies
# https://<space>.hf.space -> :7860, so allow non-loopback clients.
os.execvpe(
str(bansos_bin),
[str(bansos_bin), "start",
"--bind", "0.0.0.0", "--port", PORT,
"--unsafe-allow-non-loopback"],
env,
)
if __name__ == "__main__":
print("\n========================================\n bansos-router - Hugging Face Space\n========================================\n", flush=True)
main()
|