crewdeck 0.1.0__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.
crewdeck/__init__.py ADDED
@@ -0,0 +1,43 @@
1
+ """
2
+ CrewDeck Python SDK
3
+
4
+ Manage your AI agent teams with CrewDeck.
5
+
6
+ Quick Start:
7
+ from crewdeck import CrewDeck
8
+
9
+ deck = CrewDeck(api_key="your-api-key")
10
+
11
+ # Create a task
12
+ task = deck.tasks.create(
13
+ title="Analyze data",
14
+ description="Process the Q4 sales data",
15
+ priority="high",
16
+ assignees=["JARVIS"]
17
+ )
18
+
19
+ # Update agent status
20
+ deck.agents.update_status("JARVIS", "working")
21
+
22
+ # Send an event to the live feed
23
+ deck.events.send(
24
+ type="message",
25
+ agent_name="JARVIS",
26
+ message="Starting data analysis..."
27
+ )
28
+ """
29
+
30
+ from .client import CrewDeck
31
+ from .models import Task, Agent, Event
32
+ from .exceptions import CrewDeckError, AuthenticationError, APIError
33
+
34
+ __version__ = "0.1.0"
35
+ __all__ = [
36
+ "CrewDeck",
37
+ "Task",
38
+ "Agent",
39
+ "Event",
40
+ "CrewDeckError",
41
+ "AuthenticationError",
42
+ "APIError",
43
+ ]
crewdeck/client.py ADDED
@@ -0,0 +1,395 @@
1
+ """CrewDeck Python Client"""
2
+
3
+ from typing import List, Optional, Dict, Any
4
+ import httpx
5
+
6
+ from .models import Task, Agent, Event
7
+ from .exceptions import (
8
+ CrewDeckError,
9
+ AuthenticationError,
10
+ APIError,
11
+ NotFoundError,
12
+ RateLimitError,
13
+ )
14
+
15
+
16
+ DEFAULT_BASE_URL = "https://adept-jellyfish-320.convex.site"
17
+
18
+
19
+ class TasksAPI:
20
+ """Tasks API wrapper"""
21
+
22
+ def __init__(self, client: "CrewDeck"):
23
+ self._client = client
24
+
25
+ def create(
26
+ self,
27
+ title: str,
28
+ description: str = "",
29
+ status: str = "INBOX",
30
+ priority: str = "medium",
31
+ assignees: Optional[List[str]] = None,
32
+ tags: Optional[List[str]] = None,
33
+ ) -> Task:
34
+ """
35
+ Create a new task.
36
+
37
+ Args:
38
+ title: Task title (required)
39
+ description: Task description
40
+ status: Task status (INBOX, ASSIGNED, IN PROGRESS, REVIEW, DONE)
41
+ priority: Priority level (low, medium, high, critical)
42
+ assignees: List of agent names to assign
43
+ tags: List of tags
44
+
45
+ Returns:
46
+ Created Task object
47
+ """
48
+ data = {
49
+ "title": title,
50
+ "description": description,
51
+ "status": status,
52
+ "priority": priority,
53
+ "assignees": assignees or [],
54
+ "tags": tags or [],
55
+ }
56
+ response = self._client._request("POST", "/api/v1/tasks", json=data)
57
+ return Task(
58
+ id=response.get("taskId", ""),
59
+ title=title,
60
+ description=description,
61
+ status=status,
62
+ priority=priority,
63
+ assignees=assignees or [],
64
+ tags=tags or [],
65
+ )
66
+
67
+ def list(
68
+ self,
69
+ status: Optional[str] = None,
70
+ limit: int = 100,
71
+ ) -> List[Task]:
72
+ """
73
+ List tasks.
74
+
75
+ Args:
76
+ status: Filter by status (optional)
77
+ limit: Maximum number of tasks to return
78
+
79
+ Returns:
80
+ List of Task objects
81
+ """
82
+ params = {"limit": str(limit)}
83
+ if status:
84
+ params["status"] = status
85
+ response = self._client._request("GET", "/api/v1/tasks", params=params)
86
+ return [Task.from_dict(t) for t in response.get("tasks", [])]
87
+
88
+ def update(
89
+ self,
90
+ task_id: str,
91
+ title: Optional[str] = None,
92
+ description: Optional[str] = None,
93
+ status: Optional[str] = None,
94
+ priority: Optional[str] = None,
95
+ assignees: Optional[List[str]] = None,
96
+ tags: Optional[List[str]] = None,
97
+ output: Optional[str] = None,
98
+ ) -> None:
99
+ """
100
+ Update a task.
101
+
102
+ Args:
103
+ task_id: Task ID (required)
104
+ title: New title
105
+ description: New description
106
+ status: New status
107
+ priority: New priority
108
+ assignees: New assignees list
109
+ tags: New tags list
110
+ output: Task output/result
111
+ """
112
+ data: Dict[str, Any] = {"id": task_id}
113
+ if title is not None:
114
+ data["title"] = title
115
+ if description is not None:
116
+ data["description"] = description
117
+ if status is not None:
118
+ data["status"] = status
119
+ if priority is not None:
120
+ data["priority"] = priority
121
+ if assignees is not None:
122
+ data["assignees"] = assignees
123
+ if tags is not None:
124
+ data["tags"] = tags
125
+ if output is not None:
126
+ data["output"] = output
127
+ self._client._request("PUT", "/api/v1/tasks", json=data)
128
+
129
+ def move(self, task_id: str, status: str) -> None:
130
+ """
131
+ Move a task to a new status.
132
+
133
+ Args:
134
+ task_id: Task ID
135
+ status: New status (INBOX, ASSIGNED, IN PROGRESS, REVIEW, DONE)
136
+ """
137
+ self.update(task_id, status=status)
138
+
139
+ def complete(self, task_id: str, output: Optional[str] = None) -> None:
140
+ """
141
+ Mark a task as done.
142
+
143
+ Args:
144
+ task_id: Task ID
145
+ output: Optional output/result
146
+ """
147
+ self.update(task_id, status="DONE", output=output)
148
+
149
+
150
+ class AgentsAPI:
151
+ """Agents API wrapper"""
152
+
153
+ def __init__(self, client: "CrewDeck"):
154
+ self._client = client
155
+
156
+ def create(
157
+ self,
158
+ name: str,
159
+ avatar: str = "🤖",
160
+ role: str = "AI Agent",
161
+ level: str = "standard",
162
+ status: str = "idle",
163
+ ) -> Agent:
164
+ """
165
+ Create a new agent.
166
+
167
+ Args:
168
+ name: Agent name (required)
169
+ avatar: Emoji avatar
170
+ role: Agent role description
171
+ level: Agent level (standard, INT, LEAD, SPC)
172
+ status: Initial status (idle, working, offline)
173
+
174
+ Returns:
175
+ Created Agent object
176
+ """
177
+ data = {
178
+ "name": name,
179
+ "avatar": avatar,
180
+ "role": role,
181
+ "level": level,
182
+ "status": status,
183
+ }
184
+ response = self._client._request("POST", "/api/v1/agents", json=data)
185
+ return Agent(
186
+ id=response.get("agentId", ""),
187
+ name=name,
188
+ avatar=avatar,
189
+ role=role,
190
+ level=level,
191
+ status=status,
192
+ )
193
+
194
+ def list(self) -> List[Agent]:
195
+ """
196
+ List all agents.
197
+
198
+ Returns:
199
+ List of Agent objects
200
+ """
201
+ response = self._client._request("GET", "/api/v1/agents")
202
+ return [Agent.from_dict(a) for a in response.get("agents", [])]
203
+
204
+ def update_status(self, name: str, status: str) -> None:
205
+ """
206
+ Update an agent's status by name.
207
+
208
+ Args:
209
+ name: Agent name
210
+ status: New status (idle, working, offline)
211
+ """
212
+ self._client._request("PUT", "/api/v1/agents", json={
213
+ "name": name,
214
+ "status": status,
215
+ })
216
+
217
+ def set_working(self, name: str) -> None:
218
+ """Set agent status to working"""
219
+ self.update_status(name, "working")
220
+
221
+ def set_idle(self, name: str) -> None:
222
+ """Set agent status to idle"""
223
+ self.update_status(name, "idle")
224
+
225
+ def set_offline(self, name: str) -> None:
226
+ """Set agent status to offline"""
227
+ self.update_status(name, "offline")
228
+
229
+
230
+ class EventsAPI:
231
+ """Events API wrapper"""
232
+
233
+ def __init__(self, client: "CrewDeck"):
234
+ self._client = client
235
+
236
+ def send(
237
+ self,
238
+ type: str,
239
+ agent_name: Optional[str] = None,
240
+ message: Optional[str] = None,
241
+ task_id: Optional[str] = None,
242
+ metadata: Optional[Dict[str, Any]] = None,
243
+ ) -> None:
244
+ """
245
+ Send an event to the live feed.
246
+
247
+ Args:
248
+ type: Event type (message, status, log)
249
+ agent_name: Agent name (optional)
250
+ message: Message content
251
+ task_id: Related task ID
252
+ metadata: Additional metadata
253
+ """
254
+ event = Event(
255
+ type=type,
256
+ agent_name=agent_name,
257
+ message=message,
258
+ task_id=task_id,
259
+ metadata=metadata,
260
+ )
261
+ self._client._request("POST", "/api/v1/events", json=event.to_dict())
262
+
263
+ def message(self, content: str, agent_name: Optional[str] = None) -> None:
264
+ """
265
+ Send a message to the live feed.
266
+
267
+ Args:
268
+ content: Message content
269
+ agent_name: Agent name (optional)
270
+ """
271
+ self.send(type="message", message=content, agent_name=agent_name)
272
+
273
+ def log(
274
+ self,
275
+ message: str,
276
+ agent_name: Optional[str] = None,
277
+ metadata: Optional[Dict[str, Any]] = None,
278
+ ) -> None:
279
+ """
280
+ Send a log event.
281
+
282
+ Args:
283
+ message: Log message
284
+ agent_name: Agent name (optional)
285
+ metadata: Additional data to log
286
+ """
287
+ self.send(type="log", message=message, agent_name=agent_name, metadata=metadata)
288
+
289
+
290
+ class CrewDeck:
291
+ """
292
+ CrewDeck Python Client
293
+
294
+ Example:
295
+ deck = CrewDeck(api_key="your-api-key")
296
+
297
+ # Create a task
298
+ task = deck.tasks.create(
299
+ title="Process data",
300
+ priority="high",
301
+ assignees=["JARVIS"]
302
+ )
303
+
304
+ # Update agent status
305
+ deck.agents.set_working("JARVIS")
306
+
307
+ # Send a message
308
+ deck.events.message("Starting work...", agent_name="JARVIS")
309
+
310
+ # Complete the task
311
+ deck.tasks.complete(task.id, output="Data processed successfully")
312
+ """
313
+
314
+ def __init__(
315
+ self,
316
+ api_key: str,
317
+ base_url: str = DEFAULT_BASE_URL,
318
+ timeout: float = 30.0,
319
+ ):
320
+ """
321
+ Initialize CrewDeck client.
322
+
323
+ Args:
324
+ api_key: Your CrewDeck API key
325
+ base_url: API base URL (default: production)
326
+ timeout: Request timeout in seconds
327
+ """
328
+ if not api_key:
329
+ raise AuthenticationError("API key is required")
330
+
331
+ self.api_key = api_key
332
+ self.base_url = base_url.rstrip("/")
333
+ self.timeout = timeout
334
+
335
+ self._client = httpx.Client(
336
+ base_url=self.base_url,
337
+ headers={
338
+ "Authorization": f"Bearer {api_key}",
339
+ "Content-Type": "application/json",
340
+ },
341
+ timeout=timeout,
342
+ )
343
+
344
+ # API namespaces
345
+ self.tasks = TasksAPI(self)
346
+ self.agents = AgentsAPI(self)
347
+ self.events = EventsAPI(self)
348
+
349
+ def _request(
350
+ self,
351
+ method: str,
352
+ path: str,
353
+ params: Optional[Dict[str, str]] = None,
354
+ json: Optional[Dict[str, Any]] = None,
355
+ ) -> Dict[str, Any]:
356
+ """Make an API request"""
357
+ try:
358
+ response = self._client.request(
359
+ method=method,
360
+ url=path,
361
+ params=params,
362
+ json=json,
363
+ )
364
+
365
+ if response.status_code == 401:
366
+ raise AuthenticationError("Invalid or revoked API key")
367
+
368
+ if response.status_code == 404:
369
+ raise NotFoundError("Resource not found", status_code=404)
370
+
371
+ if response.status_code == 429:
372
+ raise RateLimitError("Rate limit exceeded", status_code=429)
373
+
374
+ if response.status_code >= 400:
375
+ try:
376
+ error_data = response.json()
377
+ message = error_data.get("error", f"API error: {response.status_code}")
378
+ except:
379
+ message = f"API error: {response.status_code}"
380
+ raise APIError(message, status_code=response.status_code)
381
+
382
+ return response.json()
383
+
384
+ except httpx.RequestError as e:
385
+ raise CrewDeckError(f"Request failed: {e}")
386
+
387
+ def close(self) -> None:
388
+ """Close the HTTP client"""
389
+ self._client.close()
390
+
391
+ def __enter__(self) -> "CrewDeck":
392
+ return self
393
+
394
+ def __exit__(self, *args) -> None:
395
+ self.close()
crewdeck/exceptions.py ADDED
@@ -0,0 +1,35 @@
1
+ """CrewDeck SDK Exceptions"""
2
+
3
+
4
+ class CrewDeckError(Exception):
5
+ """Base exception for CrewDeck SDK"""
6
+ pass
7
+
8
+
9
+ class AuthenticationError(CrewDeckError):
10
+ """Raised when API key is invalid or missing"""
11
+ pass
12
+
13
+
14
+ class APIError(CrewDeckError):
15
+ """Raised when API returns an error"""
16
+
17
+ def __init__(self, message: str, status_code: int = None, response: dict = None):
18
+ super().__init__(message)
19
+ self.status_code = status_code
20
+ self.response = response
21
+
22
+
23
+ class ValidationError(CrewDeckError):
24
+ """Raised when input validation fails"""
25
+ pass
26
+
27
+
28
+ class NotFoundError(APIError):
29
+ """Raised when a resource is not found"""
30
+ pass
31
+
32
+
33
+ class RateLimitError(APIError):
34
+ """Raised when rate limit is exceeded"""
35
+ pass
crewdeck/models.py ADDED
@@ -0,0 +1,103 @@
1
+ """CrewDeck Data Models"""
2
+
3
+ from dataclasses import dataclass, field
4
+ from typing import List, Optional, Dict, Any
5
+ from datetime import datetime
6
+
7
+
8
+ @dataclass
9
+ class Task:
10
+ """Represents a task in CrewDeck"""
11
+ id: str
12
+ title: str
13
+ description: str = ""
14
+ status: str = "INBOX"
15
+ priority: str = "medium"
16
+ assignees: List[str] = field(default_factory=list)
17
+ tags: List[str] = field(default_factory=list)
18
+ output: Optional[str] = None
19
+ created_at: Optional[datetime] = None
20
+ updated_at: Optional[datetime] = None
21
+
22
+ @classmethod
23
+ def from_dict(cls, data: Dict[str, Any]) -> "Task":
24
+ return cls(
25
+ id=data.get("id", ""),
26
+ title=data.get("title", ""),
27
+ description=data.get("description", ""),
28
+ status=data.get("status", "INBOX"),
29
+ priority=data.get("priority", "medium"),
30
+ assignees=data.get("assignees", []),
31
+ tags=data.get("tags", []),
32
+ output=data.get("output"),
33
+ created_at=datetime.fromtimestamp(data["createdAt"] / 1000) if data.get("createdAt") else None,
34
+ updated_at=datetime.fromtimestamp(data["updatedAt"] / 1000) if data.get("updatedAt") else None,
35
+ )
36
+
37
+ def to_dict(self) -> Dict[str, Any]:
38
+ return {
39
+ "id": self.id,
40
+ "title": self.title,
41
+ "description": self.description,
42
+ "status": self.status,
43
+ "priority": self.priority,
44
+ "assignees": self.assignees,
45
+ "tags": self.tags,
46
+ "output": self.output,
47
+ }
48
+
49
+
50
+ @dataclass
51
+ class Agent:
52
+ """Represents an agent in CrewDeck"""
53
+ id: str
54
+ name: str
55
+ avatar: str = "🤖"
56
+ role: str = "AI Agent"
57
+ level: str = "standard"
58
+ status: str = "idle"
59
+ created_at: Optional[datetime] = None
60
+
61
+ @classmethod
62
+ def from_dict(cls, data: Dict[str, Any]) -> "Agent":
63
+ return cls(
64
+ id=data.get("id", ""),
65
+ name=data.get("name", ""),
66
+ avatar=data.get("avatar", "🤖"),
67
+ role=data.get("role", "AI Agent"),
68
+ level=data.get("level", "standard"),
69
+ status=data.get("status", "idle"),
70
+ created_at=datetime.fromtimestamp(data["createdAt"] / 1000) if data.get("createdAt") else None,
71
+ )
72
+
73
+ def to_dict(self) -> Dict[str, Any]:
74
+ return {
75
+ "id": self.id,
76
+ "name": self.name,
77
+ "avatar": self.avatar,
78
+ "role": self.role,
79
+ "level": self.level,
80
+ "status": self.status,
81
+ }
82
+
83
+
84
+ @dataclass
85
+ class Event:
86
+ """Represents an event in CrewDeck"""
87
+ type: str
88
+ agent_name: Optional[str] = None
89
+ message: Optional[str] = None
90
+ task_id: Optional[str] = None
91
+ metadata: Optional[Dict[str, Any]] = None
92
+
93
+ def to_dict(self) -> Dict[str, Any]:
94
+ data = {"type": self.type}
95
+ if self.agent_name:
96
+ data["agentName"] = self.agent_name
97
+ if self.message:
98
+ data["message"] = self.message
99
+ if self.task_id:
100
+ data["taskId"] = self.task_id
101
+ if self.metadata:
102
+ data["metadata"] = self.metadata
103
+ return data
@@ -0,0 +1,240 @@
1
+ Metadata-Version: 2.4
2
+ Name: crewdeck
3
+ Version: 0.1.0
4
+ Summary: Python SDK for CrewDeck - AI agent management dashboard
5
+ Project-URL: Homepage, https://crewdeck.dev
6
+ Project-URL: Documentation, https://docs.crewdeck.dev
7
+ Project-URL: Repository, https://github.com/crewdeck/crewdeck-python
8
+ Author-email: CrewDeck <hello@crewdeck.dev>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: agents,ai,autogen,crewai,dashboard,langchain,management
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.8
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Requires-Python: >=3.8
23
+ Requires-Dist: httpx>=0.24.0
24
+ Provides-Extra: async
25
+ Requires-Dist: httpx[http2]>=0.24.0; extra == 'async'
26
+ Provides-Extra: dev
27
+ Requires-Dist: black>=23.0.0; extra == 'dev'
28
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
29
+ Requires-Dist: pytest>=7.0.0; extra == 'dev'
30
+ Requires-Dist: ruff>=0.1.0; extra == 'dev'
31
+ Description-Content-Type: text/markdown
32
+
33
+ # CrewDeck Python SDK
34
+
35
+ Official Python SDK for [CrewDeck](https://crewdeck.dev) — the AI agent management dashboard.
36
+
37
+ Manage your AI crew like a real team. Assign tasks, track progress, review outputs, approve results.
38
+
39
+ ## Installation
40
+
41
+ ```bash
42
+ pip install crewdeck
43
+ ```
44
+
45
+ ## Quick Start
46
+
47
+ ```python
48
+ from crewdeck import CrewDeck
49
+
50
+ # Initialize with your API key
51
+ deck = CrewDeck(api_key="your-api-key")
52
+
53
+ # Create a task
54
+ task = deck.tasks.create(
55
+ title="Analyze Q4 sales data",
56
+ description="Process and summarize the quarterly sales report",
57
+ priority="high",
58
+ assignees=["JARVIS"]
59
+ )
60
+
61
+ # Update agent status
62
+ deck.agents.set_working("JARVIS")
63
+
64
+ # Send progress updates
65
+ deck.events.message("Starting data analysis...", agent_name="JARVIS")
66
+
67
+ # Complete the task with output
68
+ deck.tasks.complete(task.id, output="Analysis complete. Revenue up 15% YoY.")
69
+ ```
70
+
71
+ ## API Reference
72
+
73
+ ### Tasks
74
+
75
+ ```python
76
+ # Create a task
77
+ task = deck.tasks.create(
78
+ title="Task title",
79
+ description="Task description",
80
+ status="INBOX", # INBOX, ASSIGNED, IN PROGRESS, REVIEW, DONE
81
+ priority="medium", # low, medium, high, critical
82
+ assignees=["JARVIS", "FRIDAY"],
83
+ tags=["data", "analysis"]
84
+ )
85
+
86
+ # List tasks
87
+ tasks = deck.tasks.list()
88
+ tasks = deck.tasks.list(status="IN PROGRESS", limit=50)
89
+
90
+ # Update a task
91
+ deck.tasks.update(
92
+ task_id="task-id",
93
+ status="IN PROGRESS",
94
+ output="Working on it..."
95
+ )
96
+
97
+ # Move task to a status
98
+ deck.tasks.move(task_id, "REVIEW")
99
+
100
+ # Complete a task
101
+ deck.tasks.complete(task_id, output="Done!")
102
+ ```
103
+
104
+ ### Agents
105
+
106
+ ```python
107
+ # Create an agent
108
+ agent = deck.agents.create(
109
+ name="JARVIS",
110
+ avatar="🤖",
111
+ role="AI Assistant",
112
+ level="LEAD", # standard, INT, LEAD, SPC
113
+ status="idle"
114
+ )
115
+
116
+ # List agents
117
+ agents = deck.agents.list()
118
+
119
+ # Update agent status
120
+ deck.agents.update_status("JARVIS", "working")
121
+
122
+ # Convenience methods
123
+ deck.agents.set_working("JARVIS")
124
+ deck.agents.set_idle("JARVIS")
125
+ deck.agents.set_offline("JARVIS")
126
+ ```
127
+
128
+ ### Events
129
+
130
+ ```python
131
+ # Send a message to the live feed
132
+ deck.events.message("Starting work...", agent_name="JARVIS")
133
+
134
+ # Send a log event
135
+ deck.events.log(
136
+ message="Processing file",
137
+ agent_name="JARVIS",
138
+ metadata={"file": "data.csv", "rows": 1000}
139
+ )
140
+
141
+ # Send a custom event
142
+ deck.events.send(
143
+ type="custom",
144
+ agent_name="JARVIS",
145
+ message="Custom event",
146
+ metadata={"key": "value"}
147
+ )
148
+ ```
149
+
150
+ ## Integration Examples
151
+
152
+ ### CrewAI
153
+
154
+ ```python
155
+ from crewai import Agent, Task, Crew
156
+ from crewdeck import CrewDeck
157
+
158
+ deck = CrewDeck(api_key="your-api-key")
159
+
160
+ # Sync CrewAI agent with CrewDeck
161
+ crewai_agent = Agent(
162
+ role="Researcher",
163
+ goal="Research topics",
164
+ backstory="Expert researcher"
165
+ )
166
+
167
+ # Create corresponding agent in CrewDeck
168
+ deck.agents.create(
169
+ name="Researcher",
170
+ avatar="🔬",
171
+ role="Research Agent",
172
+ level="INT"
173
+ )
174
+
175
+ # When starting a task
176
+ def on_task_start(task):
177
+ deck.agents.set_working("Researcher")
178
+ deck.events.message(f"Starting: {task.description}", agent_name="Researcher")
179
+
180
+ # When task completes
181
+ def on_task_complete(task, output):
182
+ deck.events.message(f"Completed: {task.description}", agent_name="Researcher")
183
+ deck.agents.set_idle("Researcher")
184
+ ```
185
+
186
+ ### LangChain
187
+
188
+ ```python
189
+ from langchain.callbacks.base import BaseCallbackHandler
190
+ from crewdeck import CrewDeck
191
+
192
+ class CrewDeckCallback(BaseCallbackHandler):
193
+ def __init__(self, deck: CrewDeck, agent_name: str):
194
+ self.deck = deck
195
+ self.agent_name = agent_name
196
+
197
+ def on_chain_start(self, serialized, inputs, **kwargs):
198
+ self.deck.agents.set_working(self.agent_name)
199
+ self.deck.events.message(f"Processing: {inputs}", agent_name=self.agent_name)
200
+
201
+ def on_chain_end(self, outputs, **kwargs):
202
+ self.deck.events.message(f"Result: {outputs}", agent_name=self.agent_name)
203
+ self.deck.agents.set_idle(self.agent_name)
204
+ ```
205
+
206
+ ## Context Manager
207
+
208
+ ```python
209
+ with CrewDeck(api_key="your-api-key") as deck:
210
+ task = deck.tasks.create(title="My task")
211
+ # Client automatically closes when done
212
+ ```
213
+
214
+ ## Error Handling
215
+
216
+ ```python
217
+ from crewdeck import CrewDeck, AuthenticationError, APIError
218
+
219
+ try:
220
+ deck = CrewDeck(api_key="invalid-key")
221
+ deck.tasks.list()
222
+ except AuthenticationError:
223
+ print("Invalid API key")
224
+ except APIError as e:
225
+ print(f"API error: {e} (status: {e.status_code})")
226
+ ```
227
+
228
+ ## Configuration
229
+
230
+ ```python
231
+ deck = CrewDeck(
232
+ api_key="your-api-key",
233
+ base_url="https://your-convex-deployment.convex.site", # Custom deployment
234
+ timeout=30.0 # Request timeout in seconds
235
+ )
236
+ ```
237
+
238
+ ## License
239
+
240
+ MIT
@@ -0,0 +1,8 @@
1
+ crewdeck/__init__.py,sha256=gacJzO7ee1B2wvd0oyX4VC8SFiDLnD2ophYHMgAE8Hg,905
2
+ crewdeck/client.py,sha256=ZcrLjDcCCscoQj2rFsGMJ-FTZOmu0YeAhQF1oX7EzP4,11266
3
+ crewdeck/exceptions.py,sha256=vOvYYdlP0wvkuuUhYE5Wj2pnuJXqmCiYcs5cobdijDM,763
4
+ crewdeck/models.py,sha256=-2cScY_4I-0wg33S5Pa3EfKBx7tSdrCOMU2RPTXjIpw,3182
5
+ crewdeck-0.1.0.dist-info/METADATA,sha256=I5IPqTQoAuF7A3cRwUfo1wgOTtPAtoTzh8oMRv6XFjQ,5884
6
+ crewdeck-0.1.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
7
+ crewdeck-0.1.0.dist-info/licenses/LICENSE,sha256=z0AZu5f134PY4UJ0L_nPuqlyZa14gMPIR-4u8M-5qjI,1065
8
+ crewdeck-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.28.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 CrewDeck
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.