alita-sdk 0.3.257__py3-none-any.whl → 0.3.562__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 (278) hide show
  1. alita_sdk/cli/__init__.py +10 -0
  2. alita_sdk/cli/__main__.py +17 -0
  3. alita_sdk/cli/agent/__init__.py +5 -0
  4. alita_sdk/cli/agent/default.py +258 -0
  5. alita_sdk/cli/agent_executor.py +155 -0
  6. alita_sdk/cli/agent_loader.py +215 -0
  7. alita_sdk/cli/agent_ui.py +228 -0
  8. alita_sdk/cli/agents.py +3601 -0
  9. alita_sdk/cli/callbacks.py +647 -0
  10. alita_sdk/cli/cli.py +168 -0
  11. alita_sdk/cli/config.py +306 -0
  12. alita_sdk/cli/context/__init__.py +30 -0
  13. alita_sdk/cli/context/cleanup.py +198 -0
  14. alita_sdk/cli/context/manager.py +731 -0
  15. alita_sdk/cli/context/message.py +285 -0
  16. alita_sdk/cli/context/strategies.py +289 -0
  17. alita_sdk/cli/context/token_estimation.py +127 -0
  18. alita_sdk/cli/formatting.py +182 -0
  19. alita_sdk/cli/input_handler.py +419 -0
  20. alita_sdk/cli/inventory.py +1073 -0
  21. alita_sdk/cli/mcp_loader.py +315 -0
  22. alita_sdk/cli/toolkit.py +327 -0
  23. alita_sdk/cli/toolkit_loader.py +85 -0
  24. alita_sdk/cli/tools/__init__.py +43 -0
  25. alita_sdk/cli/tools/approval.py +224 -0
  26. alita_sdk/cli/tools/filesystem.py +1751 -0
  27. alita_sdk/cli/tools/planning.py +389 -0
  28. alita_sdk/cli/tools/terminal.py +414 -0
  29. alita_sdk/community/__init__.py +72 -12
  30. alita_sdk/community/inventory/__init__.py +236 -0
  31. alita_sdk/community/inventory/config.py +257 -0
  32. alita_sdk/community/inventory/enrichment.py +2137 -0
  33. alita_sdk/community/inventory/extractors.py +1469 -0
  34. alita_sdk/community/inventory/ingestion.py +3172 -0
  35. alita_sdk/community/inventory/knowledge_graph.py +1457 -0
  36. alita_sdk/community/inventory/parsers/__init__.py +218 -0
  37. alita_sdk/community/inventory/parsers/base.py +295 -0
  38. alita_sdk/community/inventory/parsers/csharp_parser.py +907 -0
  39. alita_sdk/community/inventory/parsers/go_parser.py +851 -0
  40. alita_sdk/community/inventory/parsers/html_parser.py +389 -0
  41. alita_sdk/community/inventory/parsers/java_parser.py +593 -0
  42. alita_sdk/community/inventory/parsers/javascript_parser.py +629 -0
  43. alita_sdk/community/inventory/parsers/kotlin_parser.py +768 -0
  44. alita_sdk/community/inventory/parsers/markdown_parser.py +362 -0
  45. alita_sdk/community/inventory/parsers/python_parser.py +604 -0
  46. alita_sdk/community/inventory/parsers/rust_parser.py +858 -0
  47. alita_sdk/community/inventory/parsers/swift_parser.py +832 -0
  48. alita_sdk/community/inventory/parsers/text_parser.py +322 -0
  49. alita_sdk/community/inventory/parsers/yaml_parser.py +370 -0
  50. alita_sdk/community/inventory/patterns/__init__.py +61 -0
  51. alita_sdk/community/inventory/patterns/ast_adapter.py +380 -0
  52. alita_sdk/community/inventory/patterns/loader.py +348 -0
  53. alita_sdk/community/inventory/patterns/registry.py +198 -0
  54. alita_sdk/community/inventory/presets.py +535 -0
  55. alita_sdk/community/inventory/retrieval.py +1403 -0
  56. alita_sdk/community/inventory/toolkit.py +173 -0
  57. alita_sdk/community/inventory/toolkit_utils.py +176 -0
  58. alita_sdk/community/inventory/visualize.py +1370 -0
  59. alita_sdk/configurations/__init__.py +11 -0
  60. alita_sdk/configurations/ado.py +148 -2
  61. alita_sdk/configurations/azure_search.py +1 -1
  62. alita_sdk/configurations/bigquery.py +1 -1
  63. alita_sdk/configurations/bitbucket.py +94 -2
  64. alita_sdk/configurations/browser.py +18 -0
  65. alita_sdk/configurations/carrier.py +19 -0
  66. alita_sdk/configurations/confluence.py +130 -1
  67. alita_sdk/configurations/delta_lake.py +1 -1
  68. alita_sdk/configurations/figma.py +76 -5
  69. alita_sdk/configurations/github.py +65 -1
  70. alita_sdk/configurations/gitlab.py +81 -0
  71. alita_sdk/configurations/google_places.py +17 -0
  72. alita_sdk/configurations/jira.py +103 -0
  73. alita_sdk/configurations/openapi.py +111 -0
  74. alita_sdk/configurations/postman.py +1 -1
  75. alita_sdk/configurations/qtest.py +72 -3
  76. alita_sdk/configurations/report_portal.py +115 -0
  77. alita_sdk/configurations/salesforce.py +19 -0
  78. alita_sdk/configurations/service_now.py +1 -12
  79. alita_sdk/configurations/sharepoint.py +167 -0
  80. alita_sdk/configurations/sonar.py +18 -0
  81. alita_sdk/configurations/sql.py +20 -0
  82. alita_sdk/configurations/testio.py +101 -0
  83. alita_sdk/configurations/testrail.py +88 -0
  84. alita_sdk/configurations/xray.py +94 -1
  85. alita_sdk/configurations/zephyr_enterprise.py +94 -1
  86. alita_sdk/configurations/zephyr_essential.py +95 -0
  87. alita_sdk/runtime/clients/artifact.py +21 -4
  88. alita_sdk/runtime/clients/client.py +458 -67
  89. alita_sdk/runtime/clients/mcp_discovery.py +342 -0
  90. alita_sdk/runtime/clients/mcp_manager.py +262 -0
  91. alita_sdk/runtime/clients/sandbox_client.py +352 -0
  92. alita_sdk/runtime/langchain/_constants_bkup.py +1318 -0
  93. alita_sdk/runtime/langchain/assistant.py +183 -43
  94. alita_sdk/runtime/langchain/constants.py +647 -1
  95. alita_sdk/runtime/langchain/document_loaders/AlitaDocxMammothLoader.py +315 -3
  96. alita_sdk/runtime/langchain/document_loaders/AlitaExcelLoader.py +209 -31
  97. alita_sdk/runtime/langchain/document_loaders/AlitaImageLoader.py +1 -1
  98. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLinesLoader.py +77 -0
  99. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLoader.py +10 -3
  100. alita_sdk/runtime/langchain/document_loaders/AlitaMarkdownLoader.py +66 -0
  101. alita_sdk/runtime/langchain/document_loaders/AlitaPDFLoader.py +79 -10
  102. alita_sdk/runtime/langchain/document_loaders/AlitaPowerPointLoader.py +52 -15
  103. alita_sdk/runtime/langchain/document_loaders/AlitaPythonLoader.py +9 -0
  104. alita_sdk/runtime/langchain/document_loaders/AlitaTableLoader.py +1 -4
  105. alita_sdk/runtime/langchain/document_loaders/AlitaTextLoader.py +15 -2
  106. alita_sdk/runtime/langchain/document_loaders/ImageParser.py +30 -0
  107. alita_sdk/runtime/langchain/document_loaders/constants.py +189 -41
  108. alita_sdk/runtime/langchain/interfaces/llm_processor.py +4 -2
  109. alita_sdk/runtime/langchain/langraph_agent.py +407 -92
  110. alita_sdk/runtime/langchain/utils.py +102 -8
  111. alita_sdk/runtime/llms/preloaded.py +2 -6
  112. alita_sdk/runtime/models/mcp_models.py +61 -0
  113. alita_sdk/runtime/skills/__init__.py +91 -0
  114. alita_sdk/runtime/skills/callbacks.py +498 -0
  115. alita_sdk/runtime/skills/discovery.py +540 -0
  116. alita_sdk/runtime/skills/executor.py +610 -0
  117. alita_sdk/runtime/skills/input_builder.py +371 -0
  118. alita_sdk/runtime/skills/models.py +330 -0
  119. alita_sdk/runtime/skills/registry.py +355 -0
  120. alita_sdk/runtime/skills/skill_runner.py +330 -0
  121. alita_sdk/runtime/toolkits/__init__.py +28 -0
  122. alita_sdk/runtime/toolkits/application.py +14 -4
  123. alita_sdk/runtime/toolkits/artifact.py +24 -9
  124. alita_sdk/runtime/toolkits/datasource.py +13 -6
  125. alita_sdk/runtime/toolkits/mcp.py +780 -0
  126. alita_sdk/runtime/toolkits/planning.py +178 -0
  127. alita_sdk/runtime/toolkits/skill_router.py +238 -0
  128. alita_sdk/runtime/toolkits/subgraph.py +11 -6
  129. alita_sdk/runtime/toolkits/tools.py +314 -70
  130. alita_sdk/runtime/toolkits/vectorstore.py +11 -5
  131. alita_sdk/runtime/tools/__init__.py +24 -0
  132. alita_sdk/runtime/tools/application.py +16 -4
  133. alita_sdk/runtime/tools/artifact.py +367 -33
  134. alita_sdk/runtime/tools/data_analysis.py +183 -0
  135. alita_sdk/runtime/tools/function.py +100 -4
  136. alita_sdk/runtime/tools/graph.py +81 -0
  137. alita_sdk/runtime/tools/image_generation.py +218 -0
  138. alita_sdk/runtime/tools/llm.py +1013 -177
  139. alita_sdk/runtime/tools/loop.py +3 -1
  140. alita_sdk/runtime/tools/loop_output.py +3 -1
  141. alita_sdk/runtime/tools/mcp_inspect_tool.py +284 -0
  142. alita_sdk/runtime/tools/mcp_remote_tool.py +181 -0
  143. alita_sdk/runtime/tools/mcp_server_tool.py +3 -1
  144. alita_sdk/runtime/tools/planning/__init__.py +36 -0
  145. alita_sdk/runtime/tools/planning/models.py +246 -0
  146. alita_sdk/runtime/tools/planning/wrapper.py +607 -0
  147. alita_sdk/runtime/tools/router.py +2 -1
  148. alita_sdk/runtime/tools/sandbox.py +375 -0
  149. alita_sdk/runtime/tools/skill_router.py +776 -0
  150. alita_sdk/runtime/tools/tool.py +3 -1
  151. alita_sdk/runtime/tools/vectorstore.py +69 -65
  152. alita_sdk/runtime/tools/vectorstore_base.py +163 -90
  153. alita_sdk/runtime/utils/AlitaCallback.py +137 -21
  154. alita_sdk/runtime/utils/mcp_client.py +492 -0
  155. alita_sdk/runtime/utils/mcp_oauth.py +361 -0
  156. alita_sdk/runtime/utils/mcp_sse_client.py +434 -0
  157. alita_sdk/runtime/utils/mcp_tools_discovery.py +124 -0
  158. alita_sdk/runtime/utils/streamlit.py +41 -14
  159. alita_sdk/runtime/utils/toolkit_utils.py +28 -9
  160. alita_sdk/runtime/utils/utils.py +48 -0
  161. alita_sdk/tools/__init__.py +135 -37
  162. alita_sdk/tools/ado/__init__.py +2 -2
  163. alita_sdk/tools/ado/repos/__init__.py +15 -19
  164. alita_sdk/tools/ado/repos/repos_wrapper.py +12 -20
  165. alita_sdk/tools/ado/test_plan/__init__.py +26 -8
  166. alita_sdk/tools/ado/test_plan/test_plan_wrapper.py +56 -28
  167. alita_sdk/tools/ado/wiki/__init__.py +27 -12
  168. alita_sdk/tools/ado/wiki/ado_wrapper.py +114 -40
  169. alita_sdk/tools/ado/work_item/__init__.py +27 -12
  170. alita_sdk/tools/ado/work_item/ado_wrapper.py +95 -11
  171. alita_sdk/tools/advanced_jira_mining/__init__.py +12 -8
  172. alita_sdk/tools/aws/delta_lake/__init__.py +14 -11
  173. alita_sdk/tools/aws/delta_lake/tool.py +5 -1
  174. alita_sdk/tools/azure_ai/search/__init__.py +13 -8
  175. alita_sdk/tools/base/tool.py +5 -1
  176. alita_sdk/tools/base_indexer_toolkit.py +454 -110
  177. alita_sdk/tools/bitbucket/__init__.py +27 -19
  178. alita_sdk/tools/bitbucket/api_wrapper.py +285 -27
  179. alita_sdk/tools/bitbucket/cloud_api_wrapper.py +5 -5
  180. alita_sdk/tools/browser/__init__.py +41 -16
  181. alita_sdk/tools/browser/crawler.py +3 -1
  182. alita_sdk/tools/browser/utils.py +15 -6
  183. alita_sdk/tools/carrier/__init__.py +18 -17
  184. alita_sdk/tools/carrier/backend_reports_tool.py +8 -4
  185. alita_sdk/tools/carrier/excel_reporter.py +8 -4
  186. alita_sdk/tools/chunkers/__init__.py +3 -1
  187. alita_sdk/tools/chunkers/code/codeparser.py +1 -1
  188. alita_sdk/tools/chunkers/sematic/json_chunker.py +2 -1
  189. alita_sdk/tools/chunkers/sematic/markdown_chunker.py +97 -6
  190. alita_sdk/tools/chunkers/sematic/proposal_chunker.py +1 -1
  191. alita_sdk/tools/chunkers/universal_chunker.py +270 -0
  192. alita_sdk/tools/cloud/aws/__init__.py +11 -7
  193. alita_sdk/tools/cloud/azure/__init__.py +11 -7
  194. alita_sdk/tools/cloud/gcp/__init__.py +11 -7
  195. alita_sdk/tools/cloud/k8s/__init__.py +11 -7
  196. alita_sdk/tools/code/linter/__init__.py +9 -8
  197. alita_sdk/tools/code/loaders/codesearcher.py +3 -2
  198. alita_sdk/tools/code/sonar/__init__.py +20 -13
  199. alita_sdk/tools/code_indexer_toolkit.py +199 -0
  200. alita_sdk/tools/confluence/__init__.py +21 -14
  201. alita_sdk/tools/confluence/api_wrapper.py +197 -58
  202. alita_sdk/tools/confluence/loader.py +14 -2
  203. alita_sdk/tools/custom_open_api/__init__.py +11 -5
  204. alita_sdk/tools/elastic/__init__.py +10 -8
  205. alita_sdk/tools/elitea_base.py +546 -64
  206. alita_sdk/tools/figma/__init__.py +11 -8
  207. alita_sdk/tools/figma/api_wrapper.py +352 -153
  208. alita_sdk/tools/github/__init__.py +17 -17
  209. alita_sdk/tools/github/api_wrapper.py +9 -26
  210. alita_sdk/tools/github/github_client.py +81 -12
  211. alita_sdk/tools/github/schemas.py +2 -1
  212. alita_sdk/tools/github/tool.py +5 -1
  213. alita_sdk/tools/gitlab/__init__.py +18 -13
  214. alita_sdk/tools/gitlab/api_wrapper.py +224 -80
  215. alita_sdk/tools/gitlab_org/__init__.py +13 -10
  216. alita_sdk/tools/google/bigquery/__init__.py +13 -13
  217. alita_sdk/tools/google/bigquery/tool.py +5 -1
  218. alita_sdk/tools/google_places/__init__.py +20 -11
  219. alita_sdk/tools/jira/__init__.py +21 -11
  220. alita_sdk/tools/jira/api_wrapper.py +315 -168
  221. alita_sdk/tools/keycloak/__init__.py +10 -8
  222. alita_sdk/tools/localgit/__init__.py +8 -3
  223. alita_sdk/tools/localgit/local_git.py +62 -54
  224. alita_sdk/tools/localgit/tool.py +5 -1
  225. alita_sdk/tools/memory/__init__.py +38 -14
  226. alita_sdk/tools/non_code_indexer_toolkit.py +7 -2
  227. alita_sdk/tools/ocr/__init__.py +10 -8
  228. alita_sdk/tools/openapi/__init__.py +281 -108
  229. alita_sdk/tools/openapi/api_wrapper.py +883 -0
  230. alita_sdk/tools/openapi/tool.py +20 -0
  231. alita_sdk/tools/pandas/__init__.py +18 -11
  232. alita_sdk/tools/pandas/api_wrapper.py +40 -45
  233. alita_sdk/tools/pandas/dataframe/generator/base.py +3 -1
  234. alita_sdk/tools/postman/__init__.py +10 -11
  235. alita_sdk/tools/postman/api_wrapper.py +19 -8
  236. alita_sdk/tools/postman/postman_analysis.py +8 -1
  237. alita_sdk/tools/pptx/__init__.py +10 -10
  238. alita_sdk/tools/qtest/__init__.py +21 -14
  239. alita_sdk/tools/qtest/api_wrapper.py +1784 -88
  240. alita_sdk/tools/rally/__init__.py +12 -10
  241. alita_sdk/tools/report_portal/__init__.py +22 -16
  242. alita_sdk/tools/salesforce/__init__.py +21 -16
  243. alita_sdk/tools/servicenow/__init__.py +20 -16
  244. alita_sdk/tools/servicenow/api_wrapper.py +1 -1
  245. alita_sdk/tools/sharepoint/__init__.py +16 -14
  246. alita_sdk/tools/sharepoint/api_wrapper.py +179 -39
  247. alita_sdk/tools/sharepoint/authorization_helper.py +191 -1
  248. alita_sdk/tools/sharepoint/utils.py +8 -2
  249. alita_sdk/tools/slack/__init__.py +11 -7
  250. alita_sdk/tools/sql/__init__.py +21 -19
  251. alita_sdk/tools/sql/api_wrapper.py +71 -23
  252. alita_sdk/tools/testio/__init__.py +20 -13
  253. alita_sdk/tools/testrail/__init__.py +12 -11
  254. alita_sdk/tools/testrail/api_wrapper.py +214 -46
  255. alita_sdk/tools/utils/__init__.py +28 -4
  256. alita_sdk/tools/utils/content_parser.py +182 -62
  257. alita_sdk/tools/utils/text_operations.py +254 -0
  258. alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +83 -27
  259. alita_sdk/tools/xray/__init__.py +17 -14
  260. alita_sdk/tools/xray/api_wrapper.py +58 -113
  261. alita_sdk/tools/yagmail/__init__.py +8 -3
  262. alita_sdk/tools/zephyr/__init__.py +11 -7
  263. alita_sdk/tools/zephyr_enterprise/__init__.py +15 -9
  264. alita_sdk/tools/zephyr_enterprise/api_wrapper.py +30 -15
  265. alita_sdk/tools/zephyr_essential/__init__.py +15 -10
  266. alita_sdk/tools/zephyr_essential/api_wrapper.py +297 -54
  267. alita_sdk/tools/zephyr_essential/client.py +6 -4
  268. alita_sdk/tools/zephyr_scale/__init__.py +12 -8
  269. alita_sdk/tools/zephyr_scale/api_wrapper.py +39 -31
  270. alita_sdk/tools/zephyr_squad/__init__.py +11 -7
  271. {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.562.dist-info}/METADATA +184 -37
  272. alita_sdk-0.3.562.dist-info/RECORD +450 -0
  273. alita_sdk-0.3.562.dist-info/entry_points.txt +2 -0
  274. alita_sdk/tools/bitbucket/tools.py +0 -304
  275. alita_sdk-0.3.257.dist-info/RECORD +0 -343
  276. {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.562.dist-info}/WHEEL +0 -0
  277. {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.562.dist-info}/licenses/LICENSE +0 -0
  278. {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.562.dist-info}/top_level.txt +0 -0
