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.
- webagents/__init__.py +1 -1
- webagents/__main__.py +55 -0
- webagents/agents/__init__.py +1 -1
- webagents/agents/core/__init__.py +1 -1
- webagents/agents/core/base_agent.py +15 -15
- webagents/agents/core/handoffs.py +1 -1
- webagents/agents/skills/__init__.py +11 -11
- webagents/agents/skills/base.py +1 -1
- webagents/agents/skills/core/llm/litellm/__init__.py +1 -1
- webagents/agents/skills/core/llm/litellm/skill.py +1 -1
- webagents/agents/skills/core/mcp/README.md +2 -2
- webagents/agents/skills/core/mcp/skill.py +2 -2
- webagents/agents/skills/core/memory/long_term_memory/memory_skill.py +14 -14
- webagents/agents/skills/core/memory/short_term_memory/__init__.py +1 -1
- webagents/agents/skills/core/memory/short_term_memory/skill.py +1 -1
- webagents/agents/skills/core/memory/vector_memory/skill.py +6 -6
- webagents/agents/skills/core/planning/__init__.py +1 -1
- webagents/agents/skills/ecosystem/crewai/__init__.py +3 -1
- webagents/agents/skills/ecosystem/crewai/skill.py +158 -0
- webagents/agents/skills/ecosystem/database/__init__.py +3 -1
- webagents/agents/skills/ecosystem/database/skill.py +522 -0
- webagents/agents/skills/ecosystem/google/calendar/skill.py +1 -1
- webagents/agents/skills/ecosystem/mongodb/__init__.py +3 -0
- webagents/agents/skills/ecosystem/mongodb/skill.py +428 -0
- webagents/agents/skills/ecosystem/n8n/README.md +287 -0
- webagents/agents/skills/ecosystem/n8n/__init__.py +3 -0
- webagents/agents/skills/ecosystem/n8n/skill.py +341 -0
- webagents/agents/skills/ecosystem/x_com/README.md +401 -0
- webagents/agents/skills/ecosystem/x_com/__init__.py +3 -0
- webagents/agents/skills/ecosystem/x_com/skill.py +1048 -0
- webagents/agents/skills/ecosystem/zapier/README.md +363 -0
- webagents/agents/skills/ecosystem/zapier/__init__.py +3 -0
- webagents/agents/skills/ecosystem/zapier/skill.py +337 -0
- webagents/agents/skills/robutler/__init__.py +2 -2
- webagents/agents/skills/robutler/auth/__init__.py +3 -3
- webagents/agents/skills/robutler/auth/skill.py +16 -16
- webagents/agents/skills/robutler/crm/__init__.py +2 -2
- webagents/agents/skills/robutler/crm/skill.py +5 -5
- webagents/agents/skills/robutler/discovery/README.md +5 -5
- webagents/agents/skills/robutler/discovery/__init__.py +2 -2
- webagents/agents/skills/robutler/discovery/skill.py +21 -21
- webagents/agents/skills/robutler/message_history/__init__.py +2 -2
- webagents/agents/skills/robutler/message_history/skill.py +5 -5
- webagents/agents/skills/robutler/nli/__init__.py +1 -1
- webagents/agents/skills/robutler/nli/skill.py +9 -9
- webagents/agents/skills/robutler/payments/__init__.py +3 -3
- webagents/agents/skills/robutler/payments/exceptions.py +1 -1
- webagents/agents/skills/robutler/payments/skill.py +23 -23
- webagents/agents/skills/robutler/storage/__init__.py +2 -2
- webagents/agents/skills/robutler/storage/files/__init__.py +2 -2
- webagents/agents/skills/robutler/storage/files/skill.py +4 -4
- webagents/agents/skills/robutler/storage/json/__init__.py +1 -1
- webagents/agents/skills/robutler/storage/json/skill.py +3 -3
- webagents/agents/skills/robutler/storage/kv/skill.py +3 -3
- webagents/agents/skills/robutler/storage.py +6 -6
- webagents/agents/tools/decorators.py +12 -12
- webagents/server/__init__.py +3 -3
- webagents/server/context/context_vars.py +2 -2
- webagents/server/core/app.py +13 -13
- webagents/server/core/middleware.py +3 -3
- webagents/server/core/models.py +1 -1
- webagents/server/core/monitoring.py +2 -2
- webagents/server/middleware.py +1 -1
- webagents/server/models.py +2 -2
- webagents/server/monitoring.py +15 -15
- webagents/utils/logging.py +20 -20
- webagents-0.2.2.dist-info/METADATA +266 -0
- webagents-0.2.2.dist-info/RECORD +105 -0
- webagents-0.2.2.dist-info/licenses/LICENSE +20 -0
- webagents/api/__init__.py +0 -17
- webagents/api/client.py +0 -1207
- webagents/api/types.py +0 -253
- webagents-0.1.13.dist-info/METADATA +0 -32
- webagents-0.1.13.dist-info/RECORD +0 -96
- webagents-0.1.13.dist-info/licenses/LICENSE +0 -1
- {webagents-0.1.13.dist-info → webagents-0.2.2.dist-info}/WHEEL +0 -0
- {webagents-0.1.13.dist-info → webagents-0.2.2.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,287 @@
|
|
1
|
+
# n8n Skill for WebAgents
|
2
|
+
|
3
|
+
Minimalistic n8n integration for workflow automation. This skill allows you to securely manage n8n API credentials and execute workflows from your WebAgent.
|
4
|
+
|
5
|
+
## Features
|
6
|
+
|
7
|
+
- 🔐 **Secure credential storage** via auth and KV skills
|
8
|
+
- 🚀 **Execute workflows** with custom input data
|
9
|
+
- 📋 **List workflows** from your n8n instance
|
10
|
+
- 📊 **Monitor execution status** in real-time
|
11
|
+
- 🔒 **Per-user isolation** with authentication
|
12
|
+
- 🛡️ **API key validation** during setup
|
13
|
+
|
14
|
+
## Prerequisites
|
15
|
+
|
16
|
+
1. **n8n instance** (self-hosted or cloud)
|
17
|
+
2. **n8n API key** (generated from n8n Settings > n8n API)
|
18
|
+
3. **WebAgents framework** with auth and kv skills enabled
|
19
|
+
|
20
|
+
## Quick Start
|
21
|
+
|
22
|
+
### 1. Set up n8n API credentials
|
23
|
+
|
24
|
+
```python
|
25
|
+
# First, configure your n8n credentials
|
26
|
+
await agent.call_tool("n8n_setup",
|
27
|
+
api_key="n8n_api_key_here",
|
28
|
+
base_url="https://your-n8n-instance.com" # Optional, defaults to localhost:5678
|
29
|
+
)
|
30
|
+
```
|
31
|
+
|
32
|
+
### 2. List available workflows
|
33
|
+
|
34
|
+
```python
|
35
|
+
# See all workflows in your n8n instance
|
36
|
+
await agent.call_tool("n8n_list_workflows")
|
37
|
+
```
|
38
|
+
|
39
|
+
### 3. Execute a workflow
|
40
|
+
|
41
|
+
```python
|
42
|
+
# Execute a workflow with optional input data
|
43
|
+
await agent.call_tool("n8n_execute",
|
44
|
+
workflow_id="workflow_id_here",
|
45
|
+
data={"input_param": "value"} # Optional
|
46
|
+
)
|
47
|
+
```
|
48
|
+
|
49
|
+
### 4. Check execution status
|
50
|
+
|
51
|
+
```python
|
52
|
+
# Monitor workflow execution
|
53
|
+
await agent.call_tool("n8n_status",
|
54
|
+
execution_id="execution_id_from_execute_response"
|
55
|
+
)
|
56
|
+
```
|
57
|
+
|
58
|
+
## Tools Reference
|
59
|
+
|
60
|
+
### `n8n_setup(api_key, base_url=None)`
|
61
|
+
Set up n8n API credentials securely.
|
62
|
+
|
63
|
+
**Parameters:**
|
64
|
+
- `api_key` (str): Your n8n API key from Settings > n8n API
|
65
|
+
- `base_url` (str, optional): n8n instance URL (defaults to http://localhost:5678)
|
66
|
+
|
67
|
+
**Returns:**
|
68
|
+
- Success message with configuration details
|
69
|
+
- Error message if API key is invalid or instance unreachable
|
70
|
+
|
71
|
+
**Example:**
|
72
|
+
```python
|
73
|
+
result = await agent.call_tool("n8n_setup",
|
74
|
+
api_key="n8n_1a2b3c4d5e6f7g8h9i0j",
|
75
|
+
base_url="https://n8n.example.com"
|
76
|
+
)
|
77
|
+
# ✅ n8n credentials saved successfully!
|
78
|
+
# 🌐 Base URL: https://n8n.example.com
|
79
|
+
# 🔑 API key configured
|
80
|
+
```
|
81
|
+
|
82
|
+
### `n8n_execute(workflow_id, data=None)`
|
83
|
+
Execute an n8n workflow with optional input data.
|
84
|
+
|
85
|
+
**Parameters:**
|
86
|
+
- `workflow_id` (str): The ID of the workflow to execute
|
87
|
+
- `data` (dict, optional): Input data to pass to the workflow
|
88
|
+
|
89
|
+
**Returns:**
|
90
|
+
- Success message with execution ID and status
|
91
|
+
- Error message if workflow not found or execution fails
|
92
|
+
|
93
|
+
**Example:**
|
94
|
+
```python
|
95
|
+
result = await agent.call_tool("n8n_execute",
|
96
|
+
workflow_id="123",
|
97
|
+
data={"customer_email": "user@example.com", "order_id": "ORD-456"}
|
98
|
+
)
|
99
|
+
# ✅ Workflow executed successfully!
|
100
|
+
# 📋 Execution ID: exec_789
|
101
|
+
# 📊 Status: running
|
102
|
+
```
|
103
|
+
|
104
|
+
### `n8n_list_workflows()`
|
105
|
+
List all available workflows in your n8n instance.
|
106
|
+
|
107
|
+
**Parameters:** None
|
108
|
+
|
109
|
+
**Returns:**
|
110
|
+
- List of workflows with names, IDs, status, and tags
|
111
|
+
- Empty message if no workflows found
|
112
|
+
|
113
|
+
**Example:**
|
114
|
+
```python
|
115
|
+
result = await agent.call_tool("n8n_list_workflows")
|
116
|
+
# 📋 Available n8n Workflows:
|
117
|
+
#
|
118
|
+
# 🟢 **Customer Onboarding** (ID: 123)
|
119
|
+
# 🏷️ Tags: automation, customers
|
120
|
+
#
|
121
|
+
# 🔴 **Data Backup** (ID: 456)
|
122
|
+
# 🏷️ Tags: maintenance
|
123
|
+
#
|
124
|
+
# 💡 Use n8n_execute(workflow_id, data) to run a workflow
|
125
|
+
```
|
126
|
+
|
127
|
+
### `n8n_status(execution_id)`
|
128
|
+
Check the status of a workflow execution.
|
129
|
+
|
130
|
+
**Parameters:**
|
131
|
+
- `execution_id` (str): The execution ID returned from n8n_execute
|
132
|
+
|
133
|
+
**Returns:**
|
134
|
+
- Detailed execution status report
|
135
|
+
- Error message if execution not found
|
136
|
+
|
137
|
+
**Example:**
|
138
|
+
```python
|
139
|
+
result = await agent.call_tool("n8n_status", execution_id="exec_789")
|
140
|
+
# 📊 Execution Status Report
|
141
|
+
# 🆔 Execution ID: exec_789
|
142
|
+
# 🔧 Workflow ID: 123
|
143
|
+
# ✅ Status: success
|
144
|
+
# 🕐 Started: 2024-01-15T10:30:00Z
|
145
|
+
# 🕑 Finished: 2024-01-15T10:32:15Z
|
146
|
+
```
|
147
|
+
|
148
|
+
## Setup Guide
|
149
|
+
|
150
|
+
### Getting your n8n API Key
|
151
|
+
|
152
|
+
1. Open your n8n instance
|
153
|
+
2. Go to **Settings** > **n8n API**
|
154
|
+
3. Click **Create an API key**
|
155
|
+
4. Provide a label (e.g., "WebAgent Integration")
|
156
|
+
5. Set expiration time (or leave blank for no expiration)
|
157
|
+
6. Copy the generated API key
|
158
|
+
|
159
|
+
### Environment Configuration
|
160
|
+
|
161
|
+
You can set a default n8n URL via environment variable:
|
162
|
+
|
163
|
+
```bash
|
164
|
+
export N8N_BASE_URL=https://your-n8n-instance.com
|
165
|
+
```
|
166
|
+
|
167
|
+
## Usage Examples
|
168
|
+
|
169
|
+
### Simple Workflow Execution
|
170
|
+
```python
|
171
|
+
# Setup (one-time)
|
172
|
+
await agent.call_tool("n8n_setup", api_key="your_api_key")
|
173
|
+
|
174
|
+
# Execute a simple workflow
|
175
|
+
result = await agent.call_tool("n8n_execute", workflow_id="welcome_email")
|
176
|
+
print(result) # ✅ Workflow executed successfully!
|
177
|
+
```
|
178
|
+
|
179
|
+
### Workflow with Input Data
|
180
|
+
```python
|
181
|
+
# Execute workflow with custom data
|
182
|
+
customer_data = {
|
183
|
+
"name": "John Doe",
|
184
|
+
"email": "john@example.com",
|
185
|
+
"subscription": "premium"
|
186
|
+
}
|
187
|
+
|
188
|
+
result = await agent.call_tool("n8n_execute",
|
189
|
+
workflow_id="customer_welcome",
|
190
|
+
data=customer_data
|
191
|
+
)
|
192
|
+
|
193
|
+
# Monitor execution
|
194
|
+
execution_id = "exec_123" # Extract from result
|
195
|
+
status = await agent.call_tool("n8n_status", execution_id=execution_id)
|
196
|
+
print(status)
|
197
|
+
```
|
198
|
+
|
199
|
+
### Discover and Execute Workflows
|
200
|
+
```python
|
201
|
+
# First, see what workflows are available
|
202
|
+
workflows = await agent.call_tool("n8n_list_workflows")
|
203
|
+
print(workflows)
|
204
|
+
|
205
|
+
# Execute a specific workflow
|
206
|
+
result = await agent.call_tool("n8n_execute",
|
207
|
+
workflow_id="data_processing",
|
208
|
+
data={"source": "api", "format": "json"}
|
209
|
+
)
|
210
|
+
```
|
211
|
+
|
212
|
+
## Error Handling
|
213
|
+
|
214
|
+
The skill provides clear error messages for common scenarios:
|
215
|
+
|
216
|
+
- **❌ Authentication required** - User not authenticated
|
217
|
+
- **❌ API key is required** - Empty or missing API key
|
218
|
+
- **❌ Invalid API key** - API key rejected by n8n
|
219
|
+
- **❌ n8n instance not found** - Base URL unreachable
|
220
|
+
- **❌ Workflow not found** - Invalid workflow ID
|
221
|
+
- **❌ Execution not found** - Invalid execution ID
|
222
|
+
|
223
|
+
## Security
|
224
|
+
|
225
|
+
- **API keys** are stored securely using the KV skill with per-user namespacing
|
226
|
+
- **User isolation** ensures each user can only access their own credentials
|
227
|
+
- **Authentication required** for all operations
|
228
|
+
- **Automatic validation** of API keys during setup
|
229
|
+
- **Memory fallback** available if KV skill unavailable
|
230
|
+
|
231
|
+
## Architecture
|
232
|
+
|
233
|
+
```
|
234
|
+
WebAgent
|
235
|
+
├── Auth Skill (user context)
|
236
|
+
├── KV Skill (secure storage)
|
237
|
+
└── n8n Skill
|
238
|
+
├── Credential Management
|
239
|
+
├── API Communication
|
240
|
+
└── Workflow Operations
|
241
|
+
```
|
242
|
+
|
243
|
+
## Dependencies
|
244
|
+
|
245
|
+
- `auth` skill - For user authentication and context
|
246
|
+
- `kv` skill - For secure credential storage
|
247
|
+
- `httpx` - For HTTP API communication
|
248
|
+
|
249
|
+
## Troubleshooting
|
250
|
+
|
251
|
+
### "Authentication required"
|
252
|
+
Ensure your agent has proper authentication configured.
|
253
|
+
|
254
|
+
### "Invalid API key"
|
255
|
+
- Verify the API key is correct
|
256
|
+
- Check if the key has expired
|
257
|
+
- Ensure the key has necessary permissions
|
258
|
+
|
259
|
+
### "n8n instance not found"
|
260
|
+
- Verify the base URL is correct
|
261
|
+
- Check network connectivity
|
262
|
+
- Ensure n8n instance is running
|
263
|
+
|
264
|
+
### "Workflow not found"
|
265
|
+
- Use `n8n_list_workflows()` to see available workflows
|
266
|
+
- Check if the workflow ID is correct
|
267
|
+
- Verify the workflow is saved in n8n
|
268
|
+
|
269
|
+
## Limitations
|
270
|
+
|
271
|
+
- Supports n8n API v1 endpoints
|
272
|
+
- Requires n8n instance with API access enabled
|
273
|
+
- Limited to workflow execution and status monitoring
|
274
|
+
- Does not support workflow creation or modification
|
275
|
+
|
276
|
+
## Contributing
|
277
|
+
|
278
|
+
To extend this skill:
|
279
|
+
|
280
|
+
1. Add new tools following the existing pattern
|
281
|
+
2. Update tests in `tests/test_n8n_skill.py`
|
282
|
+
3. Update this documentation
|
283
|
+
4. Ensure proper error handling and user feedback
|
284
|
+
|
285
|
+
## License
|
286
|
+
|
287
|
+
Part of the WebAgents framework. See main project license.
|
@@ -0,0 +1,341 @@
|
|
1
|
+
"""
|
2
|
+
Minimalistic n8n Skill for WebAgents
|
3
|
+
|
4
|
+
This skill allows users to:
|
5
|
+
- Set up n8n API key securely (via auth/kv skills)
|
6
|
+
- Execute n8n workflows
|
7
|
+
- List available workflows
|
8
|
+
- Get workflow 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 N8nSkill(Skill):
|
25
|
+
"""Minimalistic n8n skill for workflow automation"""
|
26
|
+
|
27
|
+
def __init__(self):
|
28
|
+
super().__init__()
|
29
|
+
self.default_n8n_url = os.getenv('N8N_BASE_URL', 'http://localhost:5678')
|
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 n8n_prompt(self) -> str:
|
37
|
+
"""Prompt describing n8n capabilities"""
|
38
|
+
return """
|
39
|
+
Minimalistic n8n integration for workflow automation. Available tools:
|
40
|
+
|
41
|
+
• n8n_setup(api_key, base_url) - Set up n8n API credentials securely
|
42
|
+
• n8n_execute(workflow_id, data) - Execute a specific workflow with optional input data
|
43
|
+
• n8n_list_workflows() - List all available workflows in your n8n instance
|
44
|
+
• n8n_status(execution_id) - Check the status of a workflow execution
|
45
|
+
|
46
|
+
Features:
|
47
|
+
- Secure API key storage via KV skill
|
48
|
+
- Per-user credential isolation via Auth skill
|
49
|
+
- Execute workflows with custom input data
|
50
|
+
- Monitor workflow execution status
|
51
|
+
- List and discover available workflows
|
52
|
+
|
53
|
+
Setup: First run n8n_setup() with your n8n API key and instance URL.
|
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_n8n_credentials(self, user_id: str, api_key: str, base_url: str) -> bool:
|
77
|
+
"""Save n8n 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
|
+
'base_url': base_url,
|
84
|
+
'created_at': datetime.now().isoformat()
|
85
|
+
}
|
86
|
+
await kv_skill.kv_set(
|
87
|
+
key='credentials',
|
88
|
+
value=json.dumps(credentials),
|
89
|
+
namespace=f'n8n:{user_id}'
|
90
|
+
)
|
91
|
+
return True
|
92
|
+
else:
|
93
|
+
# Fallback to in-memory storage
|
94
|
+
if not hasattr(self.agent, '_n8n_credentials'):
|
95
|
+
self.agent._n8n_credentials = {}
|
96
|
+
self.agent._n8n_credentials[user_id] = {
|
97
|
+
'api_key': api_key,
|
98
|
+
'base_url': base_url
|
99
|
+
}
|
100
|
+
return True
|
101
|
+
except Exception as e:
|
102
|
+
self.logger.error(f"Failed to save n8n credentials: {e}")
|
103
|
+
return False
|
104
|
+
|
105
|
+
async def _load_n8n_credentials(self, user_id: str) -> Optional[Dict[str, str]]:
|
106
|
+
"""Load n8n credentials from KV skill"""
|
107
|
+
try:
|
108
|
+
kv_skill = await self._get_kv_skill()
|
109
|
+
if kv_skill:
|
110
|
+
credentials_json = await kv_skill.kv_get(
|
111
|
+
key='credentials',
|
112
|
+
namespace=f'n8n:{user_id}'
|
113
|
+
)
|
114
|
+
if credentials_json:
|
115
|
+
return json.loads(credentials_json)
|
116
|
+
else:
|
117
|
+
# Fallback to in-memory storage
|
118
|
+
if hasattr(self.agent, '_n8n_credentials'):
|
119
|
+
return self.agent._n8n_credentials.get(user_id)
|
120
|
+
return None
|
121
|
+
except Exception as e:
|
122
|
+
self.logger.error(f"Failed to load n8n credentials: {e}")
|
123
|
+
return None
|
124
|
+
|
125
|
+
async def _make_n8n_request(self, method: str, endpoint: str, data: Optional[Dict] = None, user_id: str = None) -> Dict[str, Any]:
|
126
|
+
"""Make authenticated request to n8n API"""
|
127
|
+
if not user_id:
|
128
|
+
user_id = await self._get_authenticated_user_id()
|
129
|
+
if not user_id:
|
130
|
+
raise Exception("Authentication required")
|
131
|
+
|
132
|
+
credentials = await self._load_n8n_credentials(user_id)
|
133
|
+
if not credentials:
|
134
|
+
raise Exception("n8n credentials not found. Please run n8n_setup() first.")
|
135
|
+
|
136
|
+
api_key = credentials['api_key']
|
137
|
+
base_url = credentials['base_url'].rstrip('/')
|
138
|
+
|
139
|
+
headers = {
|
140
|
+
'X-N8N-API-KEY': api_key,
|
141
|
+
'Content-Type': 'application/json'
|
142
|
+
}
|
143
|
+
|
144
|
+
url = f"{base_url}/api/v1{endpoint}"
|
145
|
+
|
146
|
+
async with httpx.AsyncClient() as client:
|
147
|
+
if method.upper() == 'GET':
|
148
|
+
response = await client.get(url, headers=headers)
|
149
|
+
elif method.upper() == 'POST':
|
150
|
+
response = await client.post(url, headers=headers, json=data)
|
151
|
+
elif method.upper() == 'PUT':
|
152
|
+
response = await client.put(url, headers=headers, json=data)
|
153
|
+
elif method.upper() == 'DELETE':
|
154
|
+
response = await client.delete(url, headers=headers)
|
155
|
+
else:
|
156
|
+
raise Exception(f"Unsupported HTTP method: {method}")
|
157
|
+
|
158
|
+
response.raise_for_status()
|
159
|
+
return response.json() if response.content else {}
|
160
|
+
|
161
|
+
# Public tools
|
162
|
+
@tool(description="Set up n8n API credentials securely. Get your API key from n8n Settings > n8n API.", scope="owner")
|
163
|
+
async def n8n_setup(self, api_key: str, base_url: str = None) -> str:
|
164
|
+
"""Set up n8n API credentials for secure access"""
|
165
|
+
user_id = await self._get_authenticated_user_id()
|
166
|
+
if not user_id:
|
167
|
+
return "❌ Authentication required"
|
168
|
+
|
169
|
+
if not api_key or not api_key.strip():
|
170
|
+
return "❌ API key is required. Generate one from n8n Settings > n8n API."
|
171
|
+
|
172
|
+
# Use provided base_url or default
|
173
|
+
n8n_url = base_url or self.default_n8n_url
|
174
|
+
if not n8n_url.startswith(('http://', 'https://')):
|
175
|
+
n8n_url = f"https://{n8n_url}"
|
176
|
+
|
177
|
+
try:
|
178
|
+
# Test the API key by making a simple request
|
179
|
+
await self._make_n8n_request('GET', '/workflows', user_id=user_id)
|
180
|
+
|
181
|
+
# If test succeeds, save credentials
|
182
|
+
success = await self._save_n8n_credentials(user_id, api_key.strip(), n8n_url)
|
183
|
+
|
184
|
+
if success:
|
185
|
+
return f"✅ n8n credentials saved successfully!\n🌐 Base URL: {n8n_url}\n🔑 API key configured"
|
186
|
+
else:
|
187
|
+
return "❌ Failed to save credentials"
|
188
|
+
|
189
|
+
except httpx.HTTPStatusError as e:
|
190
|
+
if e.response.status_code == 401:
|
191
|
+
return "❌ Invalid API key. Please check your n8n API key."
|
192
|
+
elif e.response.status_code == 404:
|
193
|
+
return f"❌ n8n instance not found at {n8n_url}. Please check the URL."
|
194
|
+
else:
|
195
|
+
return f"❌ API test failed: HTTP {e.response.status_code}"
|
196
|
+
except Exception as e:
|
197
|
+
return f"❌ Setup failed: {str(e)}"
|
198
|
+
|
199
|
+
@tool(description="Execute an n8n workflow with optional input data")
|
200
|
+
async def n8n_execute(self, workflow_id: str, data: Dict[str, Any] = None) -> str:
|
201
|
+
"""Execute an n8n workflow with optional input data"""
|
202
|
+
user_id = await self._get_authenticated_user_id()
|
203
|
+
if not user_id:
|
204
|
+
return "❌ Authentication required"
|
205
|
+
|
206
|
+
if not workflow_id or not workflow_id.strip():
|
207
|
+
return "❌ Workflow ID is required"
|
208
|
+
|
209
|
+
try:
|
210
|
+
# Prepare execution data
|
211
|
+
execution_data = {}
|
212
|
+
if data:
|
213
|
+
execution_data = data
|
214
|
+
|
215
|
+
# Execute the workflow
|
216
|
+
response = await self._make_n8n_request(
|
217
|
+
'POST',
|
218
|
+
f'/workflows/{workflow_id.strip()}/execute',
|
219
|
+
execution_data,
|
220
|
+
user_id
|
221
|
+
)
|
222
|
+
|
223
|
+
execution_id = response.get('id', 'unknown')
|
224
|
+
status = response.get('status', 'unknown')
|
225
|
+
|
226
|
+
return f"✅ Workflow executed successfully!\n📋 Execution ID: {execution_id}\n📊 Status: {status}"
|
227
|
+
|
228
|
+
except httpx.HTTPStatusError as e:
|
229
|
+
if e.response.status_code == 401:
|
230
|
+
return "❌ Authentication failed. Please run n8n_setup() again."
|
231
|
+
elif e.response.status_code == 404:
|
232
|
+
return f"❌ Workflow '{workflow_id}' not found"
|
233
|
+
else:
|
234
|
+
return f"❌ Execution failed: HTTP {e.response.status_code}"
|
235
|
+
except Exception as e:
|
236
|
+
return f"❌ Error executing workflow: {str(e)}"
|
237
|
+
|
238
|
+
@tool(description="List all available workflows in your n8n instance")
|
239
|
+
async def n8n_list_workflows(self) -> str:
|
240
|
+
"""List all available workflows"""
|
241
|
+
user_id = await self._get_authenticated_user_id()
|
242
|
+
if not user_id:
|
243
|
+
return "❌ Authentication required"
|
244
|
+
|
245
|
+
try:
|
246
|
+
response = await self._make_n8n_request('GET', '/workflows', user_id=user_id)
|
247
|
+
|
248
|
+
workflows = response.get('data', [])
|
249
|
+
|
250
|
+
if not workflows:
|
251
|
+
return "📭 No workflows found in your n8n instance"
|
252
|
+
|
253
|
+
result = ["📋 Available n8n Workflows:\n"]
|
254
|
+
|
255
|
+
for workflow in workflows:
|
256
|
+
workflow_id = workflow.get('id', 'unknown')
|
257
|
+
name = workflow.get('name', 'Unnamed')
|
258
|
+
active = workflow.get('active', False)
|
259
|
+
status_icon = "🟢" if active else "🔴"
|
260
|
+
|
261
|
+
result.append(f"{status_icon} **{name}** (ID: {workflow_id})")
|
262
|
+
|
263
|
+
# Add tags if available
|
264
|
+
tags = workflow.get('tags', [])
|
265
|
+
if tags:
|
266
|
+
tag_names = [tag.get('name', 'Unknown') for tag in tags]
|
267
|
+
result.append(f" 🏷️ Tags: {', '.join(tag_names)}")
|
268
|
+
|
269
|
+
result.append("") # Empty line for spacing
|
270
|
+
|
271
|
+
result.append("💡 Use n8n_execute(workflow_id, data) to run a workflow")
|
272
|
+
|
273
|
+
return "\n".join(result)
|
274
|
+
|
275
|
+
except httpx.HTTPStatusError as e:
|
276
|
+
if e.response.status_code == 401:
|
277
|
+
return "❌ Authentication failed. Please run n8n_setup() again."
|
278
|
+
else:
|
279
|
+
return f"❌ Failed to list workflows: HTTP {e.response.status_code}"
|
280
|
+
except Exception as e:
|
281
|
+
return f"❌ Error listing workflows: {str(e)}"
|
282
|
+
|
283
|
+
@tool(description="Check the status of a workflow execution")
|
284
|
+
async def n8n_status(self, execution_id: str) -> str:
|
285
|
+
"""Check the status of a workflow execution"""
|
286
|
+
user_id = await self._get_authenticated_user_id()
|
287
|
+
if not user_id:
|
288
|
+
return "❌ Authentication required"
|
289
|
+
|
290
|
+
if not execution_id or not execution_id.strip():
|
291
|
+
return "❌ Execution ID is required"
|
292
|
+
|
293
|
+
try:
|
294
|
+
response = await self._make_n8n_request(
|
295
|
+
'GET',
|
296
|
+
f'/executions/{execution_id.strip()}',
|
297
|
+
user_id=user_id
|
298
|
+
)
|
299
|
+
|
300
|
+
status = response.get('status', 'unknown')
|
301
|
+
workflow_id = response.get('workflowId', 'unknown')
|
302
|
+
start_time = response.get('startedAt', 'unknown')
|
303
|
+
end_time = response.get('stoppedAt', 'running')
|
304
|
+
|
305
|
+
# Status icons
|
306
|
+
status_icons = {
|
307
|
+
'success': '✅',
|
308
|
+
'error': '❌',
|
309
|
+
'running': '🔄',
|
310
|
+
'waiting': '⏳',
|
311
|
+
'canceled': '🚫'
|
312
|
+
}
|
313
|
+
|
314
|
+
status_icon = status_icons.get(status.lower(), '❓')
|
315
|
+
|
316
|
+
result = [
|
317
|
+
f"📊 Execution Status Report",
|
318
|
+
f"🆔 Execution ID: {execution_id}",
|
319
|
+
f"🔧 Workflow ID: {workflow_id}",
|
320
|
+
f"{status_icon} Status: {status}",
|
321
|
+
f"🕐 Started: {start_time}",
|
322
|
+
f"🕑 Finished: {end_time}"
|
323
|
+
]
|
324
|
+
|
325
|
+
# Add error details if execution failed
|
326
|
+
if status.lower() == 'error' and 'data' in response:
|
327
|
+
error_data = response.get('data', {})
|
328
|
+
if 'resultData' in error_data:
|
329
|
+
result.append(f"❌ Error details available in execution data")
|
330
|
+
|
331
|
+
return "\n".join(result)
|
332
|
+
|
333
|
+
except httpx.HTTPStatusError as e:
|
334
|
+
if e.response.status_code == 401:
|
335
|
+
return "❌ Authentication failed. Please run n8n_setup() again."
|
336
|
+
elif e.response.status_code == 404:
|
337
|
+
return f"❌ Execution '{execution_id}' not found"
|
338
|
+
else:
|
339
|
+
return f"❌ Status check failed: HTTP {e.response.status_code}"
|
340
|
+
except Exception as e:
|
341
|
+
return f"❌ Error checking status: {str(e)}"
|