ops_start_and_open.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  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. try:
  10. from app_common.app_common_guanlan.api import install_root as _install_root
  11. except ImportError: # 极端兜底(包结构异常时按位置上跳)
  12. from pathlib import Path as _P
  13. def _install_root(_f): return _P(_f).resolve().parents[3]
  14. import pathlib
  15. import subprocess
  16. import sys
  17. import time
  18. import urllib.request
  19. import webbrowser
  20. ROOT = _install_root(__file__)
  21. sys.path.insert(0, str(ROOT))
  22. from . import cli as G # noqa: E402
  23. from src import proc as _proc # noqa: E402 无窗口子进程
  24. def main() -> int:
  25. no_open = '--no-open' in sys.argv
  26. c = G.cfg()
  27. url = f"http://{c['host']}:{c['gateway']}/"
  28. print(f'① 启动服务 (guanlan.py serve) …', flush=True)
  29. # src.proc.run: 本脚本常由**无控制台**的运维任务/网关侧拉起, 裸 spawn 会让 serve 拿到一个
  30. # 可见控制台 (2026-09-16 实测: 现场会攒下标题为 .venv\Scripts\python.exe 的黑窗)。
  31. rc = _proc.run([sys.executable, 'guanlan.py', 'serve'], cwd=str(ROOT)).returncode
  32. if rc == 2: # serve 的退出码 2 = 网关 60 s 没就绪
  33. print(f'[X] 网关未就绪 (serve 退出码 {rc}) —— 看 logs/gateway.log')
  34. return rc
  35. if rc == 1:
  36. print(' (退出码 1 = 有模块未就绪, degraded 是正常状态 —— 例如本机模型 Ollama 未启动)')
  37. print(f'② 等 {url} 可访问 …', flush=True)
  38. for i in range(30):
  39. try:
  40. with urllib.request.urlopen(url, timeout=10) as r:
  41. if r.status == 200:
  42. print(f' 门户已就绪 (第 {i + 1} 次探测)')
  43. break
  44. except Exception:
  45. time.sleep(1)
  46. else:
  47. print(' [!] 门户 30 s 内没起来 —— 仍尝试打开浏览器, 页面可能需要刷新')
  48. if no_open:
  49. print('③ (--no-open) 不打开浏览器; 入口: ' + url)
  50. return 0
  51. print(f'③ 打开浏览器: {url}')
  52. ok = webbrowser.open(url)
  53. print(f' webbrowser.open → {ok} ({"" if ok else "未成功拉起, 请手工访问上面的地址"})')
  54. return 0
  55. if __name__ == '__main__':
  56. for _s in (sys.stdout, sys.stderr):
  57. try: _s.reconfigure(errors='replace')
  58. except Exception: pass
  59. sys.exit(main())