@@ -41,8 +41,19 @@ _safe_import_configuration('bigquery', 'bigquery', 'BigQueryConfiguration')
41
41
  _safe_import_configuration('xray', 'xray', 'XrayConfiguration')
42
42
  _safe_import_configuration('zephyr', 'zephyr', 'ZephyrConfiguration')
43
43
  _safe_import_configuration('zephyr_enterprise', 'zephyr_enterprise', 'ZephyrEnterpriseConfiguration')
44
+ _safe_import_configuration('zephyr_essential', 'zephyr_essential', 'ZephyrEssentialConfiguration')
44
45
  _safe_import_configuration('figma', 'figma', 'FigmaConfiguration')
45
46
  _safe_import_configuration('rally', 'rally', 'RallyConfiguration')
47
+ _safe_import_configuration('sonar', 'sonar', 'SonarConfiguration')
48
+ _safe_import_configuration('sql', 'sql', 'SqlConfiguration')
49
+ _safe_import_configuration('google_places', 'google_places', 'GooglePlacesConfiguration')
50
+ _safe_import_configuration('salesforce', 'salesforce', 'SalesforceConfiguration')
51
+ _safe_import_configuration('browser', 'browser', 'BrowserConfiguration')
52
+ _safe_import_configuration('sharepoint', 'sharepoint', 'SharepointConfiguration')
53
+ _safe_import_configuration('carrier', 'carrier', 'CarrierConfiguration')
54
+ _safe_import_configuration('report_portal', 'report_portal', 'ReportPortalConfiguration')
55
+ _safe_import_configuration('testio', 'testio', 'TestIOConfiguration')
56
+ _safe_import_configuration('openapi', 'openapi', 'OpenApiConfiguration')
46
57
 
