Audit cleanup: security fixes, code reduction, test hygiene (#131)

This commit is contained in:
Alexander Whitestone
2026-03-05 18:56:52 -05:00
committed by GitHub
parent e8f1dea3ec
commit aff3edb06a
33 changed files with 160 additions and 591 deletions

View File

@@ -5,7 +5,6 @@ No OpenAI dependency. Runs 100% locally on CPU.
from __future__ import annotations
import json
import logging
from typing import List, Union

View File

@@ -77,9 +77,9 @@ class DistributedWorker:
)
if result.returncode == 0:
return True
except:
except (OSError, subprocess.SubprocessError):
pass
# Check for ROCm
if os.path.exists("/opt/rocm"):
return True
@@ -93,11 +93,11 @@ class DistributedWorker:
)
if "Metal" in result.stdout:
return True
except:
except (OSError, subprocess.SubprocessError):
pass
return False
def _has_internet(self) -> bool:
"""Check if we have internet connectivity."""
try:
@@ -106,9 +106,9 @@ class DistributedWorker:
capture_output=True, timeout=5
)
return result.returncode == 0
except:
except (OSError, subprocess.SubprocessError):
return False
def _get_memory_gb(self) -> float:
"""Get total system memory in GB."""
try:
@@ -125,7 +125,7 @@ class DistributedWorker:
if line.startswith("MemTotal:"):
kb = int(line.split()[1])
return kb / (1024**2)
except:
except (OSError, ValueError):
pass
return 8.0 # Assume 8GB if we can't detect
@@ -136,9 +136,9 @@ class DistributedWorker:
["which", cmd], capture_output=True, timeout=5
)
return result.returncode == 0
except:
except (OSError, subprocess.SubprocessError):
return False
def _register_default_handlers(self):
"""Register built-in task handlers."""
self._handlers = {

View File

@@ -215,6 +215,7 @@ OLLAMA_MODEL_FALLBACK: str = "qwen2.5:14b"
def check_ollama_model_available(model_name: str) -> bool:
"""Check if a specific Ollama model is available locally."""
try:
import json
import urllib.request
url = settings.ollama_url.replace("localhost", "127.0.0.1")
@@ -224,12 +225,12 @@ def check_ollama_model_available(model_name: str) -> bool:
headers={"Accept": "application/json"},
)
with urllib.request.urlopen(req, timeout=5) as response:
import json
data = json.loads(response.read().decode())
models = [m.get("name", "").split(":")[0] for m in data.get("models", [])]
# Check for exact match or model name without tag
return any(model_name in m or m in model_name for m in models)
models = [m.get("name", "") for m in data.get("models", [])]
return any(
model_name == m or model_name == m.split(":")[0] or m.startswith(model_name)
for m in models
)
except Exception:
return False

View File

@@ -18,8 +18,6 @@ from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.trustedhost import TrustedHostMiddleware
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from config import settings
from dashboard.routes.agents import router as agents_router
from dashboard.routes.health import router as health_router
@@ -282,8 +280,8 @@ static_dir = PROJECT_ROOT / "static"
if static_dir.exists():
app.mount("/static", StaticFiles(directory=str(static_dir)), name="static")
# Global templates instance
templates = Jinja2Templates(directory=str(BASE_DIR / "templates"))
# Shared templates instance
from dashboard.templating import templates # noqa: E402
# Include routers

View File

@@ -187,7 +187,7 @@ class CSRFMiddleware(BaseHTTPMiddleware):
"/lightning/webhook",
"/_internal/",
]
return any(pattern in path for pattern in exempt_patterns)
return any(path.startswith(pattern) for pattern in exempt_patterns)
async def _validate_request(self, request: Request, csrf_cookie: Optional[str]) -> bool:
"""Validate the CSRF token in the request.

View File

@@ -1,18 +1,16 @@
import logging
from datetime import datetime
from pathlib import Path
from fastapi import APIRouter, Form, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from timmy.session import chat as timmy_chat
from dashboard.store import message_log
from dashboard.templating import templates
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/agents", tags=["agents"])
templates = Jinja2Templates(directory=str(Path(__file__).parent.parent / "templates"))
@router.get("")

View File

@@ -7,19 +7,17 @@ POST /briefing/approvals/{id}/reject — reject an item (HTMX)
"""
import logging
from pathlib import Path
from fastapi import APIRouter, Request
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.templating import Jinja2Templates
from timmy.briefing import engine as briefing_engine
from timmy import approvals as approval_store
from dashboard.templating import templates
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/briefing", tags=["briefing"])
templates = Jinja2Templates(directory=str(Path(__file__).parent.parent / "templates"))
@router.get("", response_class=HTMLResponse)

