aps-agent/server/aps_home.py

144 lines
4.5 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.

# ============================================================
# APS 用户主目录(moduleId: server-aps-home, 可重生 ✅)
# 对标 Codex ~/.codex:桌面端默认 ~/.aps/{skills,sessions,data,…}
# Web 部署默认仍用仓库内 server/data(可用 APS_HOME 覆盖)
# ============================================================
from __future__ import annotations
import json
import os
from pathlib import Path
# 与 Codex 类似的子目录约定
_LAYOUT = (
"skills", # 算法 / Agent Skill(每 skill 一目录 + manifest.json)
"sessions", # 会话落盘(桌面端持久化)
"cache",
"tmp",
"logs",
"data", # world / sqlite / knowledge / checkpoints
"packs", # 可选:用户级数据包
)
def mode() -> str:
"""运行形态:desktop | web(默认 web,便于仓库内开发与测试)。"""
m = (os.environ.get("APS_MODE") or "web").strip().lower()
return "desktop" if m in ("desktop", "desk", "electron", "app") else "web"
def aps_home() -> Path:
"""解析 APS 根目录。
优先级:
1. APS_HOME(显式,任意形态)
2. desktop → 用户主目录/.aps(对标 Codex ~/.codex;Windows=%USERPROFILE%\\.aps)
3. web → APS_DATA_DIR 或 仓库 server/data
"""
raw = os.environ.get("APS_HOME")
if raw:
return Path(raw).expanduser().resolve()
if mode() == "desktop":
# 安装到电脑后的唯一默认:用户根目录下的 .aps(不可落到安装目录)
return (Path.home() / ".aps").resolve()
data = os.environ.get("APS_DATA_DIR")
if data:
return Path(data).expanduser().resolve()
# 相对仓库:server/data(与历史路径兼容)
return Path("server/data").resolve()
def ensure_aps_home(home: Path | None = None) -> Path:
"""创建主目录与标准子目录;写入 config.json(若不存在)。"""
root = home or aps_home()
root.mkdir(parents=True, exist_ok=True)
for name in _LAYOUT:
(root / name).mkdir(parents=True, exist_ok=True)
cfg = root / "config.json"
if not cfg.exists():
cfg.write_text(
json.dumps({
"version": 1,
"mode": mode(),
"layout": list(_LAYOUT),
"note": "对标 Codex ~/.codex:skills / sessions / data 等本地落盘",
}, ensure_ascii=False, indent=2),
encoding="utf-8",
)
return root
def skills_dir() -> Path:
return ensure_aps_home() / "skills"
def sessions_dir() -> Path:
return ensure_aps_home() / "sessions"
def default_projects_path() -> str:
"""工作区元数据(项目/会话/消息):落在 ~/.aps/sessions/workspace.json。"""
if os.environ.get("APS_PROJECTS_PATH"):
return os.environ["APS_PROJECTS_PATH"]
return str(sessions_dir() / "workspace.json")
def data_dir() -> Path:
"""世界状态 / DB / 知识库等运行数据目录。"""
root = ensure_aps_home()
# web 默认 APS_HOME 就是 server/data,数据平铺在根下以保持兼容
if mode() == "web" and not os.environ.get("APS_HOME"):
return root
d = root / "data"
d.mkdir(parents=True, exist_ok=True)
return d
def path_under_data(*parts: str) -> Path:
return data_dir().joinpath(*parts)
def default_world_path() -> str:
if os.environ.get("APS_WORLD_PATH"):
return os.environ["APS_WORLD_PATH"]
return str(path_under_data("world.json"))
def default_db_path() -> str:
if os.environ.get("APS_DB_PATH"):
return os.environ["APS_DB_PATH"]
return str(path_under_data("master.db"))
def default_checkpoint_path() -> str:
if os.environ.get("APS_CHECKPOINT_PATH"):
return os.environ["APS_CHECKPOINT_PATH"]
return str(path_under_data("checkpoints.json"))
def default_knowledge_path() -> str:
if os.environ.get("APS_KNOWLEDGE_PATH"):
return os.environ["APS_KNOWLEDGE_PATH"]
return str(path_under_data("knowledge.json"))
def default_embeddings_path() -> str:
if os.environ.get("APS_EMBEDDINGS_PATH"):
return os.environ["APS_EMBEDDINGS_PATH"]
return str(path_under_data("embeddings.json"))
def describe() -> dict:
"""供 /api/system/paths 与桌面关于页展示。"""
root = ensure_aps_home()
return {
"mode": mode(),
"home": str(root),
"skills": str(skills_dir()),
"sessions": str(sessions_dir()),
"workspace": default_projects_path(),
"data": str(data_dir()),
"world": default_world_path(),
"db": default_db_path(),
}