crewai-core 1.14.5__tar.gz

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 (32) hide show
  1. crewai_core-1.14.5/.gitignore +33 -0
  2. crewai_core-1.14.5/PKG-INFO +31 -0
  3. crewai_core-1.14.5/README.md +8 -0
  4. crewai_core-1.14.5/pyproject.toml +38 -0
  5. crewai_core-1.14.5/src/crewai_core/__init__.py +1 -0
  6. crewai_core-1.14.5/src/crewai_core/auth/__init__.py +24 -0
  7. crewai_core-1.14.5/src/crewai_core/auth/constants.py +8 -0
  8. crewai_core-1.14.5/src/crewai_core/auth/oauth2.py +186 -0
  9. crewai_core-1.14.5/src/crewai_core/auth/providers/__init__.py +1 -0
  10. crewai_core-1.14.5/src/crewai_core/auth/providers/auth0.py +40 -0
  11. crewai_core-1.14.5/src/crewai_core/auth/providers/base_provider.py +46 -0
  12. crewai_core-1.14.5/src/crewai_core/auth/providers/entra_id.py +49 -0
  13. crewai_core-1.14.5/src/crewai_core/auth/providers/keycloak.py +38 -0
  14. crewai_core-1.14.5/src/crewai_core/auth/providers/okta.py +48 -0
  15. crewai_core-1.14.5/src/crewai_core/auth/providers/workos.py +36 -0
  16. crewai_core-1.14.5/src/crewai_core/auth/token.py +17 -0
  17. crewai_core-1.14.5/src/crewai_core/auth/utils.py +71 -0
  18. crewai_core-1.14.5/src/crewai_core/constants.py +22 -0
  19. crewai_core-1.14.5/src/crewai_core/lock_store.py +89 -0
  20. crewai_core-1.14.5/src/crewai_core/paths.py +26 -0
  21. crewai_core-1.14.5/src/crewai_core/plus_api.py +232 -0
  22. crewai_core-1.14.5/src/crewai_core/printer.py +103 -0
  23. crewai_core-1.14.5/src/crewai_core/project.py +109 -0
  24. crewai_core-1.14.5/src/crewai_core/py.typed +0 -0
  25. crewai_core-1.14.5/src/crewai_core/settings.py +215 -0
  26. crewai_core-1.14.5/src/crewai_core/telemetry.py +272 -0
  27. crewai_core-1.14.5/src/crewai_core/token_manager.py +151 -0
  28. crewai_core-1.14.5/src/crewai_core/tool_credentials.py +56 -0
  29. crewai_core-1.14.5/src/crewai_core/user_data.py +91 -0
  30. crewai_core-1.14.5/src/crewai_core/version.py +194 -0
  31. crewai_core-1.14.5/tests/__init__.py +0 -0
  32. crewai_core-1.14.5/tests/test_smoke.py +96 -0