47
58
  # Log import summary
48
59
  available_count = len(AVAILABLE_CONFIGURATIONS)
@@ -1,5 +1,8 @@
1
+ import re
1
2
  from typing import Optional
3
+ from urllib.parse import quote
2
4
 
5
+ import requests
3
6
  from pydantic import BaseModel, ConfigDict, Field, SecretStr
4
7
 
5
8
 
@@ -10,7 +13,8 @@ class AdoConfiguration(BaseModel):
10
13
  "label": "Ado",
11
14
  "icon_url": None,
12
15
  "section": "credentials",
13
- "type": "ado"
16
+ "type": "ado",
17
+ "categories": ["project management"],
14
18
  }
15
19
  }
16
20
  )
@@ -18,6 +22,147 @@ class AdoConfiguration(BaseModel):
18
22
  project: str = Field(description="ADO project")
19
23
  token: Optional[SecretStr] = Field(description="ADO Token")
20
24
 
25
+ @staticmethod
26
+ def check_connection(settings: dict) -> str | None:
27
+ """
28
+ Test the connection to Azure DevOps API.
29
+
30
+ Args:
31
+ settings: Dictionary containing 'organization_url', 'project', and optionally 'token'
32
+
33
+ Returns:
34
+ None if connection is successful, error message string otherwise
35
+ """
36
+ organization_url = settings.get("organization_url")
37
+ if organization_url is None or organization_url == "":
38
+ if organization_url == "":
39
+ return "Organization URL cannot be empty"
40
+ return "Organization URL is required"
41
+
42
+ # Validate organization URL format
43
+ if not isinstance(organization_url, str):
44
+ return "Organization URL must be a string"
45
+
46
+ organization_url = organization_url.strip()
47
+ if not organization_url:
48
+ return "Organization URL cannot be empty"
49
+
50
+ if not organization_url.startswith(("http://", "https://")):
51
+ return "Organization URL must start with http:// or https://"
52
+
53
+ # Remove trailing slash for consistency
54
+ organization_url = organization_url.rstrip("/")
55
+
56
+ project = settings.get("project")
57
+ if project is None or project == "":
58
+ if project == "":
59
+ return "Project cannot be empty"
60
+ return "Project is required"
61
+
62
+ # Validate project format
63
+ if not isinstance(project, str):
64
+ return "Project must be a string"
65
+
66
+ project = project.strip()
67
+ if not project:
68
+ return "Project cannot be empty"
69
+
70
+ token = settings.get("token")
71
+
72
+ # Extract secret value if it's a SecretStr
73
+ if token is not None and hasattr(token, "get_secret_value"):
74
+ token = token.get_secret_value()
75
+
76
+ # Validate token if provided
77
+ if token is not None and (not token or not token.strip()):
78
+ return "Token cannot be empty if provided"
79
+
80
+ # NOTE on verification strategy:
81
+ # - Project endpoints can work anonymously for public projects.
82
+ # That makes them a weak signal for detecting a bad/expired token.
83
+ # - If a token is provided, first validate it against a profile endpoint
84
+ # that requires authentication, then check project access.
85
+
86
+ # Strictly require a canonical organization URL so we can build reliable API URLs.
87
+ # Supported formats:
88
+ # - https://dev.azure.com/<org>
89
+ # - https://<org>.visualstudio.com
90
+ org_name: str | None = None
91
+ org_url_kind: str | None = None # 'dev.azure.com' | '*.visualstudio.com'
92
+ m = re.match(r"^https?://dev\.azure\.com/(?P<org>[^/]+)$", organization_url, flags=re.IGNORECASE)
93
+ if m:
94
+ org_name = m.group('org')
95
+ org_url_kind = 'dev.azure.com'
96
+ else:
97
+ m = re.match(r"^https?://(?P<org>[^/.]+)\.visualstudio\.com$", organization_url, flags=re.IGNORECASE)
98
+ if m:
99
+ org_name = m.group('org')
100
+ org_url_kind = '*.visualstudio.com'
101
+
102
+ if org_name is None:
103
+ return (
104
+ "Organization URL format is invalid. Use 'https://dev.azure.com/<org>' "
105
+ "(recommended) or 'https://<org>.visualstudio.com'."
106
+ )
107
+
108
+ project_encoded = quote(project, safe="")
109
+ project_url = f"{organization_url}/_apis/projects/{project_encoded}?api-version=7.0"
110
+ # Auth-required endpoint to validate PAT (works regardless of project visibility)
111
+ if org_url_kind == 'dev.azure.com':
112
+ profile_url = f"https://vssps.dev.azure.com/{org_name}/_apis/profile/profiles/me?api-version=7.1-preview.3"
113
+ else:
114
+ # For legacy org URLs, use the matching vssps host
115
+ profile_url = f"https://{org_name}.vssps.visualstudio.com/_apis/profile/profiles/me?api-version=7.1-preview.3"
116
+
117
+ try:
118
+ headers = {}
119
+ if token:
120
+ # Use Basic Auth with PAT token (username can be empty)
121
+ from requests.auth import HTTPBasicAuth
122
+ auth = HTTPBasicAuth("", token)
123
+
124
+ # 1) Validate token first (strong signal)
125
+ profile_resp = requests.get(profile_url, auth=auth, timeout=10)
126
+ if profile_resp.status_code == 200:
127
+ pass
128
+ elif profile_resp.status_code == 401:
129
+ return "Invalid or expired token (PAT). Please generate a new token and try again."
130
+ elif profile_resp.status_code == 403:
131
+ return "Token is valid but lacks permission to access profile. Check PAT scopes/permissions."
132
+ elif profile_resp.status_code == 404:
133
+ return "Organization not found. Verify the Organization URL."
134
+ else:
135
+ return f"Token validation failed (HTTP {profile_resp.status_code})."
136
+
137
+ # 2) Validate project access
138
+ response = requests.get(project_url, auth=auth, timeout=10)
139
+ else:
140
+ # Try without authentication (works for public projects)
141
+ response = requests.get(project_url, headers=headers, timeout=10)
142
+
143
+ if response.status_code == 200:
144
+ return None # Connection successful
145
+ elif response.status_code == 401:
146
+ if token:
147
+ return "Not authorized. Token may be invalid for this organization or expired."
148
+ else:
149
+ return "Authentication required - project may be private"
150
+ elif response.status_code == 403:
151
+ return "Access forbidden - token may lack required permissions for this project"
152
+ elif response.status_code == 404:
153
+ return f"Project '{project}' not found or not accessible. Check project name and organization URL."
154
+ else:
155
+ return f"Connection failed (HTTP {response.status_code})."
156
+
157
+ except requests.exceptions.Timeout:
158
+ return "Connection timeout - Azure DevOps did not respond within 10 seconds"
159
+ except requests.exceptions.ConnectionError:
160
+ return "Connection error - unable to reach Azure DevOps. Check the Organization URL and your network."
161
+ except requests.exceptions.RequestException as e:
162
+ return f"Request failed: {str(e)}"
163
+ except Exception:
164
+ return "Unexpected error during Azure DevOps connection check"
165
+
21
166
 
