PhoneWork/main.py
Yuyao Huang (Sam) 6307deb701 feat: 实现用户权限控制、会话管理和审计日志功能
- 添加用户权限检查功能,支持配置允许使用的用户列表
- 实现会话管理功能,包括会话创建、关闭、列表和切换
- 新增审计日志模块,记录所有交互信息
- 改进WebSocket连接,增加自动重连机制
- 添加健康检查端点,包含Claude服务可用性测试
- 实现会话持久化功能,重启后恢复会话状态
- 增加命令行功能支持,包括/new、/list、/close等命令
- 优化消息处理流程,支持直接传递模式
2026-03-28 08:39:32 +08:00

111 lines
2.6 KiB
Python

"""PhoneWork entry point: FastAPI app + Feishu long-connection client."""
from __future__ import annotations
import asyncio
import logging
import time
import uvicorn
from fastapi import FastAPI
from rich.logging import RichHandler
from agent.manager import manager
from bot.handler import start_websocket_client, get_ws_status
logging.basicConfig(
level=logging.DEBUG,
format="%(name)-20s %(message)s",
datefmt="[%X]",
handlers=[RichHandler(
rich_tracebacks=True,
markup=True,
show_path=False,
omit_repeated_times=False,
)],
)
for _noisy in ("httpcore", "httpx", "openai._base_client", "urllib3", "lark_oapi", "websockets"):
logging.getLogger(_noisy).setLevel(logging.WARNING)
logger = logging.getLogger(__name__)
app = FastAPI(title="PhoneWork", version="0.1.0")
START_TIME = time.time()
@app.get("/health")
async def health() -> dict:
sessions = manager.list_sessions()
ws_status = get_ws_status()
uptime = time.time() - START_TIME
result = {
"status": "ok",
"uptime_seconds": round(uptime, 1),
"active_sessions": len(sessions),
"websocket": ws_status,
}
if ws_status.get("connected"):
result["status"] = "ok"
else:
result["status"] = "degraded"
return result
@app.get("/health/claude")
async def health_claude() -> dict:
"""Smoke test: run a simple claude -p command."""
from agent.pty_process import run_claude
import tempfile
import os
start = time.time()
try:
with tempfile.TemporaryDirectory() as tmpdir:
output = await run_claude(
"Say 'pong' and nothing else",
cwd=tmpdir,
timeout=30.0,
)
elapsed = time.time() - start
return {
"status": "ok",
"elapsed_seconds": round(elapsed, 2),
"output_preview": output[:100] if output else None,
}
except asyncio.TimeoutError:
return {"status": "timeout", "elapsed_seconds": 30.0}
except Exception as e:
return {"status": "error", "error": str(e)}
@app.get("/sessions")
async def list_sessions() -> list:
return manager.list_sessions()
@app.on_event("startup")
async def startup_event() -> None:
await manager.start()
loop = asyncio.get_event_loop()
start_websocket_client(loop)
logger.info("PhoneWork started")
@app.on_event("shutdown")
async def shutdown_event() -> None:
await manager.stop()
logger.info("PhoneWork shut down")
if __name__ == "__main__":
uvicorn.run(
"main:app",
host="0.0.0.0",
port=8000,
reload=False,
log_level="info",
)