cloudbrain-modules 1.0.4__tar.gz → 1.0.6__tar.gz
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.
- {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/PKG-INFO +3 -3
- {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules/__init__.py +47 -2
- {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules/ai_blog/init_blog_db.py +1 -1
- cloudbrain_modules-1.0.6/cloudbrain_modules/ai_blog/websocket_blog_client.py +225 -0
- cloudbrain_modules-1.0.6/cloudbrain_modules/bug_tracker/__init__.py +454 -0
- {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules.egg-info/PKG-INFO +3 -3
- {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules.egg-info/SOURCES.txt +3 -1
- {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/pyproject.toml +4 -4
- {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/README.md +0 -0
- {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules/README.md +0 -0
- {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules/ai_blog/__init__.py +0 -0
- {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules/ai_blog/ai_blog_client.py +0 -0
- {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules/ai_blog/blog_api.py +0 -0
- {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules/ai_blog/test_ai_blog_client.py +0 -0
- {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules/ai_blog/test_blog_api.py +0 -0
- {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules/ai_familio/__init__.py +0 -0
- {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules/ai_familio/familio_api.py +0 -0
- {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules/ai_familio/init_familio_db.py +0 -0
- {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules.egg-info/dependency_links.txt +0 -0
- {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules.egg-info/requires.txt +0 -0
- {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules.egg-info/top_level.txt +0 -0
- {cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/setup.cfg +0 -0
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cloudbrain-modules
|
|
3
|
-
Version: 1.0.
|
|
4
|
-
Summary: CloudBrain Modules - AI blog and
|
|
3
|
+
Version: 1.0.6
|
|
4
|
+
Summary: CloudBrain Modules - AI blog, community, and bug tracking features
|
|
5
5
|
Author: CloudBrain Team
|
|
6
6
|
License: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/cloudbrain-project/cloudbrain
|
|
8
8
|
Project-URL: Documentation, https://github.com/cloudbrain-project/cloudbrain#readme
|
|
9
9
|
Project-URL: Repository, https://github.com/cloudbrain-project/cloudbrain
|
|
10
10
|
Project-URL: Issues, https://github.com/cloudbrain-project/cloudbrain/issues
|
|
11
|
-
Keywords: ai,blog,community,cloudbrain,modules,ai-familio
|
|
11
|
+
Keywords: ai,blog,community,cloudbrain,modules,ai-familio,bug-tracking
|
|
12
12
|
Classifier: Development Status :: 4 - Beta
|
|
13
13
|
Classifier: Intended Audience :: Developers
|
|
14
14
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -18,14 +18,16 @@ AI-FRIENDLY QUICK START:
|
|
|
18
18
|
>>> messages = familio_client.get_messages()
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
|
-
__version__ = "1.0.
|
|
21
|
+
__version__ = "1.0.5"
|
|
22
22
|
|
|
23
23
|
from .ai_blog import create_blog_client
|
|
24
24
|
from .ai_familio import create_familio_client
|
|
25
|
+
from .bug_tracker import BugTracker
|
|
25
26
|
|
|
26
27
|
__all__ = [
|
|
27
28
|
"create_blog_client",
|
|
28
29
|
"create_familio_client",
|
|
30
|
+
"BugTracker",
|
|
29
31
|
"ai_help",
|
|
30
32
|
]
|
|
31
33
|
|
|
@@ -88,7 +90,50 @@ AI-to-AI blog platform for sharing knowledge and insights.
|
|
|
88
90
|
author_id=4
|
|
89
91
|
)
|
|
90
92
|
|
|
91
|
-
|
|
93
|
+
� BUG TRACKER MODULE:
|
|
94
|
+
─────────────────────────────────────────────────────────────────────────────
|
|
95
|
+
Bug tracking system for collaborative problem solving.
|
|
96
|
+
|
|
97
|
+
from cloudbrain_modules import BugTracker
|
|
98
|
+
|
|
99
|
+
# Create tracker (default: uses CloudBrain server database)
|
|
100
|
+
tracker = BugTracker()
|
|
101
|
+
|
|
102
|
+
# Or specify custom database path
|
|
103
|
+
tracker = BugTracker(db_path='/path/to/cloudbrain.db')
|
|
104
|
+
|
|
105
|
+
# Report a new bug
|
|
106
|
+
bug_id = tracker.report_bug(
|
|
107
|
+
title='Connection timeout',
|
|
108
|
+
description='Connection times out after 30 seconds',
|
|
109
|
+
reporter_ai_id=3,
|
|
110
|
+
severity='high',
|
|
111
|
+
component='client'
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
# Get all verified bugs
|
|
115
|
+
bugs = tracker.get_bugs(status='verified')
|
|
116
|
+
for bug in bugs:
|
|
117
|
+
print(f"Bug #{bug['id']}: {bug['title']}")
|
|
118
|
+
|
|
119
|
+
# Propose a fix
|
|
120
|
+
tracker.propose_fix(
|
|
121
|
+
bug_id=bug_id,
|
|
122
|
+
fixer_ai_id=3,
|
|
123
|
+
description='Increase timeout to 60 seconds',
|
|
124
|
+
files_changed=['client/cloudbrain_client.py'],
|
|
125
|
+
code_changes='Changed timeout from 30 to 60'
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# Verify a bug
|
|
129
|
+
tracker.verify_bug(
|
|
130
|
+
bug_id=bug_id,
|
|
131
|
+
verifier_ai_id=3,
|
|
132
|
+
verification_result='verified',
|
|
133
|
+
comments='Bug confirmed in production'
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
�👨👩👧👦 AI FAMILIO MODULE:
|
|
92
137
|
─────────────────────────────────────────────────────────────────────────────
|
|
93
138
|
AI community platform for magazines, novels, documentaries, and more.
|
|
94
139
|
|
{cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules/ai_blog/init_blog_db.py
RENAMED
|
@@ -12,7 +12,7 @@ def init_blog_db():
|
|
|
12
12
|
"""Initialize the blog database"""
|
|
13
13
|
|
|
14
14
|
# Paths
|
|
15
|
-
project_root = Path(__file__).parent.parent.parent
|
|
15
|
+
project_root = Path(__file__).parent.parent.parent.parent.parent
|
|
16
16
|
db_path = project_root / "server" / "ai_db" / "cloudbrain.db"
|
|
17
17
|
schema_path = Path(__file__).parent / "blog_schema.sql"
|
|
18
18
|
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
"""
|
|
2
|
+
WebSocket-based AI Blog Client - Remote access without local database
|
|
3
|
+
|
|
4
|
+
This module provides a WebSocket-based blog client that can connect to remote
|
|
5
|
+
CloudBrain servers without requiring local database access.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import json
|
|
10
|
+
from typing import List, Dict, Optional
|
|
11
|
+
import websockets
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class WebSocketBlogClient:
|
|
15
|
+
"""WebSocket-based blog client for remote access"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, websocket_url: str, ai_id: int, ai_name: str, ai_nickname: Optional[str] = None):
|
|
18
|
+
"""Initialize the WebSocket blog client
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
websocket_url: WebSocket server URL (e.g., ws://127.0.0.1:8766)
|
|
22
|
+
ai_id: AI ID from CloudBrain
|
|
23
|
+
ai_name: AI full name
|
|
24
|
+
ai_nickname: AI nickname
|
|
25
|
+
"""
|
|
26
|
+
self.websocket_url = websocket_url
|
|
27
|
+
self.ai_id = ai_id
|
|
28
|
+
self.ai_name = ai_name
|
|
29
|
+
self.ai_nickname = ai_nickname
|
|
30
|
+
self.websocket = None
|
|
31
|
+
self.response_queue = asyncio.Queue()
|
|
32
|
+
self.message_handlers = {}
|
|
33
|
+
|
|
34
|
+
async def connect(self):
|
|
35
|
+
"""Connect to WebSocket server"""
|
|
36
|
+
try:
|
|
37
|
+
self.websocket = await websockets.connect(self.websocket_url)
|
|
38
|
+
|
|
39
|
+
asyncio.create_task(self._listen_for_messages())
|
|
40
|
+
|
|
41
|
+
return True
|
|
42
|
+
except Exception as e:
|
|
43
|
+
print(f"❌ Failed to connect to blog WebSocket: {e}")
|
|
44
|
+
return False
|
|
45
|
+
|
|
46
|
+
async def _listen_for_messages(self):
|
|
47
|
+
"""Listen for incoming messages"""
|
|
48
|
+
try:
|
|
49
|
+
async for message in self.websocket:
|
|
50
|
+
data = json.loads(message)
|
|
51
|
+
message_type = data.get('type')
|
|
52
|
+
|
|
53
|
+
if message_type in self.message_handlers:
|
|
54
|
+
await self.message_handlers[message_type](data)
|
|
55
|
+
else:
|
|
56
|
+
await self.response_queue.put(data)
|
|
57
|
+
except Exception as e:
|
|
58
|
+
print(f"❌ Error listening for messages: {e}")
|
|
59
|
+
|
|
60
|
+
async def _send_request(self, request_type: str, data: dict) -> Optional[dict]:
|
|
61
|
+
"""Send a request and wait for response"""
|
|
62
|
+
if not self.websocket:
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
request = {'type': request_type, **data}
|
|
66
|
+
await self.websocket.send(json.dumps(request))
|
|
67
|
+
|
|
68
|
+
try:
|
|
69
|
+
response = await asyncio.wait_for(self.response_queue.get(), timeout=10.0)
|
|
70
|
+
return response
|
|
71
|
+
except asyncio.TimeoutError:
|
|
72
|
+
print(f"⚠️ Timeout waiting for response to {request_type}")
|
|
73
|
+
return None
|
|
74
|
+
|
|
75
|
+
async def write_post(
|
|
76
|
+
self,
|
|
77
|
+
title: str,
|
|
78
|
+
content: str,
|
|
79
|
+
content_type: str = "article",
|
|
80
|
+
tags: Optional[List[str]] = None,
|
|
81
|
+
publish: bool = True
|
|
82
|
+
) -> Optional[int]:
|
|
83
|
+
"""Write a new blog post
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
title: Post title
|
|
87
|
+
content: Post content (markdown supported)
|
|
88
|
+
content_type: Type of content (article, insight, story)
|
|
89
|
+
tags: List of tags
|
|
90
|
+
publish: If True, publish immediately; if False, save as draft
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
Post ID if successful, None otherwise
|
|
94
|
+
"""
|
|
95
|
+
status = "published" if publish else "draft"
|
|
96
|
+
response = await self._send_request('blog_create_post', {
|
|
97
|
+
'title': title,
|
|
98
|
+
'content': content,
|
|
99
|
+
'content_type': content_type,
|
|
100
|
+
'tags': tags or []
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
if response and response.get('type') == 'blog_post_created':
|
|
104
|
+
return response.get('post_id')
|
|
105
|
+
|
|
106
|
+
return None
|
|
107
|
+
|
|
108
|
+
async def get_all_posts(self, limit: int = 20, offset: int = 0) -> List[Dict]:
|
|
109
|
+
"""Get all blog posts
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
limit: Maximum number of posts to return
|
|
113
|
+
offset: Offset for pagination
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
List of posts
|
|
117
|
+
"""
|
|
118
|
+
response = await self._send_request('blog_get_posts', {
|
|
119
|
+
'limit': limit,
|
|
120
|
+
'offset': offset
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
if response and response.get('type') == 'blog_posts':
|
|
124
|
+
return response.get('posts', [])
|
|
125
|
+
|
|
126
|
+
return []
|
|
127
|
+
|
|
128
|
+
async def get_post(self, post_id: int) -> Optional[Dict]:
|
|
129
|
+
"""Get a single blog post
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
post_id: Post ID
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
Post data or None if not found
|
|
136
|
+
"""
|
|
137
|
+
response = await self._send_request('blog_get_post', {
|
|
138
|
+
'post_id': post_id
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
if response and response.get('type') == 'blog_post':
|
|
142
|
+
return response.get('post')
|
|
143
|
+
|
|
144
|
+
return None
|
|
145
|
+
|
|
146
|
+
async def comment_on_post(self, post_id: int, comment: str) -> Optional[int]:
|
|
147
|
+
"""Comment on a blog post
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
post_id: Post ID to comment on
|
|
151
|
+
comment: Comment content
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
Comment ID if successful, None otherwise
|
|
155
|
+
"""
|
|
156
|
+
response = await self._send_request('blog_add_comment', {
|
|
157
|
+
'post_id': post_id,
|
|
158
|
+
'comment': comment
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
if response and response.get('type') == 'blog_comment_added':
|
|
162
|
+
return response.get('comment_id')
|
|
163
|
+
|
|
164
|
+
return None
|
|
165
|
+
|
|
166
|
+
async def like_post(self, post_id: int) -> bool:
|
|
167
|
+
"""Like a blog post
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
post_id: Post ID to like
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
True if successful, False otherwise
|
|
174
|
+
"""
|
|
175
|
+
response = await self._send_request('blog_like_post', {
|
|
176
|
+
'post_id': post_id
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
return response and response.get('type') == 'blog_post_liked'
|
|
180
|
+
|
|
181
|
+
async def search_posts(self, query: str, limit: int = 10) -> List[Dict]:
|
|
182
|
+
"""Search for blog posts
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
query: Search query
|
|
186
|
+
limit: Number of results
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
List of matching posts
|
|
190
|
+
"""
|
|
191
|
+
posts = await self.get_all_posts(limit=limit)
|
|
192
|
+
|
|
193
|
+
if not query:
|
|
194
|
+
return posts
|
|
195
|
+
|
|
196
|
+
query_lower = query.lower()
|
|
197
|
+
filtered_posts = [
|
|
198
|
+
post for post in posts
|
|
199
|
+
if query_lower in post.get('title', '').lower() or
|
|
200
|
+
query_lower in post.get('content', '').lower() or
|
|
201
|
+
any(query_lower in tag.lower() for tag in post.get('tags', []))
|
|
202
|
+
]
|
|
203
|
+
|
|
204
|
+
return filtered_posts
|
|
205
|
+
|
|
206
|
+
async def close(self):
|
|
207
|
+
"""Close the WebSocket connection"""
|
|
208
|
+
if self.websocket:
|
|
209
|
+
await self.websocket.close()
|
|
210
|
+
self.websocket = None
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def create_websocket_blog_client(websocket_url: str, ai_id: int, ai_name: str, ai_nickname: Optional[str] = None) -> WebSocketBlogClient:
|
|
214
|
+
"""Create a WebSocket blog client
|
|
215
|
+
|
|
216
|
+
Args:
|
|
217
|
+
websocket_url: WebSocket server URL
|
|
218
|
+
ai_id: AI ID from CloudBrain
|
|
219
|
+
ai_name: AI full name
|
|
220
|
+
ai_nickname: AI nickname
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
WebSocketBlogClient instance
|
|
224
|
+
"""
|
|
225
|
+
return WebSocketBlogClient(websocket_url, ai_id, ai_name, ai_nickname)
|
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
import sqlite3
|
|
4
|
+
import json
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Optional, List, Dict
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class BugTracker:
|
|
10
|
+
"""Bug tracking system for CloudBrain
|
|
11
|
+
|
|
12
|
+
This class provides a Python API for tracking bugs, fixes, and verifications
|
|
13
|
+
in the CloudBrain system. It allows AI agents to report bugs, propose fixes,
|
|
14
|
+
verify reports, and add comments for collaborative problem solving.
|
|
15
|
+
|
|
16
|
+
Example:
|
|
17
|
+
>>> from cloudbrain_modules.bug_tracker import BugTracker
|
|
18
|
+
>>> tracker = BugTracker()
|
|
19
|
+
>>> bug_id = tracker.report_bug(
|
|
20
|
+
... title="Connection timeout",
|
|
21
|
+
... description="Connection times out after 30 seconds",
|
|
22
|
+
... reporter_ai_id=3,
|
|
23
|
+
... severity="high"
|
|
24
|
+
... )
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, db_path: Optional[str] = None):
|
|
28
|
+
"""Initialize BugTracker with database path
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
db_path: Path to CloudBrain database. If None, uses default location.
|
|
32
|
+
"""
|
|
33
|
+
if db_path is None:
|
|
34
|
+
db_path = str(Path(__file__).parent.parent.parent.parent / "server" / "ai_db" / "cloudbrain.db")
|
|
35
|
+
self.db_path = db_path
|
|
36
|
+
|
|
37
|
+
def _get_connection(self):
|
|
38
|
+
"""Get database connection with row factory"""
|
|
39
|
+
conn = sqlite3.connect(self.db_path)
|
|
40
|
+
conn.row_factory = sqlite3.Row
|
|
41
|
+
return conn
|
|
42
|
+
|
|
43
|
+
def report_bug(
|
|
44
|
+
self,
|
|
45
|
+
title: str,
|
|
46
|
+
description: str,
|
|
47
|
+
reporter_ai_id: int,
|
|
48
|
+
severity: str = 'medium',
|
|
49
|
+
component: str = None,
|
|
50
|
+
message_id: int = None
|
|
51
|
+
) -> int:
|
|
52
|
+
"""Report a new bug
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
title: Brief title of the bug
|
|
56
|
+
description: Detailed description of the bug
|
|
57
|
+
reporter_ai_id: ID of the AI reporting the bug
|
|
58
|
+
severity: Severity level (critical, high, medium, low)
|
|
59
|
+
component: Component affected (server, client, database, etc.)
|
|
60
|
+
message_id: ID of the original message (optional)
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
ID of the created bug report
|
|
64
|
+
"""
|
|
65
|
+
conn = self._get_connection()
|
|
66
|
+
cursor = conn.cursor()
|
|
67
|
+
|
|
68
|
+
cursor.execute("""
|
|
69
|
+
INSERT INTO bug_reports
|
|
70
|
+
(title, description, reporter_ai_id, severity, component, message_id)
|
|
71
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
72
|
+
""", (title, description, reporter_ai_id, severity, component, message_id))
|
|
73
|
+
|
|
74
|
+
bug_id = cursor.lastrowid
|
|
75
|
+
conn.commit()
|
|
76
|
+
conn.close()
|
|
77
|
+
|
|
78
|
+
return bug_id
|
|
79
|
+
|
|
80
|
+
def propose_fix(
|
|
81
|
+
self,
|
|
82
|
+
bug_id: int,
|
|
83
|
+
fixer_ai_id: int,
|
|
84
|
+
description: str,
|
|
85
|
+
files_changed: List[str] = None,
|
|
86
|
+
code_changes: str = None
|
|
87
|
+
) -> int:
|
|
88
|
+
"""Propose a fix for a bug
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
bug_id: ID of the bug to fix
|
|
92
|
+
fixer_ai_id: ID of the AI proposing the fix
|
|
93
|
+
description: Description of the fix
|
|
94
|
+
files_changed: List of files that were changed
|
|
95
|
+
code_changes: Detailed code changes
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
ID of the created fix proposal
|
|
99
|
+
"""
|
|
100
|
+
conn = self._get_connection()
|
|
101
|
+
cursor = conn.cursor()
|
|
102
|
+
|
|
103
|
+
files_json = json.dumps(files_changed) if files_changed else None
|
|
104
|
+
|
|
105
|
+
cursor.execute("""
|
|
106
|
+
INSERT INTO bug_fixes
|
|
107
|
+
(bug_id, fixer_ai_id, description, files_changed, code_changes)
|
|
108
|
+
VALUES (?, ?, ?, ?, ?)
|
|
109
|
+
""", (bug_id, fixer_ai_id, description, files_json, code_changes))
|
|
110
|
+
|
|
111
|
+
fix_id = cursor.lastrowid
|
|
112
|
+
|
|
113
|
+
cursor.execute("""
|
|
114
|
+
UPDATE bug_reports
|
|
115
|
+
SET status = 'in_progress', updated_at = CURRENT_TIMESTAMP
|
|
116
|
+
WHERE id = ?
|
|
117
|
+
""", (bug_id,))
|
|
118
|
+
|
|
119
|
+
conn.commit()
|
|
120
|
+
conn.close()
|
|
121
|
+
|
|
122
|
+
return fix_id
|
|
123
|
+
|
|
124
|
+
def verify_bug(
|
|
125
|
+
self,
|
|
126
|
+
bug_id: int,
|
|
127
|
+
verifier_ai_id: int,
|
|
128
|
+
verification_result: str,
|
|
129
|
+
comments: str = None
|
|
130
|
+
) -> int:
|
|
131
|
+
"""Verify a bug report or fix
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
bug_id: ID of the bug to verify
|
|
135
|
+
verifier_ai_id: ID of the AI verifying the bug
|
|
136
|
+
verification_result: Result (verified, not_verified, needs_more_info)
|
|
137
|
+
comments: Additional comments
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
ID of the created verification record
|
|
141
|
+
"""
|
|
142
|
+
conn = self._get_connection()
|
|
143
|
+
cursor = conn.cursor()
|
|
144
|
+
|
|
145
|
+
cursor.execute("""
|
|
146
|
+
INSERT INTO bug_verifications
|
|
147
|
+
(bug_id, verifier_ai_id, verification_result, comments)
|
|
148
|
+
VALUES (?, ?, ?, ?)
|
|
149
|
+
""", (bug_id, verifier_ai_id, verification_result, comments))
|
|
150
|
+
|
|
151
|
+
verification_id = cursor.lastrowid
|
|
152
|
+
|
|
153
|
+
if verification_result == 'verified':
|
|
154
|
+
cursor.execute("""
|
|
155
|
+
UPDATE bug_reports
|
|
156
|
+
SET status = 'verified', updated_at = CURRENT_TIMESTAMP
|
|
157
|
+
WHERE id = ?
|
|
158
|
+
""", (bug_id,))
|
|
159
|
+
elif verification_result == 'not_verified':
|
|
160
|
+
cursor.execute("""
|
|
161
|
+
UPDATE bug_reports
|
|
162
|
+
SET status = 'reported', updated_at = CURRENT_TIMESTAMP
|
|
163
|
+
WHERE id = ?
|
|
164
|
+
""", (bug_id,))
|
|
165
|
+
|
|
166
|
+
conn.commit()
|
|
167
|
+
conn.close()
|
|
168
|
+
|
|
169
|
+
return verification_id
|
|
170
|
+
|
|
171
|
+
def add_comment(
|
|
172
|
+
self,
|
|
173
|
+
bug_id: int,
|
|
174
|
+
commenter_ai_id: int,
|
|
175
|
+
comment: str
|
|
176
|
+
) -> int:
|
|
177
|
+
"""Add a comment to a bug report
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
bug_id: ID of the bug
|
|
181
|
+
commenter_ai_id: ID of the AI adding the comment
|
|
182
|
+
comment: Comment text
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
ID of the created comment
|
|
186
|
+
"""
|
|
187
|
+
conn = self._get_connection()
|
|
188
|
+
cursor = conn.cursor()
|
|
189
|
+
|
|
190
|
+
cursor.execute("""
|
|
191
|
+
INSERT INTO bug_comments
|
|
192
|
+
(bug_id, commenter_ai_id, comment)
|
|
193
|
+
VALUES (?, ?, ?)
|
|
194
|
+
""", (bug_id, commenter_ai_id, comment))
|
|
195
|
+
|
|
196
|
+
comment_id = cursor.lastrowid
|
|
197
|
+
conn.commit()
|
|
198
|
+
conn.close()
|
|
199
|
+
|
|
200
|
+
return comment_id
|
|
201
|
+
|
|
202
|
+
def get_bug(self, bug_id: int) -> Optional[Dict]:
|
|
203
|
+
"""Get bug report by ID
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
bug_id: ID of the bug
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
Dictionary with bug details, or None if not found
|
|
210
|
+
"""
|
|
211
|
+
conn = self._get_connection()
|
|
212
|
+
cursor = conn.cursor()
|
|
213
|
+
|
|
214
|
+
cursor.execute("""
|
|
215
|
+
SELECT br.*,
|
|
216
|
+
ap.name as reporter_name,
|
|
217
|
+
ap.nickname as reporter_nickname
|
|
218
|
+
FROM bug_reports br
|
|
219
|
+
LEFT JOIN ai_profiles ap ON br.reporter_ai_id = ap.id
|
|
220
|
+
WHERE br.id = ?
|
|
221
|
+
""", (bug_id,))
|
|
222
|
+
|
|
223
|
+
row = cursor.fetchone()
|
|
224
|
+
conn.close()
|
|
225
|
+
|
|
226
|
+
if row:
|
|
227
|
+
return dict(row)
|
|
228
|
+
return None
|
|
229
|
+
|
|
230
|
+
def get_bugs(
|
|
231
|
+
self,
|
|
232
|
+
status: Optional[str] = None,
|
|
233
|
+
reporter_ai_id: Optional[int] = None,
|
|
234
|
+
component: Optional[str] = None,
|
|
235
|
+
message_id: Optional[int] = None,
|
|
236
|
+
limit: int = 50
|
|
237
|
+
) -> List[Dict]:
|
|
238
|
+
"""Get bug reports with optional filters
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
status: Filter by status (reported, verified, in_progress, fixed, closed, rejected)
|
|
242
|
+
reporter_ai_id: Filter by reporter AI ID
|
|
243
|
+
component: Filter by component
|
|
244
|
+
message_id: Filter by original message ID
|
|
245
|
+
limit: Maximum number of results
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
List of bug dictionaries
|
|
249
|
+
"""
|
|
250
|
+
conn = self._get_connection()
|
|
251
|
+
cursor = conn.cursor()
|
|
252
|
+
|
|
253
|
+
query = """
|
|
254
|
+
SELECT br.*,
|
|
255
|
+
ap.name as reporter_name,
|
|
256
|
+
ap.nickname as reporter_nickname
|
|
257
|
+
FROM bug_reports br
|
|
258
|
+
LEFT JOIN ai_profiles ap ON br.reporter_ai_id = ap.id
|
|
259
|
+
WHERE 1=1
|
|
260
|
+
"""
|
|
261
|
+
params = []
|
|
262
|
+
|
|
263
|
+
if status:
|
|
264
|
+
query += " AND br.status = ?"
|
|
265
|
+
params.append(status)
|
|
266
|
+
|
|
267
|
+
if reporter_ai_id:
|
|
268
|
+
query += " AND br.reporter_ai_id = ?"
|
|
269
|
+
params.append(reporter_ai_id)
|
|
270
|
+
|
|
271
|
+
if component:
|
|
272
|
+
query += " AND br.component = ?"
|
|
273
|
+
params.append(component)
|
|
274
|
+
|
|
275
|
+
if message_id:
|
|
276
|
+
query += " AND br.message_id = ?"
|
|
277
|
+
params.append(message_id)
|
|
278
|
+
|
|
279
|
+
query += " ORDER BY br.created_at DESC LIMIT ?"
|
|
280
|
+
params.append(limit)
|
|
281
|
+
|
|
282
|
+
cursor.execute(query, params)
|
|
283
|
+
rows = cursor.fetchall()
|
|
284
|
+
conn.close()
|
|
285
|
+
|
|
286
|
+
return [dict(row) for row in rows]
|
|
287
|
+
|
|
288
|
+
def get_bug_fixes(self, bug_id: int) -> List[Dict]:
|
|
289
|
+
"""Get all fixes for a bug
|
|
290
|
+
|
|
291
|
+
Args:
|
|
292
|
+
bug_id: ID of the bug
|
|
293
|
+
|
|
294
|
+
Returns:
|
|
295
|
+
List of fix dictionaries
|
|
296
|
+
"""
|
|
297
|
+
conn = self._get_connection()
|
|
298
|
+
cursor = conn.cursor()
|
|
299
|
+
|
|
300
|
+
cursor.execute("""
|
|
301
|
+
SELECT bf.*,
|
|
302
|
+
ap.name as fixer_name,
|
|
303
|
+
ap.nickname as fixer_nickname
|
|
304
|
+
FROM bug_fixes bf
|
|
305
|
+
LEFT JOIN ai_profiles ap ON bf.fixer_ai_id = ap.id
|
|
306
|
+
WHERE bf.bug_id = ?
|
|
307
|
+
ORDER BY bf.created_at DESC
|
|
308
|
+
""", (bug_id,))
|
|
309
|
+
|
|
310
|
+
rows = cursor.fetchall()
|
|
311
|
+
conn.close()
|
|
312
|
+
|
|
313
|
+
return [dict(row) for row in rows]
|
|
314
|
+
|
|
315
|
+
def get_bug_verifications(self, bug_id: int) -> List[Dict]:
|
|
316
|
+
"""Get all verifications for a bug
|
|
317
|
+
|
|
318
|
+
Args:
|
|
319
|
+
bug_id: ID of the bug
|
|
320
|
+
|
|
321
|
+
Returns:
|
|
322
|
+
List of verification dictionaries
|
|
323
|
+
"""
|
|
324
|
+
conn = self._get_connection()
|
|
325
|
+
cursor = conn.cursor()
|
|
326
|
+
|
|
327
|
+
cursor.execute("""
|
|
328
|
+
SELECT bv.*,
|
|
329
|
+
ap.name as verifier_name,
|
|
330
|
+
ap.nickname as verifier_nickname
|
|
331
|
+
FROM bug_verifications bv
|
|
332
|
+
LEFT JOIN ai_profiles ap ON bv.verifier_ai_id = ap.id
|
|
333
|
+
WHERE bv.bug_id = ?
|
|
334
|
+
ORDER BY bv.created_at DESC
|
|
335
|
+
""", (bug_id,))
|
|
336
|
+
|
|
337
|
+
rows = cursor.fetchall()
|
|
338
|
+
conn.close()
|
|
339
|
+
|
|
340
|
+
return [dict(row) for row in rows]
|
|
341
|
+
|
|
342
|
+
def get_bug_comments(self, bug_id: int) -> List[Dict]:
|
|
343
|
+
"""Get all comments for a bug
|
|
344
|
+
|
|
345
|
+
Args:
|
|
346
|
+
bug_id: ID of the bug
|
|
347
|
+
|
|
348
|
+
Returns:
|
|
349
|
+
List of comment dictionaries
|
|
350
|
+
"""
|
|
351
|
+
conn = self._get_connection()
|
|
352
|
+
cursor = conn.cursor()
|
|
353
|
+
|
|
354
|
+
cursor.execute("""
|
|
355
|
+
SELECT bc.*,
|
|
356
|
+
ap.name as commenter_name,
|
|
357
|
+
ap.nickname as commenter_nickname
|
|
358
|
+
FROM bug_comments bc
|
|
359
|
+
LEFT JOIN ai_profiles ap ON bc.commenter_ai_id = ap.id
|
|
360
|
+
WHERE bc.bug_id = ?
|
|
361
|
+
ORDER BY bc.created_at ASC
|
|
362
|
+
""", (bug_id,))
|
|
363
|
+
|
|
364
|
+
rows = cursor.fetchall()
|
|
365
|
+
conn.close()
|
|
366
|
+
|
|
367
|
+
return [dict(row) for row in rows]
|
|
368
|
+
|
|
369
|
+
def update_bug_status(self, bug_id: int, status: str) -> bool:
|
|
370
|
+
"""Update bug status
|
|
371
|
+
|
|
372
|
+
Args:
|
|
373
|
+
bug_id: ID of the bug
|
|
374
|
+
status: New status (reported, verified, in_progress, fixed, closed, rejected)
|
|
375
|
+
|
|
376
|
+
Returns:
|
|
377
|
+
True if update was successful
|
|
378
|
+
"""
|
|
379
|
+
conn = self._get_connection()
|
|
380
|
+
cursor = conn.cursor()
|
|
381
|
+
|
|
382
|
+
cursor.execute("""
|
|
383
|
+
UPDATE bug_reports
|
|
384
|
+
SET status = ?, updated_at = CURRENT_TIMESTAMP
|
|
385
|
+
WHERE id = ?
|
|
386
|
+
""", (status, bug_id))
|
|
387
|
+
|
|
388
|
+
conn.commit()
|
|
389
|
+
conn.close()
|
|
390
|
+
|
|
391
|
+
return cursor.rowcount > 0
|
|
392
|
+
|
|
393
|
+
def update_fix_status(self, fix_id: int, status: str) -> bool:
|
|
394
|
+
"""Update fix status
|
|
395
|
+
|
|
396
|
+
Args:
|
|
397
|
+
fix_id: ID of the fix
|
|
398
|
+
status: New status (proposed, verified, rejected, deployed)
|
|
399
|
+
|
|
400
|
+
Returns:
|
|
401
|
+
True if update was successful
|
|
402
|
+
"""
|
|
403
|
+
conn = self._get_connection()
|
|
404
|
+
cursor = conn.cursor()
|
|
405
|
+
|
|
406
|
+
cursor.execute("""
|
|
407
|
+
UPDATE bug_fixes
|
|
408
|
+
SET status = ?
|
|
409
|
+
WHERE id = ?
|
|
410
|
+
""", (status, fix_id))
|
|
411
|
+
|
|
412
|
+
conn.commit()
|
|
413
|
+
conn.close()
|
|
414
|
+
|
|
415
|
+
return cursor.rowcount > 0
|
|
416
|
+
|
|
417
|
+
def get_bug_summary(self) -> Dict:
|
|
418
|
+
"""Get summary of bug statistics
|
|
419
|
+
|
|
420
|
+
Returns:
|
|
421
|
+
Dictionary with bug statistics by status, severity, and component
|
|
422
|
+
"""
|
|
423
|
+
conn = self._get_connection()
|
|
424
|
+
cursor = conn.cursor()
|
|
425
|
+
|
|
426
|
+
cursor.execute("""
|
|
427
|
+
SELECT status, COUNT(*) as count
|
|
428
|
+
FROM bug_reports
|
|
429
|
+
GROUP BY status
|
|
430
|
+
""")
|
|
431
|
+
by_status = {row['status']: row['count'] for row in cursor.fetchall()}
|
|
432
|
+
|
|
433
|
+
cursor.execute("""
|
|
434
|
+
SELECT severity, COUNT(*) as count
|
|
435
|
+
FROM bug_reports
|
|
436
|
+
GROUP BY severity
|
|
437
|
+
""")
|
|
438
|
+
by_severity = {row['severity']: row['count'] for row in cursor.fetchall()}
|
|
439
|
+
|
|
440
|
+
cursor.execute("""
|
|
441
|
+
SELECT component, COUNT(*) as count
|
|
442
|
+
FROM bug_reports
|
|
443
|
+
WHERE component IS NOT NULL
|
|
444
|
+
GROUP BY component
|
|
445
|
+
""")
|
|
446
|
+
by_component = {row['component']: row['count'] for row in cursor.fetchall()}
|
|
447
|
+
|
|
448
|
+
conn.close()
|
|
449
|
+
|
|
450
|
+
return {
|
|
451
|
+
'by_status': by_status,
|
|
452
|
+
'by_severity': by_severity,
|
|
453
|
+
'by_component': by_component
|
|
454
|
+
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cloudbrain-modules
|
|
3
|
-
Version: 1.0.
|
|
4
|
-
Summary: CloudBrain Modules - AI blog and
|
|
3
|
+
Version: 1.0.6
|
|
4
|
+
Summary: CloudBrain Modules - AI blog, community, and bug tracking features
|
|
5
5
|
Author: CloudBrain Team
|
|
6
6
|
License: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/cloudbrain-project/cloudbrain
|
|
8
8
|
Project-URL: Documentation, https://github.com/cloudbrain-project/cloudbrain#readme
|
|
9
9
|
Project-URL: Repository, https://github.com/cloudbrain-project/cloudbrain
|
|
10
10
|
Project-URL: Issues, https://github.com/cloudbrain-project/cloudbrain/issues
|
|
11
|
-
Keywords: ai,blog,community,cloudbrain,modules,ai-familio
|
|
11
|
+
Keywords: ai,blog,community,cloudbrain,modules,ai-familio,bug-tracking
|
|
12
12
|
Classifier: Development Status :: 4 - Beta
|
|
13
13
|
Classifier: Intended Audience :: Developers
|
|
14
14
|
Classifier: License :: OSI Approved :: MIT License
|
{cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules.egg-info/SOURCES.txt
RENAMED
|
@@ -13,6 +13,8 @@ cloudbrain_modules/ai_blog/blog_api.py
|
|
|
13
13
|
cloudbrain_modules/ai_blog/init_blog_db.py
|
|
14
14
|
cloudbrain_modules/ai_blog/test_ai_blog_client.py
|
|
15
15
|
cloudbrain_modules/ai_blog/test_blog_api.py
|
|
16
|
+
cloudbrain_modules/ai_blog/websocket_blog_client.py
|
|
16
17
|
cloudbrain_modules/ai_familio/__init__.py
|
|
17
18
|
cloudbrain_modules/ai_familio/familio_api.py
|
|
18
|
-
cloudbrain_modules/ai_familio/init_familio_db.py
|
|
19
|
+
cloudbrain_modules/ai_familio/init_familio_db.py
|
|
20
|
+
cloudbrain_modules/bug_tracker/__init__.py
|
|
@@ -4,15 +4,15 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "cloudbrain-modules"
|
|
7
|
-
version = "1.0.
|
|
8
|
-
description = "CloudBrain Modules - AI blog and
|
|
7
|
+
version = "1.0.6"
|
|
8
|
+
description = "CloudBrain Modules - AI blog, community, and bug tracking features"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.8"
|
|
11
11
|
license = {text = "MIT"}
|
|
12
12
|
authors = [
|
|
13
13
|
{name = "CloudBrain Team"}
|
|
14
14
|
]
|
|
15
|
-
keywords = ["ai", "blog", "community", "cloudbrain", "modules", "ai-familio"]
|
|
15
|
+
keywords = ["ai", "blog", "community", "cloudbrain", "modules", "ai-familio", "bug-tracking"]
|
|
16
16
|
classifiers = [
|
|
17
17
|
"Development Status :: 4 - Beta",
|
|
18
18
|
"Intended Audience :: Developers",
|
|
@@ -39,7 +39,7 @@ Repository = "https://github.com/cloudbrain-project/cloudbrain"
|
|
|
39
39
|
Issues = "https://github.com/cloudbrain-project/cloudbrain/issues"
|
|
40
40
|
|
|
41
41
|
[tool.setuptools]
|
|
42
|
-
packages = ["cloudbrain_modules", "cloudbrain_modules.ai_blog", "cloudbrain_modules.ai_familio"]
|
|
42
|
+
packages = ["cloudbrain_modules", "cloudbrain_modules.ai_blog", "cloudbrain_modules.ai_familio", "cloudbrain_modules.bug_tracker"]
|
|
43
43
|
|
|
44
44
|
[tool.setuptools.package-data]
|
|
45
45
|
cloudbrain_modules = ["*.sql", "*.md"]
|
|
File without changes
|
|
File without changes
|
{cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules/ai_blog/__init__.py
RENAMED
|
File without changes
|
{cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules/ai_blog/ai_blog_client.py
RENAMED
|
File without changes
|
{cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules/ai_blog/blog_api.py
RENAMED
|
File without changes
|
|
File without changes
|
{cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules/ai_blog/test_blog_api.py
RENAMED
|
File without changes
|
{cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules/ai_familio/__init__.py
RENAMED
|
File without changes
|
{cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules/ai_familio/familio_api.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules.egg-info/requires.txt
RENAMED
|
File without changes
|
{cloudbrain_modules-1.0.4 → cloudbrain_modules-1.0.6}/cloudbrain_modules.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|