feat: run claude as non-root for bypassPermissions + replace MCP menu with live tools view
This commit is contained in:
parent
004b1efdc6
commit
318e7f01ad
1 changed files with 32 additions and 6 deletions
|
|
@ -231,6 +231,7 @@ class ClaudeProcessManager:
|
||||||
self._broadcast_queues: List[asyncio.Queue] = []
|
self._broadcast_queues: List[asyncio.Queue] = []
|
||||||
self._is_ready = False
|
self._is_ready = False
|
||||||
self._status = "not_started" # not_started, starting, ready, dead
|
self._status = "not_started" # not_started, starting, ready, dead
|
||||||
|
self._available_tools: List[dict] = [] # populated from system/init event
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def status(self):
|
def status(self):
|
||||||
|
|
@ -249,17 +250,18 @@ class ClaudeProcessManager:
|
||||||
self._status = "starting"
|
self._status = "starting"
|
||||||
logger.info("Starting Claude interactive process...")
|
logger.info("Starting Claude interactive process...")
|
||||||
|
|
||||||
|
# Run as non-root user so --permission-mode bypassPermissions is allowed
|
||||||
|
# (Claude CLI refuses bypassPermissions when running as root)
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
|
env["HOME"] = "/home/claudeuser"
|
||||||
|
|
||||||
# Build command: interactive claude with stream-json I/O
|
# Build command: interactive claude with stream-json I/O
|
||||||
# --permission-mode bypassPermissions allows the agent to use all tools
|
# --permission-mode bypassPermissions allows the agent to use all tools
|
||||||
# (Bash, file ops, MCP servers) without interactive confirmation prompts
|
# (Bash, file ops, MCP servers) without interactive confirmation prompts
|
||||||
cmd = [
|
cmd = [
|
||||||
"claude",
|
"su", "-s", "/bin/bash", "claudeuser", "-c",
|
||||||
"--output-format", "stream-json",
|
"claude --output-format stream-json --input-format stream-json "
|
||||||
"--input-format", "stream-json",
|
"--verbose --permission-mode bypassPermissions"
|
||||||
"--verbose",
|
|
||||||
"--permission-mode", "bypassPermissions",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
@ -359,7 +361,8 @@ class ClaudeProcessManager:
|
||||||
if event_type == "system" and event.get("subtype") == "init":
|
if event_type == "system" and event.get("subtype") == "init":
|
||||||
self._current_session_id = event.get("session_id")
|
self._current_session_id = event.get("session_id")
|
||||||
self._is_ready = True
|
self._is_ready = True
|
||||||
logger.info(f"Claude session initialized: {self._current_session_id}")
|
self._available_tools = event.get("tools", [])
|
||||||
|
logger.info(f"Claude session initialized: {self._current_session_id}, tools: {len(self._available_tools)}")
|
||||||
|
|
||||||
elif event_type == "result":
|
elif event_type == "result":
|
||||||
# End of a response turn
|
# End of a response turn
|
||||||
|
|
@ -636,6 +639,29 @@ async def claude_status():
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/api/claude/tools")
|
||||||
|
async def claude_tools():
|
||||||
|
"""Return the tools available in the current Claude session (from system/init)."""
|
||||||
|
tools = claude_mgr._available_tools
|
||||||
|
# Group by MCP server prefix (e.g. "mcp__truenas__list_pools" -> "truenas")
|
||||||
|
servers: dict = {}
|
||||||
|
builtin = []
|
||||||
|
for t in tools:
|
||||||
|
name = t.get("name", "")
|
||||||
|
if name.startswith("mcp__"):
|
||||||
|
parts = name.split("__")
|
||||||
|
server = parts[1] if len(parts) > 1 else "unknown"
|
||||||
|
servers.setdefault(server, []).append(name)
|
||||||
|
else:
|
||||||
|
builtin.append(name)
|
||||||
|
return {
|
||||||
|
"total": len(tools),
|
||||||
|
"mcp_servers": {k: len(v) for k, v in servers.items()},
|
||||||
|
"builtin_tools": builtin,
|
||||||
|
"raw": tools,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
# -------- Chat --------
|
# -------- Chat --------
|
||||||
|
|
||||||
@app.get("/api/chat/sessions")
|
@app.get("/api/chat/sessions")
|
||||||
|
|
|
||||||
Reference in a new issue