Spaces:
Sleeping
Sleeping
| # Graceful Shutdown Handler | |
| cleanup() { | |
| echo "π Shutting down..." | |
| pkill -TERM -f chrome-linux64 2>/dev/null || true | |
| sleep 5 | |
| # Snapshot the profile (cookies/logins) to Postgres after Chrome has closed | |
| # its files, so the next boot restores a consistent login state. | |
| if [ -n "$DATABASE_URL" ]; then | |
| echo "πΎ Backing up Chrome profile to Postgres..." | |
| /opt/profilesync/profilesync backup || echo "β οΈ Backup failed" | |
| fi | |
| exit 0 | |
| } | |
| trap cleanup SIGTERM SIGINT | |
| echo "π Starting Chrome Headless Boot Sequence on Hugging Face Spaces..." | |
| # 0. Restore Chrome profile from Postgres snapshot (survives HF restart/rebuild). | |
| # DATABASE_URL is a Space secret β never hardcode the credential here. | |
| if [ -n "$DATABASE_URL" ]; then | |
| echo "π₯ Restoring Chrome profile from Postgres..." | |
| /opt/profilesync/profilesync restore || echo "β οΈ Restore skipped/failed (first run?)" | |
| fi | |
| # 1. Structure Detection & Fix | |
| if [ -d "/home/chrome/data/data/Default" ]; then | |
| echo "β οΈ Nested structure detected. Fixing..." | |
| mv /home/chrome/data/data/* /home/chrome/data/ 2>/dev/null || true | |
| rmdir /home/chrome/data/data 2>/dev/null || true | |
| fi | |
| # 2. Deep Junk Cleanup (80% bloat removal) | |
| echo "π§Ή Performing Deep Junk Cleanup..." | |
| rm -rf /home/chrome/data/Default/Cache \ | |
| /home/chrome/data/Default/Code\ Cache \ | |
| /home/chrome/data/Default/GPUCache \ | |
| /home/chrome/data/Default/DawnGraphiteCache \ | |
| /home/chrome/data/Default/DawnWebGPUCache \ | |
| /home/chrome/data/Default/ShaderCache \ | |
| /home/chrome/data/Default/GrShaderCache \ | |
| /home/chrome/data/Default/Service\ Worker/CacheStorage \ | |
| /home/chrome/data/Default/History \ | |
| /home/chrome/data/Default/Favicons \ | |
| /home/chrome/data/Default/Top\ Sites \ | |
| /home/chrome/data/Default/Visited\ Links \ | |
| /home/chrome/data/Default/Network\ Action\ Predictor \ | |
| /home/chrome/data/Default/Network\ Persistent\ State 2>/dev/null || true | |
| rm -rf /home/chrome/data/component_crx_cache \ | |
| /home/chrome/data/optimization_guide_model_store \ | |
| /home/chrome/data/WasmTtsEngine \ | |
| /home/chrome/data/OnDeviceHeadSuggestModel \ | |
| /home/chrome/data/hyphen-data \ | |
| /home/chrome/data/ZxcvbnData \ | |
| /home/chrome/data/SafetyTips \ | |
| /home/chrome/data/Safe\ Browsing* \ | |
| /home/chrome/data/BrowserMetrics* \ | |
| /home/chrome/data/Crashpad \ | |
| /home/chrome/data/GrShaderCache \ | |
| /home/chrome/data/ShaderCache \ | |
| /home/chrome/data/GraphiteDawnCache 2>/dev/null || true | |
| # 3. Singleton/Lock removal | |
| echo "π Removing lock files..." | |
| find /home/chrome -name "Singleton*" -exec rm -f {} + | |
| rm -f /home/chrome/data/Default/LOCK /home/chrome/data/Default/LOG.old 2>/dev/null || true | |
| # 3b. Session-restore wipe. The persisted profile saves the last set of open tabs | |
| # (Sessions/, Current/Last Tabs, Current/Last Session). On boot Chrome restores | |
| # them β which resurrects stale tabs (e.g. the removed Flow tab) regardless of the | |
| # command-line URL. Deleting these makes Chrome open ONLY the launch URL; the | |
| # gpt/gemini extensions then open their own service tabs. Cookies/logins live in | |
| # separate files (Cookies, Login Data, Local Storage) and are untouched. | |
| echo "π§Ή Wiping saved session/tab restore state..." | |
| rm -rf /home/chrome/data/Default/Sessions 2>/dev/null || true | |
| rm -f /home/chrome/data/Default/Current\ Tabs \ | |
| /home/chrome/data/Default/Last\ Tabs \ | |
| /home/chrome/data/Default/Current\ Session \ | |
| /home/chrome/data/Default/Last\ Session 2>/dev/null || true | |
| # 4. Preferences Injection (Developer Mode & Crash Fix) | |
| python3 -c " | |
| import json, os | |
| pref_path = '/home/chrome/data/Default/Preferences' | |
| os.makedirs(os.path.dirname(pref_path), exist_ok=True) | |
| data = {} | |
| if os.path.exists(pref_path): | |
| try: | |
| with open(pref_path, 'r') as f: | |
| data = json.load(f) | |
| except: | |
| pass | |
| # Enable Developer Mode | |
| if 'extensions' not in data: | |
| data['extensions'] = {} | |
| if 'ui' not in data['extensions']: | |
| data['extensions']['ui'] = {} | |
| data['extensions']['ui']['developer_mode'] = True | |
| # Prevent Restore Popup | |
| if 'profile' not in data: | |
| data['profile'] = {} | |
| data['profile']['exit_type'] = 'Normal' | |
| with open(pref_path, 'w') as f: | |
| json.dump(data, f) | |
| " 2>/dev/null || true | |
| # 5. Start Go Monitor Server | |
| echo "π Starting 3-tab Monitor Server..." | |
| touch /home/chrome/chrome.log | |
| /opt/monitor3/monitor > /home/chrome/monitor.log 2>&1 & | |
| # 6. Start CDP Proxy | |
| echo "π Starting CDP Proxy..." | |
| socat TCP-LISTEN:9223,fork,bind=0.0.0.0 TCP:127.0.0.1:9222 & | |
| # 6a. Start the backend API servers on localhost. The monitor doubles as a | |
| # gateway and fronts them on :3001 (/gpt, /gemini). Each logs separately | |
| # and runs in the background so a crash never blocks the boot. | |
| echo "π§© Starting backend API servers (gpt / gemini)..." | |
| ( cd /opt/chatgpt && ./agent >> /home/chrome/chatgpt.log 2>&1 & ) | |
| ( cd /opt/gemini-srv && WS_PORT=9226 PORT=8000 ./free-gemini-api >> /home/chrome/gemini.log 2>&1 & ) | |
| # 6b. Periodic profile backup (safety net if HF kills the container without a clean | |
| # SIGTERM). Best-effort every 5 minutes; the shutdown trap does the final one. | |
| if [ -n "$DATABASE_URL" ]; then | |
| echo "ποΈ Profile auto-backup every 5 min enabled." | |
| ( while sleep 300; do /opt/profilesync/profilesync backup >/dev/null 2>&1 || true; done ) & | |
| fi | |
| # 6c. Provide a valid per-user runtime dir. Chrome's mojo/IPC layer and each MV3 | |
| # extension service worker need this; without it Chrome logs | |
| # "XDG_RUNTIME_DIR is invalid or not set" and the extension service workers | |
| # intermittently fail to spawn (DidStartWorkerFail) β the gpt/gemini | |
| # extensions never connect. Purely additive: just creates a dir and exports it. | |
| export XDG_RUNTIME_DIR=/tmp/runtime-chrome | |
| mkdir -p "$XDG_RUNTIME_DIR" && chmod 700 "$XDG_RUNTIME_DIR" | |
| # 7. Launch Chrome Headless | |
| # NOTE: do NOT add --renderer-process-limit=1 / --process-per-site / --no-zygote here. | |
| # Each extension runs an MV3 service worker (its own renderer) and the chat | |
| # pipeline injects via chrome.scripting.executeScript into the service tab. Capping renderers | |
| # starves those workers β they fail to start (DidStartWorkerFail) so the extension never | |
| # connects, and when one does start, executeScript hangs β the /gpt /gemini APIs | |
| # hang forever. HF cpu-basic has 16 GB (we use ~1β2 GB), so the memory "savings" weren't | |
| # needed and broke the extensions. Keep Chrome multi-process. | |
| echo "β¨ Launching Chrome Headless..." | |
| /opt/chrome-linux64/chrome \ | |
| --headless=new \ | |
| --no-sandbox \ | |
| --disable-setuid-sandbox \ | |
| --disable-infobars \ | |
| --disable-blink-features=AutomationControlled \ | |
| --excludeSwitches=enable-automation \ | |
| --use-fake-ui-for-media-stream \ | |
| --use-fake-device-for-media-stream \ | |
| --use-mock-keychain \ | |
| --password-store=basic \ | |
| --window-size=980,1070 \ | |
| --user-agent='Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/145.0.0.0 Safari/537.36' \ | |
| --disable-gpu \ | |
| --disable-software-rasterizer \ | |
| --disable-gpu-compositing \ | |
| --disable-breakpad \ | |
| --disable-crash-reporter \ | |
| --mute-audio \ | |
| --in-process-gpu \ | |
| --disable-dev-shm-usage \ | |
| --font-render-hinting=none \ | |
| --disable-font-subpixel-positioning \ | |
| --force-color-profile=srgb \ | |
| --remote-debugging-port=9222 \ | |
| --remote-debugging-address=127.0.0.1 \ | |
| --remote-allow-origins='*' \ | |
| --user-data-dir=/home/chrome/data \ | |
| --disable-background-networking \ | |
| --disable-sync \ | |
| --no-first-run \ | |
| --disable-default-apps \ | |
| --disable-background-timer-throttling \ | |
| --disable-renderer-backgrounding \ | |
| --disable-backgrounding-occluded-windows \ | |
| --load-extension=/opt/gpt-extension,/opt/gemini-extension \ | |
| --disable-extensions-except=/opt/gpt-extension,/opt/gemini-extension \ | |
| --disable-translate \ | |
| --memory-pressure-off \ | |
| --disable-client-side-phishing-detection \ | |
| --disable-component-update \ | |
| --disable-domain-reliability \ | |
| --disable-hang-monitor \ | |
| --disable-ipc-flooding-protection \ | |
| --disable-popup-blocking \ | |
| --disable-prompt-on-repost \ | |
| --disable-cookie-encryption \ | |
| --force-dark-mode \ | |
| --udp-force-randomized-port \ | |
| --disable-partial-raster \ | |
| --disable-features=TranslateUI,PaintHolding,IsolateOrigins,site-per-process \ | |
| --enable-features=NetworkService,NetworkServiceInProcess \ | |
| --disable-site-isolation-trials \ | |
| --js-flags="--max-old-space-size=512" \ | |
| --metrics-recording-only \ | |
| 'https://chatgpt.com/' >> /home/chrome/chrome.log 2>&1 | |