golf-mcp 0.2.16__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.
- golf/__init__.py +1 -0
- golf/auth/__init__.py +277 -0
- golf/auth/api_key.py +73 -0
- golf/auth/factory.py +360 -0
- golf/auth/helpers.py +175 -0
- golf/auth/providers.py +586 -0
- golf/auth/registry.py +256 -0
- golf/cli/__init__.py +1 -0
- golf/cli/branding.py +191 -0
- golf/cli/main.py +377 -0
- golf/commands/__init__.py +5 -0
- golf/commands/build.py +81 -0
- golf/commands/init.py +290 -0
- golf/commands/run.py +137 -0
- golf/core/__init__.py +1 -0
- golf/core/builder.py +1884 -0
- golf/core/builder_auth.py +209 -0
- golf/core/builder_metrics.py +221 -0
- golf/core/builder_telemetry.py +99 -0
- golf/core/config.py +199 -0
- golf/core/parser.py +1085 -0
- golf/core/telemetry.py +492 -0
- golf/core/transformer.py +231 -0
- golf/examples/__init__.py +0 -0
- golf/examples/basic/.env.example +4 -0
- golf/examples/basic/README.md +133 -0
- golf/examples/basic/auth.py +76 -0
- golf/examples/basic/golf.json +5 -0
- golf/examples/basic/prompts/welcome.py +27 -0
- golf/examples/basic/resources/current_time.py +34 -0
- golf/examples/basic/resources/info.py +28 -0
- golf/examples/basic/resources/weather/city.py +46 -0
- golf/examples/basic/resources/weather/client.py +48 -0
- golf/examples/basic/resources/weather/current.py +36 -0
- golf/examples/basic/resources/weather/forecast.py +36 -0
- golf/examples/basic/tools/calculator.py +94 -0
- golf/examples/basic/tools/say/hello.py +65 -0
- golf/metrics/__init__.py +10 -0
- golf/metrics/collector.py +320 -0
- golf/metrics/registry.py +12 -0
- golf/telemetry/__init__.py +23 -0
- golf/telemetry/instrumentation.py +1402 -0
- golf/utilities/__init__.py +12 -0
- golf/utilities/context.py +53 -0
- golf/utilities/elicitation.py +170 -0
- golf/utilities/sampling.py +221 -0
- golf_mcp-0.2.16.dist-info/METADATA +262 -0
- golf_mcp-0.2.16.dist-info/RECORD +52 -0
- golf_mcp-0.2.16.dist-info/WHEEL +5 -0
- golf_mcp-0.2.16.dist-info/entry_points.txt +2 -0
- golf_mcp-0.2.16.dist-info/licenses/LICENSE +201 -0
- golf_mcp-0.2.16.dist-info/top_level.txt +1 -0
golf/auth/registry.py
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
"""Provider registry system for extensible authentication providers.
|
|
2
|
+
|
|
3
|
+
This module provides a registry-based dispatch system that allows custom
|
|
4
|
+
authentication providers to be added without modifying the core factory code.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Protocol, TYPE_CHECKING
|
|
8
|
+
from abc import ABC, abstractmethod
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from fastmcp.server.auth.auth import AuthProvider
|
|
12
|
+
|
|
13
|
+
from .providers import AuthConfig
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class AuthProviderFactory(Protocol):
|
|
17
|
+
"""Protocol for auth provider factory functions.
|
|
18
|
+
|
|
19
|
+
Custom provider factories must implement this interface to be compatible
|
|
20
|
+
with the registry system.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __call__(self, config: AuthConfig) -> "AuthProvider":
|
|
24
|
+
"""Create an AuthProvider from configuration.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
config: Authentication configuration object
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
Configured FastMCP AuthProvider instance
|
|
31
|
+
|
|
32
|
+
Raises:
|
|
33
|
+
ValueError: If configuration is invalid
|
|
34
|
+
ImportError: If required dependencies are missing
|
|
35
|
+
"""
|
|
36
|
+
...
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class BaseProviderPlugin(ABC):
|
|
40
|
+
"""Base class for auth provider plugins.
|
|
41
|
+
|
|
42
|
+
Provider plugins can extend this class to provide both configuration
|
|
43
|
+
and factory logic in a single cohesive unit.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
@property
|
|
47
|
+
@abstractmethod
|
|
48
|
+
def provider_type(self) -> str:
|
|
49
|
+
"""Return the provider type identifier."""
|
|
50
|
+
...
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
@abstractmethod
|
|
54
|
+
def config_class(self) -> type[AuthConfig]:
|
|
55
|
+
"""Return the configuration class for this provider."""
|
|
56
|
+
...
|
|
57
|
+
|
|
58
|
+
@abstractmethod
|
|
59
|
+
def create_provider(self, config: AuthConfig) -> "AuthProvider":
|
|
60
|
+
"""Create the auth provider from configuration.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
config: Authentication configuration (must be instance of config_class)
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
Configured FastMCP AuthProvider instance
|
|
67
|
+
"""
|
|
68
|
+
...
|
|
69
|
+
|
|
70
|
+
def validate_config(self, config: AuthConfig) -> None:
|
|
71
|
+
"""Validate the configuration before creating provider.
|
|
72
|
+
|
|
73
|
+
Override this method to add custom validation logic.
|
|
74
|
+
Default implementation checks config is correct type.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
config: Configuration to validate
|
|
78
|
+
|
|
79
|
+
Raises:
|
|
80
|
+
ValueError: If configuration is invalid
|
|
81
|
+
"""
|
|
82
|
+
if not isinstance(config, self.config_class):
|
|
83
|
+
raise ValueError(
|
|
84
|
+
f"Expected {self.config_class.__name__} for {self.provider_type} provider, got {type(config).__name__}"
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class AuthProviderRegistry:
|
|
89
|
+
"""Registry for authentication provider factories and plugins.
|
|
90
|
+
|
|
91
|
+
This registry allows custom authentication providers to be registered
|
|
92
|
+
without modifying the core factory code. Providers can be registered
|
|
93
|
+
either as simple factory functions or as full plugin classes.
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
def __init__(self) -> None:
|
|
97
|
+
self._factories: dict[str, AuthProviderFactory] = {}
|
|
98
|
+
self._plugins: dict[str, BaseProviderPlugin] = {}
|
|
99
|
+
|
|
100
|
+
def register_factory(self, provider_type: str, factory: AuthProviderFactory) -> None:
|
|
101
|
+
"""Register a factory function for a provider type.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
provider_type: Unique identifier for the provider type
|
|
105
|
+
factory: Factory function that creates providers
|
|
106
|
+
|
|
107
|
+
Raises:
|
|
108
|
+
ValueError: If provider_type is already registered
|
|
109
|
+
"""
|
|
110
|
+
if provider_type in self._factories or provider_type in self._plugins:
|
|
111
|
+
raise ValueError(f"Provider type '{provider_type}' is already registered")
|
|
112
|
+
|
|
113
|
+
self._factories[provider_type] = factory
|
|
114
|
+
|
|
115
|
+
def register_plugin(self, plugin: BaseProviderPlugin) -> None:
|
|
116
|
+
"""Register a provider plugin.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
plugin: Provider plugin instance
|
|
120
|
+
|
|
121
|
+
Raises:
|
|
122
|
+
ValueError: If provider type is already registered
|
|
123
|
+
"""
|
|
124
|
+
provider_type = plugin.provider_type
|
|
125
|
+
if provider_type in self._factories or provider_type in self._plugins:
|
|
126
|
+
raise ValueError(f"Provider type '{provider_type}' is already registered")
|
|
127
|
+
|
|
128
|
+
self._plugins[provider_type] = plugin
|
|
129
|
+
|
|
130
|
+
def unregister(self, provider_type: str) -> None:
|
|
131
|
+
"""Unregister a provider type.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
provider_type: Provider type to remove
|
|
135
|
+
|
|
136
|
+
Raises:
|
|
137
|
+
KeyError: If provider type is not registered
|
|
138
|
+
"""
|
|
139
|
+
if provider_type in self._factories:
|
|
140
|
+
del self._factories[provider_type]
|
|
141
|
+
elif provider_type in self._plugins:
|
|
142
|
+
del self._plugins[provider_type]
|
|
143
|
+
else:
|
|
144
|
+
raise KeyError(f"Provider type '{provider_type}' is not registered")
|
|
145
|
+
|
|
146
|
+
def get_factory(self, provider_type: str) -> AuthProviderFactory:
|
|
147
|
+
"""Get factory function for a provider type.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
provider_type: Provider type to look up
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
Factory function for the provider type
|
|
154
|
+
|
|
155
|
+
Raises:
|
|
156
|
+
KeyError: If provider type is not registered
|
|
157
|
+
"""
|
|
158
|
+
# Check factories first
|
|
159
|
+
if provider_type in self._factories:
|
|
160
|
+
return self._factories[provider_type]
|
|
161
|
+
|
|
162
|
+
# Check plugins
|
|
163
|
+
if provider_type in self._plugins:
|
|
164
|
+
plugin = self._plugins[provider_type]
|
|
165
|
+
|
|
166
|
+
# Wrap plugin method to match factory signature
|
|
167
|
+
def plugin_factory(config: AuthConfig) -> "AuthProvider":
|
|
168
|
+
plugin.validate_config(config)
|
|
169
|
+
return plugin.create_provider(config)
|
|
170
|
+
|
|
171
|
+
return plugin_factory
|
|
172
|
+
|
|
173
|
+
raise KeyError(f"No provider registered for type '{provider_type}'")
|
|
174
|
+
|
|
175
|
+
def create_provider(self, config: AuthConfig) -> "AuthProvider":
|
|
176
|
+
"""Create a provider from configuration using the registry.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
config: Authentication configuration
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
Configured AuthProvider instance
|
|
183
|
+
|
|
184
|
+
Raises:
|
|
185
|
+
KeyError: If provider type is not registered
|
|
186
|
+
ValueError: If configuration is invalid
|
|
187
|
+
"""
|
|
188
|
+
provider_type = getattr(config, "provider_type", None)
|
|
189
|
+
if not provider_type:
|
|
190
|
+
raise ValueError(f"Configuration {type(config).__name__} missing provider_type attribute")
|
|
191
|
+
|
|
192
|
+
factory = self.get_factory(provider_type)
|
|
193
|
+
return factory(config)
|
|
194
|
+
|
|
195
|
+
def list_providers(self) -> list[str]:
|
|
196
|
+
"""List all registered provider types.
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
List of provider type identifiers
|
|
200
|
+
"""
|
|
201
|
+
return sorted(list(self._factories.keys()) + list(self._plugins.keys()))
|
|
202
|
+
|
|
203
|
+
def is_registered(self, provider_type: str) -> bool:
|
|
204
|
+
"""Check if a provider type is registered.
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
provider_type: Provider type to check
|
|
208
|
+
|
|
209
|
+
Returns:
|
|
210
|
+
True if provider type is registered
|
|
211
|
+
"""
|
|
212
|
+
return provider_type in self._factories or provider_type in self._plugins
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
# Global registry instance
|
|
216
|
+
_default_registry = AuthProviderRegistry()
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def get_provider_registry() -> AuthProviderRegistry:
|
|
220
|
+
"""Get the default provider registry.
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
Default AuthProviderRegistry instance
|
|
224
|
+
"""
|
|
225
|
+
return _default_registry
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def register_provider_factory(provider_type: str, factory: AuthProviderFactory) -> None:
|
|
229
|
+
"""Register a factory function in the default registry.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
provider_type: Unique identifier for the provider type
|
|
233
|
+
factory: Factory function that creates providers
|
|
234
|
+
"""
|
|
235
|
+
_default_registry.register_factory(provider_type, factory)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def register_provider_plugin(plugin: BaseProviderPlugin) -> None:
|
|
239
|
+
"""Register a provider plugin in the default registry.
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
plugin: Provider plugin instance
|
|
243
|
+
"""
|
|
244
|
+
_default_registry.register_plugin(plugin)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def create_auth_provider_from_registry(config: AuthConfig) -> "AuthProvider":
|
|
248
|
+
"""Create an auth provider using the default registry.
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
config: Authentication configuration
|
|
252
|
+
|
|
253
|
+
Returns:
|
|
254
|
+
Configured AuthProvider instance
|
|
255
|
+
"""
|
|
256
|
+
return _default_registry.create_provider(config)
|
golf/cli/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""CLI package for the GolfMCP framework."""
|
golf/cli/branding.py
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"""Golf CLI branding and visual utilities."""
|
|
2
|
+
|
|
3
|
+
from rich.console import Console
|
|
4
|
+
from rich.panel import Panel
|
|
5
|
+
from rich.text import Text
|
|
6
|
+
from rich.align import Align
|
|
7
|
+
|
|
8
|
+
# Golf brand colors (official brand colors)
|
|
9
|
+
GOLF_BLUE = "#2969FD" # Primary blue from brand: rgb(41, 105, 253)
|
|
10
|
+
GOLF_ORANGE = "#F97728" # Secondary orange from brand: rgb(249, 119, 40)
|
|
11
|
+
GOLF_GREEN = "#10B981" # Success green
|
|
12
|
+
GOLF_WHITE = "#FFFFFF"
|
|
13
|
+
|
|
14
|
+
# Simple GolfMCP text logo
|
|
15
|
+
GOLF_LOGO = """
|
|
16
|
+
██████╗ ██████╗ ██╗ ███████╗███╗ ███╗ ██████╗██████╗
|
|
17
|
+
██╔════╝ ██╔═══██╗██║ ██╔════╝████╗ ████║██╔════╝██╔══██╗
|
|
18
|
+
██║ ███╗██║ ██║██║ █████╗ ██╔████╔██║██║ ██████╔╝
|
|
19
|
+
██║ ██║██║ ██║██║ ██╔══╝ ██║╚██╔╝██║██║ ██╔═══╝
|
|
20
|
+
╚██████╔╝╚██████╔╝███████╗██║ ██║ ╚═╝ ██║╚██████╗██║
|
|
21
|
+
╚═════╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
# Simplified version for smaller spaces
|
|
25
|
+
GOLF_LOGO_SMALL = "Golf"
|
|
26
|
+
|
|
27
|
+
# Status icons with consistent styling
|
|
28
|
+
STATUS_ICONS = {
|
|
29
|
+
"success": "✓",
|
|
30
|
+
"error": "✗",
|
|
31
|
+
"warning": "⚠",
|
|
32
|
+
"info": "ℹ",
|
|
33
|
+
"building": "🔨",
|
|
34
|
+
"generating": "⚙️",
|
|
35
|
+
"packaging": "📦",
|
|
36
|
+
"server": "🚀",
|
|
37
|
+
"loading": "⭕",
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def create_welcome_banner(version: str, console: Console) -> None:
|
|
42
|
+
"""Create the main Golf welcome banner."""
|
|
43
|
+
# Create the logo with Golf in blue and MCP in orange
|
|
44
|
+
logo_lines = GOLF_LOGO.strip().split("\n")
|
|
45
|
+
logo_content = Text()
|
|
46
|
+
|
|
47
|
+
for line in logo_lines:
|
|
48
|
+
if line.strip(): # Only process non-empty lines
|
|
49
|
+
# Find where "MCP" starts (roughly at position 32 in the ASCII art)
|
|
50
|
+
golf_part = line[:32] # First part is "Golf"
|
|
51
|
+
mcp_part = line[32:] # Last part is "MCP"
|
|
52
|
+
|
|
53
|
+
logo_content.append(golf_part, style=f"bold {GOLF_BLUE}")
|
|
54
|
+
logo_content.append(mcp_part, style=f"bold {GOLF_ORANGE}")
|
|
55
|
+
logo_content.append("\n")
|
|
56
|
+
|
|
57
|
+
# Create version line
|
|
58
|
+
version_text = Text()
|
|
59
|
+
version_text.append("🚀 ", style=f"bold {GOLF_ORANGE}")
|
|
60
|
+
version_text.append(f"Golf v{version}", style=f"bold {GOLF_BLUE}")
|
|
61
|
+
version_text.append(" 🚀", style=f"bold {GOLF_ORANGE}")
|
|
62
|
+
|
|
63
|
+
# Create tagline
|
|
64
|
+
tagline_text = Text("✨ Easiest way to build production-ready MCP servers ✨", style="bold white")
|
|
65
|
+
|
|
66
|
+
# Create the full content using a renderable group approach
|
|
67
|
+
from rich.console import Group
|
|
68
|
+
|
|
69
|
+
content_group = Group(
|
|
70
|
+
Align.center(logo_content),
|
|
71
|
+
"", # Empty line for spacing
|
|
72
|
+
Align.center(version_text),
|
|
73
|
+
Align.center(tagline_text),
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
panel = Panel(
|
|
77
|
+
content_group,
|
|
78
|
+
border_style=GOLF_BLUE,
|
|
79
|
+
padding=(1, 2),
|
|
80
|
+
title="[bold]🏌️ Welcome to Golf 🏌️[/bold]",
|
|
81
|
+
title_align="center",
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
console.print(panel)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def create_command_header(title: str, subtitle: str = "", console: Console | None = None) -> None:
|
|
88
|
+
"""Create a styled command header."""
|
|
89
|
+
if console is None:
|
|
90
|
+
console = Console()
|
|
91
|
+
|
|
92
|
+
header = Text()
|
|
93
|
+
header.append("🏌️ ", style=f"bold {GOLF_ORANGE}")
|
|
94
|
+
header.append(title, style=f"bold {GOLF_BLUE}")
|
|
95
|
+
|
|
96
|
+
if subtitle:
|
|
97
|
+
header.append(f" → {subtitle}", style=f"bold {GOLF_ORANGE}")
|
|
98
|
+
|
|
99
|
+
# Create a stylish panel for the header
|
|
100
|
+
panel = Panel(
|
|
101
|
+
Align.center(header),
|
|
102
|
+
border_style=GOLF_BLUE,
|
|
103
|
+
padding=(0, 2),
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
console.print(panel)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def create_success_message(message: str, console: Console | None = None) -> None:
|
|
110
|
+
"""Create a styled success message."""
|
|
111
|
+
if console is None:
|
|
112
|
+
console = Console()
|
|
113
|
+
|
|
114
|
+
success_content = Text()
|
|
115
|
+
success_content.append("🎉 ", style=f"bold {GOLF_ORANGE}")
|
|
116
|
+
success_content.append(f"{STATUS_ICONS['success']} {message}", style=f"bold {GOLF_GREEN}")
|
|
117
|
+
success_content.append(" 🎉", style=f"bold {GOLF_ORANGE}")
|
|
118
|
+
|
|
119
|
+
success_panel = Panel(
|
|
120
|
+
Align.center(success_content),
|
|
121
|
+
border_style=GOLF_GREEN,
|
|
122
|
+
padding=(0, 2),
|
|
123
|
+
title="[bold green]SUCCESS[/bold green]",
|
|
124
|
+
title_align="center",
|
|
125
|
+
)
|
|
126
|
+
console.print(success_panel)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def create_info_panel(title: str, content: str, console: Console | None = None) -> None:
|
|
130
|
+
"""Create a styled info panel."""
|
|
131
|
+
if console is None:
|
|
132
|
+
console = Console()
|
|
133
|
+
|
|
134
|
+
# Add some visual flair to the content
|
|
135
|
+
styled_content = Text()
|
|
136
|
+
for line in content.split("\n"):
|
|
137
|
+
if line.strip():
|
|
138
|
+
styled_content.append("▶ ", style=f"bold {GOLF_ORANGE}")
|
|
139
|
+
styled_content.append(line, style="bold white")
|
|
140
|
+
styled_content.append("\n")
|
|
141
|
+
|
|
142
|
+
panel = Panel(
|
|
143
|
+
styled_content,
|
|
144
|
+
title=f"[bold {GOLF_BLUE}]🔧 {title} 🔧[/bold {GOLF_BLUE}]",
|
|
145
|
+
border_style=GOLF_BLUE,
|
|
146
|
+
padding=(1, 2),
|
|
147
|
+
)
|
|
148
|
+
console.print(panel)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def get_status_text(status: str, message: str, style: str = "") -> Text:
|
|
152
|
+
"""Get formatted status text with icon."""
|
|
153
|
+
icon = STATUS_ICONS.get(status, "•")
|
|
154
|
+
text = Text()
|
|
155
|
+
|
|
156
|
+
if status == "success":
|
|
157
|
+
text.append("🎉 ", style=f"bold {GOLF_ORANGE}")
|
|
158
|
+
text.append(f"{icon} {message}", style=f"bold {GOLF_GREEN}")
|
|
159
|
+
elif status == "error":
|
|
160
|
+
text.append("💥 ", style=f"bold {GOLF_ORANGE}")
|
|
161
|
+
text.append(f"{icon} {message}", style="bold red")
|
|
162
|
+
elif status == "warning":
|
|
163
|
+
text.append("⚡ ", style=f"bold {GOLF_ORANGE}")
|
|
164
|
+
text.append(f"{icon} {message}", style=f"bold {GOLF_ORANGE}")
|
|
165
|
+
elif status in ["building", "generating", "packaging"]:
|
|
166
|
+
text.append("🔥 ", style=f"bold {GOLF_ORANGE}")
|
|
167
|
+
text.append(f"{icon} {message}", style=f"bold {GOLF_BLUE}")
|
|
168
|
+
else:
|
|
169
|
+
text.append("💡 ", style=f"bold {GOLF_ORANGE}")
|
|
170
|
+
text.append(f"{icon} {message}", style=f"bold {GOLF_BLUE}")
|
|
171
|
+
|
|
172
|
+
return text
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def create_build_header(project_name: str, environment: str, console: Console) -> None:
|
|
176
|
+
"""Create a styled build process header."""
|
|
177
|
+
title = Text()
|
|
178
|
+
title.append("🔨 Building ", style=f"bold {GOLF_ORANGE}")
|
|
179
|
+
title.append(project_name, style=f"bold {GOLF_BLUE}")
|
|
180
|
+
title.append(f" ({environment} environment)", style=f"bold {GOLF_GREEN}")
|
|
181
|
+
|
|
182
|
+
# Create a flashy build panel
|
|
183
|
+
panel = Panel(
|
|
184
|
+
Align.center(title),
|
|
185
|
+
border_style=GOLF_ORANGE,
|
|
186
|
+
padding=(0, 2),
|
|
187
|
+
title="[bold]🚧 BUILD IN PROGRESS 🚧[/bold]",
|
|
188
|
+
title_align="center",
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
console.print(panel)
|