View File

@@ -5,21 +5,15 @@ from typing import List, Optional
from fastapi import APIRouter, Depends, Form, HTTPException, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from sqlalchemy.orm import Session
from dashboard.models.calm import JournalEntry, Task, TaskCertainty, TaskState
from dashboard.models.database import SessionLocal, engine, get_db
# Create database tables (if not already created by Alembic)
# This is typically handled by Alembic migrations in a production environment
# from dashboard.models.database import Base
# Base.metadata.create_all(bind=engine)
from dashboard.templating import templates
logger = logging.getLogger(__name__)
router = APIRouter(tags=["calm"])
templates = Jinja2Templates(directory="src/dashboard/templates")
# Helper functions for state machine logic

View File

@@ -15,7 +15,7 @@ import os
import uuid
from datetime import datetime
from fastapi import APIRouter, File, Request, UploadFile
from fastapi import APIRouter, File, HTTPException, Request, UploadFile
from fastapi.responses import JSONResponse
from config import settings
@@ -27,6 +27,7 @@ logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api", tags=["chat-api"])
_UPLOAD_DIR = os.path.join("data", "chat-uploads")
_MAX_UPLOAD_SIZE = 50 * 1024 * 1024 # 50 MB
# ── POST /api/chat ────────────────────────────────────────────────────────────
@@ -112,11 +113,13 @@ async def api_upload(file: UploadFile = File(...)):
os.makedirs(_UPLOAD_DIR, exist_ok=True)
suffix = uuid.uuid4().hex[:12]
safe_name = (file.filename or "upload").replace("/", "_").replace("\\", "_")
safe_name = os.path.basename(file.filename or "upload")
stored_name = f"{suffix}-{safe_name}"
file_path = os.path.join(_UPLOAD_DIR, stored_name)
contents = await file.read()
if len(contents) > _MAX_UPLOAD_SIZE:
raise HTTPException(status_code=413, detail="File too large (max 50 MB)")
with open(file_path, "wb") as f:
f.write(contents)

View File

