ref(heartbeat): Remove cron provisioning
Remove gateway cron helpers and startup provisioning, relying on\nheartbeat instructions instead. Also drop unused httpx dependency.\n\nCo-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -4,10 +4,9 @@ import asyncio
|
||||
import json
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
from urllib.parse import quote, urlencode, urlparse, urlunparse
|
||||
from urllib.parse import urlencode, urlparse, urlunparse
|
||||
from uuid import uuid4
|
||||
|
||||
import httpx
|
||||
import websockets
|
||||
|
||||
from app.core.config import settings
|
||||
@@ -32,54 +31,6 @@ def _build_gateway_url() -> str:
|
||||
return urlunparse(parsed._replace(query=query))
|
||||
|
||||
|
||||
def _build_gateway_http_url() -> str:
|
||||
base_url = settings.openclaw_gateway_url or "ws://127.0.0.1:18789"
|
||||
parsed = urlparse(base_url)
|
||||
if parsed.scheme in {"http", "https"}:
|
||||
scheme = parsed.scheme
|
||||
elif parsed.scheme == "wss":
|
||||
scheme = "https"
|
||||
else:
|
||||
scheme = "http"
|
||||
return urlunparse(
|
||||
parsed._replace(scheme=scheme, path="", params="", query="", fragment="")
|
||||
)
|
||||
|
||||
|
||||
def _gateway_headers() -> dict[str, str]:
|
||||
headers: dict[str, str] = {}
|
||||
if settings.openclaw_gateway_token:
|
||||
headers["Authorization"] = f"Bearer {settings.openclaw_gateway_token}"
|
||||
return headers
|
||||
|
||||
|
||||
async def _http_request(method: str, path: str, payload: dict[str, Any] | None = None) -> Any:
|
||||
base_url = _build_gateway_http_url().rstrip("/")
|
||||
url = f"{base_url}{path}"
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=10) as client:
|
||||
response = await client.request(
|
||||
method, url, json=payload, headers=_gateway_headers()
|
||||
)
|
||||
if response.status_code >= 400:
|
||||
raise OpenClawGatewayError(
|
||||
f"{response.status_code}: {response.text or 'Gateway error'}"
|
||||
)
|
||||
if not response.content:
|
||||
return None
|
||||
try:
|
||||
return response.json()
|
||||
except ValueError as exc:
|
||||
preview = response.text.strip()[:200] or "<empty>"
|
||||
raise OpenClawGatewayError(
|
||||
f"Non-JSON response from gateway: {preview}"
|
||||
) from exc
|
||||
except OpenClawGatewayError:
|
||||
raise
|
||||
except Exception as exc: # pragma: no cover - transport errors
|
||||
raise OpenClawGatewayError(str(exc)) from exc
|
||||
|
||||
|
||||
async def _await_response(ws: websockets.WebSocketClientProtocol, request_id: str) -> Any:
|
||||
while True:
|
||||
raw = await ws.recv()
|
||||
@@ -187,24 +138,3 @@ async def ensure_session(session_key: str, label: str | None = None) -> Any:
|
||||
params["label"] = label
|
||||
return await openclaw_call("sessions.patch", params)
|
||||
|
||||
|
||||
async def list_cron_jobs() -> Any:
|
||||
try:
|
||||
return await _http_request("GET", "/api/v1/cron/jobs")
|
||||
except OpenClawGatewayError:
|
||||
return await _http_request("GET", "/cron/jobs")
|
||||
|
||||
|
||||
async def upsert_cron_job(job: dict[str, Any]) -> Any:
|
||||
try:
|
||||
return await _http_request("POST", "/api/v1/cron/jobs", payload=job)
|
||||
except OpenClawGatewayError:
|
||||
return await _http_request("POST", "/cron/jobs", payload=job)
|
||||
|
||||
|
||||
async def delete_cron_job(name: str) -> Any:
|
||||
safe_name = quote(name, safe="")
|
||||
try:
|
||||
return await _http_request("DELETE", f"/api/v1/cron/jobs/{safe_name}")
|
||||
except OpenClawGatewayError:
|
||||
return await _http_request("DELETE", f"/cron/jobs/{safe_name}")
|
||||
|
||||
@@ -12,7 +12,6 @@ from app.api.tasks import router as tasks_router
|
||||
from app.core.config import settings
|
||||
from app.core.logging import configure_logging
|
||||
from app.db.session import init_db
|
||||
from app.services.cron_jobs import ensure_mission_control_cron_job
|
||||
|
||||
configure_logging()
|
||||
|
||||
@@ -30,9 +29,8 @@ if origins:
|
||||
|
||||
|
||||
@app.on_event("startup")
|
||||
async def on_startup() -> None:
|
||||
def on_startup() -> None:
|
||||
init_db()
|
||||
await ensure_mission_control_cron_job()
|
||||
|
||||
|
||||
@app.get("/health")
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from app.integrations.openclaw_gateway import (
|
||||
OpenClawGatewayError,
|
||||
list_cron_jobs,
|
||||
upsert_cron_job,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
MISSION_CONTROL_CRON_NAME = "mission-control-runner/10m"
|
||||
|
||||
|
||||
def _mission_control_runner_message() -> str:
|
||||
return (
|
||||
"You are the Mission Control Runner agent.\n\n"
|
||||
"On this scheduled tick:\n"
|
||||
"- Run the HEARTBEAT.md procedure for Mission Control (check-in, list boards, "
|
||||
"list tasks).\n"
|
||||
"- If any task is already in_progress, stop (do not claim another).\n"
|
||||
"- Otherwise, find the oldest inbox task across all boards, claim it by moving "
|
||||
"to in_progress.\n"
|
||||
"- Execute the task fully.\n"
|
||||
"- When complete, move it to review.\n"
|
||||
"- If no inbox tasks exist, do nothing.\n"
|
||||
"Only update Mission Control (no chat messages)."
|
||||
)
|
||||
|
||||
|
||||
def build_mission_control_cron_job() -> dict[str, Any]:
|
||||
return {
|
||||
"name": MISSION_CONTROL_CRON_NAME,
|
||||
"schedule": {"kind": "every", "everyMs": 600000},
|
||||
"sessionTarget": "isolated",
|
||||
"enabled": True,
|
||||
"payload": {"kind": "agentTurn", "message": _mission_control_runner_message()},
|
||||
}
|
||||
|
||||
|
||||
async def ensure_mission_control_cron_job() -> None:
|
||||
try:
|
||||
payload = await list_cron_jobs()
|
||||
except OpenClawGatewayError as exc:
|
||||
logger.warning("Gateway cron list failed: %s", exc)
|
||||
return
|
||||
|
||||
jobs: list[dict[str, Any]] = []
|
||||
if isinstance(payload, list):
|
||||
jobs = payload
|
||||
elif isinstance(payload, dict):
|
||||
jobs = list(payload.get("jobs", []))
|
||||
|
||||
job = build_mission_control_cron_job()
|
||||
if any(item.get("name") == job["name"] for item in jobs):
|
||||
logger.info("Updating gateway cron job: %s", job["name"])
|
||||
else:
|
||||
logger.info("Creating gateway cron job: %s", job["name"])
|
||||
|
||||
try:
|
||||
await upsert_cron_job(job)
|
||||
except OpenClawGatewayError as exc:
|
||||
logger.warning("Gateway cron upsert failed: %s", exc)
|
||||
@@ -20,7 +20,6 @@ dependencies = [
|
||||
"psycopg[binary]==3.2.1",
|
||||
"pydantic-settings==2.5.2",
|
||||
"python-dotenv==1.0.1",
|
||||
"httpx==0.27.2",
|
||||
"websockets==12.0",
|
||||
"rq==1.16.2",
|
||||
"redis==5.1.1",
|
||||
|
||||
@@ -6,7 +6,6 @@ alembic==1.13.2
|
||||
psycopg[binary]==3.2.1
|
||||
pydantic-settings==2.5.2
|
||||
python-dotenv==1.0.1
|
||||
httpx==0.27.2
|
||||
websockets==12.0
|
||||
rq==1.16.2
|
||||
redis==5.1.1
|
||||
|
||||
49
backend/uv.lock
generated
49
backend/uv.lock
generated
@@ -62,15 +62,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/8d/a7/4b27c50537ebca8bec139b872861f9d2bf501c5ec51fcf897cb924d9e264/black-24.10.0-py3-none-any.whl", hash = "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d", size = 206898, upload-time = "2024-10-07T19:20:48.317Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2026.1.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e0/2d/a891ca51311197f6ad14a7ef42e2399f36cf2f9bd44752b3dc4eab60fdc5/certifi-2026.1.4.tar.gz", hash = "sha256:ac726dd470482006e014ad384921ed6438c457018f4b3d204aea4281258b2120", size = 154268, upload-time = "2026-01-04T02:42:41.825Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e6/ad/3cc14f097111b4de0040c83a525973216457bbeeb63739ef1ed275c1c021/certifi-2026.1.4-py3-none-any.whl", hash = "sha256:9943707519e4add1115f44c2bc244f782c0249876bf51b6599fee1ffbedd685c", size = 152900, upload-time = "2026-01-04T02:42:40.15Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cffi"
|
||||
version = "2.0.0"
|
||||
@@ -268,19 +259,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httpcore"
|
||||
version = "1.0.9"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "certifi" },
|
||||
{ name = "h11" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httptools"
|
||||
version = "0.7.1"
|
||||
@@ -310,22 +288,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/53/cf/878f3b91e4e6e011eff6d1fa9ca39f7eb17d19c9d7971b04873734112f30/httptools-0.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:cfabda2a5bb85aa2a904ce06d974a3f30fb36cc63d7feaddec05d2050acede96", size = 88205, upload-time = "2025-10-10T03:55:00.389Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httpx"
|
||||
version = "0.27.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "anyio" },
|
||||
{ name = "certifi" },
|
||||
{ name = "httpcore" },
|
||||
{ name = "idna" },
|
||||
{ name = "sniffio" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/78/82/08f8c936781f67d9e6b9eeb8a0c8b4e406136ea4c3d1f89a5db71d42e0e6/httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2", size = 144189, upload-time = "2024-08-27T12:54:01.334Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/56/95/9377bcb415797e44274b51d46e3249eba641711cf3348050f76ee7b15ffc/httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0", size = 76395, upload-time = "2024-08-27T12:53:59.653Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.11"
|
||||
@@ -472,7 +434,6 @@ dependencies = [
|
||||
{ name = "alembic" },
|
||||
{ name = "fastapi" },
|
||||
{ name = "fastapi-clerk-auth" },
|
||||
{ name = "httpx" },
|
||||
{ name = "psycopg", extra = ["binary"] },
|
||||
{ name = "pydantic-settings" },
|
||||
{ name = "python-dotenv" },
|
||||
@@ -503,7 +464,6 @@ requires-dist = [
|
||||
{ name = "fastapi", specifier = "==0.115.4" },
|
||||
{ name = "fastapi-clerk-auth", specifier = "==0.0.9" },
|
||||
{ name = "flake8", marker = "extra == 'dev'", specifier = "==7.1.1" },
|
||||
{ name = "httpx", specifier = "==0.27.2" },
|
||||
{ name = "isort", marker = "extra == 'dev'", specifier = "==5.13.2" },
|
||||
{ name = "mypy", marker = "extra == 'dev'", specifier = "==1.11.2" },
|
||||
{ name = "psycopg", extras = ["binary"], specifier = "==3.2.1" },
|
||||
@@ -858,15 +818,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/3e/14/fd026bc74ded05e2351681545a5f626e78ef831f8edce064d61acd2e6ec7/ruff-0.6.9-py3-none-win_arm64.whl", hash = "sha256:a9641e31476d601f83cd602608739a0840e348bda93fec9f1ee816f8b6798b93", size = 8679879, upload-time = "2024-10-04T13:40:25.797Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sniffio"
|
||||
version = "1.3.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlalchemy"
|
||||
version = "2.0.34"
|
||||
|
||||
Reference in New Issue
Block a user