signalpilot-ai-internal 0.10.0__py3-none-any.whl → 0.11.24__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.
- signalpilot_ai_internal/__init__.py +1 -0
- signalpilot_ai_internal/_version.py +1 -1
- signalpilot_ai_internal/cache_service.py +22 -21
- signalpilot_ai_internal/composio_handlers.py +224 -0
- signalpilot_ai_internal/composio_service.py +511 -0
- signalpilot_ai_internal/database_config_handlers.py +182 -0
- signalpilot_ai_internal/database_config_service.py +166 -0
- signalpilot_ai_internal/databricks_schema_service.py +907 -0
- signalpilot_ai_internal/file_scanner_service.py +5 -146
- signalpilot_ai_internal/handlers.py +388 -9
- signalpilot_ai_internal/integrations_config.py +256 -0
- signalpilot_ai_internal/log_utils.py +31 -0
- signalpilot_ai_internal/mcp_handlers.py +532 -0
- signalpilot_ai_internal/mcp_server_manager.py +298 -0
- signalpilot_ai_internal/mcp_service.py +1255 -0
- signalpilot_ai_internal/oauth_token_store.py +141 -0
- signalpilot_ai_internal/schema_search_config.yml +17 -11
- signalpilot_ai_internal/schema_search_service.py +85 -4
- signalpilot_ai_internal/signalpilot_home.py +961 -0
- signalpilot_ai_internal/snowflake_schema_service.py +2 -0
- signalpilot_ai_internal/test_dbt_mcp_server.py +180 -0
- signalpilot_ai_internal/unified_database_schema_service.py +2 -0
- signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/schemas/signalpilot-ai-internal/package.json.orig → signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/package.json +15 -48
- signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/package.json → signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/schemas/signalpilot-ai-internal/package.json.orig +9 -52
- {signalpilot_ai_internal-0.10.0.data → signalpilot_ai_internal-0.11.24.data}/data/share/jupyter/labextensions/signalpilot-ai-internal/schemas/signalpilot-ai-internal/plugin.json +7 -1
- signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/122.bab318d6caadb055e29c.js +1 -0
- signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/129.868ca665e6fc225c20a0.js +1 -0
- signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/179.fd45a2e75d471d0aa3b9.js +7 -0
- signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/220.81105a94aa873fc51a94.js +1 -0
- signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/262.a002dd4630d3b6404a90.js +1 -0
- signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/353.cc6f6ecacd703bcdb468.js +1 -0
- signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/364.817a883549d55a0e0576.js +1 -0
- signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/384.a4daecd44f1e9364e44a.js +1 -0
- signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/439.667225aab294fb5ed161.js +1 -0
- signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/447.8138af2522716e5a926f.js +1 -0
- signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/476.925c73e32f3c07448da0.js +1 -0
- signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/477.aaa4cc9e87801fb45f5b.js +1 -0
- signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/481.370056149a59022b700c.js +1 -0
- signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/510.868ca665e6fc225c20a0.js +1 -0
- signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/512.835f97f7ccfc70ff5c93.js +1 -0
- signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/57.6c13335f73de089d6b1e.js +1 -0
- signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/574.ad2709e91ebcac5bbe68.js +1 -0
- signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/635.bddbab8e464fe31f0393.js +1 -0
- signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/713.fda1bcdb10497b0a6ade.js +1 -0
- signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/741.d046701f475fcbf6697d.js +1 -0
- signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/785.c306dffd4cfe8a613d13.js +1 -0
- signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/801.e39898b6f336539f228c.js +1 -0
- signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/880.77cc0ca10a1860df1b52.js +1 -0
- signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/936.4e2850b2af985ed0d378.js +1 -0
- signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/956.eeffe67d7781fd63ef4b.js +2 -0
- signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/remoteEntry.055f50d20a31f3068c72.js +1 -0
- {signalpilot_ai_internal-0.10.0.data → signalpilot_ai_internal-0.11.24.data}/data/share/jupyter/labextensions/signalpilot-ai-internal/static/third-party-licenses.json +47 -29
- {signalpilot_ai_internal-0.10.0.dist-info → signalpilot_ai_internal-0.11.24.dist-info}/METADATA +14 -31
- signalpilot_ai_internal-0.11.24.dist-info/RECORD +66 -0
- signalpilot_ai_internal-0.11.24.dist-info/licenses/LICENSE +7 -0
- signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/122.e2dadf63dc64d7b5f1ee.js +0 -1
- signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/220.328403b5545f268b95c6.js +0 -1
- signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/262.726e1da31a50868cb297.js +0 -1
- signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/330.af2e9cb5def5ae2b84d5.js +0 -1
- signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/353.972abe1d2d66f083f9cc.js +0 -1
- signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/364.dbec4c2dc12e7b050dcc.js +0 -1
- signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/384.fa432bdb7fb6b1c95ad6.js +0 -1
- signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/439.37e271d7a80336daabe2.js +0 -1
- signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/476.ad22ccddd74ee306fb56.js +0 -1
- signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/481.73c7a9290b7d35a8b9c1.js +0 -1
- signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/512.b58fc0093d080b8ee61c.js +0 -1
- signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/553.b4042a795c91d9ff71ef.js +0 -2
- signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/57.e9acd2e1f9739037f1ab.js +0 -1
- signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/635.9720593ee20b768da3ca.js +0 -1
- signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/713.8e6edc9a965bdd578ca7.js +0 -1
- signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/741.dc49867fafb03ea2ba4d.js +0 -1
- signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/742.91e7b516c8699eea3373.js +0 -1
- signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/785.2d75de1a8d2c3131a8db.js +0 -1
- signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/786.770dc7bcab77e14cc135.js +0 -7
- signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/801.ca9e114a30896b669a3c.js +0 -1
- signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/880.25ddd15aca09421d3765.js +0 -1
- signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/888.34054db17bcf6e87ec95.js +0 -1
- signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/remoteEntry.b05b2f0c9617ba28370d.js +0 -1
- signalpilot_ai_internal-0.10.0.dist-info/RECORD +0 -50
- signalpilot_ai_internal-0.10.0.dist-info/licenses/LICENSE +0 -29
- {signalpilot_ai_internal-0.10.0.data → signalpilot_ai_internal-0.11.24.data}/data/etc/jupyter/jupyter_server_config.d/signalpilot_ai.json +0 -0
- {signalpilot_ai_internal-0.10.0.data → signalpilot_ai_internal-0.11.24.data}/data/share/jupyter/labextensions/signalpilot-ai-internal/install.json +0 -0
- /signalpilot_ai_internal-0.10.0.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/553.b4042a795c91d9ff71ef.js.LICENSE.txt → /signalpilot_ai_internal-0.11.24.data/data/share/jupyter/labextensions/signalpilot-ai-internal/static/956.eeffe67d7781fd63ef4b.js.LICENSE.txt +0 -0
- {signalpilot_ai_internal-0.10.0.data → signalpilot_ai_internal-0.11.24.data}/data/share/jupyter/labextensions/signalpilot-ai-internal/static/style.js +0 -0
- {signalpilot_ai_internal-0.10.0.dist-info → signalpilot_ai_internal-0.11.24.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Integration Configuration for Composio OAuth Apps
|
|
3
|
+
This file contains static configuration for integrations with MCP servers.
|
|
4
|
+
IMPORTANT: This file is NOT exposed to the frontend for security reasons.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import base64
|
|
8
|
+
import json
|
|
9
|
+
import logging
|
|
10
|
+
from typing import Dict, List, Any
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
# Integration configuration - defines how to set up MCP servers after OAuth
|
|
15
|
+
INTEGRATION_CONFIG: Dict[str, Dict[str, Any]] = {
|
|
16
|
+
'notion': {
|
|
17
|
+
'id': 'notion',
|
|
18
|
+
'name': 'Notion',
|
|
19
|
+
'description': 'Connect to Notion workspaces for reading and searching pages',
|
|
20
|
+
'mcp_server_id': 'notion-integration',
|
|
21
|
+
'mcp_server_name': 'Notion (Composio)',
|
|
22
|
+
'mcp_command': 'npx',
|
|
23
|
+
'mcp_args': ['-y', '@notionhq/notion-mcp-server'],
|
|
24
|
+
# Maps Composio credential fields to MCP server env vars
|
|
25
|
+
'env_mapping': {
|
|
26
|
+
'access_token': 'NOTION_TOKEN',
|
|
27
|
+
},
|
|
28
|
+
'whitelisted_tools': [
|
|
29
|
+
'API-post-search',
|
|
30
|
+
'API-get-block-children',
|
|
31
|
+
'API-retrieve-a-page',
|
|
32
|
+
'API-retrieve-a-database',
|
|
33
|
+
'API-post-database-query',
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
'slack': {
|
|
37
|
+
'id': 'slack',
|
|
38
|
+
'name': 'Slack',
|
|
39
|
+
'description': 'Connect to Slack workspaces for searching and reading messages',
|
|
40
|
+
'mcp_server_id': 'slack-integration',
|
|
41
|
+
'mcp_server_name': 'Slack (Composio)',
|
|
42
|
+
# Note: This requires the slack-mcp-server to be installed
|
|
43
|
+
# Using npx with a placeholder - user may need to adjust based on their setup
|
|
44
|
+
'mcp_command': 'npx',
|
|
45
|
+
'mcp_args': ['-y', 'slack-mcp-server@latest', '--transport', 'stdio'],
|
|
46
|
+
# Maps Composio credential fields to MCP server env vars
|
|
47
|
+
'env_mapping': {
|
|
48
|
+
'access_token': 'SLACK_MCP_XOXP_TOKEN',
|
|
49
|
+
},
|
|
50
|
+
'whitelisted_tools': [
|
|
51
|
+
'conversations_search_messages',
|
|
52
|
+
'conversations_history',
|
|
53
|
+
'conversations_replies',
|
|
54
|
+
'channels_list',
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
'google': {
|
|
58
|
+
'id': 'google',
|
|
59
|
+
'name': 'Google Docs',
|
|
60
|
+
'description': 'Connect to Google Drive and Docs for searching and reading documents',
|
|
61
|
+
'mcp_server_id': 'google-integration',
|
|
62
|
+
'mcp_server_name': 'Google Docs (Composio)',
|
|
63
|
+
# Note: This requires uvx to be installed
|
|
64
|
+
# --single-user makes USER_GOOGLE_EMAIL the default, skipping email in tool calls
|
|
65
|
+
'mcp_command': 'uvx',
|
|
66
|
+
'mcp_args': ['workspace-mcp', '--tools', 'drive', 'docs', '--single-user'],
|
|
67
|
+
# Maps Composio credential fields to MCP server env vars
|
|
68
|
+
'env_mapping': {
|
|
69
|
+
'client_id': 'GOOGLE_OAUTH_CLIENT_ID',
|
|
70
|
+
'client_secret': 'GOOGLE_OAUTH_CLIENT_SECRET',
|
|
71
|
+
'access_token': 'GOOGLE_ACCESS_TOKEN',
|
|
72
|
+
'refresh_token': 'GOOGLE_REFRESH_TOKEN',
|
|
73
|
+
'email': 'USER_GOOGLE_EMAIL', # User's Google email from OAuth
|
|
74
|
+
},
|
|
75
|
+
'whitelisted_tools': [
|
|
76
|
+
'start_google_auth',
|
|
77
|
+
'search_docs',
|
|
78
|
+
'get_doc_content',
|
|
79
|
+
'list_docs_in_folder',
|
|
80
|
+
'inspect_doc_structure',
|
|
81
|
+
'read_document_comments',
|
|
82
|
+
'create_document_comment',
|
|
83
|
+
'reply_to_document_comment',
|
|
84
|
+
'resolve_document_comment',
|
|
85
|
+
'search_drive_files',
|
|
86
|
+
'list_drive_items',
|
|
87
|
+
'get_drive_file_content',
|
|
88
|
+
'get_drive_file_download_url',
|
|
89
|
+
'list_drive_items_in_folder',
|
|
90
|
+
],
|
|
91
|
+
},
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def get_integration_config(integration_id: str) -> Dict[str, Any] | None:
|
|
96
|
+
"""Get configuration for a specific integration."""
|
|
97
|
+
return INTEGRATION_CONFIG.get(integration_id)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def get_all_integration_ids() -> List[str]:
|
|
101
|
+
"""Get list of all integration IDs."""
|
|
102
|
+
return list(INTEGRATION_CONFIG.keys())
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def get_integration_info_for_frontend() -> List[Dict[str, str]]:
|
|
106
|
+
"""
|
|
107
|
+
Get integration info safe to expose to frontend.
|
|
108
|
+
Only includes id, name, and description - no MCP commands or env mappings.
|
|
109
|
+
"""
|
|
110
|
+
return [
|
|
111
|
+
{
|
|
112
|
+
'id': config['id'],
|
|
113
|
+
'name': config['name'],
|
|
114
|
+
'description': config['description'],
|
|
115
|
+
}
|
|
116
|
+
for config in INTEGRATION_CONFIG.values()
|
|
117
|
+
]
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def get_mcp_server_config(integration_id: str, credentials: Dict[str, Any]) -> Dict[str, Any] | None:
|
|
121
|
+
"""
|
|
122
|
+
Generate MCP server configuration for an integration.
|
|
123
|
+
Maps credentials from Composio to the appropriate env vars for the MCP server.
|
|
124
|
+
|
|
125
|
+
DEPRECATED: Use get_mcp_server_config_for_storage() and store tokens separately.
|
|
126
|
+
"""
|
|
127
|
+
config = INTEGRATION_CONFIG.get(integration_id)
|
|
128
|
+
if not config:
|
|
129
|
+
return None
|
|
130
|
+
|
|
131
|
+
# Build environment variables from credentials
|
|
132
|
+
env = {}
|
|
133
|
+
for cred_field, env_var in config['env_mapping'].items():
|
|
134
|
+
if cred_field in credentials:
|
|
135
|
+
env[env_var] = credentials[cred_field]
|
|
136
|
+
|
|
137
|
+
return {
|
|
138
|
+
'id': config['mcp_server_id'],
|
|
139
|
+
'name': config['mcp_server_name'],
|
|
140
|
+
'command': config['mcp_command'],
|
|
141
|
+
'args': config['mcp_args'],
|
|
142
|
+
'env': env,
|
|
143
|
+
'enabled': True,
|
|
144
|
+
'enabledTools': config['whitelisted_tools'],
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def get_mcp_server_config_for_storage(integration_id: str) -> Dict[str, Any] | None:
|
|
149
|
+
"""
|
|
150
|
+
Generate MCP server configuration for storage (WITHOUT credentials).
|
|
151
|
+
Tokens should be stored separately using OAuthTokenStore and injected at runtime.
|
|
152
|
+
|
|
153
|
+
Returns config suitable for mcp.json without sensitive data.
|
|
154
|
+
"""
|
|
155
|
+
config = INTEGRATION_CONFIG.get(integration_id)
|
|
156
|
+
if not config:
|
|
157
|
+
return None
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
'id': config['mcp_server_id'],
|
|
161
|
+
'name': config['mcp_server_name'],
|
|
162
|
+
'command': config['mcp_command'],
|
|
163
|
+
'args': config['mcp_args'],
|
|
164
|
+
# No env vars with tokens - they're stored securely elsewhere
|
|
165
|
+
'enabled': True,
|
|
166
|
+
'enabledTools': config['whitelisted_tools'],
|
|
167
|
+
# Mark as OAuth integration so MCP service knows to look up tokens
|
|
168
|
+
'isOAuthIntegration': True,
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _extract_email_from_id_token(id_token: str) -> str | None:
|
|
173
|
+
"""
|
|
174
|
+
Extract email from a Google OAuth id_token (JWT).
|
|
175
|
+
|
|
176
|
+
The id_token is a JWT with three parts: header.payload.signature
|
|
177
|
+
We decode the payload to get the email claim.
|
|
178
|
+
"""
|
|
179
|
+
try:
|
|
180
|
+
# JWT has 3 parts separated by dots
|
|
181
|
+
parts = id_token.split('.')
|
|
182
|
+
if len(parts) != 3:
|
|
183
|
+
return None
|
|
184
|
+
|
|
185
|
+
# Decode the payload (second part)
|
|
186
|
+
# Add padding if needed for base64 decoding
|
|
187
|
+
payload_b64 = parts[1]
|
|
188
|
+
padding = 4 - len(payload_b64) % 4
|
|
189
|
+
if padding != 4:
|
|
190
|
+
payload_b64 += '=' * padding
|
|
191
|
+
|
|
192
|
+
payload_json = base64.urlsafe_b64decode(payload_b64)
|
|
193
|
+
payload = json.loads(payload_json)
|
|
194
|
+
|
|
195
|
+
# Extract email from claims
|
|
196
|
+
email = payload.get('email')
|
|
197
|
+
if email:
|
|
198
|
+
logger.debug(f"[Integrations] Extracted email from id_token: {email}")
|
|
199
|
+
return email
|
|
200
|
+
|
|
201
|
+
return None
|
|
202
|
+
except Exception as e:
|
|
203
|
+
logger.warning(f"[Integrations] Failed to extract email from id_token: {e}")
|
|
204
|
+
return None
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def build_env_from_credentials(integration_id: str, credentials: Dict[str, Any]) -> Dict[str, str]:
|
|
208
|
+
"""
|
|
209
|
+
Build environment variables from Composio credentials.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
integration_id: The integration ID
|
|
213
|
+
credentials: Credentials from Composio
|
|
214
|
+
|
|
215
|
+
Returns:
|
|
216
|
+
Dict of environment variable name -> value
|
|
217
|
+
"""
|
|
218
|
+
config = INTEGRATION_CONFIG.get(integration_id)
|
|
219
|
+
if not config:
|
|
220
|
+
return {}
|
|
221
|
+
|
|
222
|
+
env = {}
|
|
223
|
+
for cred_field, env_var in config['env_mapping'].items():
|
|
224
|
+
if cred_field in credentials:
|
|
225
|
+
env[env_var] = credentials[cred_field]
|
|
226
|
+
|
|
227
|
+
# For Google integration, try to extract email from various sources if not directly available
|
|
228
|
+
if integration_id == 'google' and 'USER_GOOGLE_EMAIL' not in env:
|
|
229
|
+
email = None
|
|
230
|
+
|
|
231
|
+
# Try common field names for email
|
|
232
|
+
for field_name in ['email', 'userEmail', 'user_email', 'Email']:
|
|
233
|
+
if field_name in credentials:
|
|
234
|
+
email = credentials[field_name]
|
|
235
|
+
break
|
|
236
|
+
|
|
237
|
+
# Try to extract from id_token if not found directly
|
|
238
|
+
if not email:
|
|
239
|
+
id_token = credentials.get('id_token')
|
|
240
|
+
if id_token:
|
|
241
|
+
email = _extract_email_from_id_token(id_token)
|
|
242
|
+
|
|
243
|
+
if email:
|
|
244
|
+
env['USER_GOOGLE_EMAIL'] = email
|
|
245
|
+
else:
|
|
246
|
+
logger.warning(f"[Integrations] Could not find email in Google credentials. Keys: {list(credentials.keys())}")
|
|
247
|
+
|
|
248
|
+
return env
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def get_mcp_server_id_for_integration(integration_id: str) -> str | None:
|
|
252
|
+
"""Get the MCP server ID for an integration."""
|
|
253
|
+
config = INTEGRATION_CONFIG.get(integration_id)
|
|
254
|
+
if config:
|
|
255
|
+
return config['mcp_server_id']
|
|
256
|
+
return None
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Logging utilities for SignalPilot AI.
|
|
3
|
+
Provides controlled print function that respects SIGNALPILOT_DEBUG environment variable.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import builtins
|
|
7
|
+
import os
|
|
8
|
+
|
|
9
|
+
# Debug mode controlled by environment variable
|
|
10
|
+
# Set SIGNALPILOT_DEBUG=1 to enable verbose logging (disabled by default)
|
|
11
|
+
_DEBUG = os.environ.get('SIGNALPILOT_DEBUG', '0').lower() not in ('0', 'false', 'no')
|
|
12
|
+
|
|
13
|
+
# Store the original print function
|
|
14
|
+
_original_print = builtins.print
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def print(*args, **kwargs) -> None:
|
|
18
|
+
"""Conditionally print messages based on debug mode.
|
|
19
|
+
|
|
20
|
+
Only prints when SIGNALPILOT_DEBUG environment variable is not set to 0/false/no.
|
|
21
|
+
Error messages (containing 'ERROR') are always printed regardless of debug mode.
|
|
22
|
+
"""
|
|
23
|
+
message = ' '.join(str(arg) for arg in args)
|
|
24
|
+
# Always print errors, otherwise only print in debug mode
|
|
25
|
+
if 'ERROR' in message or _DEBUG:
|
|
26
|
+
_original_print(*args, **kwargs)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def is_debug_enabled() -> bool:
|
|
30
|
+
"""Check if debug mode is enabled."""
|
|
31
|
+
return _DEBUG
|