webagents 0.1.13__py3-none-any.whl → 0.2.2__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.
Files changed (77) hide show
  1. webagents/__init__.py +1 -1
  2. webagents/__main__.py +55 -0
  3. webagents/agents/__init__.py +1 -1
  4. webagents/agents/core/__init__.py +1 -1
  5. webagents/agents/core/base_agent.py +15 -15
  6. webagents/agents/core/handoffs.py +1 -1
  7. webagents/agents/skills/__init__.py +11 -11
  8. webagents/agents/skills/base.py +1 -1
  9. webagents/agents/skills/core/llm/litellm/__init__.py +1 -1
  10. webagents/agents/skills/core/llm/litellm/skill.py +1 -1
  11. webagents/agents/skills/core/mcp/README.md +2 -2
  12. webagents/agents/skills/core/mcp/skill.py +2 -2
  13. webagents/agents/skills/core/memory/long_term_memory/memory_skill.py +14 -14
  14. webagents/agents/skills/core/memory/short_term_memory/__init__.py +1 -1
  15. webagents/agents/skills/core/memory/short_term_memory/skill.py +1 -1
  16. webagents/agents/skills/core/memory/vector_memory/skill.py +6 -6
  17. webagents/agents/skills/core/planning/__init__.py +1 -1
  18. webagents/agents/skills/ecosystem/crewai/__init__.py +3 -1
  19. webagents/agents/skills/ecosystem/crewai/skill.py +158 -0
  20. webagents/agents/skills/ecosystem/database/__init__.py +3 -1
  21. webagents/agents/skills/ecosystem/database/skill.py +522 -0
  22. webagents/agents/skills/ecosystem/google/calendar/skill.py +1 -1
  23. webagents/agents/skills/ecosystem/mongodb/__init__.py +3 -0
  24. webagents/agents/skills/ecosystem/mongodb/skill.py +428 -0
  25. webagents/agents/skills/ecosystem/n8n/README.md +287 -0
  26. webagents/agents/skills/ecosystem/n8n/__init__.py +3 -0
  27. webagents/agents/skills/ecosystem/n8n/skill.py +341 -0
  28. webagents/agents/skills/ecosystem/x_com/README.md +401 -0
  29. webagents/agents/skills/ecosystem/x_com/__init__.py +3 -0
  30. webagents/agents/skills/ecosystem/x_com/skill.py +1048 -0
  31. webagents/agents/skills/ecosystem/zapier/README.md +363 -0
  32. webagents/agents/skills/ecosystem/zapier/__init__.py +3 -0
  33. webagents/agents/skills/ecosystem/zapier/skill.py +337 -0
  34. webagents/agents/skills/robutler/__init__.py +2 -2
  35. webagents/agents/skills/robutler/auth/__init__.py +3 -3
  36. webagents/agents/skills/robutler/auth/skill.py +16 -16
  37. webagents/agents/skills/robutler/crm/__init__.py +2 -2
  38. webagents/agents/skills/robutler/crm/skill.py +5 -5
  39. webagents/agents/skills/robutler/discovery/README.md +5 -5
  40. webagents/agents/skills/robutler/discovery/__init__.py +2 -2
  41. webagents/agents/skills/robutler/discovery/skill.py +21 -21
  42. webagents/agents/skills/robutler/message_history/__init__.py +2 -2
  43. webagents/agents/skills/robutler/message_history/skill.py +5 -5
  44. webagents/agents/skills/robutler/nli/__init__.py +1 -1
  45. webagents/agents/skills/robutler/nli/skill.py +9 -9
  46. webagents/agents/skills/robutler/payments/__init__.py +3 -3
  47. webagents/agents/skills/robutler/payments/exceptions.py +1 -1
  48. webagents/agents/skills/robutler/payments/skill.py +23 -23
  49. webagents/agents/skills/robutler/storage/__init__.py +2 -2
  50. webagents/agents/skills/robutler/storage/files/__init__.py +2 -2
  51. webagents/agents/skills/robutler/storage/files/skill.py +4 -4
  52. webagents/agents/skills/robutler/storage/json/__init__.py +1 -1
  53. webagents/agents/skills/robutler/storage/json/skill.py +3 -3
  54. webagents/agents/skills/robutler/storage/kv/skill.py +3 -3
  55. webagents/agents/skills/robutler/storage.py +6 -6
  56. webagents/agents/tools/decorators.py +12 -12
  57. webagents/server/__init__.py +3 -3
  58. webagents/server/context/context_vars.py +2 -2
  59. webagents/server/core/app.py +13 -13
  60. webagents/server/core/middleware.py +3 -3
  61. webagents/server/core/models.py +1 -1
  62. webagents/server/core/monitoring.py +2 -2
  63. webagents/server/middleware.py +1 -1
  64. webagents/server/models.py +2 -2
  65. webagents/server/monitoring.py +15 -15
  66. webagents/utils/logging.py +20 -20
  67. webagents-0.2.2.dist-info/METADATA +266 -0
  68. webagents-0.2.2.dist-info/RECORD +105 -0
  69. webagents-0.2.2.dist-info/licenses/LICENSE +20 -0
  70. webagents/api/__init__.py +0 -17
  71. webagents/api/client.py +0 -1207
  72. webagents/api/types.py +0 -253
  73. webagents-0.1.13.dist-info/METADATA +0 -32
  74. webagents-0.1.13.dist-info/RECORD +0 -96
  75. webagents-0.1.13.dist-info/licenses/LICENSE +0 -1
  76. {webagents-0.1.13.dist-info → webagents-0.2.2.dist-info}/WHEEL +0 -0
  77. {webagents-0.1.13.dist-info → webagents-0.2.2.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,363 @@
1
+ # Zapier Skill for WebAgents
2
+
3
+ Minimalistic Zapier integration for workflow automation. This skill allows you to securely manage Zapier API credentials and trigger Zaps from your WebAgent.
4
+
5
+ ## Features
6
+
7
+ - 🔐 **Secure credential storage** via auth and KV skills
8
+ - ⚡ **Trigger Zaps** with custom input data
9
+ - 📋 **List Zaps** from your Zapier account
10
+ - 📊 **Monitor task status** in real-time
11
+ - 🔒 **Per-user isolation** with authentication
12
+ - 🛡️ **API key validation** during setup
13
+
14
+ ## Prerequisites
15
+
16
+ 1. **Zapier account** (free or paid)
17
+ 2. **Zapier API key** (generated from Zapier account settings)
18
+ 3. **WebAgents framework** with auth and kv skills enabled
19
+
20
+ ## Quick Start
21
+
22
+ ### 1. Set up Zapier API credentials
23
+
24
+ ```python
25
+ # First, configure your Zapier credentials
26
+ await agent.call_tool("zapier_setup",
27
+ api_key="your_zapier_api_key_here"
28
+ )
29
+ ```
30
+
31
+ ### 2. List available Zaps
32
+
33
+ ```python
34
+ # See all Zaps in your Zapier account
35
+ await agent.call_tool("zapier_list_zaps")
36
+ ```
37
+
38
+ ### 3. Trigger a Zap
39
+
40
+ ```python
41
+ # Trigger a Zap with optional input data
42
+ await agent.call_tool("zapier_trigger",
43
+ zap_id="zap_id_here",
44
+ data={"name": "John", "email": "john@example.com"} # Optional
45
+ )
46
+ ```
47
+
48
+ ### 4. Check task status
49
+
50
+ ```python
51
+ # Monitor Zap execution
52
+ await agent.call_tool("zapier_status",
53
+ task_id="task_id_from_trigger_response"
54
+ )
55
+ ```
56
+
57
+ ## Tools Reference
58
+
59
+ ### `zapier_setup(api_key)`
60
+ Set up Zapier API credentials securely.
61
+
62
+ **Parameters:**
63
+ - `api_key` (str): Your Zapier API key from account settings
64
+
65
+ **Returns:**
66
+ - Success message with account information
67
+ - Error message if API key is invalid
68
+
69
+ **Example:**
70
+ ```python
71
+ result = await agent.call_tool("zapier_setup",
72
+ api_key="AK_1a2b3c4d5e6f7g8h9i0j"
73
+ )
74
+ # ✅ Zapier credentials saved successfully!
75
+ # 🔑 API key configured
76
+ # 📊 Found 5 Zaps in your account
77
+ ```
78
+
79
+ ### `zapier_trigger(zap_id, data=None)`
80
+ Trigger a Zapier Zap with optional input data.
81
+
82
+ **Parameters:**
83
+ - `zap_id` (str): The ID of the Zap to trigger
84
+ - `data` (dict, optional): Input data to pass to the Zap
85
+
86
+ **Returns:**
87
+ - Success message with task ID and status
88
+ - Error message if Zap not found or trigger fails
89
+
90
+ **Example:**
91
+ ```python
92
+ result = await agent.call_tool("zapier_trigger",
93
+ zap_id="12345",
94
+ data={"customer_name": "Jane Doe", "order_total": 99.99}
95
+ )
96
+ # ✅ Zap triggered successfully!
97
+ # 📋 Task ID: task_67890
98
+ # 📊 Status: triggered
99
+ ```
100
+
101
+ ### `zapier_list_zaps()`
102
+ List all available Zaps in your Zapier account.
103
+
104
+ **Parameters:** None
105
+
106
+ **Returns:**
107
+ - List of Zaps with names, IDs, status, and trigger apps
108
+ - Empty message if no Zaps found
109
+
110
+ **Example:**
111
+ ```python
112
+ result = await agent.call_tool("zapier_list_zaps")
113
+ # 📋 Available Zapier Zaps:
114
+ #
115
+ # 🟢 **Email to Slack** (ID: 12345)
116
+ # 📊 Status: on
117
+ # 🔗 Trigger: Gmail
118
+ #
119
+ # 🔴 **Form to Spreadsheet** (ID: 67890)
120
+ # 📊 Status: off
121
+ # 🔗 Trigger: Typeform
122
+ #
123
+ # 💡 Use zapier_trigger(zap_id, data) to trigger a Zap
124
+ ```
125
+
126
+ ### `zapier_status(task_id)`
127
+ Check the status of a Zap execution.
128
+
129
+ **Parameters:**
130
+ - `task_id` (str): The task ID returned from zapier_trigger
131
+
132
+ **Returns:**
133
+ - Detailed task status report
134
+ - Error message if task not found
135
+
136
+ **Example:**
137
+ ```python
138
+ result = await agent.call_tool("zapier_status", task_id="task_67890")
139
+ # 📊 Zap Execution Status Report
140
+ # 🆔 Task ID: task_67890
141
+ # 🔧 Zap ID: 12345
142
+ # ✅ Status: success
143
+ # 🕐 Created: 2024-01-15T10:30:00Z
144
+ # 🕑 Updated: 2024-01-15T10:32:15Z
145
+ ```
146
+
147
+ ## Setup Guide
148
+
149
+ ### Getting your Zapier API Key
150
+
151
+ 1. Log into your Zapier account
152
+ 2. Go to **Account Settings** → **Developer**
153
+ 3. Click **Manage API Keys**
154
+ 4. Click **Create API Key**
155
+ 5. Give it a name (e.g., "WebAgent Integration")
156
+ 6. Copy the generated API key
157
+
158
+ ### Zapier API Limits
159
+
160
+ - **Free accounts**: 100 tasks/month
161
+ - **Paid accounts**: Based on your plan
162
+ - **Rate limits**: 1 request per second per API key
163
+
164
+ ## Usage Examples
165
+
166
+ ### Simple Zap Trigger
167
+ ```python
168
+ # Setup (one-time)
169
+ await agent.call_tool("zapier_setup", api_key="your_api_key")
170
+
171
+ # Trigger a simple Zap
172
+ result = await agent.call_tool("zapier_trigger", zap_id="welcome_email_zap")
173
+ print(result) # ✅ Zap triggered successfully!
174
+ ```
175
+
176
+ ### Zap with Input Data
177
+ ```python
178
+ # Trigger Zap with custom data
179
+ lead_data = {
180
+ "name": "John Smith",
181
+ "email": "john@company.com",
182
+ "company": "Acme Corp",
183
+ "source": "website_form"
184
+ }
185
+
186
+ result = await agent.call_tool("zapier_trigger",
187
+ zap_id="lead_processing_zap",
188
+ data=lead_data
189
+ )
190
+
191
+ # Monitor execution
192
+ task_id = "task_123" # Extract from result
193
+ status = await agent.call_tool("zapier_status", task_id=task_id)
194
+ print(status)
195
+ ```
196
+
197
+ ### Discover and Trigger Zaps
198
+ ```python
199
+ # First, see what Zaps are available
200
+ zaps = await agent.call_tool("zapier_list_zaps")
201
+ print(zaps)
202
+
203
+ # Trigger a specific Zap
204
+ result = await agent.call_tool("zapier_trigger",
205
+ zap_id="data_sync_zap",
206
+ data={"sync_type": "full", "timestamp": "2024-01-15T10:00:00Z"}
207
+ )
208
+ ```
209
+
210
+ ### Error Handling Example
211
+ ```python
212
+ try:
213
+ result = await agent.call_tool("zapier_trigger", zap_id="test_zap")
214
+ if "✅" in result:
215
+ print("Zap triggered successfully!")
216
+ elif "❌" in result:
217
+ print(f"Error: {result}")
218
+ except Exception as e:
219
+ print(f"Unexpected error: {e}")
220
+ ```
221
+
222
+ ## Common Use Cases
223
+
224
+ ### 1. Lead Processing
225
+ ```python
226
+ # New lead from website form
227
+ lead_info = {
228
+ "name": request.form["name"],
229
+ "email": request.form["email"],
230
+ "message": request.form["message"]
231
+ }
232
+
233
+ # Trigger Zap to add to CRM and send welcome email
234
+ await agent.call_tool("zapier_trigger",
235
+ zap_id="lead_processing",
236
+ data=lead_info
237
+ )
238
+ ```
239
+
240
+ ### 2. Order Fulfillment
241
+ ```python
242
+ # New order processed
243
+ order_data = {
244
+ "order_id": "ORD-123",
245
+ "customer_email": "customer@example.com",
246
+ "items": [{"name": "Product A", "qty": 2}],
247
+ "total": 49.98
248
+ }
249
+
250
+ # Trigger fulfillment workflow
251
+ await agent.call_tool("zapier_trigger",
252
+ zap_id="order_fulfillment",
253
+ data=order_data
254
+ )
255
+ ```
256
+
257
+ ### 3. Data Synchronization
258
+ ```python
259
+ # Sync data between systems
260
+ sync_data = {
261
+ "source": "database",
262
+ "destination": "crm",
263
+ "record_count": 150,
264
+ "sync_timestamp": datetime.now().isoformat()
265
+ }
266
+
267
+ await agent.call_tool("zapier_trigger",
268
+ zap_id="data_sync",
269
+ data=sync_data
270
+ )
271
+ ```
272
+
273
+ ## Error Handling
274
+
275
+ The skill provides clear error messages for common scenarios:
276
+
277
+ - **❌ Authentication required** - User not authenticated
278
+ - **❌ API key is required** - Empty or missing API key
279
+ - **❌ Invalid API key** - API key rejected by Zapier
280
+ - **❌ API key doesn't have required permissions** - Limited API key
281
+ - **❌ Zap not found** - Invalid Zap ID
282
+ - **❌ Permission denied** - Zap disabled or no access
283
+ - **❌ Task not found** - Invalid task ID
284
+
285
+ ## Security
286
+
287
+ - **API keys** are stored securely using the KV skill with per-user namespacing
288
+ - **User isolation** ensures each user can only access their own credentials
289
+ - **Authentication required** for all operations
290
+ - **Automatic validation** of API keys during setup
291
+ - **Memory fallback** available if KV skill unavailable
292
+
293
+ ## Architecture
294
+
295
+ ```
296
+ WebAgent
297
+ ├── Auth Skill (user context)
298
+ ├── KV Skill (secure storage)
299
+ └── Zapier Skill
300
+ ├── Credential Management
301
+ ├── API Communication
302
+ └── Zap Operations
303
+ ```
304
+
305
+ ## Dependencies
306
+
307
+ - `auth` skill - For user authentication and context
308
+ - `kv` skill - For secure credential storage
309
+ - `httpx` - For HTTP API communication
310
+
311
+ ## Troubleshooting
312
+
313
+ ### "Authentication required"
314
+ Ensure your agent has proper authentication configured.
315
+
316
+ ### "Invalid API key"
317
+ - Verify the API key is correct
318
+ - Check if the key has been revoked
319
+ - Ensure you copied the full key
320
+
321
+ ### "API key doesn't have required permissions"
322
+ - Check if your Zapier plan supports API access
323
+ - Verify the API key has necessary permissions
324
+ - Contact Zapier support if issues persist
325
+
326
+ ### "Zap not found"
327
+ - Use `zapier_list_zaps()` to see available Zaps
328
+ - Check if the Zap ID is correct
329
+ - Verify the Zap exists and is accessible
330
+
331
+ ### "Permission denied"
332
+ - Check if the Zap is enabled/turned on
333
+ - Verify you have access to the Zap
334
+ - Ensure the Zap isn't paused or has errors
335
+
336
+ ## Limitations
337
+
338
+ - Supports Zapier REST API v1 endpoints
339
+ - Limited to triggering existing Zaps (no Zap creation/modification)
340
+ - Rate limited by Zapier (1 request/second)
341
+ - Task status checking depends on Zapier's API availability
342
+ - Some Zap types may not support external triggering
343
+
344
+ ## Zapier Integration Tips
345
+
346
+ 1. **Test your Zaps** in Zapier before triggering via API
347
+ 2. **Use descriptive Zap names** for easier identification
348
+ 3. **Enable Zaps** before trying to trigger them
349
+ 4. **Check Zap history** in Zapier for debugging
350
+ 5. **Monitor task limits** to avoid hitting quotas
351
+
352
+ ## Contributing
353
+
354
+ To extend this skill:
355
+
356
+ 1. Add new tools following the existing pattern
357
+ 2. Update tests in `tests/test_zapier_skill.py`
358
+ 3. Update this documentation
359
+ 4. Ensure proper error handling and user feedback
360
+
361
+ ## License
362
+
363
+ Part of the WebAgents framework. See main project license.
@@ -0,0 +1,3 @@
1
+ from .skill import ZapierSkill
2
+
3
+ __all__ = ['ZapierSkill']
@@ -0,0 +1,337 @@
1
+ """
2
+ Minimalistic Zapier Skill for WebAgents
3
+
4
+ This skill allows users to:
5
+ - Set up Zapier API key securely (via auth/kv skills)
6
+ - Trigger Zapier workflows (Zaps)
7
+ - List available Zaps
8
+ - Get Zap execution status
9
+
10
+ Uses auth skill for user context and kv skill for secure API key storage.
11
+ """
12
+
13
+ import os
14
+ import httpx
15
+ import json
16
+ from typing import Dict, Any, Optional, List
17
+ from datetime import datetime
18
+
19
+ from webagents.agents.skills.base import Skill
20
+ from webagents.agents.tools.decorators import tool, prompt
21
+ from webagents.server.context.context_vars import get_context
22
+
23
+
24
+ class ZapierSkill(Skill):
25
+ """Minimalistic Zapier skill for workflow automation"""
26
+
27
+ def __init__(self):
28
+ super().__init__()
29
+ self.api_base = "https://api.zapier.com/v1"
30
+
31
+ def get_dependencies(self) -> List[str]:
32
+ """Skill dependencies"""
33
+ return ['auth', 'kv']
34
+
35
+ @prompt(priority=40, scope=["owner", "all"])
36
+ def zapier_prompt(self) -> str:
37
+ """Prompt describing Zapier capabilities"""
38
+ return """
39
+ Minimalistic Zapier integration for workflow automation. Available tools:
40
+
41
+ • zapier_setup(api_key) - Set up Zapier API credentials securely
42
+ • zapier_trigger(zap_id, data) - Trigger a specific Zap with optional input data
43
+ • zapier_list_zaps() - List all available Zaps in your Zapier account
44
+ • zapier_status(task_id) - Check the status of a Zap execution
45
+
46
+ Features:
47
+ - Secure API key storage via KV skill
48
+ - Per-user credential isolation via Auth skill
49
+ - Trigger Zaps with custom input data
50
+ - Monitor Zap execution status
51
+ - List and discover available Zaps
52
+
53
+ Setup: First run zapier_setup() with your Zapier API key from your account settings.
54
+ """
55
+
56
+ # Helper methods for auth and kv skills
57
+ async def _get_auth_skill(self):
58
+ """Get auth skill for user context"""
59
+ return self.agent.skills.get('auth')
60
+
61
+ async def _get_kv_skill(self):
62
+ """Get KV skill for secure storage"""
63
+ return self.agent.skills.get('kv')
64
+
65
+ async def _get_authenticated_user_id(self) -> Optional[str]:
66
+ """Get authenticated user ID from context"""
67
+ try:
68
+ context = get_context()
69
+ if context and context.auth and context.auth.authenticated:
70
+ return context.auth.user_id
71
+ return None
72
+ except Exception as e:
73
+ self.logger.error(f"Failed to get user context: {e}")
74
+ return None
75
+
76
+ async def _save_zapier_credentials(self, user_id: str, api_key: str) -> bool:
77
+ """Save Zapier credentials securely using KV skill"""
78
+ try:
79
+ kv_skill = await self._get_kv_skill()
80
+ if kv_skill:
81
+ credentials = {
82
+ 'api_key': api_key,
83
+ 'created_at': datetime.now().isoformat()
84
+ }
85
+ await kv_skill.kv_set(
86
+ key='credentials',
87
+ value=json.dumps(credentials),
88
+ namespace=f'zapier:{user_id}'
89
+ )
90
+ return True
91
+ else:
92
+ # Fallback to in-memory storage
93
+ if not hasattr(self.agent, '_zapier_credentials'):
94
+ self.agent._zapier_credentials = {}
95
+ self.agent._zapier_credentials[user_id] = {
96
+ 'api_key': api_key
97
+ }
98
+ return True
99
+ except Exception as e:
100
+ self.logger.error(f"Failed to save Zapier credentials: {e}")
101
+ return False
102
+
103
+ async def _load_zapier_credentials(self, user_id: str) -> Optional[Dict[str, str]]:
104
+ """Load Zapier credentials from KV skill"""
105
+ try:
106
+ kv_skill = await self._get_kv_skill()
107
+ if kv_skill:
108
+ credentials_json = await kv_skill.kv_get(
109
+ key='credentials',
110
+ namespace=f'zapier:{user_id}'
111
+ )
112
+ if credentials_json:
113
+ return json.loads(credentials_json)
114
+ else:
115
+ # Fallback to in-memory storage
116
+ if hasattr(self.agent, '_zapier_credentials'):
117
+ return self.agent._zapier_credentials.get(user_id)
118
+ return None
119
+ except Exception as e:
120
+ self.logger.error(f"Failed to load Zapier credentials: {e}")
121
+ return None
122
+
123
+ async def _make_zapier_request(self, method: str, endpoint: str, data: Optional[Dict] = None, user_id: str = None) -> Dict[str, Any]:
124
+ """Make authenticated request to Zapier API"""
125
+ if not user_id:
126
+ user_id = await self._get_authenticated_user_id()
127
+ if not user_id:
128
+ raise Exception("Authentication required")
129
+
130
+ credentials = await self._load_zapier_credentials(user_id)
131
+ if not credentials:
132
+ raise Exception("Zapier credentials not found. Please run zapier_setup() first.")
133
+
134
+ api_key = credentials['api_key']
135
+
136
+ headers = {
137
+ 'X-API-Key': api_key,
138
+ 'Content-Type': 'application/json'
139
+ }
140
+
141
+ url = f"{self.api_base}{endpoint}"
142
+
143
+ async with httpx.AsyncClient() as client:
144
+ if method.upper() == 'GET':
145
+ response = await client.get(url, headers=headers)
146
+ elif method.upper() == 'POST':
147
+ response = await client.post(url, headers=headers, json=data)
148
+ elif method.upper() == 'PUT':
149
+ response = await client.put(url, headers=headers, json=data)
150
+ elif method.upper() == 'DELETE':
151
+ response = await client.delete(url, headers=headers)
152
+ else:
153
+ raise Exception(f"Unsupported HTTP method: {method}")
154
+
155
+ response.raise_for_status()
156
+ return response.json() if response.content else {}
157
+
158
+ # Public tools
159
+ @tool(description="Set up Zapier API credentials securely. Get your API key from Zapier account settings.", scope="owner")
160
+ async def zapier_setup(self, api_key: str) -> str:
161
+ """Set up Zapier API credentials for secure access"""
162
+ user_id = await self._get_authenticated_user_id()
163
+ if not user_id:
164
+ return "❌ Authentication required"
165
+
166
+ if not api_key or not api_key.strip():
167
+ return "❌ API key is required. Get one from your Zapier account settings."
168
+
169
+ try:
170
+ # Test the API key by making a simple request
171
+ test_response = await self._make_zapier_request('GET', '/zaps', user_id=user_id)
172
+
173
+ # If test succeeds, save credentials
174
+ success = await self._save_zapier_credentials(user_id, api_key.strip())
175
+
176
+ if success:
177
+ zap_count = len(test_response.get('data', []))
178
+ return f"✅ Zapier credentials saved successfully!\n🔑 API key configured\n📊 Found {zap_count} Zaps in your account"
179
+ else:
180
+ return "❌ Failed to save credentials"
181
+
182
+ except httpx.HTTPStatusError as e:
183
+ if e.response.status_code == 401:
184
+ return "❌ Invalid API key. Please check your Zapier API key."
185
+ elif e.response.status_code == 403:
186
+ return "❌ API key doesn't have required permissions."
187
+ else:
188
+ return f"❌ API test failed: HTTP {e.response.status_code}"
189
+ except Exception as e:
190
+ return f"❌ Setup failed: {str(e)}"
191
+
192
+ @tool(description="Trigger a Zapier Zap with optional input data")
193
+ async def zapier_trigger(self, zap_id: str, data: Dict[str, Any] = None) -> str:
194
+ """Trigger a Zapier Zap with optional input data"""
195
+ user_id = await self._get_authenticated_user_id()
196
+ if not user_id:
197
+ return "❌ Authentication required"
198
+
199
+ if not zap_id or not zap_id.strip():
200
+ return "❌ Zap ID is required"
201
+
202
+ try:
203
+ # Prepare trigger data
204
+ trigger_data = data or {}
205
+
206
+ # Trigger the Zap
207
+ response = await self._make_zapier_request(
208
+ 'POST',
209
+ f'/zaps/{zap_id.strip()}/trigger',
210
+ trigger_data,
211
+ user_id
212
+ )
213
+
214
+ task_id = response.get('id', 'unknown')
215
+ status = response.get('status', 'triggered')
216
+
217
+ return f"✅ Zap triggered successfully!\n📋 Task ID: {task_id}\n📊 Status: {status}"
218
+
219
+ except httpx.HTTPStatusError as e:
220
+ if e.response.status_code == 401:
221
+ return "❌ Authentication failed. Please run zapier_setup() again."
222
+ elif e.response.status_code == 404:
223
+ return f"❌ Zap '{zap_id}' not found"
224
+ elif e.response.status_code == 403:
225
+ return "❌ Permission denied. Check if Zap is enabled and you have access."
226
+ else:
227
+ return f"❌ Trigger failed: HTTP {e.response.status_code}"
228
+ except Exception as e:
229
+ return f"❌ Error triggering Zap: {str(e)}"
230
+
231
+ @tool(description="List all available Zaps in your Zapier account")
232
+ async def zapier_list_zaps(self) -> str:
233
+ """List all available Zaps"""
234
+ user_id = await self._get_authenticated_user_id()
235
+ if not user_id:
236
+ return "❌ Authentication required"
237
+
238
+ try:
239
+ response = await self._make_zapier_request('GET', '/zaps', user_id=user_id)
240
+
241
+ zaps = response.get('data', [])
242
+
243
+ if not zaps:
244
+ return "📭 No Zaps found in your Zapier account"
245
+
246
+ result = ["📋 Available Zapier Zaps:\n"]
247
+
248
+ for zap in zaps:
249
+ zap_id = zap.get('id', 'unknown')
250
+ title = zap.get('title', 'Untitled Zap')
251
+ status = zap.get('status', 'unknown')
252
+
253
+ # Status icons
254
+ status_icon = "🟢" if status == 'on' else "🔴" if status == 'off' else "⚠️"
255
+
256
+ result.append(f"{status_icon} **{title}** (ID: {zap_id})")
257
+ result.append(f" 📊 Status: {status}")
258
+
259
+ # Add trigger and action info if available
260
+ steps = zap.get('steps', [])
261
+ if steps:
262
+ trigger_app = steps[0].get('app', {}).get('title', 'Unknown') if steps else 'Unknown'
263
+ result.append(f" 🔗 Trigger: {trigger_app}")
264
+
265
+ result.append("") # Empty line for spacing
266
+
267
+ result.append("💡 Use zapier_trigger(zap_id, data) to trigger a Zap")
268
+
269
+ return "\n".join(result)
270
+
271
+ except httpx.HTTPStatusError as e:
272
+ if e.response.status_code == 401:
273
+ return "❌ Authentication failed. Please run zapier_setup() again."
274
+ else:
275
+ return f"❌ Failed to list Zaps: HTTP {e.response.status_code}"
276
+ except Exception as e:
277
+ return f"❌ Error listing Zaps: {str(e)}"
278
+
279
+ @tool(description="Check the status of a Zap execution")
280
+ async def zapier_status(self, task_id: str) -> str:
281
+ """Check the status of a Zap execution"""
282
+ user_id = await self._get_authenticated_user_id()
283
+ if not user_id:
284
+ return "❌ Authentication required"
285
+
286
+ if not task_id or not task_id.strip():
287
+ return "❌ Task ID is required"
288
+
289
+ try:
290
+ response = await self._make_zapier_request(
291
+ 'GET',
292
+ f'/tasks/{task_id.strip()}',
293
+ user_id=user_id
294
+ )
295
+
296
+ status = response.get('status', 'unknown')
297
+ zap_id = response.get('zap_id', 'unknown')
298
+ created_at = response.get('created_at', 'unknown')
299
+ updated_at = response.get('updated_at', 'running')
300
+
301
+ # Status icons
302
+ status_icons = {
303
+ 'success': '✅',
304
+ 'error': '❌',
305
+ 'running': '🔄',
306
+ 'waiting': '⏳',
307
+ 'cancelled': '🚫',
308
+ 'throttled': '🐌'
309
+ }
310
+
311
+ status_icon = status_icons.get(status.lower(), '❓')
312
+
313
+ result = [
314
+ f"📊 Zap Execution Status Report",
315
+ f"🆔 Task ID: {task_id}",
316
+ f"🔧 Zap ID: {zap_id}",
317
+ f"{status_icon} Status: {status}",
318
+ f"🕐 Created: {created_at}",
319
+ f"🕑 Updated: {updated_at}"
320
+ ]
321
+
322
+ # Add error details if execution failed
323
+ if status.lower() == 'error' and 'error' in response:
324
+ error_msg = response.get('error', 'Unknown error')
325
+ result.append(f"❌ Error: {error_msg}")
326
+
327
+ return "\n".join(result)
328
+
329
+ except httpx.HTTPStatusError as e:
330
+ if e.response.status_code == 401:
331
+ return "❌ Authentication failed. Please run zapier_setup() again."
332
+ elif e.response.status_code == 404:
333
+ return f"❌ Task '{task_id}' not found"
334
+ else:
335
+ return f"❌ Status check failed: HTTP {e.response.status_code}"
336
+ except Exception as e:
337
+ return f"❌ Error checking status: {str(e)}"