feat(delegation): 添加直连模式下的委托公告回调机制

- 引入 DirectAnnouncementCallback 类型用于处理直连模式下的公告
- 在 DelegationManager 中添加 _direct_announcement_callback 属性和设置方法
- 实现 _notify_direct_announcement 方法用于在非总线模式下将公告回写到本地会话
- 在委托取消、完成和分组完成时添加对直连公告的通知逻辑

feat(web): 增加 WebSocket 广播器支持实时会话更新通知

- 创建 WebSocketBroadcaster 类用于跟踪认证的 WebSocket 连接并广播 JSON 事件
- 在应用启动时初始化 websocket_broadcaster 实例
- 实现连接注册、注销和消息广播功能
- 添加过期连接清理机制

feat(agent): 新增系统公告处理方法支持本地处理

- 在 AgentLoop 中添加 process_system_announcement 方法用于在无常驻 run() 场景下处理系统公告
- 创建 InboundMessage 并通过 _process_message 进行处理

feat(cron): 改进定时任务的会话路由解析和实时更新

- 添加 _resolve_cron_session_key 和 _infer_cron_route_from_session_key 辅助函数
- 在 cron 任务执行完成后通过 WebSocket 广播会话更新事件
- 在添加定时任务时自动推断目标会话的渠道和聊天 ID

refactor: 项目名称从 Boardware Genius 统一改为 Boardware Agent Sandbox

- 更新前端页面标题和描述文本中的产品名称
- 添加新的品牌 Logo 图片资源
- 在前端布局中使用新的 Logo 显示
- 更新授权门户中的品牌信息和 Logo 显示

feat(frontend): 添加会话更新事件监听实现消息自动刷新

- 定义 SessionUpdatedEvent 类型接口
- 在 ChatPage 中添加会话更新事件的处理逻辑
- 当收到会话更新事件时自动重新加载会话列表和当前会话消息

feat(api): 扩展定时任务 API 支持会话键参数

- 在 addCronJob API 参数中添加 session_key 字段
- 更新前端 Cron 页面的表单处理以传递当前会话键
This commit is contained in:
2026-03-18 14:31:56 +08:00
parent dd3e83541c
commit 0c180f48f2
21 changed files with 470 additions and 57 deletions

View File

@ -3,6 +3,7 @@ from __future__ import annotations
import argparse
import json
import subprocess
from pathlib import Path
from typing import Any
@ -28,6 +29,22 @@ def load_instances(path: Path) -> list[dict[str, Any]]:
return [item for item in items if isinstance(item, dict)]
def is_container_running(container_name: str) -> bool:
name = container_name.strip()
if not name:
return False
try:
result = subprocess.run(
["docker", "inspect", "-f", "{{.State.Running}}", name],
check=False,
capture_output=True,
text=True,
)
except OSError:
return False
return result.returncode == 0 and result.stdout.strip().lower() == "true"
def render_server(instance_host: str, container_name: str, upstream_port: int) -> str:
return f"""server {{
listen 80;
@ -62,7 +79,12 @@ def render(instances: list[dict[str, Any]], upstream_port: int) -> str:
for item in sorted(instances, key=lambda value: str(value.get("instance_host", ""))):
instance_host = str(item.get("instance_host", "") or "").strip()
container_name = str(item.get("container_name", "") or "").strip()
if not instance_host or not container_name or instance_host in seen_hosts:
if (
not instance_host
or not container_name
or instance_host in seen_hosts
or not is_container_running(container_name)
):
continue
seen_hosts.add(instance_host)
blocks.append(render_server(instance_host, container_name, upstream_port))