feat(agents): Move templates into dedicated folder
Provisioning now reads template files from templates/ and includes the\nbase URL for agent runtime setup. Remove unused root orchestration\ndocs to keep the repo tidy.\n\nCo-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
93
backend/app/services/agent_provisioning.py
Normal file
93
backend/app/services/agent_provisioning.py
Normal file
@@ -0,0 +1,93 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
from uuid import uuid4
|
||||
|
||||
from app.core.config import settings
|
||||
from app.integrations.openclaw_gateway import send_message
|
||||
from app.models.agents import Agent
|
||||
|
||||
TEMPLATE_FILES = [
|
||||
"AGENTS.md",
|
||||
"BOOT.md",
|
||||
"BOOTSTRAP.md",
|
||||
"HEARTBEAT.md",
|
||||
"IDENTITY.md",
|
||||
"SOUL.md",
|
||||
"TOOLS.md",
|
||||
"USER.md",
|
||||
]
|
||||
|
||||
|
||||
def _repo_root() -> Path:
|
||||
return Path(__file__).resolve().parents[3]
|
||||
|
||||
|
||||
def _templates_root() -> Path:
|
||||
return _repo_root() / "templates"
|
||||
|
||||
|
||||
def _slugify(value: str) -> str:
|
||||
slug = re.sub(r"[^a-z0-9]+", "-", value.lower()).strip("-")
|
||||
return slug or uuid4().hex
|
||||
|
||||
|
||||
def _read_templates() -> dict[str, str]:
|
||||
root = _templates_root()
|
||||
templates: dict[str, str] = {}
|
||||
for name in TEMPLATE_FILES:
|
||||
path = root / name
|
||||
if path.exists():
|
||||
templates[name] = path.read_text(encoding="utf-8").strip()
|
||||
else:
|
||||
templates[name] = ""
|
||||
return templates
|
||||
|
||||
|
||||
def _render_file_block(name: str, content: str) -> str:
|
||||
body = content if content else f"# {name}\n\nTODO: add content\n"
|
||||
return f"\n{name}\n```md\n{body}\n```\n"
|
||||
|
||||
|
||||
def _workspace_path(agent_name: str) -> str:
|
||||
root = settings.openclaw_workspace_root or "~/.openclaw/workspaces"
|
||||
root = root.rstrip("/")
|
||||
return f"{root}/{_slugify(agent_name)}"
|
||||
|
||||
|
||||
def build_provisioning_message(agent: Agent) -> str:
|
||||
templates = _read_templates()
|
||||
agent_id = _slugify(agent.name)
|
||||
workspace_path = _workspace_path(agent.name)
|
||||
session_key = agent.openclaw_session_id or ""
|
||||
base_url = settings.base_url or ""
|
||||
|
||||
file_blocks = "".join(
|
||||
_render_file_block(name, templates.get(name, "")) for name in TEMPLATE_FILES
|
||||
)
|
||||
|
||||
return (
|
||||
"Provision a new OpenClaw agent workspace.\n\n"
|
||||
f"Agent name: {agent.name}\n"
|
||||
f"Agent id: {agent_id}\n"
|
||||
f"Session key: {session_key}\n"
|
||||
f"Workspace path: {workspace_path}\n\n"
|
||||
f"Base URL: {base_url or 'UNSET'}\n\n"
|
||||
"Steps:\n"
|
||||
"1) Create the workspace directory.\n"
|
||||
"2) Write the files below with the exact contents.\n"
|
||||
f"3) Set BASE_URL to {base_url or '{{BASE_URL}}'} for the agent runtime.\n"
|
||||
"4) Replace placeholders like {{AGENT_NAME}}, {{AGENT_ID}}, {{BASE_URL}}, {{AUTH_TOKEN}}.\n"
|
||||
"5) Leave BOOTSTRAP.md in place; the agent should run it on first start and delete it.\n"
|
||||
"6) Register agent id in OpenClaw so it uses this workspace path.\n\n"
|
||||
"Files:" + file_blocks
|
||||
)
|
||||
|
||||
|
||||
async def send_provisioning_message(agent: Agent) -> None:
|
||||
main_session = settings.openclaw_main_session_key
|
||||
if not main_session:
|
||||
return
|
||||
message = build_provisioning_message(agent)
|
||||
await send_message(message, session_key=main_session, deliver=False)
|
||||
Reference in New Issue
Block a user