diff --git a/backend/agenteval/intelligent_eval/openclaw_client.py b/backend/agenteval/intelligent_eval/openclaw_client.py index 7cd1d23..0d83767 100644 --- a/backend/agenteval/intelligent_eval/openclaw_client.py +++ b/backend/agenteval/intelligent_eval/openclaw_client.py @@ -26,16 +26,19 @@ class OpenClawCron: class OpenClawClient: """Client for OpenClaw CLI commands.""" - def __init__(self, openclaw_bin: str = "openclaw"): + def __init__(self, openclaw_bin: str = "docker", container_name: str = "openclaw-eval", gateway_token: str = "agenteval-openclaw-token-2026"): self.openclaw_bin = openclaw_bin + self.container_name = container_name + self.gateway_token = gateway_token async def _run_command(self, *args: str) -> tuple[int, str, str]: - """Run an OpenClaw CLI command. + """Run an OpenClaw CLI command via docker exec. Returns: (returncode, stdout, stderr) """ - cmd = [self.openclaw_bin] + list(args) + # token 需要放在子命令後面 + cmd = [self.openclaw_bin, "exec", self.container_name, "openclaw"] + list(args) + [f"--token={self.gateway_token}"] _logger.debug(f"Running OpenClaw command: {' '.join(cmd)}") process = await asyncio.create_subprocess_exec( @@ -64,8 +67,8 @@ class OpenClawClient: Args: name: Cron job name schedule: Cron schedule expression (e.g., "* * * * *") - skill: Skill to execute - state: Initial state (JSON) + skill: Skill to execute (sent as message to trigger skill) + state: Initial state (JSON, stored in description) Returns: Cron job ID @@ -74,29 +77,32 @@ class OpenClawClient: RuntimeError: If creation fails """ args = [ - "automations", - "create", - schedule, + "cron", + "add", + "--cron", schedule, f"--name={name}", - f"--skill={skill}", + f"--message={skill}", + "--json", ] if state: - args.append(f"--state={json.dumps(state)}") + # Store state in description since there's no --state option + args.append(f"--description=state:{json.dumps(state)}") returncode, stdout, stderr = await self._run_command(*args) if returncode != 0: raise RuntimeError(f"Failed to create cron: {stderr}") - # Parse cron ID from output - # Expected output format: "Created automation " - lines = stdout.strip().split("\n") - for line in lines: - if "Created automation" in line: - cron_id = line.split()[-1] + # Parse cron ID from JSON output + try: + data = json.loads(stdout) + cron_id = data.get("id") + if cron_id: _logger.info(f"Created OpenClaw cron {cron_id}: {name}") return cron_id + except json.JSONDecodeError: + pass raise RuntimeError(f"Failed to parse cron ID from output: {stdout}") @@ -110,8 +116,8 @@ class OpenClawClient: RuntimeError: If deletion fails """ returncode, stdout, stderr = await self._run_command( - "automations", - "remove", + "cron", + "rm", cron_id, ) @@ -130,7 +136,7 @@ class OpenClawClient: RuntimeError: If listing fails """ returncode, stdout, stderr = await self._run_command( - "automations", + "cron", "list", "--json", ) diff --git a/deploy/t480/Dockerfile b/deploy/t480/Dockerfile index 1cb80f7..ba161cf 100644 --- a/deploy/t480/Dockerfile +++ b/deploy/t480/Dockerfile @@ -24,9 +24,15 @@ RUN sed -i \ -e 's|security.debian.org|mirrors.aliyun.com/debian-security|g' \ /etc/apt/sources.list 2>/dev/null || true -# Install any system build dependencies that may be needed for Python wheels. +# Install Docker CLI from Aliyun mirror (for China network) and build dependencies. RUN apt-get update \ - && apt-get install -y --no-install-recommends gcc \ + && apt-get install -y --no-install-recommends gcc ca-certificates curl gnupg \ + && install -m 0755 -d /etc/apt/keyrings \ + && curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg \ + && chmod a+r /etc/apt/keyrings/docker.gpg \ + && echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://mirrors.aliyun.com/docker-ce/linux/debian $(. /etc/os-release && echo $VERSION_CODENAME) stable" > /etc/apt/sources.list.d/docker.list \ + && apt-get update \ + && apt-get install -y --no-install-recommends docker-ce-cli \ && rm -rf /var/lib/apt/lists/* COPY pyproject.toml README.md alembic.ini ./ diff --git a/deploy/t480/docker-compose.yml b/deploy/t480/docker-compose.yml index 5cffb14..42955a0 100644 --- a/deploy/t480/docker-compose.yml +++ b/deploy/t480/docker-compose.yml @@ -20,6 +20,8 @@ services: - ../../backend:/app/backend:ro # Mount .env so Pydantic Settings can load deployment-specific config. - ../../.env:/app/.env:ro + # Mount Docker socket for managing openclaw-eval container (cron pool). + - /var/run/docker.sock:/var/run/docker.sock restart: unless-stopped openclaw-eval: @@ -31,4 +33,5 @@ services: - ../../data/openclaw:/home/node/.openclaw environment: - AGENTEVAL_API_URL=http://agenteval:8000 + - OPENCLAW_GATEWAY_TOKEN=agenteval-openclaw-token-2026 restart: unless-stopped diff --git a/frontend/web/src/App.tsx b/frontend/web/src/App.tsx index a2be86f..b468be0 100644 --- a/frontend/web/src/App.tsx +++ b/frontend/web/src/App.tsx @@ -99,6 +99,7 @@ const menuItems: MenuProps['items'] = [ label: '智能评估', children: [ { key: '/intelligent-evals', icon: , label: '评估列表' }, + { key: '/cron-pool', icon: , label: 'Cron 池监控' }, ], }, {