22
167
  class AdoReposConfiguration(BaseModel):
23
168
  model_config = ConfigDict(
@@ -26,7 +171,8 @@ class AdoReposConfiguration(BaseModel):
26
171
  "label": "ADO repos",
27
172
  "icon_url": "ado-repos-icon.svg",
28
173
  "section": "credentials",
29
- "type": "ado_repos"
174
+ "type": "ado_repos",
175
+ "categories": ["code repositories"],
30
176
  }
31
177
  }
32
178
  )
@@ -12,7 +12,7 @@ class AzureSearchConfiguration(BaseModel):
12
12
  "hidden": True,
13
13
  "section": "credentials",
14
14
  "type": "azure_search",
15
- "categories": ["search"],
15
+ "categories": ["other"],
16
16
  "extra_categories": ["azure", "cognitive search", "vector database", "knowledge base"],
17
17
  }
18
18
  }
@@ -12,7 +12,7 @@ class BigQueryConfiguration(BaseModel):
12
12
  "hidden": True,
13
13
  "section": "credentials",
14
14
  "type": "bigquery",
15
- "categories": ["database"],
15
+ "categories": ["other"],
16
16
  "extra_categories": ["google", "gcp", "data warehouse", "analytics"],
17
17
  }
18
18
  }
@@ -1,5 +1,3 @@
1
- from typing import Optional
2
-
3
1
  from pydantic import BaseModel, ConfigDict, Field, SecretStr
