将运行态数据(对话/用户工作区/日志等)默认迁出源码树到 ~/.agents/<mode>, 对齐 ~/.claude、~/.codex 惯例;同时为日志加上开关与轮转,并归档/清理仓库内 长期堆积的实验残留与误产生文件。 ## 路径变量重写(核心) - 重写 config/paths.py:运行态根按模式分流,host -> ~/.agents/host, 其它(默认 docker/web)-> ~/.agents/web。 - 三级解析优先级(高→低): 1) 具体目录变量 DATA_DIR / LOGS_DIR / USER_SPACE_DIR / API_USER_SPACE_DIR 2) 模式根变量 AGENTS_HOST_HOME / AGENTS_WEB_HOME 3) 兜底 ~/.agents/<mode> - 导出变量名保持不变,20+ 处消费方零改动;统一 config/sub_agent.py 重复的 路径解析逻辑,将 sub_agent 任务目录/状态文件收编进 DATA_DIR; SUB_AGENT_PROJECT_RESULTS_DIR 仍随工作区(有意保留)。 - config/*.json、prompts/、agentskills/ 属配置/资源,仍锚定源码树。 ## 日志止血 - 新增 utils/log_rotation.py:按大小轮转(默认 20MB×3 份)+ 按份清理 (dump 默认保留最近 30 个),阈值可由 AGENT_LOG_ROTATE_MAX_BYTES / AGENT_LOG_ROTATE_BACKUPS / AGENT_DUMP_KEEP 覆盖。 - utils/api_client.py:API 请求体 dump 默认关闭(AGENT_API_DUMP_ENABLED 开启), 收编两处写死的 logs/ 硬编码路径改用 LOGS_DIR;dump 关闭时 request_dump 安全置空。 - utils/host_workspace_debug.py、server/utils_common.py(chunk/conn_diag 等)、 utils/logger.py(TaskLogger/ErrorLogger)统一接入轮转。 ## 迁移与运维脚本 - scripts/migrate_runtime_data.py:源码树 -> 运行态根,复制+备份+校验+可回滚+幂等, logs 丢弃不迁;import config 复用程序同一套路径解析(模式由 .env 决定), 覆盖 data/users/api 及 sub_agent/tasks(-> data/sub_agent_tasks)。 - scripts/cleanup_misplaced_web.sh:清理误迁到 ~/.agents/web 的副本(带 chflags 兜底)。 ## 测试 - 重写 test/test_config_paths_resolution.py:覆盖默认分流、模式切换、模式根变量覆盖、 具体目录变量最高优先级、相对路径锚定 repo root、源码树配置项不随迁移等 6 个用例。 ## 仓库清理 - 删除旧版子智能体目录 sub_agent/(逻辑已统一在 easyagent/)。 - 删除误产生/垃圾文件:误敲命令生成的 "ystemctl status ..."、空文件 .zhouyanbo / testfile_from_ai、运行态日志/pid、test_playwright.png、test_system_message.py 等。 - 实验残留与历史文档(BUG_FIX/POLLING 变更日志、SUB_AGENT 文档、翻译资料、 model_tests、compact_result、奇奇怪怪的bug、截图、goal_research)归档到 本地 _experiments/,并从版本控制移除(已加入 .gitignore)。 - .gitignore:新增 api/、_experiments/,清理已失效的 sub_agent/* 条目与冗余项。 ## 文档 - CLAUDE.md / AGENTS.md:补充数据目录与路径变量、日志策略、迁移流程, 更新目录结构(移除 sub_agent、新增 _experiments/scripts),调试日志路径改为 ~/.agents/<mode>/logs/。
128 lines
4.8 KiB
Python
128 lines
4.8 KiB
Python
"""项目路径与目录配置。
|
||
|
||
运行态数据(对话、用户工作区、日志等)默认存放在用户主目录下的
|
||
``~/.agents`` 中,并按运行模式分流:
|
||
|
||
- 宿主机模式(``TERMINAL_SANDBOX_MODE=host``)-> ``~/.agents/host``
|
||
- 其它模式(默认 docker / web) -> ``~/.agents/web``
|
||
|
||
对齐 ``~/.claude``、``~/.codex`` 的惯例,把运行态产物移出源码树。
|
||
|
||
路径解析优先级(从高到低):
|
||
|
||
1. 具体目录环境变量(``DATA_DIR`` / ``LOGS_DIR`` / ``USER_SPACE_DIR`` /
|
||
``API_USER_SPACE_DIR``)——单独覆盖某个目录,最高优先级;
|
||
2. 当前模式的根目录环境变量(``AGENTS_HOST_HOME`` 或 ``AGENTS_WEB_HOME``)——
|
||
整体搬迁该模式下的全部运行态数据;
|
||
3. 兜底默认值 ``~/.agents/<mode>``。
|
||
|
||
注意:``config/*.json``(custom_models、host_workspaces、auto_approval 等)属于
|
||
配置而非运行态数据,仍锚定在源码树内,不随运行态根目录迁移。
|
||
"""
|
||
|
||
import os
|
||
from pathlib import Path
|
||
|
||
|
||
_REPO_ROOT = Path(__file__).resolve().parents[1]
|
||
|
||
|
||
def _norm(path: Path) -> Path:
|
||
"""展开 ``~``、转绝对路径并归一化。相对路径相对仓库根目录展开。"""
|
||
path = path.expanduser()
|
||
if not path.is_absolute():
|
||
path = _REPO_ROOT / path
|
||
return path.resolve()
|
||
|
||
|
||
def _env(name: str) -> str:
|
||
return str(os.environ.get(name, "") or "").strip()
|
||
|
||
|
||
def _resolve_repo_path(raw_value: str, default: str) -> str:
|
||
"""解析锚定在源码树的路径(配置文件、提示词等)。
|
||
|
||
相对路径相对仓库根目录展开,绝对路径与 ``~`` 原样支持。
|
||
"""
|
||
candidate = str(raw_value or "").strip() or str(default)
|
||
return str(_norm(Path(candidate)))
|
||
|
||
|
||
def _runtime_mode() -> str:
|
||
"""运行模式:``host`` 或 ``web``。
|
||
|
||
仅当 ``TERMINAL_SANDBOX_MODE=host`` 时为 host 模式,其余(默认 docker)归为 web。
|
||
直接读环境变量,避免对 ``config.terminal`` 形成导入依赖。
|
||
"""
|
||
return "host" if _env("TERMINAL_SANDBOX_MODE").lower() == "host" else "web"
|
||
|
||
|
||
_MODE = _runtime_mode()
|
||
|
||
# 当前模式的运行态根目录。
|
||
# 优先级:模式根变量(AGENTS_HOST_HOME / AGENTS_WEB_HOME)> 默认 ~/.agents/<mode>。
|
||
_MODE_HOME_ENV = "AGENTS_HOST_HOME" if _MODE == "host" else "AGENTS_WEB_HOME"
|
||
_DEFAULT_RUNTIME_ROOT = Path("~/.agents") / _MODE
|
||
RUNTIME_ROOT = str(_norm(Path(_env(_MODE_HOME_ENV) or _DEFAULT_RUNTIME_ROOT)))
|
||
|
||
|
||
def _runtime_dir(env_name: str, subdir: str) -> str:
|
||
"""解析运行态子目录。
|
||
|
||
优先级:具体目录环境变量 > 当前模式根目录下的对应子目录。
|
||
具体目录变量支持相对路径(相对仓库根目录)、绝对路径与 ``~``。
|
||
"""
|
||
raw = _env(env_name)
|
||
if raw:
|
||
return str(_norm(Path(raw)))
|
||
return str(Path(RUNTIME_ROOT) / subdir)
|
||
|
||
|
||
# ── 运行态目录(默认落在 ~/.agents/<mode> 下,可被各自的环境变量覆盖)──
|
||
DATA_DIR = _runtime_dir("DATA_DIR", "data")
|
||
LOGS_DIR = _runtime_dir("LOGS_DIR", "logs")
|
||
USER_SPACE_DIR = _runtime_dir("USER_SPACE_DIR", "users")
|
||
# API 专用用户与工作区(与网页用户隔离)
|
||
API_USER_SPACE_DIR = _runtime_dir("API_USER_SPACE_DIR", "api/users")
|
||
|
||
# ── 启动工作区 ──
|
||
# 默认项目路径,可通过环境变量覆盖以指向宿主机任意目录。
|
||
DEFAULT_PROJECT_PATH = _resolve_repo_path(os.environ.get("DEFAULT_PROJECT_PATH", ""), "./project")
|
||
# 兼容旧配置:若仍有模块读取 HOST_PROJECT_PATH,保留该键(实际宿主机路径选择改由 JSON 管理)。
|
||
HOST_PROJECT_PATH = _resolve_repo_path(os.environ.get("HOST_PROJECT_PATH", ""), DEFAULT_PROJECT_PATH)
|
||
|
||
# ── 源码树内的配置与资源(不随运行态根目录迁移)──
|
||
PROMPTS_DIR = _resolve_repo_path(os.environ.get("PROMPTS_DIR", ""), "./prompts")
|
||
AGENT_SKILLS_DIR = _resolve_repo_path(os.environ.get("AGENT_SKILLS_DIR", ""), "./agentskills")
|
||
WORKSPACE_SKILLS_DIRNAME = ".agents/skills"
|
||
# 宿主机模式工作区配置文件(JSON,属于配置,锚定源码树)。
|
||
HOST_WORKSPACES_FILE = _resolve_repo_path(os.environ.get("HOST_WORKSPACES_FILE", ""), "./config/host_workspaces.json")
|
||
|
||
# ── 基于 DATA_DIR 派生的数据文件 ──
|
||
USERS_DB_FILE = f"{DATA_DIR}/users.json"
|
||
INVITE_CODES_FILE = f"{DATA_DIR}/invite_codes.json"
|
||
ADMIN_POLICY_FILE = f"{DATA_DIR}/admin_policy.json"
|
||
API_USERS_DB_FILE = f"{DATA_DIR}/api_users.json"
|
||
API_TOKENS_FILE = f"{DATA_DIR}/api_tokens.json"
|
||
API_USAGE_FILE = f"{DATA_DIR}/api_usage.json"
|
||
|
||
__all__ = [
|
||
"RUNTIME_ROOT",
|
||
"DEFAULT_PROJECT_PATH",
|
||
"HOST_WORKSPACES_FILE",
|
||
"HOST_PROJECT_PATH",
|
||
"PROMPTS_DIR",
|
||
"DATA_DIR",
|
||
"LOGS_DIR",
|
||
"AGENT_SKILLS_DIR",
|
||
"WORKSPACE_SKILLS_DIRNAME",
|
||
"USER_SPACE_DIR",
|
||
"USERS_DB_FILE",
|
||
"INVITE_CODES_FILE",
|
||
"ADMIN_POLICY_FILE",
|
||
"API_USER_SPACE_DIR",
|
||
"API_USERS_DB_FILE",
|
||
"API_TOKENS_FILE",
|
||
"API_USAGE_FILE",
|
||
]
|