| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869 |
- #!/usr/bin/env python3
- # -*- coding: utf-8 -*-
- """启动全部服务, 起来后**自动打开** http://127.0.0.1:<gateway>/ —— 给运维控制台用 (2026-09-12)。
- 顺序: ① 跑 guanlan.py serve (它自己会等 healthz) → ② 等服务真正可访问(轮询) → ③ 打开浏览器。
- `--no-open` 时只启动不开浏览器(用于"完整重启"这类不该弹窗的场景)。
- 打开浏览器用 webbrowser (标准库): 本机单用户离线环境, 网关进程与用户同一会话, 能正常拉起默认浏览器。
- """
- from __future__ import annotations
- try:
- from app_common.app_common_guanlan.api import install_root as _install_root
- except ImportError: # 极端兜底(包结构异常时按位置上跳)
- from pathlib import Path as _P
- def _install_root(_f): return _P(_f).resolve().parents[3]
- import pathlib
- import subprocess
- import sys
- import time
- import urllib.request
- import webbrowser
- ROOT = _install_root(__file__)
- sys.path.insert(0, str(ROOT))
- from . import cli as G # noqa: E402
- from src import proc as _proc # noqa: E402 无窗口子进程
- def main() -> int:
- no_open = '--no-open' in sys.argv
- c = G.cfg()
- url = f"http://{c['host']}:{c['gateway']}/"
- print(f'① 启动服务 (guanlan.py serve) …', flush=True)
- # src.proc.run: 本脚本常由**无控制台**的运维任务/网关侧拉起, 裸 spawn 会让 serve 拿到一个
- # 可见控制台 (2026-09-16 实测: 现场会攒下标题为 .venv\Scripts\python.exe 的黑窗)。
- rc = _proc.run([sys.executable, 'guanlan.py', 'serve'], cwd=str(ROOT)).returncode
- if rc == 2: # serve 的退出码 2 = 网关 60 s 没就绪
- print(f'[X] 网关未就绪 (serve 退出码 {rc}) —— 看 logs/gateway.log')
- return rc
- if rc == 1:
- print(' (退出码 1 = 有模块未就绪, degraded 是正常状态 —— 例如本机模型 Ollama 未启动)')
- print(f'② 等 {url} 可访问 …', flush=True)
- for i in range(30):
- try:
- with urllib.request.urlopen(url, timeout=10) as r:
- if r.status == 200:
- print(f' 门户已就绪 (第 {i + 1} 次探测)')
- break
- except Exception:
- time.sleep(1)
- else:
- print(' [!] 门户 30 s 内没起来 —— 仍尝试打开浏览器, 页面可能需要刷新')
- if no_open:
- print('③ (--no-open) 不打开浏览器; 入口: ' + url)
- return 0
- print(f'③ 打开浏览器: {url}')
- ok = webbrowser.open(url)
- print(f' webbrowser.open → {ok} ({"" if ok else "未成功拉起, 请手工访问上面的地址"})')
- return 0
- if __name__ == '__main__':
- for _s in (sys.stdout, sys.stderr):
- try: _s.reconfigure(errors='replace')
- except Exception: pass
- sys.exit(main())
|