4
2
 
5
3
 
@@ -30,3 +28,97 @@ class BitbucketConfiguration(BaseModel):
30
28
  url: str = Field(description="Bitbucket URL")
31
29
  username: str = Field(description="Bitbucket Username")
32
30
  password: SecretStr = Field(description="Bitbucket Password/App Password")
31
+
32
+ @staticmethod
33
+ def check_connection(settings: dict) -> str | None:
34
+ """
35
+ Check the connection to Bitbucket.
36
+
37
+ Args:
38
+ settings: Dictionary containing Bitbucket configuration
39
+ - url: Bitbucket instance URL (required)
40
+ - username: Bitbucket username (required)
41
+ - password: Password or App Password (required)
42
+
43
+ Returns:
44
+ None if connection successful, error message string if failed
45
+ """
46
+ import requests
47
+ from requests.auth import HTTPBasicAuth
48
+
49
+ # Validate url
50
+ url = settings.get("url", "").strip()
51
+ if not url:
52
+ return "Bitbucket URL is required"
53
+
54
+ # Normalize URL - remove trailing slashes
55
+ url = url.rstrip("/")
56
+
57
+ # Basic URL validation
58
+ if not url.startswith(("http://", "https://")):
59
+ return "Bitbucket URL must start with http:// or https://"
60
+
61
+ # Validate username
62
+ username = settings.get("username", "").strip()
63
+ if not username:
64
+ return "Bitbucket username is required"
65
+
66
+ # Validate password
67
+ password = settings.get("password")
68
+ if not password:
69
+ return "Bitbucket password is required"
70
+
71
+ # Extract password value if it's a SecretStr
72
+ password_value = password.get_secret_value() if hasattr(password, 'get_secret_value') else password
73
+
74
+ if not password_value or not str(password_value).strip():
75
+ return "Bitbucket password cannot be empty"
76
+
77
+ # Detect if this is Bitbucket Cloud or Server/Data Center
78
+ is_cloud = "bitbucket.org" in url.lower() or "api.bitbucket.org" in url.lower()
79
+ is_correct_bitbucket_domain = "bitbucket" in url.lower()
80
+
81
+ if is_cloud:
82
+ # Bitbucket Cloud: Use API v2.0
83
+ # Endpoint: /2.0/user - returns current authenticated user
84
+ test_url = f"{url}/2.0/user"
85
+ else:
86
+ # Bitbucket Server/Data Center: Use API v1.0
87
+ # Endpoint: /rest/api/1.0/users/{username}
88
+ test_url = f"{url}/rest/api/1.0/users/{username}"
89
+
90
+ try:
91
+ response = requests.get(
92
+ test_url,
93
+ auth=HTTPBasicAuth(username, str(password_value).strip()),
94
+ timeout=10
95
+ )
96
+
97
+ # Check response status
98
+ if response.status_code == 200:
99
+ # Successfully connected and authenticated
100
+ return None
101
+ elif response.status_code == 401:
102
+ return "Authentication failed: invalid username or password"
103
+ elif response.status_code == 403:
104
+ return "Access forbidden: check user permissions"
105
+ elif response.status_code == 404:
106
+ if not is_correct_bitbucket_domain:
107
+ return f"Url you provided is incorrect. Please provide correct server or cloud bitbucket url."
108
+ if is_cloud:
109
+ return "Bitbucket API endpoint not found: please provide the correct bitbucket cloud URL"
110
+ else:
111
+ return "Bitbucket API endpoint not found: please provide the correct bitbucket server URL"
112
+ else:
113
+ return f"Bitbucket API returned status code {response.status_code}"
114
+
115
+ except requests.exceptions.SSLError as e:
116
+ return f"SSL certificate verification failed: {str(e)}"
117
+ except requests.exceptions.ConnectionError:
118
+ return f"Cannot connect to Bitbucket at {url if not is_cloud else 'api.bitbucket.org'}: connection refused"
119
+ except requests.exceptions.Timeout:
120
+ return f"Connection to Bitbucket at {url if not is_cloud else 'api.bitbucket.org'} timed out"
121
+ except requests.exceptions.RequestException as e:
122
+ return f"Error connecting to Bitbucket: {str(e)}"
123
+ except Exception as e:
124
+ return f"Unexpected error: {str(e)}"
@@ -0,0 +1,18 @@
1
+ from pydantic import BaseModel, ConfigDict, Field, SecretStr
2
+
3
+
4
+ class BrowserConfiguration(BaseModel):
5
+ model_config = ConfigDict(
6
+ json_schema_extra={
7
+ "metadata": {
8
+ "label": "Browser",
9
+ "icon_url": "browser.svg",
10
+ "section": "credentials",
11
+ "type": "browser",
12
+ "categories": ["testing"],
13
+ "extra_categories": ["browser", "google", "search", "web"],
14
+ }
15
+ }
16
+ )
17
+ google_cse_id: str = Field(description="Google CSE id", default=None)
18
+ google_api_key: SecretStr = Field(description="Google API key", json_schema_extra={'secret': True})
@@ -0,0 +1,19 @@
1
+ from pydantic import BaseModel, ConfigDict, Field, SecretStr
2
+
3
+
4
+ class CarrierConfiguration(BaseModel):
5
+ model_config = ConfigDict(
6
+ json_schema_extra={
7
+ "metadata": {
8
+ "label": "Carrier",
9
+ "icon_url": "carrier.svg",
10
+ "section": "credentials",
11
+ "type": "carrier",
12
+ "categories": ["testing"],
13
+ "extra_categories": ["carrier", "security", "testing", "vulnerability"],
14
+ }
15
+ }
16
+ )
17
+ organization: str = Field(description="Carrier Organization")
18
+ url: str = Field(description="Carrier URL")
19
+ private_token: SecretStr = Field(description="Carrier Private Token")
@@ -1,5 +1,7 @@
1
1
  from typing import Optional
