refactor: remove unused local agent workspace logic and simplify provisioning
This commit is contained in:
@@ -35,13 +35,6 @@ class Settings(BaseSettings):
|
|||||||
cors_origins: str = ""
|
cors_origins: str = ""
|
||||||
base_url: str = ""
|
base_url: str = ""
|
||||||
|
|
||||||
# Optional: local directory where the backend is allowed to write "preserved" agent
|
|
||||||
# workspace files (e.g. USER.md/SELF.md/MEMORY.md). If empty, local
|
|
||||||
# writes are disabled and provisioning relies on the gateway API.
|
|
||||||
#
|
|
||||||
# Security note: do NOT point this at arbitrary system paths in production.
|
|
||||||
local_agent_workspace_root: str = ""
|
|
||||||
|
|
||||||
# Database lifecycle
|
# Database lifecycle
|
||||||
db_auto_migrate: bool = False
|
db_auto_migrate: bool = False
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import hashlib
|
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
@@ -215,40 +214,6 @@ def _workspace_path(agent: Agent, workspace_root: str) -> str:
|
|||||||
return f"{root}/workspace-{_slugify(key)}"
|
return f"{root}/workspace-{_slugify(key)}"
|
||||||
|
|
||||||
|
|
||||||
def _ensure_workspace_file(
|
|
||||||
workspace_path: str,
|
|
||||||
name: str,
|
|
||||||
content: str,
|
|
||||||
*,
|
|
||||||
overwrite: bool = False,
|
|
||||||
) -> None:
|
|
||||||
if not workspace_path or not name:
|
|
||||||
return
|
|
||||||
# Only write to a dedicated, explicitly-configured local directory.
|
|
||||||
# Using `gateway.workspace_root` directly here is unsafe.
|
|
||||||
# CodeQL correctly flags that value because it is DB-backed config.
|
|
||||||
base_root = (settings.local_agent_workspace_root or "").strip()
|
|
||||||
if not base_root:
|
|
||||||
return
|
|
||||||
base = Path(base_root).expanduser()
|
|
||||||
|
|
||||||
# Derive a stable, safe directory name from the untrusted workspace path.
|
|
||||||
# This prevents path traversal and avoids writing to arbitrary locations.
|
|
||||||
digest = hashlib.sha256(workspace_path.encode("utf-8")).hexdigest()[:16]
|
|
||||||
root = base / f"gateway-workspace-{digest}"
|
|
||||||
|
|
||||||
# Ensure `name` is a plain filename (no path separators).
|
|
||||||
if Path(name).name != name:
|
|
||||||
return
|
|
||||||
path = root / name
|
|
||||||
if not overwrite and path.exists():
|
|
||||||
return
|
|
||||||
root.mkdir(parents=True, exist_ok=True)
|
|
||||||
tmp_path = path.with_suffix(f"{path.suffix}.tmp")
|
|
||||||
tmp_path.write_text(content, encoding="utf-8")
|
|
||||||
tmp_path.replace(path)
|
|
||||||
|
|
||||||
|
|
||||||
def _build_context(
|
def _build_context(
|
||||||
agent: Agent,
|
agent: Agent,
|
||||||
board: Board,
|
board: Board,
|
||||||
@@ -795,13 +760,6 @@ class BaseAgentLifecycleManager(ABC):
|
|||||||
template_overrides=self._template_overrides(),
|
template_overrides=self._template_overrides(),
|
||||||
)
|
)
|
||||||
|
|
||||||
for name in PRESERVE_AGENT_EDITABLE_FILES:
|
|
||||||
content = rendered.get(name)
|
|
||||||
if not content:
|
|
||||||
continue
|
|
||||||
with suppress(OSError):
|
|
||||||
_ensure_workspace_file(workspace_path, name, content, overwrite=False)
|
|
||||||
|
|
||||||
await self._set_agent_files(
|
await self._set_agent_files(
|
||||||
agent_id=agent_id,
|
agent_id=agent_id,
|
||||||
rendered=rendered,
|
rendered=rendered,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from uuid import UUID, uuid4
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
import app.services.openclaw.provisioning as agent_provisioning
|
import app.services.openclaw.provisioning as agent_provisioning
|
||||||
|
from app.services.openclaw.agent_service import AgentLifecycleService
|
||||||
from app.services.openclaw.shared import GatewayAgentIdentity
|
from app.services.openclaw.shared import GatewayAgentIdentity
|
||||||
|
|
||||||
|
|
||||||
@@ -65,6 +66,20 @@ def test_agent_key_uses_session_key_when_present(monkeypatch):
|
|||||||
agent2 = _AgentStub(name="Alice", openclaw_session_id=None)
|
agent2 = _AgentStub(name="Alice", openclaw_session_id=None)
|
||||||
assert agent_provisioning._agent_key(agent2) == "slugged"
|
assert agent_provisioning._agent_key(agent2) == "slugged"
|
||||||
|
|
||||||
|
def test_workspace_path_preserves_tilde_in_workspace_root():
|
||||||
|
# Mission Control accepts a user-entered workspace root (from the UI) and must
|
||||||
|
# treat it as an opaque string. In particular, we must not expand "~" to a
|
||||||
|
# filesystem path since that behavior depends on the host environment.
|
||||||
|
agent = _AgentStub(name="Alice", openclaw_session_id="agent:alice:main")
|
||||||
|
assert agent_provisioning._workspace_path(agent, "~/.openclaw") == "~/.openclaw/workspace-alice"
|
||||||
|
|
||||||
|
|
||||||
|
def test_agent_lifecycle_workspace_path_preserves_tilde_in_workspace_root():
|
||||||
|
assert (
|
||||||
|
AgentLifecycleService.workspace_path("Alice", "~/.openclaw")
|
||||||
|
== "~/.openclaw/workspace-alice"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class _GatewayStub:
|
class _GatewayStub:
|
||||||
|
|||||||
Reference in New Issue
Block a user