_ops_start_and_open.py 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """启动全部服务, 起来后**自动打开** http://127.0.0.1:<gateway>/ —— 给运维控制台用 (2026-09-12)。
  4. 顺序: ① 跑 guanlan.py serve (它自己会等 healthz) → ② 等服务真正可访问(轮询) → ③ 打开浏览器。
  5. `--no-open` 时只启动不开浏览器(用于"完整重启"这类不该弹窗的场景)。
  6. 打开浏览器用 webbrowser (标准库): 本机单用户离线环境, 网关进程与用户同一会话, 能正常拉起默认浏览器。
  7. """
  8. from __future__ import annotations
  9. import pathlib
  10. import subprocess
  11. import sys
  12. import time
  13. import urllib.request
  14. import webbrowser
  15. ROOT = pathlib.Path(__file__).resolve().parents[1]
  16. sys.path.insert(0, str(ROOT))
  17. import guanlan as G # noqa: E402
  18. def main() -> int:
  19. no_open = '--no-open' in sys.argv
  20. c = G.cfg()
  21. url = f"http://{c['host']}:{c['gateway']}/"
  22. print(f'① 启动服务 (guanlan.py serve) …', flush=True)
  23. rc = subprocess.run([sys.executable, 'guanlan.py', 'serve'], cwd=str(ROOT)).returncode
  24. if rc == 2: # serve 的退出码 2 = 网关 60 s 没就绪
  25. print(f'[X] 网关未就绪 (serve 退出码 {rc}) —— 看 logs/gateway.log')
  26. return rc
  27. if rc == 1:
  28. print(' (退出码 1 = 有模块未就绪, degraded 是正常状态 —— 例如本机模型 Ollama 未启动)')
  29. print(f'② 等 {url} 可访问 …', flush=True)
  30. for i in range(30):
  31. try:
  32. with urllib.request.urlopen(url, timeout=10) as r:
  33. if r.status == 200:
  34. print(f' 门户已就绪 (第 {i + 1} 次探测)')
  35. break
  36. except Exception:
  37. time.sleep(1)
  38. else:
  39. print(' [!] 门户 30 s 内没起来 —— 仍尝试打开浏览器, 页面可能需要刷新')
  40. if no_open:
  41. print('③ (--no-open) 不打开浏览器; 入口: ' + url)
  42. return 0
  43. print(f'③ 打开浏览器: {url}')
  44. ok = webbrowser.open(url)
  45. print(f' webbrowser.open → {ok} ({"" if ok else "未成功拉起, 请手工访问上面的地址"})')
  46. return 0
  47. if __name__ == '__main__':
  48. for _s in (sys.stdout, sys.stderr):
  49. try: _s.reconfigure(errors='replace')
  50. except Exception: pass
  51. sys.exit(main())