2
+ from urllib.parse import urlparse, urlunparse
2
3
 
4
+ from atlassian import Confluence
3
5
  from pydantic import BaseModel, ConfigDict, Field, SecretStr
4
6
 
5
7
 
@@ -34,4 +36,131 @@ class ConfluenceConfiguration(BaseModel):
34
36
  base_url: str = Field(description="Confluence URL")
35
37
  username: Optional[str] = Field(description="Confluence Username", default=None)
36
38
  api_key: Optional[SecretStr] = Field(description="Confluence API Key", default=None)
37
- token: Optional[SecretStr] = Field(description="Confluence Token", default=None)
39
+ token: Optional[SecretStr] = Field(description="Confluence Token", default=None)
40
+
41
+ @staticmethod
42
+ def check_connection(settings: dict) -> str | None:
43
+ """
44
+ Check the connection to Confluence.
45
+
46
+ Args:
47
+ settings: Dictionary containing Confluence configuration
48
+ - base_url: Confluence instance URL (required)
49
+ - username: Username for Basic Auth (optional)
50
+ - api_key: API key/password for Basic Auth (optional)
51
+ - token: Bearer token for authentication (optional)
52
+
53
+ Returns:
54
+ None if connection successful, error message string if failed
55
+ """
56
+ import requests
57
+ from requests.auth import HTTPBasicAuth
58
+
59
+ # Validate base_url
60
+ base_url_input = settings.get("base_url", "")
61
+ base_url = base_url_input.strip() if isinstance(base_url_input, str) else ""
62
+ if not base_url:
63
+ return "Confluence URL is required"
64
+
65
+ # Basic URL validation
66
+ if not base_url.startswith(("http://", "https://")):
67
+ return "Confluence URL must start with http:// or https://"
68
+
69
+ # Normalize URL - remove trailing slashes
70
+ base_url = base_url.rstrip("/")
71
+
72
+ # Build candidate base URLs.
73
+ # Confluence Cloud REST API is typically under /wiki. Users often paste
74
+ # https://<site>.atlassian.net and shouldn't be forced to know about /wiki.
75
+ parsed = urlparse(base_url)
76
+ host = (parsed.hostname or "").lower()
77
+ path = parsed.path or ""
78
+
79
+ def with_wiki_path(url: str) -> str:
80
+ p = urlparse(url)
81
+ # Keep existing path if it already starts with /wiki
82
+ if (p.path or "").startswith("/wiki"):
83
+ return url
84
+ # Append /wiki, preserving any existing path (rare but safe)
85
+ new_path = (p.path or "") + "/wiki"
86
+ return urlunparse(p._replace(path=new_path.rstrip("/")))
87
+
88
+ candidate_base_urls: list[str] = []
89
+ if host.endswith(".atlassian.net"):
90
+ # For Atlassian Cloud, prefer the /wiki variant first
91
+ candidate_base_urls.append(with_wiki_path(base_url))
92
+ candidate_base_urls.append(base_url)
93
+ # De-duplicate while preserving order
94
+ candidate_base_urls = list(dict.fromkeys(candidate_base_urls))
95
+
96
+ # Check authentication credentials
97
+ username = settings.get("username")
98
+ api_key = settings.get("api_key")
99
+ token = settings.get("token")
100
+
101
+ api_key_value = api_key.get_secret_value() if hasattr(api_key, 'get_secret_value') else api_key
102
+ token_value = token.get_secret_value() if hasattr(token, 'get_secret_value') else token
103
+
104
+ # Validate authentication - at least one method must be provided
105
+ has_basic_auth = bool(username and api_key_value and str(api_key_value).strip())
106
+ has_token = bool(token_value and str(token_value).strip())
107
+
108
+ # Determine authentication method
109
+ auth_headers = {}
110
+ auth = None
111
+
112
+ if has_token:
113
+ # Bearer token authentication
114
+ auth_headers["Authorization"] = f"Bearer {token_value}"
115
+ elif has_basic_auth:
116
+ # Basic authentication
117
+ auth = HTTPBasicAuth(username, api_key_value)
118
+ else:
119
+ return "Authentication required: provide either token or both username and api_key"
120
+
121
+ try:
122
+ # Test connection using /rest/api/user/current endpoint
123
+ # This endpoint returns current user info and validates authentication
124
+ last_status = None
125
+ for candidate_base in candidate_base_urls:
126
+ test_url = f"{candidate_base}/rest/api/user/current"
127
+ response = requests.get(
128
+ test_url,
129
+ auth=auth,
130
+ headers=auth_headers,
131
+ timeout=10
132
+ )
133
+ last_status = response.status_code
134
+
135
+ if response.status_code == 200:
136
+ return None
137
+
138
+ # If we get 404 on the first candidate, try the next one
139
+ if response.status_code == 404:
140
+ continue
141
+
142
+ if response.status_code == 401:
143
+ return "Invalid credentials (401) - check token or username/api_key"
144
+ if response.status_code == 403:
145
+ return "Access forbidden (403) - credentials lack Confluence permissions"
146
+ if response.status_code == 429:
147
+ return "Rate limited (429) - please try again later"
148
+ if 500 <= response.status_code <= 599:
149
+ return f"Confluence service error (HTTP {response.status_code})"
150
+ return f"Confluence request failed (HTTP {response.status_code})"
151
+
152
+ # All candidates returned 404
153
+ return "Confluence API endpoint not found (404) - verify the Confluence URL"
154
+
155
+ except requests.exceptions.SSLError as e:
156
+ if 'Hostname mismatch' in str(e):
157
+ return "SSL error - hostname mismatch. Verify the Confluence URL"
158
+ return "SSL error - certificate verification failed"
159
+ except requests.exceptions.ConnectionError:
160
+ return "Connection error - unable to reach Confluence. Check URL and network."
161
+ except requests.exceptions.Timeout:
162
+ return "Connection timeout - Confluence did not respond within 10 seconds. Check URL and network."
163
+ except requests.exceptions.RequestException as e:
164
+ return f"Request failed: {str(e)}"
165
+ except Exception:
166
+ return "Unexpected error during Confluence connection check"
@@ -12,7 +12,7 @@ class DeltaLakeConfiguration(BaseModel):
12
12
  "hidden": True,
