refactor(beaver): 移除Hermes相关引用和迁移代码,完善Beaver后端主线实现

移除了所有Hermes相关的命名引用,包括:
- 从.gitignore中清理相关构建缓存文件
- 将README中的beaver-home路径配置更新
- 完善backend/README.md文档说明Beaver后端主线实现
- 移除Hermes风格的相关注释和兼容性代码
- 清理nanobot环境变量兼容性处理
- 删除技能迁移和服务迁移相关功能代码
- 更新测试用例中相关命名和函数名

BREAKING CHANGE: 移除了Hermes迁移相关API和CLI命令,不再支持nanobot环境变量兼容性
This commit is contained in:
2026-05-14 17:20:32 +08:00
parent b59968167e
commit 3b0af173cc
57 changed files with 245 additions and 4109 deletions

View File

@ -1,7 +1,5 @@
"""CLI entry for Beaver."""
from pathlib import Path
try:
import typer
except ModuleNotFoundError: # pragma: no cover - fallback for skeleton-only environments
@ -29,8 +27,6 @@ except ModuleNotFoundError: # pragma: no cover - fallback for skeleton-only env
typer = _FallbackTyper() # type: ignore[assignment]
from beaver.services.agent_service import AgentService
from beaver.services.hermes_migration import HermesMigrationService
from beaver.skills.specs import SkillSpecStore
app = typer.Typer(help="Beaver backend CLI") if hasattr(typer, "Typer") else typer
@ -59,26 +55,6 @@ def run(
typer.echo(result.output_text)
@app.command("migrate-hermes")
def migrate_hermes(
repo: str = typer.Option(..., "--repo", help="Local checkout of https://github.com/NousResearch/hermes-agent."),
workspace: str | None = typer.Option(None, "--workspace", help="Workspace root to import skills into."),
manifest: str | None = typer.Option(None, "--manifest", help="Path for hermes_migration_manifest.json."),
dry_run: bool = typer.Option(False, "--dry-run", help="Only write the manifest without importing skills."),
) -> None:
"""Import no-credential Hermes Agent skills and write a manifest."""
service = AgentService(workspace=workspace)
loaded = service.create_loop().boot()
store = loaded.skill_spec_store or SkillSpecStore(loaded.workspace)
migration = HermesMigrationService(store, manifest_path=Path(manifest) if manifest else None)
result = migration.migrate(repo, dry_run=dry_run)
typer.echo(
f"Hermes migration complete: {len(result['included'])} included, "
f"{len(result['skipped'])} skipped."
)
def main() -> None:
"""Project script entrypoint."""
app()

View File

@ -58,7 +58,7 @@ LOCAL_TOOL_CATEGORIES = {
def _workspace_path(value: str | None = None) -> Path:
raw = value or os.getenv("BEAVER_WORKSPACE") or os.getenv("NANOBOT_WORKSPACE")
raw = value or os.getenv("BEAVER_WORKSPACE")
if raw:
return Path(raw).expanduser().resolve()
return Path.cwd()

View File

@ -23,7 +23,6 @@ from beaver.foundation.models import CronExecutionResult, CronRunRecord
from beaver.integrations.mcp import MCPConnectionManager
from beaver.services.agent_service import NOTIFICATION_SESSION_ID, AgentService
from beaver.services.cron_service import CronService, schedule_from_api
from beaver.services.skill_migration import SkillMigrationService
from beaver.services.skillhub_service import SkillHubService
from beaver.skills.learning import SkillLearningWorker, SkillLearningWorkerConfig
from beaver.skills.catalog.utils import parse_frontmatter
@ -305,7 +304,7 @@ def create_app(
)
app.state.auth_tokens = {}
app.state.handoff_codes = {}
app.state.auth_file = Path(os.getenv("BEAVER_AUTH_FILE") or os.getenv("NANOBOT_AUTH_FILE") or "")
app.state.auth_file = Path(os.getenv("BEAVER_AUTH_FILE") or "")
max_file_size = 50 * 1024 * 1024
@app.get("/api/ping", response_model=WebStatusResponse)
@ -427,7 +426,6 @@ def create_app(
_clean_text(payload.get("base_url"))
or config.backend_identity.public_base_url
or os.getenv("BEAVER_FRONTEND_PUBLIC_BASE_URL")
or os.getenv("NANOBOT_FRONTEND_PUBLIC_BASE_URL")
or str(request.base_url).rstrip("/")
)
frontend_base_url = _clean_text(payload.get("frontend_base_url")) or public_base_url
@ -526,7 +524,7 @@ def create_app(
return {
"id": username,
"username": username,
"email": os.getenv("BEAVER_BACKEND_IDENTITY__EMAIL") or os.getenv("NANOBOT_BACKEND_IDENTITY__EMAIL", ""),
"email": os.getenv("BEAVER_BACKEND_IDENTITY__EMAIL", ""),
"role": "owner",
"quota_tier": "single-user",
}
@ -1184,30 +1182,6 @@ def create_app(
)
return result
@app.get("/api/tools/servers")
async def list_tool_servers(request: Request) -> list[dict[str, Any]]:
return await list_mcp_servers(request)
@app.get("/api/tools")
async def list_tools(request: Request) -> dict[str, Any]:
servers = await list_mcp_servers(request)
tool_groups = await list_mcp_tools(request)
server_map = {server["id"]: server for server in servers}
grouped = {"local": [], "online": []}
for group in tool_groups:
server = server_map.get(group["server_id"], {})
kind = str(server.get("kind") or "online")
item = {
**group,
"server_name": server.get("name") or group["server_id"],
"transport": server.get("transport"),
"kind": kind,
"category": server.get("category") or kind,
"status": server.get("status"),
}
grouped["local" if kind == "local" else "online"].append(item)
return {"servers": servers, "groups": grouped}
@app.get("/api/skills")
async def list_skills(request: Request) -> list[dict[str, Any]]:
loaded = get_agent_service(request).create_loop().boot()
@ -1301,19 +1275,6 @@ def create_app(
raise HTTPException(status_code=400, detail=str(exc)) from exc
return draft
@app.post("/api/skills/migrate")
async def migrate_skills(request: Request) -> dict[str, Any]:
loaded = get_agent_service(request).create_loop().boot()
return SkillMigrationService(loaded.skill_spec_store).migrate_all() # type: ignore[arg-type]
@app.get("/api/skills/migration-manifest")
async def get_skill_migration_manifest(request: Request) -> dict[str, Any]:
loaded = get_agent_service(request).create_loop().boot()
path = loaded.workspace / "skill_migration_manifest.json"
if not path.exists():
return {"included": [], "skipped": []}
return json.loads(path.read_text(encoding="utf-8"))
@app.get("/api/marketplaces/skills/search")
async def search_skillhub(
request: Request,
@ -2482,7 +2443,7 @@ def _provider_enabled(provider_name: str, provider_cfg: Any) -> bool:
def _auth_file_path() -> Path:
raw = os.getenv("BEAVER_AUTH_FILE") or os.getenv("NANOBOT_AUTH_FILE")
raw = os.getenv("BEAVER_AUTH_FILE")
if raw:
return Path(raw)
return Path.home() / ".beaver" / "web_auth_users.json"
@ -2542,7 +2503,7 @@ def _issue_web_token(app: FastAPI, username: str) -> str:
def _handoff_ttl_seconds() -> int:
raw = os.getenv("NANOBOT_HANDOFF_CODE_TTL_SECONDS", "90").strip()
raw = os.getenv("BEAVER_HANDOFF_CODE_TTL_SECONDS", "90").strip()
try:
return max(15, int(raw))
except ValueError:
@ -2550,7 +2511,7 @@ def _handoff_ttl_seconds() -> int:
def _handoff_replay_window_seconds() -> int:
raw = os.getenv("NANOBOT_HANDOFF_REPLAY_WINDOW_SECONDS", "15").strip()
raw = os.getenv("BEAVER_HANDOFF_REPLAY_WINDOW_SECONDS", "15").strip()
try:
return max(1, int(raw))
except ValueError:
@ -2637,22 +2598,18 @@ def _require_web_user(app: FastAPI, authorization: str | None) -> str:
def _backend_connection_view(request: Request) -> dict[str, Any]:
public_base_url = (
os.getenv("BEAVER_BACKEND_IDENTITY__PUBLIC_BASE_URL")
or os.getenv("NANOBOT_BACKEND_IDENTITY__PUBLIC_BASE_URL")
or os.getenv("BEAVER_FRONTEND_PUBLIC_BASE_URL")
or os.getenv("NANOBOT_FRONTEND_PUBLIC_BASE_URL")
or str(request.base_url).rstrip("/")
)
backend_id = (
os.getenv("BEAVER_BACKEND_IDENTITY__BACKEND_ID")
or os.getenv("NANOBOT_BACKEND_IDENTITY__BACKEND_ID")
or os.getenv("BEAVER_BACKEND_IDENTITY__CLIENT_ID")
or os.getenv("NANOBOT_BACKEND_IDENTITY__CLIENT_ID")
)
client_id = os.getenv("BEAVER_BACKEND_IDENTITY__CLIENT_ID") or os.getenv("NANOBOT_BACKEND_IDENTITY__CLIENT_ID") or backend_id
client_id = os.getenv("BEAVER_BACKEND_IDENTITY__CLIENT_ID") or backend_id
return {
"backend_id": backend_id,
"client_id": client_id,
"name": os.getenv("BEAVER_BACKEND_IDENTITY__NAME") or os.getenv("NANOBOT_BACKEND_IDENTITY__NAME") or backend_id,
"name": os.getenv("BEAVER_BACKEND_IDENTITY__NAME") or backend_id,
"public_base_url": public_base_url,
"api_base_url": public_base_url,
"frontend_base_url": public_base_url,
@ -2663,16 +2620,14 @@ def _backend_connection_view(request: Request) -> dict[str, Any]:
def _local_backend_view() -> dict[str, Any]:
return {
"backend_id": os.getenv("BEAVER_BACKEND_IDENTITY__BACKEND_ID") or os.getenv("NANOBOT_BACKEND_IDENTITY__BACKEND_ID"),
"client_id": os.getenv("BEAVER_BACKEND_IDENTITY__CLIENT_ID") or os.getenv("NANOBOT_BACKEND_IDENTITY__CLIENT_ID"),
"name": os.getenv("BEAVER_BACKEND_IDENTITY__NAME") or os.getenv("NANOBOT_BACKEND_IDENTITY__NAME"),
"backend_id": os.getenv("BEAVER_BACKEND_IDENTITY__BACKEND_ID"),
"client_id": os.getenv("BEAVER_BACKEND_IDENTITY__CLIENT_ID"),
"name": os.getenv("BEAVER_BACKEND_IDENTITY__NAME"),
"public_base_url": os.getenv("BEAVER_BACKEND_IDENTITY__PUBLIC_BASE_URL")
or os.getenv("NANOBOT_BACKEND_IDENTITY__PUBLIC_BASE_URL")
or os.getenv("BEAVER_FRONTEND_PUBLIC_BASE_URL")
or os.getenv("NANOBOT_FRONTEND_PUBLIC_BASE_URL"),
or os.getenv("BEAVER_FRONTEND_PUBLIC_BASE_URL"),
"authz": {
"enabled": (os.getenv("BEAVER_AUTHZ__ENABLED") or os.getenv("NANOBOT_AUTHZ__ENABLED", "")).strip() in {"1", "true", "True"},
"base_url": os.getenv("BEAVER_AUTHZ__BASE_URL") or os.getenv("NANOBOT_AUTHZ__BASE_URL"),
"enabled": os.getenv("BEAVER_AUTHZ__ENABLED", "").strip() in {"1", "true", "True"},
"base_url": os.getenv("BEAVER_AUTHZ__BASE_URL"),
},
}