fix(ci): fix black/ruff failures, add CoC and PR template

- Fix Python formatting (sync no longer strips blank lines from .py files)
- Remove test_community_build.py (tests private sync module)
- Fix ruff warnings in test files
- Add CODE_OF_CONDUCT.md
- Add .github/PULL_REQUEST_TEMPLATE.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
airano
2026-02-17 18:19:39 +03:30
parent c9083d86b1
commit c73e39f6e4
135 changed files with 1185 additions and 317 deletions

View File

@@ -10,6 +10,7 @@ from typing import Any
import aiohttp
class N8nClient:
"""
n8n REST API client for HTTP communication.

View File

@@ -5,6 +5,7 @@ from typing import Any
from plugins.n8n.client import N8nClient
def get_tool_specifications() -> list[dict[str, Any]]:
"""Return tool specifications for ToolGenerator"""
return [
@@ -85,9 +86,11 @@ def get_tool_specifications() -> list[dict[str, Any]]:
},
]
# === HANDLER FUNCTIONS ===
# Note: list_credentials is not supported in n8n Public API (GET /credentials returns 405)
async def get_credential(client: N8nClient, credential_id: str) -> str:
"""Get credential metadata"""
try:
@@ -106,6 +109,7 @@ async def get_credential(client: N8nClient, credential_id: str) -> str:
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def create_credential(client: N8nClient, name: str, type: str, data: dict[str, Any]) -> str:
"""Create a new credential"""
try:
@@ -124,6 +128,7 @@ async def create_credential(client: N8nClient, name: str, type: str, data: dict[
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def delete_credential(client: N8nClient, credential_id: str) -> str:
"""Delete a credential"""
try:
@@ -134,6 +139,7 @@ async def delete_credential(client: N8nClient, credential_id: str) -> str:
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def get_credential_schema(client: N8nClient, credential_type: str) -> str:
"""Get credential type schema"""
try:
@@ -144,6 +150,7 @@ async def get_credential_schema(client: N8nClient, credential_type: str) -> str:
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def transfer_credential(
client: N8nClient, credential_id: str, destination_project_id: str
) -> str:

View File

@@ -6,6 +6,7 @@ from typing import Any
from plugins.n8n.client import N8nClient
def get_tool_specifications() -> list[dict[str, Any]]:
"""Return tool specifications for ToolGenerator"""
return [
@@ -191,8 +192,10 @@ def get_tool_specifications() -> list[dict[str, Any]]:
},
]
# === HANDLER FUNCTIONS ===
async def list_executions(
client: N8nClient,
workflow_id: str | None = None,
@@ -238,6 +241,7 @@ async def list_executions(
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def get_execution(client: N8nClient, execution_id: str, include_data: bool = True) -> str:
"""Get execution details"""
try:
@@ -268,6 +272,7 @@ async def get_execution(client: N8nClient, execution_id: str, include_data: bool
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def delete_execution(client: N8nClient, execution_id: str) -> str:
"""Delete a single execution"""
try:
@@ -280,6 +285,7 @@ async def delete_execution(client: N8nClient, execution_id: str) -> str:
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def delete_executions(client: N8nClient, execution_ids: list[str]) -> str:
"""Bulk delete executions"""
try:
@@ -305,6 +311,7 @@ async def delete_executions(client: N8nClient, execution_ids: list[str]) -> str:
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def stop_execution(client: N8nClient, execution_id: str) -> str:
"""Stop a running execution"""
try:
@@ -317,6 +324,7 @@ async def stop_execution(client: N8nClient, execution_id: str) -> str:
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def retry_execution(client: N8nClient, execution_id: str) -> str:
"""Retry a failed execution"""
try:
@@ -360,6 +368,7 @@ async def retry_execution(client: N8nClient, execution_id: str) -> str:
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def get_execution_data(client: N8nClient, execution_id: str) -> str:
"""Get full execution data"""
try:
@@ -383,6 +392,7 @@ async def get_execution_data(client: N8nClient, execution_id: str) -> str:
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def wait_for_execution(
client: N8nClient, execution_id: str, timeout_seconds: int = 60, poll_interval: int = 2
) -> str:

View File

@@ -5,6 +5,7 @@ from typing import Any
from plugins.n8n.client import N8nClient
def get_tool_specifications() -> list[dict[str, Any]]:
"""Return tool specifications for ToolGenerator"""
return [
@@ -132,6 +133,7 @@ def get_tool_specifications() -> list[dict[str, Any]]:
},
]
async def list_projects(client: N8nClient, limit: int = 100, cursor: str | None = None) -> str:
try:
response = await client.list_projects(limit=limit, cursor=cursor)
@@ -146,6 +148,7 @@ async def list_projects(client: N8nClient, limit: int = 100, cursor: str | None
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def get_project(client: N8nClient, project_id: str) -> str:
try:
project = await client.get_project(project_id)
@@ -156,6 +159,7 @@ async def get_project(client: N8nClient, project_id: str) -> str:
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def create_project(client: N8nClient, name: str) -> str:
try:
project = await client.create_project(name)
@@ -170,6 +174,7 @@ async def create_project(client: N8nClient, name: str) -> str:
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def update_project(client: N8nClient, project_id: str, name: str) -> str:
try:
project = await client.update_project(project_id, name)
@@ -184,6 +189,7 @@ async def update_project(client: N8nClient, project_id: str, name: str) -> str:
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def delete_project(client: N8nClient, project_id: str) -> str:
try:
await client.delete_project(project_id)
@@ -191,6 +197,7 @@ async def delete_project(client: N8nClient, project_id: str) -> str:
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def add_project_users(client: N8nClient, project_id: str, users: list[dict[str, str]]) -> str:
try:
relations = [{"userId": u["user_id"], "role": u["role"]} for u in users]
@@ -202,6 +209,7 @@ async def add_project_users(client: N8nClient, project_id: str, users: list[dict
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def change_project_user_role(
client: N8nClient, project_id: str, user_id: str, role: str
) -> str:
@@ -217,6 +225,7 @@ async def change_project_user_role(
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def remove_project_user(client: N8nClient, project_id: str, user_id: str) -> str:
try:
await client.remove_project_user(project_id, user_id)

View File

@@ -5,6 +5,7 @@ from typing import Any
from plugins.n8n.client import N8nClient
def get_tool_specifications() -> list[dict[str, Any]]:
"""Return tool specifications for ToolGenerator"""
return [
@@ -60,6 +61,7 @@ def get_tool_specifications() -> list[dict[str, Any]]:
},
]
async def run_security_audit(client: N8nClient, categories: list[str] | None = None) -> str:
"""Run security audit"""
try:
@@ -84,6 +86,7 @@ async def run_security_audit(client: N8nClient, categories: list[str] | None = N
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def source_control_pull(
client: N8nClient, variables: dict[str, str] | None = None, force: bool = False
) -> str:
@@ -98,6 +101,7 @@ async def source_control_pull(
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def get_instance_info(client: N8nClient) -> str:
"""Get instance information"""
try:
@@ -129,6 +133,7 @@ async def get_instance_info(client: N8nClient) -> str:
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def health_check(client: N8nClient) -> str:
"""Check instance health"""
try:

View File

@@ -5,6 +5,7 @@ from typing import Any
from plugins.n8n.client import N8nClient
def get_tool_specifications() -> list[dict[str, Any]]:
"""Return tool specifications for ToolGenerator"""
return [
@@ -85,6 +86,7 @@ def get_tool_specifications() -> list[dict[str, Any]]:
},
]
async def list_tags(client: N8nClient, limit: int = 100, cursor: str | None = None) -> str:
try:
response = await client.list_tags(limit=limit, cursor=cursor)
@@ -99,6 +101,7 @@ async def list_tags(client: N8nClient, limit: int = 100, cursor: str | None = No
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def get_tag(client: N8nClient, tag_id: str) -> str:
try:
tag = await client.get_tag(tag_id)
@@ -108,6 +111,7 @@ async def get_tag(client: N8nClient, tag_id: str) -> str:
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def create_tag(client: N8nClient, name: str) -> str:
try:
tag = await client.create_tag(name)
@@ -122,6 +126,7 @@ async def create_tag(client: N8nClient, name: str) -> str:
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def update_tag(client: N8nClient, tag_id: str, name: str) -> str:
try:
tag = await client.update_tag(tag_id, name)
@@ -136,6 +141,7 @@ async def update_tag(client: N8nClient, tag_id: str, name: str) -> str:
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def delete_tag(client: N8nClient, tag_id: str) -> str:
try:
await client.delete_tag(tag_id)
@@ -143,6 +149,7 @@ async def delete_tag(client: N8nClient, tag_id: str) -> str:
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def delete_tags(client: N8nClient, tag_ids: list[str]) -> str:
try:
deleted, failed = [], []

View File

@@ -5,6 +5,7 @@ from typing import Any
from plugins.n8n.client import N8nClient
def get_tool_specifications() -> list[dict[str, Any]]:
"""Return tool specifications for ToolGenerator"""
return [
@@ -89,6 +90,7 @@ def get_tool_specifications() -> list[dict[str, Any]]:
},
]
async def list_users(
client: N8nClient, limit: int = 100, cursor: str | None = None, include_role: bool = True
) -> str:
@@ -115,6 +117,7 @@ async def list_users(
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def get_user(client: N8nClient, user_id: str, include_role: bool = True) -> str:
try:
user = await client.get_user(user_id, include_role)
@@ -135,6 +138,7 @@ async def get_user(client: N8nClient, user_id: str, include_role: bool = True) -
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def create_user(client: N8nClient, email: str, role: str = "global:member") -> str:
try:
users = [{"email": email, "role": role}]
@@ -150,6 +154,7 @@ async def create_user(client: N8nClient, email: str, role: str = "global:member"
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def delete_user(client: N8nClient, user_id: str) -> str:
try:
await client.delete_user(user_id)
@@ -157,6 +162,7 @@ async def delete_user(client: N8nClient, user_id: str) -> str:
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def change_user_role(client: N8nClient, user_id: str, new_role: str) -> str:
try:
await client.change_user_role(user_id, new_role)

View File

@@ -5,6 +5,7 @@ from typing import Any
from plugins.n8n.client import N8nClient
def get_tool_specifications() -> list[dict[str, Any]]:
"""Return tool specifications for ToolGenerator"""
return [
@@ -92,6 +93,7 @@ def get_tool_specifications() -> list[dict[str, Any]]:
},
]
async def list_variables(client: N8nClient, limit: int = 100, cursor: str | None = None) -> str:
try:
response = await client.list_variables(limit=limit, cursor=cursor)
@@ -106,6 +108,7 @@ async def list_variables(client: N8nClient, limit: int = 100, cursor: str | None
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def get_variable(client: N8nClient, key: str) -> str:
try:
variable = await client.get_variable(key)
@@ -119,6 +122,7 @@ async def get_variable(client: N8nClient, key: str) -> str:
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def create_variable(client: N8nClient, key: str, value: str) -> str:
try:
await client.create_variable(key, value)
@@ -133,6 +137,7 @@ async def create_variable(client: N8nClient, key: str, value: str) -> str:
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def update_variable(client: N8nClient, key: str, value: str) -> str:
try:
await client.update_variable(key, value)
@@ -147,6 +152,7 @@ async def update_variable(client: N8nClient, key: str, value: str) -> str:
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def delete_variable(client: N8nClient, key: str) -> str:
try:
await client.delete_variable(key)
@@ -154,6 +160,7 @@ async def delete_variable(client: N8nClient, key: str) -> str:
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def set_variables(client: N8nClient, variables: dict[str, str]) -> str:
try:
created, updated, failed = [], [], []

View File

@@ -5,6 +5,7 @@ from typing import Any
from plugins.n8n.client import N8nClient
def get_tool_specifications() -> list[dict[str, Any]]:
"""Return tool specifications for ToolGenerator"""
return [
@@ -319,8 +320,10 @@ def get_tool_specifications() -> list[dict[str, Any]]:
},
]
# === HANDLER FUNCTIONS ===
async def list_workflows(
client: N8nClient,
active: bool | None = None,
@@ -360,6 +363,7 @@ async def list_workflows(
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def get_workflow(client: N8nClient, workflow_id: str) -> str:
"""Get workflow details"""
try:
@@ -386,6 +390,7 @@ async def get_workflow(client: N8nClient, workflow_id: str) -> str:
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def create_workflow(
client: N8nClient,
name: str,
@@ -426,6 +431,7 @@ async def create_workflow(
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def update_workflow(
client: N8nClient,
workflow_id: str,
@@ -466,6 +472,7 @@ async def update_workflow(
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def delete_workflow(client: N8nClient, workflow_id: str) -> str:
"""Delete a workflow"""
try:
@@ -478,6 +485,7 @@ async def delete_workflow(client: N8nClient, workflow_id: str) -> str:
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def activate_workflow(client: N8nClient, workflow_id: str) -> str:
"""Activate a workflow"""
try:
@@ -498,6 +506,7 @@ async def activate_workflow(client: N8nClient, workflow_id: str) -> str:
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def deactivate_workflow(client: N8nClient, workflow_id: str) -> str:
"""Deactivate a workflow"""
try:
@@ -518,6 +527,7 @@ async def deactivate_workflow(client: N8nClient, workflow_id: str) -> str:
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def execute_workflow(client: N8nClient, workflow_id: str) -> str:
"""Execute a workflow manually"""
try:
@@ -538,6 +548,7 @@ async def execute_workflow(client: N8nClient, workflow_id: str) -> str:
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def execute_workflow_with_data(
client: N8nClient, workflow_id: str, data: dict[str, Any]
) -> str:
@@ -561,6 +572,7 @@ async def execute_workflow_with_data(
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def duplicate_workflow(client: N8nClient, workflow_id: str, new_name: str) -> str:
"""Duplicate a workflow"""
try:
@@ -589,6 +601,7 @@ async def duplicate_workflow(client: N8nClient, workflow_id: str, new_name: str)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def export_workflow(client: N8nClient, workflow_id: str) -> str:
"""Export workflow as JSON"""
try:
@@ -602,6 +615,7 @@ async def export_workflow(client: N8nClient, workflow_id: str) -> str:
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def import_workflow(
client: N8nClient, workflow_json: dict[str, Any], name_override: str | None = None
) -> str:
@@ -634,6 +648,7 @@ async def import_workflow(
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def get_workflow_tags(client: N8nClient, workflow_id: str) -> str:
"""Get tags assigned to a workflow"""
try:
@@ -646,6 +661,7 @@ async def get_workflow_tags(client: N8nClient, workflow_id: str) -> str:
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def set_workflow_tags(
client: N8nClient, workflow_id: str, tag_ids: list[str] | None = None
) -> str:

View File

@@ -12,6 +12,7 @@ from plugins.base import BasePlugin
from plugins.n8n import handlers
from plugins.n8n.client import N8nClient
class N8nPlugin(BasePlugin):
"""
n8n Automation Plugin - Comprehensive workflow management.