fix: register task_request handler and fix Docker 403 errors on macOS (#86)
This commit is contained in:
committed by
GitHub
parent
da5745db48
commit
add3f7a07a
@@ -46,6 +46,11 @@ RUN mkdir -p /app/data
|
||||
# ── Non-root user for production ─────────────────────────────────────────────
|
||||
RUN groupadd -r timmy && useradd -r -g timmy -d /app -s /sbin/nologin timmy \
|
||||
&& chown -R timmy:timmy /app
|
||||
# Ensure static/ and data/ are world-readable so bind-mounted files
|
||||
# from the macOS host remain accessible when running as the timmy user.
|
||||
# Docker Desktop for Mac bind mounts inherit host permissions, which may
|
||||
# not include the container's timmy UID — chmod o+rX fixes 403 errors.
|
||||
RUN chmod -R o+rX /app/static /app/data
|
||||
USER timmy
|
||||
|
||||
# ── Environment ──────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -22,6 +22,12 @@ services:
|
||||
build: .
|
||||
image: timmy-time:latest
|
||||
container_name: timmy-dashboard
|
||||
# Run as root in the dev compose because bind-mounted host files
|
||||
# (./src, ./static) may not be readable by the image's non-root
|
||||
# "timmy" user — this is the #1 cause of 403 errors on macOS.
|
||||
# Production (docker-compose.prod.yml) uses no bind mounts and
|
||||
# correctly runs as the Dockerfile's non-root USER.
|
||||
user: "0:0"
|
||||
ports:
|
||||
- "8000:8000"
|
||||
volumes:
|
||||
|
||||
@@ -231,11 +231,53 @@ async def _task_processor_loop() -> None:
|
||||
"""Handler for bug_report tasks - acknowledge and mark completed."""
|
||||
return f"Bug report acknowledged: {task.title}"
|
||||
|
||||
def handle_task_request(task):
|
||||
"""Handler for task_request tasks — user-queued work items from chat."""
|
||||
try:
|
||||
now = datetime.now()
|
||||
context = (
|
||||
f"[System: Current date/time is {now.strftime('%A, %B %d, %Y at %I:%M %p')}]\n"
|
||||
f"[System: You have been assigned a task from the queue. "
|
||||
f"Complete it and provide your response.]\n\n"
|
||||
f"Task: {task.title}\n"
|
||||
)
|
||||
if task.description and task.description != task.title:
|
||||
context += f"Details: {task.description}\n"
|
||||
|
||||
response = timmy_chat(context)
|
||||
|
||||
# Push response to user via WebSocket
|
||||
try:
|
||||
from infrastructure.ws_manager.handler import ws_manager
|
||||
|
||||
asyncio.create_task(
|
||||
ws_manager.broadcast(
|
||||
"timmy_response",
|
||||
{
|
||||
"task_id": task.id,
|
||||
"response": response,
|
||||
},
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
logger.debug("Failed to push response via WS: %s", e)
|
||||
|
||||
return response
|
||||
except Exception as e:
|
||||
logger.error("Task request failed: %s", e)
|
||||
try:
|
||||
from infrastructure.error_capture import capture_error
|
||||
capture_error(e, source="task_request_handler")
|
||||
except Exception:
|
||||
pass
|
||||
return f"Error: {str(e)}"
|
||||
|
||||
# Register handlers
|
||||
task_processor.register_handler("chat_response", handle_chat_response)
|
||||
task_processor.register_handler("thought", handle_thought)
|
||||
task_processor.register_handler("internal", handle_thought)
|
||||
task_processor.register_handler("bug_report", handle_bug_report)
|
||||
task_processor.register_handler("task_request", handle_task_request)
|
||||
|
||||
# ── Reconcile zombie tasks from previous crash ──
|
||||
zombie_count = task_processor.reconcile_zombie_tasks()
|
||||
|
||||
@@ -876,6 +876,49 @@ class TestTaskProcessor:
|
||||
refreshed = get_task(task.id)
|
||||
assert refreshed.status == TaskStatus.APPROVED
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_task_request_type_has_handler(self):
|
||||
"""task_request tasks are processed (not backlogged) when a handler is registered.
|
||||
|
||||
Regression test: previously task_request had no handler, causing all
|
||||
user-queued tasks from chat to be immediately backlogged.
|
||||
"""
|
||||
from swarm.task_processor import TaskProcessor
|
||||
from swarm.task_queue.models import create_task, get_task, TaskStatus
|
||||
|
||||
tp = TaskProcessor("task-request-test")
|
||||
tp.register_handler("task_request", lambda task: f"Completed: {task.title}")
|
||||
|
||||
task = create_task(
|
||||
title="Refactor the login module",
|
||||
description="Create a task to refactor the login module",
|
||||
task_type="task_request",
|
||||
assigned_to="task-request-test",
|
||||
created_by="user",
|
||||
)
|
||||
|
||||
result = await tp.process_single_task(task)
|
||||
assert result is not None
|
||||
|
||||
refreshed = get_task(task.id)
|
||||
assert refreshed.status == TaskStatus.COMPLETED
|
||||
assert "Refactor" in refreshed.result
|
||||
|
||||
def test_chat_queue_request_creates_task_request_type(self, client):
|
||||
"""Chat messages that match queue patterns create task_request tasks."""
|
||||
from swarm.task_queue.models import list_tasks
|
||||
|
||||
client.post(
|
||||
"/agents/timmy/chat",
|
||||
data={"message": "Add refactor the login module to the task queue"},
|
||||
)
|
||||
|
||||
tasks = list_tasks(assigned_to="timmy")
|
||||
task_request_tasks = [t for t in tasks if t.task_type == "task_request"]
|
||||
assert len(task_request_tasks) >= 1
|
||||
assert any("login" in t.title.lower() or "refactor" in t.title.lower()
|
||||
for t in task_request_tasks)
|
||||
|
||||
|
||||
# ── Backlog Route Tests ─────────────────────────────────────────────────
|
||||
|
||||
|
||||
Reference in New Issue
Block a user