feat: role-based tool access, confirmation gates, and new orchestrator tools
- auth_utils: get_user_role() reads role from auth.json (admin|user, default user) - manage_passwords: new `role` command to promote/demote users (admin-only by convention) - tools/__init__: TOOL_ROLES map, CONFIRM_REQUIRED set, get_tools_for_role(), get_openai_tools_for_role() — both orchestrators now filter tools by caller's role - tools/system: cortex_restart (detached subprocess, 5s delay), cortex_logs (admin-only) - tools/web: http_fetch — direct URL fetch, distinct from web_search - tools/files: file_list (directory listing), file_write (restricted paths, admin-only) - tools/notify: nc_talk_send — proactive outbound via notification.py - orchestrator_engine + openai_orchestrator: user_role param; CONFIRM_REQUIRED tools return a confirmation-request result instead of executing — loop breaks after Claude asks user to confirm in a follow-up message - home/scott/auth.json: role set to admin Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
"""
|
||||
Web search tool — DuckDuckGo backend.
|
||||
|
||||
Uses the duckduckgo-search library. Set DDG_API_KEY in .env for a paid account
|
||||
(higher rate limits). The free unauthenticated tier works for moderate usage.
|
||||
Web tools — search (DuckDuckGo) and direct HTTP fetch.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
import httpx
|
||||
|
||||
from config import settings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -48,3 +48,29 @@ def _sync_search(query: str, max_results: int) -> list[dict]:
|
||||
except Exception as e:
|
||||
logger.warning("DuckDuckGo search error: %s", e)
|
||||
return []
|
||||
|
||||
|
||||
async def http_fetch(
|
||||
url: str,
|
||||
method: str = "GET",
|
||||
body: str | None = None,
|
||||
timeout: int = 15,
|
||||
) -> str:
|
||||
"""Fetch a URL directly and return the response body.
|
||||
|
||||
Unlike web_search, this hits a specific URL — useful for health checks,
|
||||
API probing, JSON endpoints, webhook testing, etc.
|
||||
Response body is capped at 8 KB.
|
||||
"""
|
||||
method = method.upper()
|
||||
timeout = min(max(int(timeout), 1), 60)
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=timeout, follow_redirects=True) as client:
|
||||
resp = await client.request(method, url, content=body)
|
||||
body_text = resp.text[:8192]
|
||||
return f"HTTP {resp.status_code} {resp.url}\n\n{body_text}"
|
||||
except httpx.HTTPError as e:
|
||||
return f"HTTP error: {e}"
|
||||
except Exception as e:
|
||||
logger.warning("http_fetch error for %s: %s", url, e)
|
||||
return f"Error: {e}"
|
||||
|
||||
Reference in New Issue
Block a user