agent-Specialization/config/paths.py

204 lines
7.3 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.

"""项目路径与目录配置。
运行态数据存放在 ``~/.agents/agents/`` 下:
::
~/.agents/agents/ ← data_root默认可通过 AGENTS_DATA_ROOT 覆盖)
├── settings.json ← 唯一配置文件
├── config/ ← 部署级 JSONhost/web 模式共享
├── host/ ← host 模式(单用户)
│ ├── data/ ← 对话/记忆/数据库
│ └── logs/
└── web/ ← web 模式(多用户)
├── users/ ← 每用户独立工作区
│ └── <name>/data/
└── logs/
路径解析优先级(从高到低):
1. 具体目录环境变量(``DATA_DIR`` / ``LOGS_DIR`` / ``USER_SPACE_DIR`` /
``API_USER_SPACE_DIR``)——单独覆盖某个目录,最高优先级;
2. ``AGENTS_DATA_ROOT`` ——整体搬迁数据根目录;
3. 兜底默认值 ``~/.agents/agents``。
注意:``config/*.json`` 分两类,不再一概锚定源码树:
- **开发者配置 / 程序能力**``docker_risk_markers.json`` / ``skill_hints.json``
是程序行为的一部分,随版本演进,仍锚定源码树。
- **部署者自定义配置**``custom_models`` / ``host_workspaces`` /
``auto_approval`` / ``goal_review`` / ``forbidden_commands`` /
``host_sandbox_policy``):因部署/机器而异、或含密钥,外置到
``<data_root>/config/``(即 ``DEPLOY_CONFIG_DIR``)。读取走
``resolve_deploy_config``,回退链为:部署目录 -> 源码树 ``.json`` ->
源码树 ``.json.example``。
"""
import json
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 模式。
优先从 settings.json 读取,其次环境变量,最后默认 host。
"""
# 尝试从已加载的 settings.json 中读取
mode = _env("TERMINAL_SANDBOX_MODE").lower()
if mode == "host":
return "host"
if mode == "web" or mode == "docker":
return "web"
return "host"
# ── 数据根目录data_root──
# 优先级AGENTS_DATA_ROOT 环境变量 > 默认 ~/.agents/agents
_DEFAULT_DATA_ROOT = Path("~/.agents/agents")
RUNTIME_ROOT = str(_norm(Path(_env("AGENTS_DATA_ROOT") or _DEFAULT_DATA_ROOT)))
_MODE = _runtime_mode()
def _runtime_dir(env_name: str, subdir: str) -> str:
"""解析运行态子目录。
优先级:具体目录环境变量 > data_root/{mode}/subdir。
具体目录变量支持相对路径(相对仓库根目录)、绝对路径与 ``~``。
"""
raw = _env(env_name)
if raw:
return str(_norm(Path(raw)))
return str(Path(RUNTIME_ROOT) / _MODE / subdir)
# ── 运行态目录(默认落在 <data_root>/<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")
# ── 用户自定义 skill 归档目录host 模式全局docker/web 模式可与 per-user 并存)──
CUSTOM_SKILLS_DIR = str(Path(RUNTIME_ROOT) / _MODE / "agentskills")
# ── 是否宿主机模式 ──
IS_HOST_MODE = _MODE == "host"
# ── 固定 web 模式路径host 模式下作为额外数据源,同时读取)──
_WEB_ROOT = Path(RUNTIME_ROOT) / "web"
WEB_DATA_DIR = str(_WEB_ROOT / "data")
WEB_USER_SPACE_DIR = str(_WEB_ROOT / "users")
# ── 部署级配置目录 —— 放在 data_root 根下,两个模式共享 ──
_DEFAULT_DEPLOY_CONFIG = Path(RUNTIME_ROOT) / "config"
DEPLOY_CONFIG_DIR = str(_norm(Path(_env("DEPLOY_CONFIG_DIR") or _DEFAULT_DEPLOY_CONFIG)))
# 源码树内的配置种子目录(首次运行/开发环境的默认内容来源)。
_SEED_CONFIG_DIR = _REPO_ROOT / "config"
def deploy_config_path(filename: str) -> str:
"""部署级配置文件应写入/读取的位置(``<data_root>/config`` 下)。
用于会被运行时写回的文件(如 host_workspaces / host_sandbox_policy
始终指向部署目录,不回退源码树。
"""
return str(Path(DEPLOY_CONFIG_DIR) / filename)
def resolve_deploy_config(filename: str) -> str:
"""解析部署级配置的只读路径,回退链(高→低):
1. ``<data_root>/config/<filename>``(部署者真实配置)
2. 源码树 ``config/<filename>``(仓库内种子)
3. 源码树 ``config/<filename>.example``(示例兜底)
都不存在时返回部署目录下的目标路径,由调用方处理「文件不存在」。
仅用于定位,不在此做拷贝(拷贝交给 setup / 迁移脚本)。
"""
deployed = Path(DEPLOY_CONFIG_DIR) / filename
if deployed.exists():
return str(deployed)
seed = _SEED_CONFIG_DIR / filename
if seed.exists():
return str(seed)
example = _SEED_CONFIG_DIR / f"{filename}.example"
if example.exists():
return str(example)
return str(deployed)
# ── 启动工作区 ──
DEFAULT_PROJECT_PATH = _resolve_repo_path(os.environ.get("DEFAULT_PROJECT_PATH", ""), "./project")
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"
HOST_WORKSPACES_FILE = _resolve_repo_path(
os.environ.get("HOST_WORKSPACES_FILE", ""),
deploy_config_path("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",
"IS_HOST_MODE",
"USER_SPACE_DIR",
"WEB_DATA_DIR",
"WEB_USER_SPACE_DIR",
"USERS_DB_FILE",
"INVITE_CODES_FILE",
"ADMIN_POLICY_FILE",
"API_USER_SPACE_DIR",
"CUSTOM_SKILLS_DIR",
"API_USERS_DB_FILE",
"API_TOKENS_FILE",
"API_USAGE_FILE",
"DEPLOY_CONFIG_DIR",
"deploy_config_path",
"resolve_deploy_config",
]