vibe-remote 2.1.6__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- config/__init__.py +37 -0
- config/paths.py +56 -0
- config/v2_compat.py +74 -0
- config/v2_config.py +206 -0
- config/v2_sessions.py +73 -0
- config/v2_settings.py +115 -0
- core/__init__.py +0 -0
- core/controller.py +736 -0
- core/handlers/__init__.py +13 -0
- core/handlers/command_handlers.py +342 -0
- core/handlers/message_handler.py +365 -0
- core/handlers/session_handler.py +233 -0
- core/handlers/settings_handler.py +362 -0
- modules/__init__.py +0 -0
- modules/agent_router.py +58 -0
- modules/agents/__init__.py +38 -0
- modules/agents/base.py +91 -0
- modules/agents/claude_agent.py +344 -0
- modules/agents/codex_agent.py +368 -0
- modules/agents/opencode_agent.py +2155 -0
- modules/agents/service.py +41 -0
- modules/agents/subagent_router.py +136 -0
- modules/claude_client.py +154 -0
- modules/im/__init__.py +63 -0
- modules/im/base.py +323 -0
- modules/im/factory.py +60 -0
- modules/im/formatters/__init__.py +4 -0
- modules/im/formatters/base_formatter.py +639 -0
- modules/im/formatters/slack_formatter.py +127 -0
- modules/im/slack.py +2091 -0
- modules/session_manager.py +138 -0
- modules/settings_manager.py +587 -0
- vibe/__init__.py +6 -0
- vibe/__main__.py +12 -0
- vibe/_version.py +34 -0
- vibe/api.py +412 -0
- vibe/cli.py +637 -0
- vibe/runtime.py +213 -0
- vibe/service_main.py +101 -0
- vibe/templates/slack_manifest.json +65 -0
- vibe/ui/dist/assets/index-8g3mNwMK.js +35 -0
- vibe/ui/dist/assets/index-M55aMB5R.css +1 -0
- vibe/ui/dist/assets/logo-BzryTZ7u.png +0 -0
- vibe/ui/dist/index.html +17 -0
- vibe/ui/dist/logo.png +0 -0
- vibe/ui/dist/vite.svg +1 -0
- vibe/ui_server.py +346 -0
- vibe_remote-2.1.6.dist-info/METADATA +295 -0
- vibe_remote-2.1.6.dist-info/RECORD +52 -0
- vibe_remote-2.1.6.dist-info/WHEEL +4 -0
- vibe_remote-2.1.6.dist-info/entry_points.txt +2 -0
- vibe_remote-2.1.6.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import Dict, List, Optional, Union, Any
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from claude_code_sdk import ClaudeSDKClient
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class UserSession:
|
|
15
|
+
user_id: Union[int, str]
|
|
16
|
+
chat_id: Union[int, str]
|
|
17
|
+
is_executing: bool = False
|
|
18
|
+
created_at: datetime = field(default_factory=datetime.now)
|
|
19
|
+
last_activity: datetime = field(default_factory=datetime.now)
|
|
20
|
+
# Map of session_id to ClaudeSDKClient instance
|
|
21
|
+
claude_clients: Dict[str, ClaudeSDKClient] = field(default_factory=dict)
|
|
22
|
+
# Map of session_id to message receiver task
|
|
23
|
+
receiver_tasks: Dict[str, asyncio.Task] = field(default_factory=dict)
|
|
24
|
+
# Map of session_id to boolean indicating if session is waiting for result
|
|
25
|
+
session_active: Dict[str, bool] = field(default_factory=dict)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
async def cleanup_clients(self):
|
|
29
|
+
"""Cleanup all Claude SDK clients and receiver tasks"""
|
|
30
|
+
# Cancel all receiver tasks first
|
|
31
|
+
for session_id, task in self.receiver_tasks.items():
|
|
32
|
+
if not task.done():
|
|
33
|
+
task.cancel()
|
|
34
|
+
try:
|
|
35
|
+
await task
|
|
36
|
+
except asyncio.CancelledError:
|
|
37
|
+
pass
|
|
38
|
+
logger.info(f"Cancelled receiver task for session {session_id}")
|
|
39
|
+
|
|
40
|
+
# Then disconnect clients
|
|
41
|
+
for session_id, client in self.claude_clients.items():
|
|
42
|
+
try:
|
|
43
|
+
await client.disconnect()
|
|
44
|
+
logger.info(f"Disconnected Claude client for session {session_id}")
|
|
45
|
+
except Exception as e:
|
|
46
|
+
logger.error(f"Error disconnecting Claude client for session {session_id}: {e}")
|
|
47
|
+
|
|
48
|
+
self.receiver_tasks.clear()
|
|
49
|
+
|
|
50
|
+
def get_status(self) -> str:
|
|
51
|
+
"""Get session status summary"""
|
|
52
|
+
status = f"š Session Status\n"
|
|
53
|
+
status += f"āāāāāāāāāāāāāāāā\n"
|
|
54
|
+
status += f"User ID: {self.user_id}\n"
|
|
55
|
+
status += f"Active sessions: {len(self.claude_clients)}\n"
|
|
56
|
+
status += f"Status: {'š¢ Connected' if self.claude_clients else 'ā No active session'}\n"
|
|
57
|
+
status += f"Last activity: {self.last_activity.strftime('%Y-%m-%d %H:%M:%S')}"
|
|
58
|
+
|
|
59
|
+
if self.claude_clients:
|
|
60
|
+
status += "\n\nš Active Claude sessions:"
|
|
61
|
+
for session_id in self.claude_clients:
|
|
62
|
+
status += f"\n⢠{session_id}"
|
|
63
|
+
else:
|
|
64
|
+
status += "\n\nš¬ Send a message to start a conversation"
|
|
65
|
+
|
|
66
|
+
return status
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class SessionManager:
|
|
70
|
+
def __init__(self):
|
|
71
|
+
self.sessions: Dict[Union[int, str], UserSession] = {}
|
|
72
|
+
self._lock = asyncio.Lock()
|
|
73
|
+
|
|
74
|
+
async def get_or_create_session(self, user_id: Union[int, str], chat_id: Union[int, str]) -> UserSession:
|
|
75
|
+
"""Get existing session or create new one"""
|
|
76
|
+
async with self._lock:
|
|
77
|
+
if user_id not in self.sessions:
|
|
78
|
+
self.sessions[user_id] = UserSession(user_id=user_id, chat_id=chat_id)
|
|
79
|
+
logger.info(f"Created new session for user {user_id}")
|
|
80
|
+
|
|
81
|
+
return self.sessions[user_id]
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
async def clear_session(self, user_id: Union[int, str]) -> str:
|
|
85
|
+
"""Clear user's session and disconnect all Claude clients"""
|
|
86
|
+
if user_id not in self.sessions:
|
|
87
|
+
return "No active session found."
|
|
88
|
+
|
|
89
|
+
session = self.sessions[user_id]
|
|
90
|
+
|
|
91
|
+
# Cleanup all Claude SDK clients and receiver tasks
|
|
92
|
+
client_count = len(session.claude_clients)
|
|
93
|
+
await session.cleanup_clients()
|
|
94
|
+
session.claude_clients.clear()
|
|
95
|
+
session.receiver_tasks.clear()
|
|
96
|
+
|
|
97
|
+
return f"Cleared {client_count} active Claude session(s)."
|
|
98
|
+
|
|
99
|
+
async def get_status(self, user_id: Union[int, str]) -> str:
|
|
100
|
+
"""Get user's session status"""
|
|
101
|
+
if user_id not in self.sessions:
|
|
102
|
+
return "No active session. Send a message to start."
|
|
103
|
+
|
|
104
|
+
session = self.sessions[user_id]
|
|
105
|
+
return session.get_status()
|
|
106
|
+
|
|
107
|
+
async def set_executing(self, user_id: Union[int, str], is_executing: bool):
|
|
108
|
+
"""Set execution status for user session"""
|
|
109
|
+
if user_id in self.sessions:
|
|
110
|
+
async with self._lock:
|
|
111
|
+
self.sessions[user_id].is_executing = is_executing
|
|
112
|
+
self.sessions[user_id].last_activity = datetime.now()
|
|
113
|
+
|
|
114
|
+
async def is_executing(self, user_id: Union[int, str]) -> bool:
|
|
115
|
+
"""Check if user has an active execution"""
|
|
116
|
+
if user_id not in self.sessions:
|
|
117
|
+
return False
|
|
118
|
+
return self.sessions[user_id].is_executing
|
|
119
|
+
|
|
120
|
+
async def cleanup_inactive_sessions(self, inactive_hours: int = 24):
|
|
121
|
+
"""Clean up inactive sessions"""
|
|
122
|
+
async with self._lock:
|
|
123
|
+
current_time = datetime.now()
|
|
124
|
+
to_remove = []
|
|
125
|
+
|
|
126
|
+
for user_id, session in self.sessions.items():
|
|
127
|
+
time_diff = current_time - session.last_activity
|
|
128
|
+
if time_diff.total_seconds() > inactive_hours * 3600:
|
|
129
|
+
to_remove.append(user_id)
|
|
130
|
+
|
|
131
|
+
for user_id in to_remove:
|
|
132
|
+
session = self.sessions[user_id]
|
|
133
|
+
# Cleanup Claude SDK clients before removing session
|
|
134
|
+
await session.cleanup_clients()
|
|
135
|
+
del self.sessions[user_id]
|
|
136
|
+
logger.info(f"Cleaned up inactive session for user {user_id}")
|
|
137
|
+
|
|
138
|
+
return len(to_remove)
|