@@ -0,0 +1,33 @@
1
+ .DS_Store
2
+ .pytest_cache
3
+ __pycache__
4
+ dist/
5
+ .env
6
+ assets/*
7
+ .idea
8
+ test/
9
+ docs_crew/
10
+ chroma.sqlite3
11
+ old_en.json
12
+ db/
13
+ test.py
14
+ rc-tests/*
15
+ *.pkl
16
+ temp/*
17
+ .vscode/*
18
+ crew_tasks_output.json
19
+ .codesight
20
+ .mypy_cache
21
+ .ruff_cache
22
+ .venv
23
+ test_flow.html
24
+ crewairules.mdc
25
+ plan.md
26
+ conceptual_plan.md
27
+ build_image
28
+ chromadb-*.lock
29
+ .claude
30
+ .crewai/memory
31
+ blogs/*
32
+ secrets/*
33
+ UNKNOWN.egg-info/
@@ -0,0 +1,31 @@
1
+ Metadata-Version: 2.4
2
+ Name: crewai-core
3
+ Version: 1.14.5
4
+ Summary: Shared utilities for CrewAI — version, paths, user-data, telemetry, printer.
5
+ Project-URL: Homepage, https://crewai.com
6
+ Project-URL: Documentation, https://docs.crewai.com
7
+ Project-URL: Repository, https://github.com/crewAIInc/crewAI
8
+ Author-email: "Greyson R. LaLonde" <greyson@crewai.com>
9
+ Requires-Python: <3.14,>=3.10
10
+ Requires-Dist: appdirs~=1.4.4
11
+ Requires-Dist: cryptography>=42.0
12
+ Requires-Dist: httpx~=0.28.1
13
+ Requires-Dist: opentelemetry-api~=1.34.0
14
+ Requires-Dist: opentelemetry-exporter-otlp-proto-http~=1.34.0
15
+ Requires-Dist: opentelemetry-sdk~=1.34.0
16
+ Requires-Dist: packaging>=23.0
17
+ Requires-Dist: portalocker~=2.7.0
18
+ Requires-Dist: pydantic<2.13,>=2.11.9
19
+ Requires-Dist: pyjwt<3,>=2.9.0
20
+ Requires-Dist: rich>=13.7.1
21
+ Requires-Dist: tomli~=2.0.2
22
+ Description-Content-Type: text/markdown
23
+
24
+ # crewai-core
25
+
26
+ Shared utilities used by both `crewai` and `crewai-cli`: version lookup, storage
27
+ paths, user-data helpers, telemetry, and the printer.
28
+
29
+ This package is a leaf — it has no dependency on the `crewai` framework — and is
30
+ pulled in transitively by `crewai` and `crewai-cli`. End users do not normally
31
+ install it directly.
@@ -0,0 +1,8 @@
1
+ # crewai-core
2
+
3
+ Shared utilities used by both `crewai` and `crewai-cli`: version lookup, storage
4
+ paths, user-data helpers, telemetry, and the printer.
5
+
6
+ This package is a leaf — it has no dependency on the `crewai` framework — and is
7
+ pulled in transitively by `crewai` and `crewai-cli`. End users do not normally
8
+ install it directly.
@@ -0,0 +1,38 @@
1
+ [project]
2
+ name = "crewai-core"
3
+ dynamic = ["version"]
4
+ description = "Shared utilities for CrewAI — version, paths, user-data, telemetry, printer."
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "Greyson R. LaLonde", email = "greyson@crewai.com" }
8
+ ]
9
+ requires-python = ">=3.10, <3.14"
10
+ dependencies = [
11
+ "appdirs~=1.4.4",
12
+ "cryptography>=42.0",
13
+ "httpx~=0.28.1",
14
+ "packaging>=23.0",
15
+ "portalocker~=2.7.0",
16
+ "pyjwt>=2.9.0,<3",
17
+ "pydantic>=2.11.9,<2.13",
18
+ "rich>=13.7.1",
19
+ "opentelemetry-api~=1.34.0",
20
+ "opentelemetry-sdk~=1.34.0",
21
+ "opentelemetry-exporter-otlp-proto-http~=1.34.0",
22
+ "tomli~=2.0.2",
23
+ ]
24
+
25
+ [project.urls]
26
+ Homepage = "https://crewai.com"
27
+ Documentation = "https://docs.crewai.com"
28
+ Repository = "https://github.com/crewAIInc/crewAI"
29
+
30
+ [build-system]
31
+ requires = ["hatchling"]
32
+ build-backend = "hatchling.build"
33
+
34
+ [tool.hatch.version]
35
+ path = "src/crewai_core/__init__.py"
36
+
37
+ [tool.hatch.build.targets.wheel]
38
+ packages = ["src/crewai_core"]
@@ -0,0 +1 @@
1
+ __version__ = "1.14.5"
@@ -0,0 +1,24 @@
1
+ """OAuth2 authentication primitives — shared by crewai and crewai-cli."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from crewai_core.auth.oauth2 import (
6
+ AuthenticationCommand as AuthenticationCommand,
7
+ Oauth2Settings as Oauth2Settings,
8
+ ProviderFactory as ProviderFactory,
9
+ )
10
+ from crewai_core.auth.token import (
11
+ AuthError as AuthError,
12
+ get_auth_token as get_auth_token,
13
+ )
14
+ from crewai_core.auth.utils import validate_jwt_token as validate_jwt_token
15
+
16
+
17
+ __all__ = [
18
+ "AuthError",
19
+ "AuthenticationCommand",
20
+ "Oauth2Settings",
21
+ "ProviderFactory",
22
+ "get_auth_token",
23
+ "validate_jwt_token",
24
+ ]
@@ -0,0 +1,8 @@
1
+ """Authentication constants."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Final
6
+
7
+
8
+ ALGORITHMS: Final[list[str]] = ["RS256"]
@@ -0,0 +1,186 @@
1
+ """OAuth2 device-flow authentication for the CrewAI platform."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import time
6
+ from typing import TYPE_CHECKING, Any, TypeVar, cast
7
+ import webbrowser
8
+
9
+ import httpx
10
+ from pydantic import BaseModel, Field
11
+ from rich.console import Console
12
+
13
+ from crewai_core.auth.utils import validate_jwt_token
14
+ from crewai_core.settings import Settings
15
+ from crewai_core.token_manager import TokenManager
16
+
17
+
18
+ console = Console()
19
+
20
+ TOauth2Settings = TypeVar("TOauth2Settings", bound="Oauth2Settings")
21
+
22
+
23
+ class Oauth2Settings(BaseModel):
24
+ """OAuth2 provider configuration."""
25
+
26
+ provider: str = Field(
27
+ description="OAuth2 provider used for authentication (e.g., workos, okta, auth0)."
28
+ )
29
+ client_id: str = Field(
30
+ description="OAuth2 client ID issued by the provider, used during authentication requests."
31
+ )
32
+ domain: str = Field(
33
+ description="OAuth2 provider's domain (e.g., your-org.auth0.com) used for issuing tokens."
34
+ )
35
+ audience: str | None = Field(
36
+ description="OAuth2 audience value, typically used to identify the target API or resource.",
37
+ default=None,
38
+ )
39
+ extra: dict[str, Any] = Field(
40
+ description="Extra configuration for the OAuth2 provider.",
41
+ default={},
42
+ )
43
+
44
+ @classmethod
45
+ def from_settings(cls: type[TOauth2Settings]) -> TOauth2Settings:
46
+ """Build an ``Oauth2Settings`` instance from the persisted CrewAI settings."""
47
+ settings = Settings()
48
+
49
+ return cls(
50
+ provider=settings.oauth2_provider,
51
+ domain=settings.oauth2_domain,
52
+ client_id=settings.oauth2_client_id,
53
+ audience=settings.oauth2_audience,
54
+ extra=settings.oauth2_extra,
55
+ )
56
+
57
+
58
+ if TYPE_CHECKING:
59
+ from crewai_core.auth.providers.base_provider import BaseProvider
60
+
61
+
62
+ class ProviderFactory:
63
+ """Factory for resolving the configured OAuth2 provider."""
64
+
65
+ @classmethod
66
+ def from_settings(
67
+ cls: type["ProviderFactory"], # noqa: UP037
68
+ settings: Oauth2Settings | None = None,
69
+ ) -> "BaseProvider": # noqa: UP037
70
+ """Create a provider instance from settings, importing the module dynamically."""
71
+ settings = settings or Oauth2Settings.from_settings()
72
+
73
+ import importlib
74
+
75
+ module = importlib.import_module(
76
+ f"crewai_core.auth.providers.{settings.provider.lower()}"
77
+ )
78
+ provider = getattr(
79
+ module,
80
+ f"{''.join(word.capitalize() for word in settings.provider.split('_'))}Provider",
81
+ )
82
+
83
+ return cast("BaseProvider", provider(settings))
84
+
85
+
86
+ class AuthenticationCommand:
87
+ """Drives the OAuth2 device-flow login against the configured provider."""
88
+
89
+ def __init__(self) -> None:
90
+ self.token_manager = TokenManager()
91
+ self.oauth2_provider = ProviderFactory.from_settings()
92
+
93
+ def login(self) -> None:
94
+ """Sign in to the CrewAI platform via the OAuth2 device flow."""
95
+ console.print("Signing in to CrewAI AMP...\n", style="bold blue")
96
+
97
+ device_code_data = self._get_device_code()
98
+ self._display_auth_instructions(device_code_data)
99
+
100
+ return self._poll_for_token(device_code_data)
101
+
102
+ def _get_device_code(self) -> dict[str, Any]:
103
+ """Request a device code from the provider."""
104
+ device_code_payload = {
105
+ "client_id": self.oauth2_provider.get_client_id(),
106
+ "scope": " ".join(self.oauth2_provider.get_oauth_scopes()),
107
+ "audience": self.oauth2_provider.get_audience(),
108
+ }
109
+ response = httpx.post(
110
+ url=self.oauth2_provider.get_authorize_url(),
111
+ data=device_code_payload,
112
+ timeout=20,
113
+ )
114
+ response.raise_for_status()
115
+ return cast(dict[str, Any], response.json())
116
+
117
+ def _display_auth_instructions(self, device_code_data: dict[str, str]) -> None:
118
+ """Print and open the verification URL the user must visit."""
119
+ verification_uri = device_code_data.get(
120
+ "verification_uri_complete", device_code_data.get("verification_uri", "")
121
+ )
122
+
123
+ console.print("1. Navigate to: ", verification_uri)
124
+ console.print("2. Enter the following code: ", device_code_data["user_code"])
125
+ webbrowser.open(verification_uri)
126
+
127
+ def _poll_for_token(self, device_code_data: dict[str, Any]) -> None:
128
+ """Poll the token endpoint until authentication completes or times out."""
129
+ token_payload = {
130
+ "grant_type": "urn:ietf:params:oauth:grant-type:device_code",
131
+ "device_code": device_code_data["device_code"],
132
+ "client_id": self.oauth2_provider.get_client_id(),
133
+ }
134
+
135
+ console.print("\nWaiting for authentication... ", style="bold blue", end="")
136
+
137
+ attempts = 0
138
+ while True and attempts < 10:
139
+ response = httpx.post(
140
+ self.oauth2_provider.get_token_url(), data=token_payload, timeout=30
141
+ )
142
+ token_data = response.json()
143
+
144
+ if response.status_code == 200:
145
+ self._validate_and_save_token(token_data)
146
+
147
+ console.print(
148
+ "Success!",
149
+ style="bold green",
150
+ )
151
+
152
+ self._post_login()
153
+
154
+ console.print("\n[bold green]Welcome to CrewAI AMP![/bold green]\n")
155
+ return
156
+
157
+ if token_data["error"] not in ("authorization_pending", "slow_down"):
158
+ raise httpx.HTTPError(
159
+ token_data.get("error_description") or token_data.get("error")
160
+ )
161
+
162
+ time.sleep(device_code_data["interval"])
163
+ attempts += 1
164
+
165
+ console.print(
166
+ "Timeout: Failed to get the token. Please try again.", style="bold red"
167
+ )
168
+
169
+ def _validate_and_save_token(self, token_data: dict[str, Any]) -> None:
170
+ """Validate the JWT and persist it via the token manager."""
171
+ jwt_token = token_data["access_token"]
172
+ issuer = self.oauth2_provider.get_issuer()
173
+ jwt_token_data = {
174
+ "jwt_token": jwt_token,
175
+ "jwks_url": self.oauth2_provider.get_jwks_url(),
176
+ "issuer": issuer,
177
+ "audience": self.oauth2_provider.get_audience(),
178
+ }
179
+
180
+ decoded_token = validate_jwt_token(**jwt_token_data)
181
+
182
+ expires_at = decoded_token.get("exp", 0)
183
+ self.token_manager.save_tokens(jwt_token, expires_at)
184
+
185
+ def _post_login(self) -> None:
186
+ """Hook called after a successful login. Override to extend behavior."""
@@ -0,0 +1 @@
1
+ """OAuth2 authentication providers."""
@@ -0,0 +1,40 @@
1
+ """Auth0 OAuth2 provider."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from crewai_core.auth.providers.base_provider import BaseProvider
6
+
7
+
8
+ class Auth0Provider(BaseProvider):
9
+ """Auth0 OAuth2 provider implementation."""
10
+
11
+ def get_authorize_url(self) -> str:
12
+ return f"https://{self._get_domain()}/oauth/device/code"
13
+
14
+ def get_token_url(self) -> str:
15
+ return f"https://{self._get_domain()}/oauth/token"
16
+
17
+ def get_jwks_url(self) -> str:
18
+ return f"https://{self._get_domain()}/.well-known/jwks.json"
19
+
20
+ def get_issuer(self) -> str:
21
+ return f"https://{self._get_domain()}/"
22
+
23
+ def get_audience(self) -> str:
24
+ if self.settings.audience is None:
25
+ raise ValueError(
26
+ "Audience is required. Please set it in the configuration."
27
+ )
28
+ return self.settings.audience
29
+
30
+ def get_client_id(self) -> str:
31
+ if self.settings.client_id is None:
32
+ raise ValueError(
33
+ "Client ID is required. Please set it in the configuration."
34
+ )
35
+ return self.settings.client_id
36
+
37
+ def _get_domain(self) -> str:
38
+ if self.settings.domain is None:
39
+ raise ValueError("Domain is required. Please set it in the configuration.")
40
+ return self.settings.domain
@@ -0,0 +1,46 @@
1
+ """Base OAuth2 provider interface."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from abc import ABC, abstractmethod
6
+
7
+ from crewai_core.auth.oauth2 import Oauth2Settings
8
+
9
+
10
+ class BaseProvider(ABC):
11
+ """Abstract base class for OAuth2 providers."""
12
+
13
+ def __init__(self, settings: Oauth2Settings):
14
+ self.settings = settings
15
+
16
+ @abstractmethod
17
+ def get_authorize_url(self) -> str:
18
+ """Return the authorization endpoint URL."""
19
+
20
+ @abstractmethod
21
+ def get_token_url(self) -> str:
22
+ """Return the token endpoint URL."""
23
+
24
+ @abstractmethod
25
+ def get_jwks_url(self) -> str:
26
+ """Return the JWKS endpoint URL."""
27
+
28
+ @abstractmethod
29
+ def get_issuer(self) -> str:
30
+ """Return the OAuth issuer identifier."""
31
+
32
+ @abstractmethod
33
+ def get_audience(self) -> str:
34
+ """Return the OAuth audience identifier."""
35
+
36
+ @abstractmethod
37
+ def get_client_id(self) -> str:
38
+ """Return the OAuth client identifier."""
39
+
40
+ def get_required_fields(self) -> list[str]:
41
+ """Return provider-specific keys required inside ``Oauth2Settings.extra``."""
42
+ return []
43
+
44
+ def get_oauth_scopes(self) -> list[str]:
45
+ """Return the OAuth scopes to request."""
46
+ return ["openid", "profile", "email"]
@@ -0,0 +1,49 @@
1
+ """Entra ID (Azure AD) OAuth2 provider."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import cast
6
+
7
+ from crewai_core.auth.providers.base_provider import BaseProvider
8
+
9
+
10
+ class EntraIdProvider(BaseProvider):
11
+ """Entra ID (Azure AD) OAuth2 provider implementation."""
12
+
13
+ def get_authorize_url(self) -> str:
14
+ return f"{self._base_url()}/oauth2/v2.0/devicecode"
15
+
16
+ def get_token_url(self) -> str:
17
+ return f"{self._base_url()}/oauth2/v2.0/token"
18
+
19
+ def get_jwks_url(self) -> str:
20
+ return f"{self._base_url()}/discovery/v2.0/keys"
21
+
22
+ def get_issuer(self) -> str:
23
+ return f"{self._base_url()}/v2.0"
24
+
25
+ def get_audience(self) -> str:
26
+ if self.settings.audience is None:
27
+ raise ValueError(
28
+ "Audience is required. Please set it in the configuration."
29
+ )
30
+ return self.settings.audience
31
+
32
+ def get_client_id(self) -> str:
33
+ if self.settings.client_id is None:
34
+ raise ValueError(
35
+ "Client ID is required. Please set it in the configuration."
36
+ )
37
+ return self.settings.client_id
38
+
39
+ def get_oauth_scopes(self) -> list[str]:
40
+ return [
41
+ *super().get_oauth_scopes(),
42
+ *cast(str, self.settings.extra.get("scope", "")).split(),
43
+ ]
44
+
45
+ def get_required_fields(self) -> list[str]:
46
+ return ["scope"]
47
+
48
+ def _base_url(self) -> str:
49
+ return f"https://login.microsoftonline.com/{self.settings.domain}"
@@ -0,0 +1,38 @@
1
+ """Keycloak OAuth2 provider."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from crewai_core.auth.providers.base_provider import BaseProvider
6
+
7
+
8
+ class KeycloakProvider(BaseProvider):
9
+ """Keycloak OAuth2 provider implementation."""
10
+
11
+ def get_authorize_url(self) -> str:
12
+ return f"{self._oauth2_base_url()}/realms/{self.settings.extra.get('realm')}/protocol/openid-connect/auth/device"
13
+
14
+ def get_token_url(self) -> str:
15
+ return f"{self._oauth2_base_url()}/realms/{self.settings.extra.get('realm')}/protocol/openid-connect/token"
16
+
17
+ def get_jwks_url(self) -> str:
18
+ return f"{self._oauth2_base_url()}/realms/{self.settings.extra.get('realm')}/protocol/openid-connect/certs"
19
+
20
+ def get_issuer(self) -> str:
21
+ return f"{self._oauth2_base_url()}/realms/{self.settings.extra.get('realm')}"
22
+
23
+ def get_audience(self) -> str:
24
+ return self.settings.audience or "no-audience-provided"
25
+
26
+ def get_client_id(self) -> str:
27
+ if self.settings.client_id is None:
28
+ raise ValueError(
29
+ "Client ID is required. Please set it in the configuration."
30
+ )
31
+ return self.settings.client_id
32
+
33
+ def get_required_fields(self) -> list[str]:
34
+ return ["realm"]
35
+
36
+ def _oauth2_base_url(self) -> str:
37
+ domain = self.settings.domain.removeprefix("https://").removeprefix("http://")
38
+ return f"https://{domain}"
@@ -0,0 +1,48 @@
1
+ """Okta OAuth2 provider."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from crewai_core.auth.providers.base_provider import BaseProvider
6
+
7
+
8
+ class OktaProvider(BaseProvider):
9
+ """Okta OAuth2 provider implementation."""
10
+
11
+ def get_authorize_url(self) -> str:
12
+ return f"{self._oauth2_base_url()}/v1/device/authorize"
13
+
14
+ def get_token_url(self) -> str:
15
+ return f"{self._oauth2_base_url()}/v1/token"
16
+
17
+ def get_jwks_url(self) -> str:
18
+ return f"{self._oauth2_base_url()}/v1/keys"
19
+
20
+ def get_issuer(self) -> str:
21
+ return self._oauth2_base_url().removesuffix("/oauth2")
22
+
23
+ def get_audience(self) -> str:
24
+ if self.settings.audience is None:
25
+ raise ValueError(
26
+ "Audience is required. Please set it in the configuration."
27
+ )
28
+ return self.settings.audience
29
+
30
+ def get_client_id(self) -> str:
31
+ if self.settings.client_id is None:
32
+ raise ValueError(
33
+ "Client ID is required. Please set it in the configuration."
34
+ )
35
+ return self.settings.client_id
36
+
37
+ def get_required_fields(self) -> list[str]:
38
+ return ["authorization_server_name", "using_org_auth_server"]
39
+
40
+ def _oauth2_base_url(self) -> str:
41
+ using_org_auth_server = self.settings.extra.get("using_org_auth_server", False)
42
+
43
+ if using_org_auth_server:
44
+ base_url = f"https://{self.settings.domain}/oauth2"
45
+ else:
46
+ base_url = f"https://{self.settings.domain}/oauth2/{self.settings.extra.get('authorization_server_name', 'default')}"
47
+
48
+ return f"{base_url}"
@@ -0,0 +1,36 @@
1
+ """WorkOS OAuth2 provider."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from crewai_core.auth.providers.base_provider import BaseProvider
6
+
7
+
8
+ class WorkosProvider(BaseProvider):
9
+ """WorkOS OAuth2 provider implementation."""
10
+
11
+ def get_authorize_url(self) -> str:
12
+ return f"https://{self._get_domain()}/oauth2/device_authorization"
13
+
14
+ def get_token_url(self) -> str:
15
+ return f"https://{self._get_domain()}/oauth2/token"
16
+
17
+ def get_jwks_url(self) -> str:
18
+ return f"https://{self._get_domain()}/oauth2/jwks"
19
+
20
+ def get_issuer(self) -> str:
21
+ return f"https://{self._get_domain()}"
22
+
23
+ def get_audience(self) -> str:
24
+ return self.settings.audience or ""
25
+
26
+ def get_client_id(self) -> str:
27
+ if self.settings.client_id is None:
28
+ raise ValueError(
29
+ "Client ID is required. Please set it in the configuration."
30
+ )
31
+ return self.settings.client_id
32
+
33
+ def _get_domain(self) -> str:
34
+ if self.settings.domain is None:
35
+ raise ValueError("Domain is required. Please set it in the configuration.")
36
+ return self.settings.domain
@@ -0,0 +1,17 @@
1
+ """Authentication token retrieval."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from crewai_core.token_manager import TokenManager
6
+
7
+
8
+ class AuthError(Exception):
9
+ """Raised when authentication fails."""
10
+
11
+
12
+ def get_auth_token() -> str:
13
+ """Return the saved authentication token; raise ``AuthError`` if missing."""
14
+ access_token = TokenManager().get_token()
15
+ if not access_token:
16
+ raise AuthError("No token found, make sure you are logged in")
17
+ return access_token