13
13
  "section": "credentials",
14
14
  "type": "delta_lake",
15
- "categories": ["database"],
15
+ "categories": ["other"],
16
16
  "extra_categories": ["aws", "data lake", "analytics", "storage"],
17
17
  }
18
18
  }
@@ -1,8 +1,30 @@
1
+ from json import JSONDecodeError
1
2
  from typing import Optional
2
3
 
4
+ import requests
3
5
  from pydantic import BaseModel, ConfigDict, Field, SecretStr
4
6
 
5
7
 
8
+ def _parse_error_response(response: requests.Response) -> Optional[str]:
9
+ """
10
+ Parse error response from Figma API to extract detailed error message.
11
+
12
+ Args:
13
+ response: Response object from requests
14
+
15
+ Returns:
16
+ Detailed error message if found, None otherwise
17
+ """
18
+ try:
19
+ json_response = response.json()
20
+ error = json_response.get("err") or json_response.get("error")
21
+ if error and 'Invalid token' in str(error):
22
+ return "Invalid token. Please verify the Figma token and try again."
23
+ except (JSONDecodeError, KeyError, AttributeError):
24
+ pass
25
+ return None
26
+
27
+
6
28
  class FigmaConfiguration(BaseModel):
7
29
  model_config = ConfigDict(
8
30
  json_schema_extra={
@@ -16,10 +38,6 @@ class FigmaConfiguration(BaseModel):
16
38
  {
17
39
  "name": "Token",
18
40
  "fields": ["token"]
19
- },
20
- {
21
- "name": "Oath2",
22
- "fields": ["oauth2"]
23
41
  }
24
42
  ]
25
43
  }
