golf-mcp 0.1.14__tar.gz → 0.1.17__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.
Potentially problematic release.
This version of golf-mcp might be problematic. Click here for more details.
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/.docs/docs.md +2 -2
- {golf_mcp-0.1.14/src/golf_mcp.egg-info → golf_mcp-0.1.17}/PKG-INFO +43 -3
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/README.md +41 -1
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/pyproject.toml +4 -4
- golf_mcp-0.1.17/src/golf/__init__.py +1 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/cli/main.py +21 -12
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/commands/init.py +63 -1
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/commands/run.py +12 -4
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/core/builder.py +181 -61
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/core/config.py +13 -0
- golf_mcp-0.1.17/src/golf/core/parser.py +1059 -0
- golf_mcp-0.1.17/src/golf/core/platform.py +180 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/core/telemetry.py +48 -17
- golf_mcp-0.1.17/src/golf/examples/api_key/.env.example +1 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/examples/api_key/README.md +10 -10
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/examples/api_key/golf.json +1 -2
- golf_mcp-0.1.17/src/golf/examples/basic/.env.example +4 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/examples/basic/golf.json +1 -2
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/telemetry/instrumentation.py +26 -48
- {golf_mcp-0.1.14 → golf_mcp-0.1.17/src/golf_mcp.egg-info}/PKG-INFO +43 -3
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf_mcp.egg-info/SOURCES.txt +1 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf_mcp.egg-info/requires.txt +1 -1
- golf_mcp-0.1.14/src/golf/__init__.py +0 -1
- golf_mcp-0.1.14/src/golf/core/parser.py +0 -506
- golf_mcp-0.1.14/src/golf/examples/api_key/.env.example +0 -5
- golf_mcp-0.1.14/src/golf/examples/basic/.env.example +0 -5
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/.docs/fast-mcp.md +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/.docs/fastmcp-example-1.py +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/.docs/fastmcp-example-2.py +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/.docs/mcp.md +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/.docs/oauth-implementation.md +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/.docs/oauth.md +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/LICENSE +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/MANIFEST.in +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/setup.cfg +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/auth/__init__.py +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/auth/api_key.py +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/auth/helpers.py +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/auth/oauth.py +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/auth/provider.py +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/cli/__init__.py +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/commands/__init__.py +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/commands/build.py +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/core/__init__.py +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/core/builder_auth.py +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/core/builder_telemetry.py +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/core/transformer.py +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/examples/__init__.py +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/examples/api_key/.env +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/examples/api_key/pre_build.py +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/examples/api_key/tools/issues/create.py +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/examples/api_key/tools/issues/list.py +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/examples/api_key/tools/repos/list.py +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/examples/api_key/tools/search/code.py +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/examples/api_key/tools/users/get.py +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/examples/basic/.env +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/examples/basic/README.md +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/examples/basic/pre_build.py +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/examples/basic/prompts/welcome.py +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/examples/basic/resources/current_time.py +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/examples/basic/resources/info.py +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/examples/basic/resources/weather/common.py +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/examples/basic/resources/weather/current.py +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/examples/basic/resources/weather/forecast.py +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/examples/basic/tools/github_user.py +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/examples/basic/tools/hello.py +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/examples/basic/tools/payments/charge.py +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/examples/basic/tools/payments/common.py +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/examples/basic/tools/payments/refund.py +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf/telemetry/__init__.py +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf_mcp.egg-info/dependency_links.txt +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf_mcp.egg-info/entry_points.txt +0 -0
- {golf_mcp-0.1.14 → golf_mcp-0.1.17}/src/golf_mcp.egg-info/top_level.txt +0 -0
|
@@ -49,7 +49,7 @@ Given a component file `C` with absolute path
|
|
|
49
49
|
let `PathRev = [pₙ, …, p₁]` (reverse order of parent dirs under the category). The **ID** is
|
|
50
50
|
|
|
51
51
|
```
|
|
52
|
-
<filename> + ("
|
|
52
|
+
<filename> + ("_" + "_".join(PathRev) if PathRev else "")
|
|
53
53
|
```
|
|
54
54
|
|
|
55
55
|
### Formal Definition (BNF)
|
|
@@ -190,7 +190,7 @@ def run(charge_id: str, amount: int) -> dict:
|
|
|
190
190
|
from tools.payments.refund import submit as _submit
|
|
191
191
|
|
|
192
192
|
mcp.tool(
|
|
193
|
-
name="
|
|
193
|
+
name="submit_refund_payments",
|
|
194
194
|
description="Submit a refund request to Stripe."
|
|
195
195
|
)(_submit.run)
|
|
196
196
|
```
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: golf-mcp
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.17
|
|
4
4
|
Summary: Framework for building MCP servers
|
|
5
5
|
Author-email: Antoni Gmitruk <antoni@golf.dev>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -21,7 +21,7 @@ Description-Content-Type: text/markdown
|
|
|
21
21
|
License-File: LICENSE
|
|
22
22
|
Requires-Dist: typer>=0.15.4
|
|
23
23
|
Requires-Dist: rich>=14.0.0
|
|
24
|
-
Requires-Dist: fastmcp
|
|
24
|
+
Requires-Dist: fastmcp<2.6.0,>=2.0.0
|
|
25
25
|
Requires-Dist: pydantic>=2.11.0
|
|
26
26
|
Requires-Dist: python-dotenv>=1.1.0
|
|
27
27
|
Requires-Dist: black>=24.10.0
|
|
@@ -128,7 +128,7 @@ A Golf project initialized with `golf init` will have a structure similar to thi
|
|
|
128
128
|
|
|
129
129
|
- **`golf.json`**: Configures server name, port, transport, telemetry, and other build settings.
|
|
130
130
|
- **`tools/`**, **`resources/`**, **`prompts/`**: Contain your Python files, each defining a single component. These directories can also contain nested subdirectories to further organize your components (e.g., `tools/payments/charge.py`). The module docstring of each file serves as the component's description.
|
|
131
|
-
- Component IDs are automatically derived from their file path. For example, `tools/hello.py` becomes `hello`, and a nested file like `tools/payments/submit.py` would become `
|
|
131
|
+
- Component IDs are automatically derived from their file path. For example, `tools/hello.py` becomes `hello`, and a nested file like `tools/payments/submit.py` would become `submit_payments` (filename, followed by reversed parent directories under the main category, joined by underscores).
|
|
132
132
|
- **`common.py`** (not shown, but can be placed in subdirectories like `tools/payments/common.py`): Used to share code (clients, models, etc.) among components in the same subdirectory.
|
|
133
133
|
|
|
134
134
|
## Example: Defining a Tool
|
|
@@ -178,6 +178,15 @@ The `golf.json` file is the heart of your Golf project configuration. Here's wha
|
|
|
178
178
|
// - "streamable-http": HTTP with streaming support
|
|
179
179
|
// - "stdio": Standard I/O (for CLI integration)
|
|
180
180
|
|
|
181
|
+
// HTTP Transport Configuration (optional)
|
|
182
|
+
"stateless_http": false, // Make streamable-http transport stateless (new session per request)
|
|
183
|
+
// When true, server restarts won't break existing client connections
|
|
184
|
+
|
|
185
|
+
// Health Check Configuration (optional)
|
|
186
|
+
"health_check_enabled": false, // Enable health check endpoint for Kubernetes/load balancers
|
|
187
|
+
"health_check_path": "/health", // HTTP path for health check endpoint
|
|
188
|
+
"health_check_response": "OK", // Response text returned by health check
|
|
189
|
+
|
|
181
190
|
// OpenTelemetry Configuration (optional)
|
|
182
191
|
"opentelemetry_enabled": false, // Enable distributed tracing
|
|
183
192
|
"opentelemetry_default_exporter": "console" // Default exporter if OTEL_TRACES_EXPORTER not set
|
|
@@ -193,11 +202,42 @@ The `golf.json` file is the heart of your Golf project configuration. Here's wha
|
|
|
193
202
|
- `"streamable-http"` provides HTTP streaming for traditional API clients
|
|
194
203
|
- `"stdio"` enables integration with command-line tools and scripts
|
|
195
204
|
- **`host` & `port`**: Control where your server listens. Use `"127.0.0.1"` for local development or `"0.0.0.0"` to accept external connections.
|
|
205
|
+
- **`stateless_http`**: When true, makes the streamable-http transport stateless by creating a new session for each request. This ensures that server restarts don't break existing client connections, making the server truly stateless.
|
|
206
|
+
- **`health_check_enabled`**: When true, enables a health check endpoint for Kubernetes readiness/liveness probes and load balancers
|
|
207
|
+
- **`health_check_path`**: Customizable path for the health check endpoint (defaults to "/health")
|
|
208
|
+
- **`health_check_response`**: Customizable response text for successful health checks (defaults to "OK")
|
|
196
209
|
- **`opentelemetry_enabled`**: When true, enables distributed tracing for debugging and monitoring your MCP server
|
|
197
210
|
- **`opentelemetry_default_exporter`**: Sets the default trace exporter. Can be overridden by the `OTEL_TRACES_EXPORTER` environment variable
|
|
198
211
|
|
|
199
212
|
## Features
|
|
200
213
|
|
|
214
|
+
### 🏥 Health Check Support
|
|
215
|
+
|
|
216
|
+
Golf includes built-in health check endpoint support for production deployments. When enabled, it automatically adds a custom HTTP route that can be used by:
|
|
217
|
+
- Kubernetes readiness and liveness probes
|
|
218
|
+
- Load balancers and reverse proxies
|
|
219
|
+
- Monitoring systems
|
|
220
|
+
- Container orchestration platforms
|
|
221
|
+
|
|
222
|
+
#### Configuration
|
|
223
|
+
|
|
224
|
+
Enable health checks in your `golf.json`:
|
|
225
|
+
```json
|
|
226
|
+
{
|
|
227
|
+
"health_check_enabled": true,
|
|
228
|
+
"health_check_path": "/health",
|
|
229
|
+
"health_check_response": "Service is healthy"
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
The generated server will include a route like:
|
|
234
|
+
```python
|
|
235
|
+
@mcp.custom_route('/health', methods=["GET"])
|
|
236
|
+
async def health_check(request: Request) -> PlainTextResponse:
|
|
237
|
+
"""Health check endpoint for Kubernetes and load balancers."""
|
|
238
|
+
return PlainTextResponse("Service is healthy")
|
|
239
|
+
```
|
|
240
|
+
|
|
201
241
|
### 🔍 OpenTelemetry Support
|
|
202
242
|
|
|
203
243
|
Golf includes built-in OpenTelemetry instrumentation for distributed tracing. When enabled, it automatically traces:
|
|
@@ -90,7 +90,7 @@ A Golf project initialized with `golf init` will have a structure similar to thi
|
|
|
90
90
|
|
|
91
91
|
- **`golf.json`**: Configures server name, port, transport, telemetry, and other build settings.
|
|
92
92
|
- **`tools/`**, **`resources/`**, **`prompts/`**: Contain your Python files, each defining a single component. These directories can also contain nested subdirectories to further organize your components (e.g., `tools/payments/charge.py`). The module docstring of each file serves as the component's description.
|
|
93
|
-
- Component IDs are automatically derived from their file path. For example, `tools/hello.py` becomes `hello`, and a nested file like `tools/payments/submit.py` would become `
|
|
93
|
+
- Component IDs are automatically derived from their file path. For example, `tools/hello.py` becomes `hello`, and a nested file like `tools/payments/submit.py` would become `submit_payments` (filename, followed by reversed parent directories under the main category, joined by underscores).
|
|
94
94
|
- **`common.py`** (not shown, but can be placed in subdirectories like `tools/payments/common.py`): Used to share code (clients, models, etc.) among components in the same subdirectory.
|
|
95
95
|
|
|
96
96
|
## Example: Defining a Tool
|
|
@@ -140,6 +140,15 @@ The `golf.json` file is the heart of your Golf project configuration. Here's wha
|
|
|
140
140
|
// - "streamable-http": HTTP with streaming support
|
|
141
141
|
// - "stdio": Standard I/O (for CLI integration)
|
|
142
142
|
|
|
143
|
+
// HTTP Transport Configuration (optional)
|
|
144
|
+
"stateless_http": false, // Make streamable-http transport stateless (new session per request)
|
|
145
|
+
// When true, server restarts won't break existing client connections
|
|
146
|
+
|
|
147
|
+
// Health Check Configuration (optional)
|
|
148
|
+
"health_check_enabled": false, // Enable health check endpoint for Kubernetes/load balancers
|
|
149
|
+
"health_check_path": "/health", // HTTP path for health check endpoint
|
|
150
|
+
"health_check_response": "OK", // Response text returned by health check
|
|
151
|
+
|
|
143
152
|
// OpenTelemetry Configuration (optional)
|
|
144
153
|
"opentelemetry_enabled": false, // Enable distributed tracing
|
|
145
154
|
"opentelemetry_default_exporter": "console" // Default exporter if OTEL_TRACES_EXPORTER not set
|
|
@@ -155,11 +164,42 @@ The `golf.json` file is the heart of your Golf project configuration. Here's wha
|
|
|
155
164
|
- `"streamable-http"` provides HTTP streaming for traditional API clients
|
|
156
165
|
- `"stdio"` enables integration with command-line tools and scripts
|
|
157
166
|
- **`host` & `port`**: Control where your server listens. Use `"127.0.0.1"` for local development or `"0.0.0.0"` to accept external connections.
|
|
167
|
+
- **`stateless_http`**: When true, makes the streamable-http transport stateless by creating a new session for each request. This ensures that server restarts don't break existing client connections, making the server truly stateless.
|
|
168
|
+
- **`health_check_enabled`**: When true, enables a health check endpoint for Kubernetes readiness/liveness probes and load balancers
|
|
169
|
+
- **`health_check_path`**: Customizable path for the health check endpoint (defaults to "/health")
|
|
170
|
+
- **`health_check_response`**: Customizable response text for successful health checks (defaults to "OK")
|
|
158
171
|
- **`opentelemetry_enabled`**: When true, enables distributed tracing for debugging and monitoring your MCP server
|
|
159
172
|
- **`opentelemetry_default_exporter`**: Sets the default trace exporter. Can be overridden by the `OTEL_TRACES_EXPORTER` environment variable
|
|
160
173
|
|
|
161
174
|
## Features
|
|
162
175
|
|
|
176
|
+
### 🏥 Health Check Support
|
|
177
|
+
|
|
178
|
+
Golf includes built-in health check endpoint support for production deployments. When enabled, it automatically adds a custom HTTP route that can be used by:
|
|
179
|
+
- Kubernetes readiness and liveness probes
|
|
180
|
+
- Load balancers and reverse proxies
|
|
181
|
+
- Monitoring systems
|
|
182
|
+
- Container orchestration platforms
|
|
183
|
+
|
|
184
|
+
#### Configuration
|
|
185
|
+
|
|
186
|
+
Enable health checks in your `golf.json`:
|
|
187
|
+
```json
|
|
188
|
+
{
|
|
189
|
+
"health_check_enabled": true,
|
|
190
|
+
"health_check_path": "/health",
|
|
191
|
+
"health_check_response": "Service is healthy"
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
The generated server will include a route like:
|
|
196
|
+
```python
|
|
197
|
+
@mcp.custom_route('/health', methods=["GET"])
|
|
198
|
+
async def health_check(request: Request) -> PlainTextResponse:
|
|
199
|
+
"""Health check endpoint for Kubernetes and load balancers."""
|
|
200
|
+
return PlainTextResponse("Service is healthy")
|
|
201
|
+
```
|
|
202
|
+
|
|
163
203
|
### 🔍 OpenTelemetry Support
|
|
164
204
|
|
|
165
205
|
Golf includes built-in OpenTelemetry instrumentation for distributed tracing. When enabled, it automatically traces:
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "golf-mcp"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.17"
|
|
8
8
|
description = "Framework for building MCP servers"
|
|
9
9
|
authors = [
|
|
10
10
|
{name = "Antoni Gmitruk", email = "antoni@golf.dev"}
|
|
@@ -28,7 +28,7 @@ classifiers = [
|
|
|
28
28
|
dependencies = [
|
|
29
29
|
"typer>=0.15.4",
|
|
30
30
|
"rich>=14.0.0",
|
|
31
|
-
"fastmcp>=2.0.0",
|
|
31
|
+
"fastmcp>=2.0.0,<2.6.0",
|
|
32
32
|
"pydantic>=2.11.0",
|
|
33
33
|
"python-dotenv>=1.1.0",
|
|
34
34
|
"black>=24.10.0",
|
|
@@ -64,7 +64,7 @@ golf = ["examples/**/*"]
|
|
|
64
64
|
|
|
65
65
|
[tool.poetry]
|
|
66
66
|
name = "golf-mcp"
|
|
67
|
-
version = "0.1.
|
|
67
|
+
version = "0.1.17"
|
|
68
68
|
description = "Framework for building MCP servers with zero boilerplate"
|
|
69
69
|
authors = ["Antoni Gmitruk <antoni@golf.dev>"]
|
|
70
70
|
license = "Apache-2.0"
|
|
@@ -86,7 +86,7 @@ classifiers = [
|
|
|
86
86
|
|
|
87
87
|
[tool.poetry.dependencies]
|
|
88
88
|
python = ">=3.8" # Match requires-python
|
|
89
|
-
fastmcp = ">=2.0.0"
|
|
89
|
+
fastmcp = ">=2.0.0,<2.6.0"
|
|
90
90
|
typer = {extras = ["all"], version = ">=0.15.4"}
|
|
91
91
|
pydantic = ">=2.11.0"
|
|
92
92
|
rich = ">=14.0.0"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.17"
|
|
@@ -121,7 +121,11 @@ def build_dev(
|
|
|
121
121
|
None, "--output-dir", "-o", help="Directory to output the built project"
|
|
122
122
|
),
|
|
123
123
|
) -> None:
|
|
124
|
-
"""Build a development version with environment variables copied.
|
|
124
|
+
"""Build a development version with app environment variables copied.
|
|
125
|
+
|
|
126
|
+
Golf credentials (GOLF_*) are always loaded from .env for build operations.
|
|
127
|
+
All environment variables are copied to the built project for development.
|
|
128
|
+
"""
|
|
125
129
|
# Find project root directory
|
|
126
130
|
project_root, config_path = find_project_root()
|
|
127
131
|
|
|
@@ -162,7 +166,7 @@ def build_dev(
|
|
|
162
166
|
e,
|
|
163
167
|
context="Development build with environment variables",
|
|
164
168
|
operation="build_dev",
|
|
165
|
-
additional_props={"environment": "dev", "copy_env": True}
|
|
169
|
+
additional_props={"environment": "dev", "copy_env": True},
|
|
166
170
|
)
|
|
167
171
|
raise
|
|
168
172
|
|
|
@@ -173,7 +177,14 @@ def build_prod(
|
|
|
173
177
|
None, "--output-dir", "-o", help="Directory to output the built project"
|
|
174
178
|
),
|
|
175
179
|
) -> None:
|
|
176
|
-
"""Build a production version
|
|
180
|
+
"""Build a production version for deployment.
|
|
181
|
+
|
|
182
|
+
Golf credentials (GOLF_*) are always loaded from .env for build operations
|
|
183
|
+
(platform registration, resource updates). App environment variables are
|
|
184
|
+
NOT copied for security - provide them in your deployment environment.
|
|
185
|
+
|
|
186
|
+
Your production deployment must include GOLF_* vars for runtime telemetry.
|
|
187
|
+
"""
|
|
177
188
|
# Find project root directory
|
|
178
189
|
project_root, config_path = find_project_root()
|
|
179
190
|
|
|
@@ -214,7 +225,7 @@ def build_prod(
|
|
|
214
225
|
e,
|
|
215
226
|
context="Production build without environment variables",
|
|
216
227
|
operation="build_prod",
|
|
217
|
-
additional_props={"environment": "prod", "copy_env": False}
|
|
228
|
+
additional_props={"environment": "prod", "copy_env": False},
|
|
218
229
|
)
|
|
219
230
|
raise
|
|
220
231
|
|
|
@@ -275,15 +286,13 @@ def run(
|
|
|
275
286
|
|
|
276
287
|
build_project(project_root, settings, dist_dir)
|
|
277
288
|
except Exception as e:
|
|
278
|
-
console.print(
|
|
279
|
-
f"[bold red]Error building project:[/bold red] {str(e)}"
|
|
280
|
-
)
|
|
289
|
+
console.print(f"[bold red]Error building project:[/bold red] {str(e)}")
|
|
281
290
|
track_detailed_error(
|
|
282
291
|
"cli_run_failed",
|
|
283
292
|
e,
|
|
284
293
|
context="Auto-build before running server",
|
|
285
294
|
operation="auto_build_before_run",
|
|
286
|
-
additional_props={"auto_build": True}
|
|
295
|
+
additional_props={"auto_build": True},
|
|
287
296
|
)
|
|
288
297
|
raise
|
|
289
298
|
else:
|
|
@@ -326,11 +335,11 @@ def run(
|
|
|
326
335
|
# 2: General interrupt/graceful shutdown
|
|
327
336
|
shutdown_type = {
|
|
328
337
|
130: "UserInterrupt",
|
|
329
|
-
143: "GracefulShutdown",
|
|
338
|
+
143: "GracefulShutdown",
|
|
330
339
|
137: "ForcedShutdown",
|
|
331
|
-
2: "Interrupt"
|
|
340
|
+
2: "Interrupt",
|
|
332
341
|
}.get(return_code, "GracefulShutdown")
|
|
333
|
-
|
|
342
|
+
|
|
334
343
|
track_event(
|
|
335
344
|
"cli_run_shutdown",
|
|
336
345
|
{
|
|
@@ -362,7 +371,7 @@ def run(
|
|
|
362
371
|
e,
|
|
363
372
|
context="Server execution or startup failure",
|
|
364
373
|
operation="run_server_execution",
|
|
365
|
-
additional_props={"has_dist_dir": dist_dir.exists() if dist_dir else False}
|
|
374
|
+
additional_props={"has_dist_dir": dist_dir.exists() if dist_dir else False},
|
|
366
375
|
)
|
|
367
376
|
raise
|
|
368
377
|
|
|
@@ -7,7 +7,12 @@ from rich.console import Console
|
|
|
7
7
|
from rich.progress import Progress, SpinnerColumn, TextColumn
|
|
8
8
|
from rich.prompt import Confirm
|
|
9
9
|
|
|
10
|
-
from golf.core.telemetry import
|
|
10
|
+
from golf.core.telemetry import (
|
|
11
|
+
track_command,
|
|
12
|
+
track_event,
|
|
13
|
+
set_telemetry_enabled,
|
|
14
|
+
load_telemetry_preference,
|
|
15
|
+
)
|
|
11
16
|
|
|
12
17
|
console = Console()
|
|
13
18
|
|
|
@@ -95,6 +100,9 @@ def initialize_project(
|
|
|
95
100
|
# Copy directory structure
|
|
96
101
|
_copy_template(template_dir, output_dir, project_name)
|
|
97
102
|
|
|
103
|
+
# Ask for telemetry consent
|
|
104
|
+
_prompt_for_telemetry_consent()
|
|
105
|
+
|
|
98
106
|
# Create virtual environment
|
|
99
107
|
console.print("[bold green]Project initialized successfully![/bold green]")
|
|
100
108
|
console.print("\nTo get started, run:")
|
|
@@ -207,6 +215,60 @@ def _copy_template(source_dir: Path, target_dir: Path, project_name: str) -> Non
|
|
|
207
215
|
f.write("dist/\n")
|
|
208
216
|
|
|
209
217
|
|
|
218
|
+
def _prompt_for_telemetry_consent() -> None:
|
|
219
|
+
"""Prompt user for telemetry consent and save their preference."""
|
|
220
|
+
import os
|
|
221
|
+
|
|
222
|
+
# Skip prompt in test mode, when telemetry is explicitly disabled, or if preference already exists
|
|
223
|
+
if os.environ.get("GOLF_TEST_MODE", "").lower() in ("1", "true", "yes", "on"):
|
|
224
|
+
return
|
|
225
|
+
|
|
226
|
+
# Skip if telemetry is explicitly disabled in environment
|
|
227
|
+
if os.environ.get("GOLF_TELEMETRY", "").lower() in ("0", "false", "no", "off"):
|
|
228
|
+
return
|
|
229
|
+
|
|
230
|
+
# Check if user already has a saved preference
|
|
231
|
+
existing_preference = load_telemetry_preference()
|
|
232
|
+
if existing_preference is not None:
|
|
233
|
+
return # User already made a choice
|
|
234
|
+
|
|
235
|
+
console.print()
|
|
236
|
+
console.rule("[bold blue]Anonymous usage analytics[/bold blue]", style="blue")
|
|
237
|
+
console.print()
|
|
238
|
+
console.print(
|
|
239
|
+
"Golf can collect [bold]anonymous usage analytics[/bold] to help improve the tool."
|
|
240
|
+
)
|
|
241
|
+
console.print()
|
|
242
|
+
console.print("[dim]What we collect:[/dim]")
|
|
243
|
+
console.print(" • Command usage (init, build, run)")
|
|
244
|
+
console.print(" • Error types (to fix bugs)")
|
|
245
|
+
console.print(" • Golf version and Python version")
|
|
246
|
+
console.print(" • Operating system type")
|
|
247
|
+
console.print()
|
|
248
|
+
console.print("[dim]What we DON'T collect:[/dim]")
|
|
249
|
+
console.print(" • Your code or project content")
|
|
250
|
+
console.print(" • File paths or project names")
|
|
251
|
+
console.print(" • Personal information")
|
|
252
|
+
console.print(" • IP addresses")
|
|
253
|
+
console.print()
|
|
254
|
+
console.print(
|
|
255
|
+
"You can change this anytime by setting GOLF_TELEMETRY=0 in your environment."
|
|
256
|
+
)
|
|
257
|
+
console.print()
|
|
258
|
+
|
|
259
|
+
enable_telemetry = Confirm.ask(
|
|
260
|
+
"[bold]Enable anonymous usage analytics?[/bold]", default=False
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
set_telemetry_enabled(enable_telemetry, persist=True)
|
|
264
|
+
|
|
265
|
+
if enable_telemetry:
|
|
266
|
+
console.print("[green]✓[/green] Anonymous analytics enabled")
|
|
267
|
+
else:
|
|
268
|
+
console.print("[yellow]○[/yellow] Anonymous analytics disabled")
|
|
269
|
+
console.print()
|
|
270
|
+
|
|
271
|
+
|
|
210
272
|
def _is_text_file(path: Path) -> bool:
|
|
211
273
|
"""Check if a file is a text file that needs variable substitution.
|
|
212
274
|
|
|
@@ -70,13 +70,21 @@ def run_server(
|
|
|
70
70
|
elif process.returncode == 130:
|
|
71
71
|
console.print("[yellow]Server stopped by user interrupt (Ctrl+C)[/yellow]")
|
|
72
72
|
elif process.returncode == 143:
|
|
73
|
-
console.print(
|
|
73
|
+
console.print(
|
|
74
|
+
"[yellow]Server stopped by SIGTERM (graceful shutdown)[/yellow]"
|
|
75
|
+
)
|
|
74
76
|
elif process.returncode == 137:
|
|
75
|
-
console.print(
|
|
77
|
+
console.print(
|
|
78
|
+
"[yellow]Server stopped by SIGKILL (forced shutdown)[/yellow]"
|
|
79
|
+
)
|
|
76
80
|
elif process.returncode in [1, 2]:
|
|
77
|
-
console.print(
|
|
81
|
+
console.print(
|
|
82
|
+
f"[red]Server exited with error code {process.returncode}[/red]"
|
|
83
|
+
)
|
|
78
84
|
else:
|
|
79
|
-
console.print(
|
|
85
|
+
console.print(
|
|
86
|
+
f"[orange]Server exited with code {process.returncode}[/orange]"
|
|
87
|
+
)
|
|
80
88
|
|
|
81
89
|
return process.returncode
|
|
82
90
|
except KeyboardInterrupt:
|