agent-Specialization/server/context/__init__.py
JOJO 66fa90d6cc feat(server): headless 启动入口(Gateway 专用路由面)+ approval 拆独立蓝图
- 新增 server/headless_app.py:仅挂 gateway/tasks/status/approval/usage 五件蓝图,
  无 web 站点路由面(/login、会话管理、静态页全砍),根路径返回 HTML 告知页;
  供 CLI 探测无服务时 spawn(同 RuntimeService 单例/数据目录/端口 8091,
  与 full 形态互斥不并存)
- server/chat/approval.py 拆出 approval_bp(tool/plan/question 6 条路由 URL 不变),
  解除对 chat_bp 的 import 循环,full/headless 双形态共用
- cli/src/gateway.ts:spawn 切 server.headless_app;python 解释器改 pickPython
  依赖探测(import yaml/flask 逐个试 .venv → homebrew 3.12/3.11 → python3),
  修本机 .venv 缺 pyyaml 导致的自启动即崩
- 修复 socket 移除残留雷:make_terminal_callback 定义随 broadcast.py 被删,
  workflow_runtime_api 模块级 import 即炸;resources.py 补兼容空操作(恒返 None)
- AGENTS.md §2 补 headless 启动命令、§12.5 补兼容空操作与 headless 形态说明

验证:tsc  py_compile  冒烟 6/6  headless 装配 47 路由 
8093 真实冒烟(health/Bearer/404 面/告知页)

Co-authored-by: Astrion powered by Kimi-K3 <astrion-agent@users.noreply.github.com>
2026-09-11 19:00:22 +08:00

72 lines
2.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# server/context/__init__.py - 兼容入口(原 server/context.py 拆分为子包)
# 所有历史导入路径 `from server.context import X` 不变。
#
# 2026-09-08G4 拆解):改为 PEP 562 模块级 __getattr__ 懒加载——
# 符号首次被引用时才加载对应子模块避免任务核心层server.tasks
# import 单个符号就连带拉起全部子模块(含 Web 适配层的 flask 依赖链)。
from importlib import import_module as _import_module
_SYMBOL_MODULE = {
# identity
"NoWorkspaceError": "server.context.identity",
"RuntimeIdentity": "server.context.identity",
"_resolve_user_role": "server.context.identity",
# broadcast
# personalization
"_apply_workspace_personalization_preferences": "server.context.personalization",
# usage
"get_or_create_usage_tracker": "server.context.usage",
# upload
"get_gui_manager": "server.context.upload",
"get_upload_guard": "server.context.upload",
"build_upload_error_response": "server.context.upload",
# conversation
"ensure_conversation_loaded": "server.context.conversation",
"apply_conversation_overrides": "server.context.conversation",
# resources
"get_user_resources": "server.context.resources",
"make_terminal_callback": "server.context.resources",
"_make_terminal_key": "server.context.resources",
"_touch_terminal_activity": "server.context.resources",
"_set_terminal_workspace_label": "server.context.resources",
"_ensure_workspace_skills_synced": "server.context.resources",
# decorators
"with_terminal": "server.context.decorators",
"get_terminal_for_sid": "server.context.decorators",
# reaper
"reset_system_state": "server.context.reaper",
"reap_idle_conversation_terminals": "server.context.reaper",
"start_conversation_terminal_reaper": "server.context.reaper",
}
__all__ = [
"NoWorkspaceError",
"RuntimeIdentity",
"get_user_resources",
"make_terminal_callback",
"with_terminal",
"get_terminal_for_sid",
"get_gui_manager",
"get_upload_guard",
"build_upload_error_response",
"ensure_conversation_loaded",
"apply_conversation_overrides",
"reset_system_state",
"get_or_create_usage_tracker",
"reap_idle_conversation_terminals",
"start_conversation_terminal_reaper",
]
def __getattr__(name):
module_name = _SYMBOL_MODULE.get(name)
if module_name is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
value = getattr(_import_module(module_name), name)
globals()[name] = value # 缓存,后续访问直接命中
return value
def __dir__():
return sorted(set(list(globals()) + list(_SYMBOL_MODULE)))