@@ -32,4 +50,57 @@ class FigmaConfiguration(BaseModel):
32
50
  }
33
51
  )
34
52
  token: Optional[SecretStr] = Field(description="Figma Token", json_schema_extra={"secret": True}, default=None)
35
- oauth2: Optional[SecretStr] = Field(description="OAuth2 Token", json_schema_extra={"secret": True}, default=None)
53
+
54
+ @staticmethod
55
+ def check_connection(settings: dict) -> str | None:
56
+ """
57
+ Test the connection to Figma API.
58
+
59
+ Args:
60
+ settings: Dictionary containing 'token' (required)
61
+
62
+ Returns:
63
+ None if connection is successful, error message string otherwise
64
+ """
65
+ token = settings.get("token")
66
+ if token is None:
67
+ return "Token is required"
68
+
69
+ # Extract secret value if it's a SecretStr
70
+ if hasattr(token, "get_secret_value"):
71
+ token = token.get_secret_value()
72
+
73
+ # Validate token is not empty
74
+ if not token or not token.strip():
75
+ return "Token cannot be empty"
76
+
77
+ # Figma API endpoint
78
+ base_url = "https://api.figma.com"
79
+ endpoint = f"{base_url}/v1/me"
80
+
81
+ try:
82
+ response = requests.get(
83
+ endpoint,
84
+ headers={"X-Figma-Token": token},
85
+ timeout=10,
86
+ )
87
+
88
+ if response.status_code == 200:
89
+ return None # Connection successful
90
+ elif response.status_code == 401:
91
+ detailed_error = _parse_error_response(response)
92
+ return detailed_error if detailed_error else "Invalid token"
93
+ elif response.status_code == 403:
94
+ detailed_error = _parse_error_response(response)
95
+ return detailed_error if detailed_error else "Access forbidden - token may lack required permissions"
96
+ else:
97
+ return f"Connection failed with status {response.status_code}"
98
+
99
+ except requests.exceptions.Timeout:
100
+ return "Connection timeout - Figma API is not responding"
101
+ except requests.exceptions.ConnectionError:
102
+ return "Connection error - unable to reach Figma API"
103
+ except requests.exceptions.RequestException as e:
104
+ return f"Request failed: {str(e)}"
105
+ except Exception as e:
106
+ return f"Unexpected error: {str(e)}"