@@ -9,18 +9,16 @@ GET /grok/stats — Usage statistics (JSON)
"""
import logging
from pathlib import Path
from fastapi import APIRouter, Form, Request
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.templating import Jinja2Templates
from config import settings
from dashboard.templating import templates
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/grok", tags=["grok"])
templates = Jinja2Templates(directory=str(Path(__file__).parent.parent / "templates"))
# In-memory toggle state (persists per process lifetime)
_grok_mode_active: bool = False

View File

@@ -4,16 +4,13 @@ DEPRECATED: Personas replaced by brain task queue.
This module is kept for UI compatibility.
"""
from pathlib import Path
from fastapi import APIRouter, Request
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.templating import Jinja2Templates
from brain.client import BrainClient
from dashboard.templating import templates
router = APIRouter(tags=["marketplace"])
templates = Jinja2Templates(directory=str(Path(__file__).parent.parent / "templates"))
# Orchestrator only — personas deprecated
AGENT_CATALOG = [

View File

@@ -1,11 +1,9 @@
"""Memory (vector store) routes for browsing and searching memories."""
from pathlib import Path
from typing import Optional
from fastapi import APIRouter, Form, HTTPException, Request
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.templating import Jinja2Templates
from timmy.memory.vector_store import (
store_memory,
@@ -17,9 +15,9 @@ from timmy.memory.vector_store import (
update_personal_fact,
delete_memory,
)
from dashboard.templating import templates
router = APIRouter(prefix="/memory", tags=["memory"])
templates = Jinja2Templates(directory=str(Path(__file__).parent.parent / "templates"))
@router.get("", response_class=HTMLResponse)

View File

@@ -8,16 +8,13 @@ The /mobile/local endpoint loads a small LLM directly into the
browser via WebLLM so Timmy can run on an iPhone with no server.
"""
from pathlib import Path
from fastapi import APIRouter, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from config import settings
from dashboard.templating import templates
router = APIRouter(tags=["mobile"])
templates = Jinja2Templates(directory=str(Path(__file__).parent.parent / "templates"))
@router.get("/mobile", response_class=HTMLResponse)

View File

@@ -10,7 +10,6 @@ from typing import Any, Optional
from fastapi import APIRouter, HTTPException, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from pydantic import BaseModel
from config import settings
@@ -21,12 +20,12 @@ from infrastructure.models.registry import (
ModelRole,
model_registry,
)
from dashboard.templating import templates
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/models", tags=["models"])
api_router = APIRouter(prefix="/api/v1/models", tags=["models-api"])
templates = Jinja2Templates(directory=str(Path(__file__).parent.parent / "templates"))
# ── Pydantic schemas ──────────────────────────────────────────────────────────

View File

@@ -1,15 +1,12 @@
"""Cascade Router status routes."""
from pathlib import Path
from fastapi import APIRouter, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from timmy.cascade_adapter import get_cascade_adapter
from dashboard.templating import templates
router = APIRouter(prefix="/router", tags=["router"])
templates = Jinja2Templates(directory=str(Path(__file__).parent.parent / "templates"))
@router.get("/status", response_class=HTMLResponse)

View File

@@ -9,18 +9,16 @@ GET /spark/predictions — HTMX partial: EIDOS predictions
import json
import logging
from pathlib import Path
from fastapi import APIRouter, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from spark.engine import spark_engine
from dashboard.templating import templates
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/spark", tags=["spark"])
templates = Jinja2Templates(directory=str(Path(__file__).parent.parent / "templates"))
@router.get("/ui", response_class=HTMLResponse)

View File

@@ -2,19 +2,17 @@
import json
import logging
from pathlib import Path
from typing import Optional
from fastapi import APIRouter, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from spark.engine import spark_engine
from dashboard.templating import templates
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/swarm", tags=["swarm"])
templates = Jinja2Templates(directory=str(Path(__file__).parent.parent / "templates"))
@router.get("/events", response_class=HTMLResponse)

View File

@@ -1,16 +1,15 @@
"""System-level dashboard routes (ledger, upgrades, etc.)."""
import logging
from pathlib import Path
from fastapi import APIRouter, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from dashboard.templating import templates
logger = logging.getLogger(__name__)
router = APIRouter(tags=["system"])
templates = Jinja2Templates(directory=str(Path(__file__).parent.parent / "templates"))
@router.get("/lightning/ledger", response_class=HTMLResponse)

View File

@@ -8,16 +8,15 @@ POST /celery/api/{id}/revoke — cancel a running task
"""
import logging
from pathlib import Path
from fastapi import APIRouter, Request
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.templating import Jinja2Templates
from dashboard.templating import templates
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/celery", tags=["celery"])
templates = Jinja2Templates(directory=str(Path(__file__).parent.parent / "templates"))
# In-memory record of submitted task IDs for the dashboard display.
# In production this would use the Celery result backend directly,

View File

@@ -6,18 +6,16 @@ GET /thinking/api/{id}/chain — follow a thought chain
"""
import logging
from pathlib import Path
from fastapi import APIRouter, Request
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.templating import Jinja2Templates
from timmy.thinking import thinking_engine
from dashboard.templating import templates
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/thinking", tags=["thinking"])
templates = Jinja2Templates(directory=str(Path(__file__).parent.parent / "templates"))
@router.get("", response_class=HTMLResponse)

View File

@@ -3,16 +3,13 @@
Shows available tools and usage statistics.
"""
from pathlib import Path
from fastapi import APIRouter, Request
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.templating import Jinja2Templates
from timmy.tools import get_all_available_tools
from dashboard.templating import templates
router = APIRouter(tags=["tools"])
templates = Jinja2Templates(directory=str(Path(__file__).parent.parent / "templates"))
@router.get("/tools", response_class=HTMLResponse)

View File

@@ -9,16 +9,14 @@ import logging
from fastapi import APIRouter, Form, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from pathlib import Path
from integrations.voice.nlu import detect_intent, extract_command
from timmy.agent import create_timmy
from dashboard.templating import templates
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/voice", tags=["voice"])
templates = Jinja2Templates(directory=str(Path(__file__).parent.parent / "templates"))
@router.post("/nlu")

View File

@@ -0,0 +1,7 @@
"""Shared Jinja2Templates instance for all dashboard routes."""
from pathlib import Path
from fastapi.templating import Jinja2Templates
templates = Jinja2Templates(directory=str(Path(__file__).parent / "templates"))

View File

@@ -18,8 +18,6 @@ from enum import Enum
from pathlib import Path
from typing import Any, Optional
from pathlib import Path
try:
import yaml
except ImportError:

View File

@@ -7,6 +7,7 @@ system events.
"""
import asyncio
import collections
import json
import logging
from dataclasses import asdict, dataclass
@@ -34,8 +35,7 @@ class WebSocketManager:
def __init__(self) -> None:
self._connections: list[WebSocket] = []
self._event_history: list[WSEvent] = []
self._max_history = 100
self._event_history: collections.deque[WSEvent] = collections.deque(maxlen=100)
async def connect(self, websocket: WebSocket) -> None:
"""Accept a new WebSocket connection."""
@@ -46,7 +46,7 @@ class WebSocketManager:
len(self._connections),
)
# Send recent history to the new client
for event in self._event_history[-20:]:
for event in list(self._event_history)[-20:]:
try:
await websocket.send_text(event.to_json())
except Exception:
@@ -69,8 +69,6 @@ class WebSocketManager:
timestamp=datetime.now(timezone.utc).isoformat(),
)
self._event_history.append(ws_event)
if len(self._event_history) > self._max_history:
self._event_history = self._event_history[-self._max_history:]
message = ws_event.to_json()
disconnected = []
@@ -78,7 +76,10 @@ class WebSocketManager:
for ws in self._connections:
try:
await ws.send_text(message)
except ConnectionError:
disconnected.append(ws)
except Exception:
logger.warning("Unexpected WebSocket send error", exc_info=True)
disconnected.append(ws)
# Clean up dead connections
@@ -128,8 +129,6 @@ class WebSocketManager:
Returns:
Number of clients notified
"""
import json
message = json.dumps(data)
disconnected = []
count = 0

View File

@@ -20,7 +20,7 @@ from agno.agent import Agent
from agno.db.sqlite import SqliteDb
from agno.models.ollama import Ollama
from config import settings
from config import check_ollama_model_available, settings
from timmy.prompts import get_system_prompt
from timmy.tools import create_full_toolkit
@@ -64,27 +64,7 @@ _SMALL_MODEL_PATTERNS = (
def _check_model_available(model_name: str) -> bool:
"""Check if an Ollama model is available locally."""
try:
import urllib.request
import json
url = settings.ollama_url.replace("localhost", "127.0.0.1")
req = urllib.request.Request(
f"{url}/api/tags",
method="GET",
headers={"Accept": "application/json"},
)
with urllib.request.urlopen(req, timeout=5) as response:
data = json.loads(response.read().decode())
models = [m.get("name", "") for m in data.get("models", [])]
# Check for exact match or model name without tag
return any(
model_name == m or model_name == m.split(":")[0] or m.startswith(model_name)
for m in models
)
except Exception as exc:
logger.debug("Could not check model availability: %s", exc)
return False
return check_ollama_model_available(model_name)
def _pull_model(model_name: str) -> bool:

View File

@@ -121,67 +121,5 @@ def down():
subprocess.run(["docker", "compose", "down"], check=True)
@app.command(name="ingest-report")
def ingest_report(
file: Optional[str] = typer.Argument(
None, help="Path to JSON report file (reads stdin if omitted)",
),
dry_run: bool = typer.Option(
False, "--dry-run", help="Validate report and show what would be created",
),
):
"""Ingest a structured test report and create bug_report tasks.
Reads a JSON report with an array of bugs and creates one task per bug
in the internal task queue. The task processor will then attempt to
create GitHub Issues for each.
Examples:
timmy ingest-report report.json
timmy ingest-report --dry-run report.json
cat report.json | timmy ingest-report
"""
import json
import sys
# Read input
if file:
try:
with open(file) as f:
raw = f.read()
except FileNotFoundError:
typer.echo(f"File not found: {file}", err=True)
raise typer.Exit(1)
else:
if sys.stdin.isatty():
typer.echo("Reading from stdin (paste JSON, then Ctrl+D)...")
raw = sys.stdin.read()
# Parse JSON
try:
data = json.loads(raw)
except json.JSONDecodeError as exc:
typer.echo(f"Invalid JSON: {exc}", err=True)
raise typer.Exit(1)
reporter = data.get("reporter", "unknown")
bugs = data.get("bugs", [])
if not bugs:
typer.echo("No bugs in report.", err=True)
raise typer.Exit(1)
typer.echo(f"Report: {len(bugs)} bug(s) from {reporter}")
if dry_run:
for bug in bugs:
typer.echo(f" [{bug.get('severity', '?')}] {bug.get('title', '(no title)')}")
typer.echo("(dry run — no tasks created)")
return
typer.echo("Task queue not available (swarm module removed).", err=True)
raise typer.Exit(1)
def main():
app()