lkr-dev-cli 0.0.31__tar.gz → 0.0.32__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 (39) hide show
  1. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/PKG-INFO +28 -32
  2. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/README.md +9 -20
  3. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/lkr/auth/main.py +17 -5
  4. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/lkr/auth_service.py +4 -3
  5. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/lkr/logger.py +33 -22
  6. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/lkr/main.py +25 -6
  7. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/lkr/observability/main.py +2 -0
  8. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/lkr/tools/main.py +3 -0
  9. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/pyproject.toml +20 -11
  10. lkr_dev_cli-0.0.32/test.py +16 -0
  11. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/tests/test_dependency_resolution.py +59 -1
  12. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/uv.lock +327 -21
  13. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/.github/workflows/release.yml +0 -0
  14. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/.github/workflows/test-dependencies.yml +0 -0
  15. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/.gitignore +0 -0
  16. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/.python-version +0 -0
  17. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/.vscode/launch.json +0 -0
  18. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/.vscode/settings.json +0 -0
  19. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/Dockerfile +0 -0
  20. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/LICENSE +0 -0
  21. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/Makefile +0 -0
  22. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/cloudbuild.yaml +0 -0
  23. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/lkr/__init__.py +0 -0
  24. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/lkr/auth/__init__.py +0 -0
  25. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/lkr/auth/oauth.py +0 -0
  26. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/lkr/classes.py +0 -0
  27. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/lkr/constants.py +0 -0
  28. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/lkr/custom_types.py +0 -0
  29. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/lkr/exceptions.py +0 -0
  30. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/lkr/mcp/classes.py +0 -0
  31. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/lkr/mcp/main.py +0 -0
  32. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/lkr/mcp/utils.py +0 -0
  33. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/lkr/observability/classes.py +0 -0
  34. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/lkr/observability/embed_container.html +0 -0
  35. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/lkr/observability/utils.py +0 -0
  36. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/lkr/tools/classes.py +0 -0
  37. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/lkr.md +0 -0
  38. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/tests/TESTING.md +0 -0
  39. {lkr_dev_cli-0.0.31 → lkr_dev_cli-0.0.32}/tests/test_deps.sh +0 -0
@@ -1,32 +1,39 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lkr-dev-cli
3
- Version: 0.0.31
3
+ Version: 0.0.32
4
4
  Summary: lkr: a command line interface for looker
5
5
  Author: bwebs
6
6
  License-Expression: MIT
7
7
  License-File: LICENSE
8
8
  Requires-Python: >=3.12
9
- Requires-Dist: cryptography>=42.0.0
10
9
  Requires-Dist: looker-sdk>=25.4.0
11
10
  Requires-Dist: pydantic>=2.11.4
12
11
  Requires-Dist: pydash>=8.0.5
13
- Requires-Dist: questionary>=2.1.0
14
- Requires-Dist: requests>=2.31.0
15
- Requires-Dist: structlog>=25.3.0
16
- Requires-Dist: typer>=0.15.2
17
12
  Provides-Extra: all
13
+ Requires-Dist: cryptography>=42.0.0; extra == 'all'
18
14
  Requires-Dist: duckdb>=1.2.2; extra == 'all'
19
- Requires-Dist: fastapi>=0.115.12; extra == 'all'
15
+ Requires-Dist: fastapi[standard]>=0.115.12; extra == 'all'
20
16
  Requires-Dist: mcp[cli]>=1.9.2; extra == 'all'
17
+ Requires-Dist: questionary>=2.1.0; extra == 'all'
18
+ Requires-Dist: requests>=2.31.0; extra == 'all'
21
19
  Requires-Dist: selenium>=4.32.0; extra == 'all'
22
- Provides-Extra: embed-observability
23
- Requires-Dist: fastapi>=0.115.12; extra == 'embed-observability'
24
- Requires-Dist: selenium>=4.32.0; extra == 'embed-observability'
20
+ Requires-Dist: structlog>=25.3.0; extra == 'all'
21
+ Requires-Dist: typer>=0.15.2; extra == 'all'
22
+ Provides-Extra: cli
23
+ Requires-Dist: cryptography>=42.0.0; extra == 'cli'
24
+ Requires-Dist: questionary>=2.1.0; extra == 'cli'
25
+ Requires-Dist: requests>=2.31.0; extra == 'cli'
26
+ Requires-Dist: structlog>=25.3.0; extra == 'cli'
27
+ Requires-Dist: typer>=0.15.2; extra == 'cli'
25
28
  Provides-Extra: mcp
26
29
  Requires-Dist: duckdb>=1.2.2; extra == 'mcp'
30
+ Requires-Dist: fastapi[standard]>=0.115.12; extra == 'mcp'
27
31
  Requires-Dist: mcp[cli]>=1.9.2; extra == 'mcp'
28
- Provides-Extra: user-attribute-updater
29
- Requires-Dist: fastapi>=0.115.12; extra == 'user-attribute-updater'
32
+ Provides-Extra: observability
33
+ Requires-Dist: fastapi[standard]>=0.115.12; extra == 'observability'
34
+ Requires-Dist: selenium>=4.32.0; extra == 'observability'
35
+ Provides-Extra: tools
36
+ Requires-Dist: fastapi[standard]>=0.115.12; extra == 'tools'
30
37
  Description-Content-Type: text/markdown
31
38
 
32
39
  # lkr cli
@@ -35,9 +42,9 @@ The `lkr` cli is a tool for interacting with Looker. It combines Looker's SDK an
35
42
 
36
43
  ## Usage
37
44
 
38
- `uv` makes everyone's life easier. Go [install it](https://docs.astral.sh/uv/getting-started/installation/). You can start using `lkr` by running `uv run --with lkr-dev-cli lkr --help`.
45
+ `uv` makes everyone's life easier. Go [install it](https://docs.astral.sh/uv/getting-started/installation/). You can start using `lkr` by running `uv run --with lkr-dev-cli[all] lkr --help`.
39
46
 
40
- Alternatively, you can install `lkr` with `pip install lkr-dev-cli` and use commands directly like `lkr <command>`.
47
+ Alternatively, you can install `lkr` with `pip install lkr-dev-cli[all]` and use commands directly like `lkr <command>`.
41
48
 
42
49
  We also have a public docker image that you can use to run `lkr` commands.
43
50
 
@@ -55,7 +62,7 @@ See the [prerequisites section](#oauth2-prerequisites)
55
62
  Login to `lkr`
56
63
 
57
64
  ```bash
58
- uv run --with lkr-dev-cli lkr auth login
65
+ uv run --with lkr-dev-cli[all] lkr auth login
59
66
  ```
60
67
 
61
68
  - Select a new instance
@@ -68,7 +75,7 @@ You will be redirected to the Looker OAuth authorization page, click Allow. If y
68
75
  If everything is successful, you will see `Successfully authenticated!`. Test it with
69
76
 
70
77
  ```bash
71
- uv run --with lkr-dev-cli lkr auth whoami
78
+ uv run --with lkr-dev-cli[all] lkr auth whoami
72
79
  ```
73
80
 
74
81
  ### Using API Key
@@ -76,7 +83,7 @@ uv run --with lkr-dev-cli lkr auth whoami
76
83
  If you provide environment variables for `LOOKERSDK_CLIENT_ID`, `LOOKERSDK_CLIENT_SECRET`, and `LOOKERSDK_BASE_URL`, `lkr` will use the API key to authenticate and the commands. We also support command line arguments to pass in the client id, client secret, and base url.
77
84
 
78
85
  ```bash
79
- uv run --with lkr-dev-cli lkr --client-id <your client id> --client-secret <your client secret> --base-url <your instance url> auth whoami
86
+ uv run --with lkr-dev-cli[all] lkr --client-id <your client id> --client-secret <your client secret> --base-url <your instance url> auth whoami
80
87
  ```
81
88
 
82
89
 
@@ -112,7 +119,7 @@ Built into the `lkr` is an MCP server. Right now its tools are based on helping
112
119
  "mcpServers": {
113
120
  "lkr-mcp": {
114
121
  "command": "uv",
115
- "args": ["run", "--with", "lkr-dev-cli", "lkr", "mcp", "run"]
122
+ "args": ["run", "--with", "lkr-dev-cli[all]", "lkr", "mcp", "run"]
116
123
  },
117
124
  "lkr-mcp-docker": {
118
125
  "command": "docker",
@@ -362,12 +369,9 @@ The `lkr` CLI supports optional dependencies that enable additional functionalit
362
369
 
363
370
  ### Available Extras
364
371
 
365
- - **`mcp`**: Enables the MCP (Model Context Protocol) server functionality
366
- - Includes: `mcp[cli]>=1.9.2`, `duckdb>=1.2.2`
367
- - **`embed-observability`**: Enables the observability embed monitoring features
368
- - Includes: `fastapi>=0.115.12`, `selenium>=4.32.0`
369
- - **`user-attribute-updater`**: Enables the user attribute updater functionality
370
- - Includes: `fastapi>=0.115.12`
372
+ - **`mcp`**: Enables the MCP (Model Context Protocol) server functionality and `lkr mcp` commands
373
+ - **`observability`**: Enables the observability embed monitoring features and `lkr observability` commands
374
+ - **`tools`**: Enables the user attribute updater functionality and `lkr tools` commands
371
375
 
372
376
  ### Installing Optional Dependencies
373
377
 
@@ -399,11 +403,3 @@ pip install lkr-dev-cli[all]
399
403
  # Install specific extras
400
404
  pip install lkr-dev-cli[mcp,embed-observability,user-attribute-updater]
401
405
  ```
402
-
403
- ### What Each Extra Enables
404
-
405
- - **`mcp`**: Use the MCP server with tools like Cursor for enhanced IDE integration
406
- - **`embed-observability`**: Run the observability embed server for monitoring Looker dashboard performance
407
- - **`user-attribute-updater`**: Deploy the user attribute updater service for OIDC token management
408
-
409
- All extras are designed to work together seamlessly, and installing `all` is equivalent to installing all individual extras.
@@ -4,9 +4,9 @@ The `lkr` cli is a tool for interacting with Looker. It combines Looker's SDK an
4
4
 
5
5
  ## Usage
6
6
 
7
- `uv` makes everyone's life easier. Go [install it](https://docs.astral.sh/uv/getting-started/installation/). You can start using `lkr` by running `uv run --with lkr-dev-cli lkr --help`.
7
+ `uv` makes everyone's life easier. Go [install it](https://docs.astral.sh/uv/getting-started/installation/). You can start using `lkr` by running `uv run --with lkr-dev-cli[all] lkr --help`.
8
8
 
9
- Alternatively, you can install `lkr` with `pip install lkr-dev-cli` and use commands directly like `lkr <command>`.
9
+ Alternatively, you can install `lkr` with `pip install lkr-dev-cli[all]` and use commands directly like `lkr <command>`.
10
10
 
11
11
  We also have a public docker image that you can use to run `lkr` commands.
12
12
 
@@ -24,7 +24,7 @@ See the [prerequisites section](#oauth2-prerequisites)
24
24
  Login to `lkr`
25
25
 
26
26
  ```bash
27
- uv run --with lkr-dev-cli lkr auth login
27
+ uv run --with lkr-dev-cli[all] lkr auth login
28
28
  ```
29
29
 
30
30
  - Select a new instance
@@ -37,7 +37,7 @@ You will be redirected to the Looker OAuth authorization page, click Allow. If y
37
37
  If everything is successful, you will see `Successfully authenticated!`. Test it with
38
38
 
39
39
  ```bash
40
- uv run --with lkr-dev-cli lkr auth whoami
40
+ uv run --with lkr-dev-cli[all] lkr auth whoami
41
41
  ```
42
42
 
43
43
  ### Using API Key
@@ -45,7 +45,7 @@ uv run --with lkr-dev-cli lkr auth whoami
45
45
  If you provide environment variables for `LOOKERSDK_CLIENT_ID`, `LOOKERSDK_CLIENT_SECRET`, and `LOOKERSDK_BASE_URL`, `lkr` will use the API key to authenticate and the commands. We also support command line arguments to pass in the client id, client secret, and base url.
46
46
 
47
47
  ```bash
48
- uv run --with lkr-dev-cli lkr --client-id <your client id> --client-secret <your client secret> --base-url <your instance url> auth whoami
48
+ uv run --with lkr-dev-cli[all] lkr --client-id <your client id> --client-secret <your client secret> --base-url <your instance url> auth whoami
49
49
  ```
50
50
 
51
51
 
@@ -81,7 +81,7 @@ Built into the `lkr` is an MCP server. Right now its tools are based on helping
81
81
  "mcpServers": {
82
82
  "lkr-mcp": {
83
83
  "command": "uv",
84
- "args": ["run", "--with", "lkr-dev-cli", "lkr", "mcp", "run"]
84
+ "args": ["run", "--with", "lkr-dev-cli[all]", "lkr", "mcp", "run"]
85
85
  },
86
86
  "lkr-mcp-docker": {
87
87
  "command": "docker",
@@ -331,12 +331,9 @@ The `lkr` CLI supports optional dependencies that enable additional functionalit
331
331
 
332
332
  ### Available Extras
333
333
 
334
- - **`mcp`**: Enables the MCP (Model Context Protocol) server functionality
335
- - Includes: `mcp[cli]>=1.9.2`, `duckdb>=1.2.2`
336
- - **`embed-observability`**: Enables the observability embed monitoring features
337
- - Includes: `fastapi>=0.115.12`, `selenium>=4.32.0`
338
- - **`user-attribute-updater`**: Enables the user attribute updater functionality
339
- - Includes: `fastapi>=0.115.12`
334
+ - **`mcp`**: Enables the MCP (Model Context Protocol) server functionality and `lkr mcp` commands
335
+ - **`observability`**: Enables the observability embed monitoring features and `lkr observability` commands
336
+ - **`tools`**: Enables the user attribute updater functionality and `lkr tools` commands
340
337
 
341
338
  ### Installing Optional Dependencies
342
339
 
@@ -368,11 +365,3 @@ pip install lkr-dev-cli[all]
368
365
  # Install specific extras
369
366
  pip install lkr-dev-cli[mcp,embed-observability,user-attribute-updater]
370
367
  ```
371
-
372
- ### What Each Extra Enables
373
-
374
- - **`mcp`**: Use the MCP server with tools like Cursor for enhanced IDE integration
375
- - **`embed-observability`**: Run the observability embed server for monitoring Looker dashboard performance
376
- - **`user-attribute-updater`**: Deploy the user attribute updater service for OIDC token management
377
-
378
- All extras are designed to work together seamlessly, and installing `all` is equivalent to installing all individual extras.
@@ -1,16 +1,25 @@
1
1
  import urllib.parse
2
2
  from typing import Annotated, List, Union
3
3
 
4
- import questionary
5
4
  import typer
6
5
  from looker_sdk.rtl.auth_token import AccessToken, AuthToken
7
- from rich.console import Console
8
- from rich.table import Table
9
6
 
10
7
  from lkr.auth.oauth import OAuth2PKCE
11
8
  from lkr.auth_service import get_auth
12
9
  from lkr.logger import logger
13
10
 
11
+ QUESTIONARY_AVAILABLE = True
12
+ RICH_AVAILABLE = True
13
+ try:
14
+ import questionary
15
+ except ModuleNotFoundError:
16
+ QUESTIONARY_AVAILABLE = False
17
+ try:
18
+ from rich.console import Console
19
+ from rich.table import Table
20
+ except ModuleNotFoundError:
21
+ RICH_AVAILABLE = False
22
+
14
23
  __all__ = ["group"]
15
24
 
16
25
  group = typer.Typer(name="auth", help="Authentication commands for LookML Repository")
@@ -199,7 +208,7 @@ def list(ctx: typer.Context):
199
208
  """
200
209
  List all authenticated Looker instances
201
210
  """
202
- console = Console()
211
+ console = Console() if RICH_AVAILABLE else None
203
212
  auth = get_auth(ctx)
204
213
  all_instances = auth.list_auth()
205
214
  if not all_instances:
@@ -213,7 +222,10 @@ def list(ctx: typer.Context):
213
222
  instance[1],
214
223
  "Yes" if instance[3] else "No",
215
224
  )
216
- console.print(table)
225
+ if console:
226
+ console.print(table)
227
+ else:
228
+ print(table)
217
229
 
218
230
 
219
231
  if __name__ == "__main__":
@@ -3,10 +3,11 @@ import os
3
3
  import sqlite3
4
4
  import types
5
5
  from datetime import datetime, timedelta, timezone
6
- from typing import List, Self, Tuple, Union
6
+ from typing import List, Self, Tuple, Union, TYPE_CHECKING
7
7
 
8
8
  import requests
9
- import typer
9
+ if TYPE_CHECKING:
10
+ import typer
10
11
  from looker_sdk.rtl import serialize
11
12
  from looker_sdk.rtl.api_settings import ApiSettings, SettingsConfig
12
13
  from looker_sdk.rtl.auth_session import AuthSession, CryptoHash, OAuthSession
@@ -25,7 +26,7 @@ from lkr.logger import logger
25
26
  __all__ = ["get_auth", "ApiKeyAuthSession", "DbOAuthSession"]
26
27
 
27
28
 
28
- def get_auth(ctx: typer.Context | LkrCtxObj) -> Union["SqlLiteAuth", "ApiKeyAuth"]:
29
+ def get_auth(ctx: Union["typer.Context", LkrCtxObj]) -> Union["SqlLiteAuth", "ApiKeyAuth"]:
29
30
  if isinstance(ctx, LkrCtxObj):
30
31
  lkr_ctx = ctx
31
32
  else:
@@ -1,19 +1,28 @@
1
1
  import logging
2
2
  import os
3
+ from lkr.custom_types import LogLevel
3
4
 
4
- import structlog
5
- from rich.console import Console
6
- from rich.logging import RichHandler
7
- from rich.theme import Theme
5
+ STRUCT_LOG_AVAILABLE = True
6
+ RICH_AVAILABLE = True
7
+ try:
8
+ import structlog
9
+ except ModuleNotFoundError:
10
+ STRUCT_LOG_AVAILABLE = False
11
+ try:
12
+ from rich.console import Console
13
+ from rich.logging import RichHandler
14
+ from rich.theme import Theme
15
+ except ModuleNotFoundError:
16
+ RICH_AVAILABLE = False
8
17
 
9
- from lkr.custom_types import LogLevel
10
18
 
11
- structlog.configure(
12
- processors=[
13
- structlog.processors.TimeStamper(fmt="iso"),
14
- structlog.processors.JSONRenderer(),
15
- ]
16
- )
19
+ if STRUCT_LOG_AVAILABLE:
20
+ structlog.configure(
21
+ processors=[
22
+ structlog.processors.TimeStamper(fmt="iso"),
23
+ structlog.processors.JSONRenderer(),
24
+ ]
25
+ )
17
26
 
18
27
  # Define a custom theme for our logging
19
28
  theme = Theme(
@@ -24,10 +33,10 @@ theme = Theme(
24
33
  "logging.level.error": "bold red",
25
34
  "logging.level.critical": "bold white on red",
26
35
  }
27
- )
36
+ ) if RICH_AVAILABLE else None
28
37
 
29
38
  # Create a console for logging
30
- console = Console(theme=theme)
39
+ console = Console(theme=theme) if RICH_AVAILABLE else None
31
40
 
32
41
  # Configure the logging handler
33
42
  handler = RichHandler(
@@ -37,7 +46,7 @@ handler = RichHandler(
37
46
  markup=True,
38
47
  rich_tracebacks=True,
39
48
  tracebacks_show_locals=True,
40
- )
49
+ ) if RICH_AVAILABLE else None
41
50
 
42
51
  # Get log level from environment variable, defaulting to INFO
43
52
  DEFAULT_LOG_LEVEL = "INFO"
@@ -50,25 +59,27 @@ logging.basicConfig(
50
59
  ), # Fallback to INFO if invalid level
51
60
  format="%(message)s",
52
61
  datefmt="[%X]",
53
- handlers=[handler],
62
+ handlers=[handler] if handler else [],
54
63
  )
55
64
 
56
65
  # Create a logger for the application
57
66
  logger = logging.getLogger("lkr")
58
- structured_logger = structlog.get_logger("lkr.structured")
67
+ structured_logger = structlog.get_logger("lkr.structured") if STRUCT_LOG_AVAILABLE else None
59
68
 
60
69
 
61
70
  # Configure the requests_transport logger to only show debug messages when LOG_LEVEL is DEBUG
62
- requests_logger = logging.getLogger("looker_sdk.rtl.requests_transport")
63
- if log_level != "DEBUG":
71
+ requests_logger = logging.getLogger("looker_sdk.rtl.requests_transport") if RICH_AVAILABLE else None
72
+ if log_level != "DEBUG" and requests_logger:
64
73
  requests_logger.setLevel(logging.WARNING)
65
74
 
66
75
 
67
76
  def set_log_level(level: LogLevel):
68
77
  """Set the logging level for the application."""
69
78
  logger.setLevel(getattr(logging, level.value))
70
- logging.getLogger("lkr.structured").setLevel(getattr(logging, level.value))
79
+ if structured_logger:
80
+ structured_logger.setLevel(getattr(logging, level.value))
71
81
  # Update requests_transport logger level based on the new level
72
- requests_logger.setLevel(
73
- logging.DEBUG if level == LogLevel.DEBUG else logging.WARNING
74
- )
82
+ if requests_logger:
83
+ requests_logger.setLevel(
84
+ logging.DEBUG if level == LogLevel.DEBUG else logging.WARNING
85
+ )
@@ -7,9 +7,6 @@ from lkr.auth.main import group as auth_group
7
7
  from lkr.classes import LkrCtxObj
8
8
  from lkr.custom_types import LogLevel
9
9
  from lkr.logger import logger
10
- from lkr.mcp.main import group as mcp_group
11
- from lkr.observability.main import group as observability_group
12
- from lkr.tools.main import group as tools_group
13
10
 
14
11
  app = typer.Typer(
15
12
  name="lkr",
@@ -19,10 +16,32 @@ app = typer.Typer(
19
16
  )
20
17
 
21
18
  app.add_typer(auth_group, name="auth")
22
- app.add_typer(mcp_group, name="mcp")
23
- app.add_typer(observability_group, name="observability")
24
- app.add_typer(tools_group, name="tools")
25
19
 
20
+ IMPORT_ERROR = None
21
+
22
+ def add_optional_typer_group(app, import_path, group_name, extra_message=None):
23
+ try:
24
+ module_path, attr = import_path.rsplit(".", 1)
25
+ mod = __import__(module_path, fromlist=[attr])
26
+ group = getattr(mod, attr)
27
+ app.add_typer(group, name=group_name)
28
+ except ModuleNotFoundError as import_error:
29
+ @app.command(
30
+ name=group_name,
31
+ add_help_option=False,
32
+ context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
33
+ )
34
+ def fallback(import_error=import_error):
35
+ msg = f"{group_name} tools (dependencies not available, try installing optional dependencies: lkr-dev-cli\\[{group_name}])"
36
+ if extra_message:
37
+ msg += f" {extra_message}"
38
+ logger.error(msg)
39
+ logger.error(import_error)
40
+ raise typer.Exit(1)
41
+
42
+ add_optional_typer_group(app, "lkr.mcp.main.group", "mcp")
43
+ add_optional_typer_group(app, "lkr.observability.main.group", "observability")
44
+ add_optional_typer_group(app, "lkr.tools.main.group", "tools")
26
45
 
27
46
  @app.callback()
28
47
  def callback(
@@ -39,6 +39,8 @@ DEFAULT_PERMISSIONS = set(
39
39
 
40
40
  observability_ctx = ObservabilityCtxObj()
41
41
 
42
+ if not structured_logger:
43
+ raise Exception("Structured logger is not available")
42
44
 
43
45
  def get_embed_sdk_obj(
44
46
  dashboard_id: str = Query(...),
@@ -11,6 +11,9 @@ __all__ = ["group"]
11
11
 
12
12
  group = typer.Typer()
13
13
 
14
+ if not logger:
15
+ raise Exception("Logger is not available")
16
+
14
17
 
15
18
  @group.command()
16
19
  def user_attribute_updater(
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "lkr-dev-cli"
3
- version = "0.0.31"
3
+ version = "0.0.32"
4
4
  description = "lkr: a command line interface for looker"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -11,29 +11,38 @@ requires-python = ">=3.12"
11
11
  dependencies = [
12
12
  "looker-sdk>=25.4.0",
13
13
  "pydantic>=2.11.4",
14
+ "pydash>=8.0.5"
15
+ ]
16
+
17
+ [project.optional-dependencies]
18
+
19
+ cli = [
14
20
  "typer>=0.15.2",
15
21
  "requests>=2.31.0",
16
22
  "cryptography>=42.0.0",
17
- "pydash>=8.0.5",
18
23
  "structlog>=25.3.0",
19
- "questionary>=2.1.0",
24
+ "questionary>=2.1.0"
20
25
  ]
21
-
22
- [project.optional-dependencies]
23
26
  mcp = [
24
27
  "mcp[cli]>=1.9.2",
25
- "duckdb>=1.2.2"
28
+ "duckdb>=1.2.2",
29
+ "fastapi[standard]>=0.115.12"
26
30
  ]
27
- embed-observability = [
28
- "fastapi>=0.115.12",
31
+ observability = [
32
+ "fastapi[standard]>=0.115.12",
29
33
  "selenium>=4.32.0"
30
34
  ]
31
- user-attribute-updater = [
32
- "fastapi>=0.115.12"
35
+ tools = [
36
+ "fastapi[standard]>=0.115.12"
33
37
  ]
34
38
  all = [
39
+ "typer>=0.15.2",
40
+ "requests>=2.31.0",
41
+ "cryptography>=42.0.0",
42
+ "structlog>=25.3.0",
43
+ "questionary>=2.1.0",
35
44
  "mcp[cli]>=1.9.2",
36
- "fastapi>=0.115.12",
45
+ "fastapi[standard]>=0.115.12",
37
46
  "selenium>=4.32.0",
38
47
  "duckdb>=1.2.2"
39
48
  ]
@@ -0,0 +1,16 @@
1
+ from lkr.tools.classes import UserAttributeUpdater
2
+
3
+
4
+ updater = UserAttributeUpdater(
5
+ user_attribute="test",
6
+ value="test",
7
+ update_type="default",
8
+ )
9
+ print(updater.model_dump_json())
10
+
11
+
12
+
13
+
14
+
15
+
16
+
@@ -7,6 +7,9 @@ This test verifies that:
7
7
  2. uv sync --extra [all individual extras]
8
8
 
9
9
  Both resolve to the same lock file, ensuring consistency in dependency resolution.
10
+
11
+ This test also verifies that the UserAttributeUpdater can be imported, instantiated, and model_dump_json() works
12
+ with only the base dependencies (no extras installed).
10
13
  """
11
14
 
12
15
  import subprocess
@@ -16,6 +19,7 @@ import hashlib
16
19
  from pathlib import Path
17
20
  import pytest
18
21
  import tomllib
22
+ import os
19
23
 
20
24
 
21
25
  def get_file_hash(file_path: Path) -> str:
@@ -173,8 +177,62 @@ def test_individual_extras_consistency():
173
177
  print(f"ℹ️ Dev dependencies ({len(dev_deps)} items) are excluded from 'all' extra as expected")
174
178
 
175
179
 
180
+ def test_user_attribute_updater_base_deps_only(tmp_path):
181
+ """
182
+ Test that UserAttributeUpdater can be imported, instantiated, and model_dump_json() works
183
+ in a fresh environment with only base dependencies (no extras installed).
184
+ This is done by creating a temp dir, running 'uv sync' (no extras), and running a subprocess.
185
+ """
186
+ FILENAME = "test_user_attribute_updater.py"
187
+ # Copy minimal project files to temp dir
188
+ project_root = Path(__file__).parent.parent
189
+ temp_dir = Path(tmp_path) / "base_deps_env"
190
+ if temp_dir.exists():
191
+ shutil.rmtree(temp_dir)
192
+ temp_dir.mkdir()
193
+ for item in ["pyproject.toml", "README.md", "LICENSE"]:
194
+ src = project_root / item
195
+ if src.exists():
196
+ shutil.copy2(src, temp_dir / item)
197
+ lkr_src = project_root / "lkr"
198
+ if lkr_src.exists():
199
+ shutil.copytree(lkr_src, temp_dir / "lkr")
200
+
201
+ # Run 'uv sync' in the temp dir (no extras)
202
+ subprocess.run(["uv", "sync"], cwd=temp_dir, check=True)
203
+
204
+ # Write a small test script to the temp dir
205
+ test_script = temp_dir / FILENAME
206
+ test_script.write_text(
207
+ """
208
+ from lkr.tools.classes import UserAttributeUpdater
209
+ updater = UserAttributeUpdater(user_attribute='test', value='test', update_type='default')
210
+ print(updater.model_dump_json())
211
+ """
212
+ )
213
+
214
+ # Run the script using the temp dir as the working directory and PYTHONPATH
215
+ env = dict(**os.environ)
216
+ env["PYTHONPATH"] = str(temp_dir)
217
+ result = subprocess.run(
218
+ ["uv", "run", FILENAME],
219
+ cwd=temp_dir,
220
+ env=env,
221
+ capture_output=True,
222
+ text=True,
223
+ check=True,
224
+ )
225
+ output = result.stdout.strip()
226
+ assert '"user_attribute":"test"' in output
227
+ assert '"value":"test"' not in output
228
+ assert '"update_type":"default"' in output
229
+
230
+
176
231
  if __name__ == "__main__":
177
232
  # Run the tests directly if script is executed
178
233
  test_dependency_resolution_consistency()
179
234
  test_individual_extras_consistency()
180
- print("All tests passed!")
235
+ test_user_attribute_updater_base_deps_only("./tmp")
236
+ print("All tests passed!")
237
+
238
+
@@ -180,6 +180,15 @@ wheels = [
180
180
  { url = "https://files.pythonhosted.org/packages/2a/4b/3256759723b7e66380397d958ca07c59cfc3fb5c794fb5516758afd05d41/cryptography-45.0.4-cp37-abi3-win_amd64.whl", hash = "sha256:627ba1bc94f6adf0b0a2e35d87020285ead22d9f648c7e75bb64f367375f3b22", size = 3395508 },
181
181
  ]
182
182
 
183
+ [[package]]
184
+ name = "dnspython"
185
+ version = "2.7.0"
186
+ source = { registry = "https://pypi.org/simple" }
187
+ sdist = { url = "https://files.pythonhosted.org/packages/b5/4a/263763cb2ba3816dd94b08ad3a33d5fdae34ecb856678773cc40a3605829/dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1", size = 345197 }
188
+ wheels = [
189
+ { url = "https://files.pythonhosted.org/packages/68/1b/e0a87d256e40e8c888847551b20a017a6b98139178505dc7ffb96f04e954/dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86", size = 313632 },
190
+ ]
191
+
183
192
  [[package]]
184
193
  name = "duckdb"
185
194
  version = "1.3.1"
@@ -202,6 +211,19 @@ wheels = [
202
211
  { url = "https://files.pythonhosted.org/packages/94/b9/f5ae51f7331f79c184fd96456c0896de875149fdeb092084fd20433ec97c/duckdb-1.3.1-cp313-cp313-win_amd64.whl", hash = "sha256:3eb045a9bf92da890d890cde2f676b3bda61b9de3b7dc46cbaaf75875b41e4b0", size = 11302770 },
203
212
  ]
204
213
 
214
+ [[package]]
215
+ name = "email-validator"
216
+ version = "2.2.0"
217
+ source = { registry = "https://pypi.org/simple" }
218
+ dependencies = [
219
+ { name = "dnspython" },
220
+ { name = "idna" },
221
+ ]
222
+ sdist = { url = "https://files.pythonhosted.org/packages/48/ce/13508a1ec3f8bb981ae4ca79ea40384becc868bfae97fd1c942bb3a001b1/email_validator-2.2.0.tar.gz", hash = "sha256:cb690f344c617a714f22e66ae771445a1ceb46821152df8e165c5f9a364582b7", size = 48967 }
223
+ wheels = [
224
+ { url = "https://files.pythonhosted.org/packages/d7/ee/bf0adb559ad3c786f12bcbc9296b3f5675f529199bef03e2df281fa1fadb/email_validator-2.2.0-py3-none-any.whl", hash = "sha256:561977c2d73ce3611850a06fa56b414621e0c8faa9d66f2611407d87465da631", size = 33521 },
225
+ ]
226
+
205
227
  [[package]]
206
228
  name = "fastapi"
207
229
  version = "0.115.14"
@@ -216,6 +238,35 @@ wheels = [
216
238
  { url = "https://files.pythonhosted.org/packages/53/50/b1222562c6d270fea83e9c9075b8e8600b8479150a18e4516a6138b980d1/fastapi-0.115.14-py3-none-any.whl", hash = "sha256:6c0c8bf9420bd58f565e585036d971872472b4f7d3f6c73b698e10cffdefb3ca", size = 95514 },
217
239
  ]
218
240
 
241
+ [package.optional-dependencies]
242
+ standard = [
243
+ { name = "email-validator" },
244
+ { name = "fastapi-cli", extra = ["standard"] },
245
+ { name = "httpx" },
246
+ { name = "jinja2" },
247
+ { name = "python-multipart" },
248
+ { name = "uvicorn", extra = ["standard"] },
249
+ ]
250
+
251
+ [[package]]
252
+ name = "fastapi-cli"
253
+ version = "0.0.7"
254
+ source = { registry = "https://pypi.org/simple" }
255
+ dependencies = [
256
+ { name = "rich-toolkit" },
257
+ { name = "typer" },
258
+ { name = "uvicorn", extra = ["standard"] },
259
+ ]
260
+ sdist = { url = "https://files.pythonhosted.org/packages/fe/73/82a5831fbbf8ed75905bacf5b2d9d3dfd6f04d6968b29fe6f72a5ae9ceb1/fastapi_cli-0.0.7.tar.gz", hash = "sha256:02b3b65956f526412515907a0793c9094abd4bfb5457b389f645b0ea6ba3605e", size = 16753 }
261
+ wheels = [
262
+ { url = "https://files.pythonhosted.org/packages/a1/e6/5daefc851b514ce2287d8f5d358ae4341089185f78f3217a69d0ce3a390c/fastapi_cli-0.0.7-py3-none-any.whl", hash = "sha256:d549368ff584b2804336c61f192d86ddea080c11255f375959627911944804f4", size = 10705 },
263
+ ]
264
+
265
+ [package.optional-dependencies]
266
+ standard = [
267
+ { name = "uvicorn", extra = ["standard"] },
268
+ ]
269
+
219
270
  [[package]]
220
271
  name = "h11"
221
272
  version = "0.16.0"
@@ -238,6 +289,28 @@ wheels = [
238
289
  { url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784 },
239
290
  ]
240
291
 
292
+ [[package]]
293
+ name = "httptools"
294
+ version = "0.6.4"
295
+ source = { registry = "https://pypi.org/simple" }
296
+ sdist = { url = "https://files.pythonhosted.org/packages/a7/9a/ce5e1f7e131522e6d3426e8e7a490b3a01f39a6696602e1c4f33f9e94277/httptools-0.6.4.tar.gz", hash = "sha256:4e93eee4add6493b59a5c514da98c939b244fce4a0d8879cd3f466562f4b7d5c", size = 240639 }
297
+ wheels = [
298
+ { url = "https://files.pythonhosted.org/packages/bb/0e/d0b71465c66b9185f90a091ab36389a7352985fe857e352801c39d6127c8/httptools-0.6.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:df017d6c780287d5c80601dafa31f17bddb170232d85c066604d8558683711a2", size = 200683 },
299
+ { url = "https://files.pythonhosted.org/packages/e2/b8/412a9bb28d0a8988de3296e01efa0bd62068b33856cdda47fe1b5e890954/httptools-0.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:85071a1e8c2d051b507161f6c3e26155b5c790e4e28d7f236422dbacc2a9cc44", size = 104337 },
300
+ { url = "https://files.pythonhosted.org/packages/9b/01/6fb20be3196ffdc8eeec4e653bc2a275eca7f36634c86302242c4fbb2760/httptools-0.6.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69422b7f458c5af875922cdb5bd586cc1f1033295aa9ff63ee196a87519ac8e1", size = 508796 },
301
+ { url = "https://files.pythonhosted.org/packages/f7/d8/b644c44acc1368938317d76ac991c9bba1166311880bcc0ac297cb9d6bd7/httptools-0.6.4-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16e603a3bff50db08cd578d54f07032ca1631450ceb972c2f834c2b860c28ea2", size = 510837 },
302
+ { url = "https://files.pythonhosted.org/packages/52/d8/254d16a31d543073a0e57f1c329ca7378d8924e7e292eda72d0064987486/httptools-0.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec4f178901fa1834d4a060320d2f3abc5c9e39766953d038f1458cb885f47e81", size = 485289 },
303
+ { url = "https://files.pythonhosted.org/packages/5f/3c/4aee161b4b7a971660b8be71a92c24d6c64372c1ab3ae7f366b3680df20f/httptools-0.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f9eb89ecf8b290f2e293325c646a211ff1c2493222798bb80a530c5e7502494f", size = 489779 },
304
+ { url = "https://files.pythonhosted.org/packages/12/b7/5cae71a8868e555f3f67a50ee7f673ce36eac970f029c0c5e9d584352961/httptools-0.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:db78cb9ca56b59b016e64b6031eda5653be0589dba2b1b43453f6e8b405a0970", size = 88634 },
305
+ { url = "https://files.pythonhosted.org/packages/94/a3/9fe9ad23fd35f7de6b91eeb60848986058bd8b5a5c1e256f5860a160cc3e/httptools-0.6.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ade273d7e767d5fae13fa637f4d53b6e961fb7fd93c7797562663f0171c26660", size = 197214 },
306
+ { url = "https://files.pythonhosted.org/packages/ea/d9/82d5e68bab783b632023f2fa31db20bebb4e89dfc4d2293945fd68484ee4/httptools-0.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:856f4bc0478ae143bad54a4242fccb1f3f86a6e1be5548fecfd4102061b3a083", size = 102431 },
307
+ { url = "https://files.pythonhosted.org/packages/96/c1/cb499655cbdbfb57b577734fde02f6fa0bbc3fe9fb4d87b742b512908dff/httptools-0.6.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:322d20ea9cdd1fa98bd6a74b77e2ec5b818abdc3d36695ab402a0de8ef2865a3", size = 473121 },
308
+ { url = "https://files.pythonhosted.org/packages/af/71/ee32fd358f8a3bb199b03261f10921716990808a675d8160b5383487a317/httptools-0.6.4-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d87b29bd4486c0093fc64dea80231f7c7f7eb4dc70ae394d70a495ab8436071", size = 473805 },
309
+ { url = "https://files.pythonhosted.org/packages/8a/0a/0d4df132bfca1507114198b766f1737d57580c9ad1cf93c1ff673e3387be/httptools-0.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:342dd6946aa6bda4b8f18c734576106b8a31f2fe31492881a9a160ec84ff4bd5", size = 448858 },
310
+ { url = "https://files.pythonhosted.org/packages/1e/6a/787004fdef2cabea27bad1073bf6a33f2437b4dbd3b6fb4a9d71172b1c7c/httptools-0.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b36913ba52008249223042dca46e69967985fb4051951f94357ea681e1f5dc0", size = 452042 },
311
+ { url = "https://files.pythonhosted.org/packages/4d/dc/7decab5c404d1d2cdc1bb330b1bf70e83d6af0396fd4fc76fc60c0d522bf/httptools-0.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:28908df1b9bb8187393d5b5db91435ccc9c8e891657f9cbb42a2541b44c82fc8", size = 87682 },
312
+ ]
313
+
241
314
  [[package]]
242
315
  name = "httpx"
243
316
  version = "0.28.1"
@@ -280,6 +353,18 @@ wheels = [
280
353
  { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050 },
281
354
  ]
282
355
 
356
+ [[package]]
357
+ name = "jinja2"
358
+ version = "3.1.6"
359
+ source = { registry = "https://pypi.org/simple" }
360
+ dependencies = [
361
+ { name = "markupsafe" },
362
+ ]
363
+ sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 }
364
+ wheels = [
365
+ { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 },
366
+ ]
367
+
283
368
  [[package]]
284
369
  name = "jsonschema"
285
370
  version = "4.24.0"
@@ -312,33 +397,41 @@ name = "lkr-dev-cli"
312
397
  version = "0.0.0"
313
398
  source = { editable = "." }
314
399
  dependencies = [
315
- { name = "cryptography" },
316
400
  { name = "looker-sdk" },
317
401
  { name = "pydantic" },
318
402
  { name = "pydash" },
319
- { name = "questionary" },
320
- { name = "requests" },
321
- { name = "structlog" },
322
- { name = "typer" },
323
403
  ]
324
404
 
325
405
  [package.optional-dependencies]
326
406
  all = [
407
+ { name = "cryptography" },
327
408
  { name = "duckdb" },
328
- { name = "fastapi" },
409
+ { name = "fastapi", extra = ["standard"] },
329
410
  { name = "mcp", extra = ["cli"] },
411
+ { name = "questionary" },
412
+ { name = "requests" },
330
413
  { name = "selenium" },
414
+ { name = "structlog" },
415
+ { name = "typer" },
331
416
  ]
332
- embed-observability = [
333
- { name = "fastapi" },
334
- { name = "selenium" },
417
+ cli = [
418
+ { name = "cryptography" },
419
+ { name = "questionary" },
420
+ { name = "requests" },
421
+ { name = "structlog" },
422
+ { name = "typer" },
335
423
  ]
336
424
  mcp = [
337
425
  { name = "duckdb" },
426
+ { name = "fastapi", extra = ["standard"] },
338
427
  { name = "mcp", extra = ["cli"] },
339
428
  ]
340
- user-attribute-updater = [
341
- { name = "fastapi" },
429
+ observability = [
430
+ { name = "fastapi", extra = ["standard"] },
431
+ { name = "selenium" },
432
+ ]
433
+ tools = [
434
+ { name = "fastapi", extra = ["standard"] },
342
435
  ]
343
436
 
344
437
  [package.dev-dependencies]
@@ -349,25 +442,31 @@ dev = [
349
442
 
350
443
  [package.metadata]
351
444
  requires-dist = [
352
- { name = "cryptography", specifier = ">=42.0.0" },
445
+ { name = "cryptography", marker = "extra == 'all'", specifier = ">=42.0.0" },
446
+ { name = "cryptography", marker = "extra == 'cli'", specifier = ">=42.0.0" },
353
447
  { name = "duckdb", marker = "extra == 'all'", specifier = ">=1.2.2" },
354
448
  { name = "duckdb", marker = "extra == 'mcp'", specifier = ">=1.2.2" },
355
- { name = "fastapi", marker = "extra == 'all'", specifier = ">=0.115.12" },
356
- { name = "fastapi", marker = "extra == 'embed-observability'", specifier = ">=0.115.12" },
357
- { name = "fastapi", marker = "extra == 'user-attribute-updater'", specifier = ">=0.115.12" },
449
+ { name = "fastapi", extras = ["standard"], marker = "extra == 'all'", specifier = ">=0.115.12" },
450
+ { name = "fastapi", extras = ["standard"], marker = "extra == 'mcp'", specifier = ">=0.115.12" },
451
+ { name = "fastapi", extras = ["standard"], marker = "extra == 'observability'", specifier = ">=0.115.12" },
452
+ { name = "fastapi", extras = ["standard"], marker = "extra == 'tools'", specifier = ">=0.115.12" },
358
453
  { name = "looker-sdk", specifier = ">=25.4.0" },
359
454
  { name = "mcp", extras = ["cli"], marker = "extra == 'all'", specifier = ">=1.9.2" },
360
455
  { name = "mcp", extras = ["cli"], marker = "extra == 'mcp'", specifier = ">=1.9.2" },
361
456
  { name = "pydantic", specifier = ">=2.11.4" },
362
457
  { name = "pydash", specifier = ">=8.0.5" },
363
- { name = "questionary", specifier = ">=2.1.0" },
364
- { name = "requests", specifier = ">=2.31.0" },
458
+ { name = "questionary", marker = "extra == 'all'", specifier = ">=2.1.0" },
459
+ { name = "questionary", marker = "extra == 'cli'", specifier = ">=2.1.0" },
460
+ { name = "requests", marker = "extra == 'all'", specifier = ">=2.31.0" },
461
+ { name = "requests", marker = "extra == 'cli'", specifier = ">=2.31.0" },
365
462
  { name = "selenium", marker = "extra == 'all'", specifier = ">=4.32.0" },
366
- { name = "selenium", marker = "extra == 'embed-observability'", specifier = ">=4.32.0" },
367
- { name = "structlog", specifier = ">=25.3.0" },
368
- { name = "typer", specifier = ">=0.15.2" },
463
+ { name = "selenium", marker = "extra == 'observability'", specifier = ">=4.32.0" },
464
+ { name = "structlog", marker = "extra == 'all'", specifier = ">=25.3.0" },
465
+ { name = "structlog", marker = "extra == 'cli'", specifier = ">=25.3.0" },
466
+ { name = "typer", marker = "extra == 'all'", specifier = ">=0.15.2" },
467
+ { name = "typer", marker = "extra == 'cli'", specifier = ">=0.15.2" },
369
468
  ]
370
- provides-extras = ["mcp", "embed-observability", "user-attribute-updater", "all"]
469
+ provides-extras = ["cli", "mcp", "observability", "tools", "all"]
371
470
 
372
471
  [package.metadata.requires-dev]
373
472
  dev = [
@@ -402,6 +501,44 @@ wheels = [
402
501
  { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
403
502
  ]
404
503
 
504
+ [[package]]
505
+ name = "markupsafe"
506
+ version = "3.0.2"
507
+ source = { registry = "https://pypi.org/simple" }
508
+ sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 }
509
+ wheels = [
510
+ { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 },
511
+ { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 },
512
+ { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 },
513
+ { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 },
514
+ { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 },
515
+ { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 },
516
+ { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 },
517
+ { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 },
518
+ { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 },
519
+ { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 },
520
+ { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 },
521
+ { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 },
522
+ { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 },
523
+ { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 },
524
+ { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 },
525
+ { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 },
526
+ { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 },
527
+ { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 },
528
+ { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 },
529
+ { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 },
530
+ { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 },
531
+ { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 },
532
+ { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 },
533
+ { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 },
534
+ { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 },
535
+ { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 },
536
+ { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 },
537
+ { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 },
538
+ { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 },
539
+ { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 },
540
+ ]
541
+
405
542
  [[package]]
406
543
  name = "mcp"
407
544
  version = "1.10.1"
@@ -624,6 +761,32 @@ wheels = [
624
761
  { url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546 },
625
762
  ]
626
763
 
764
+ [[package]]
765
+ name = "pyyaml"
766
+ version = "6.0.2"
767
+ source = { registry = "https://pypi.org/simple" }
768
+ sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 }
769
+ wheels = [
770
+ { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 },
771
+ { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 },
772
+ { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 },
773
+ { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 },
774
+ { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 },
775
+ { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 },
776
+ { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 },
777
+ { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 },
778
+ { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 },
779
+ { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 },
780
+ { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 },
781
+ { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 },
782
+ { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 },
783
+ { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 },
784
+ { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 },
785
+ { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 },
786
+ { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 },
787
+ { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 },
788
+ ]
789
+
627
790
  [[package]]
628
791
  name = "questionary"
629
792
  version = "2.1.0"
@@ -678,6 +841,20 @@ wheels = [
678
841
  { url = "https://files.pythonhosted.org/packages/0d/9b/63f4c7ebc259242c89b3acafdb37b41d1185c07ff0011164674e9076b491/rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", size = 243229 },
679
842
  ]
680
843
 
844
+ [[package]]
845
+ name = "rich-toolkit"
846
+ version = "0.14.8"
847
+ source = { registry = "https://pypi.org/simple" }
848
+ dependencies = [
849
+ { name = "click" },
850
+ { name = "rich" },
851
+ { name = "typing-extensions" },
852
+ ]
853
+ sdist = { url = "https://files.pythonhosted.org/packages/1b/de/d3d329d670bb271ee82e7bbc2946f985b2782f4cae2857138ed94be1335b/rich_toolkit-0.14.8.tar.gz", hash = "sha256:1f77b32e6c25d9e3644c1efbce00d8d90daf2457b3abdb4699e263c03b9ca6cf", size = 110926 }
854
+ wheels = [
855
+ { url = "https://files.pythonhosted.org/packages/78/39/c0fd75955aa963a15c642dfe6fb2acdd1fd2114028ec5ff2e2fd26218ad7/rich_toolkit-0.14.8-py3-none-any.whl", hash = "sha256:c54bda82b93145a79bbae04c3e15352e6711787c470728ff41fdfa0c2f0c11ae", size = 24975 },
856
+ ]
857
+
681
858
  [[package]]
682
859
  name = "rpds-py"
683
860
  version = "0.25.1"
@@ -923,6 +1100,104 @@ wheels = [
923
1100
  { url = "https://files.pythonhosted.org/packages/d2/e2/dc81b1bd1dcfe91735810265e9d26bc8ec5da45b4c0f6237e286819194c3/uvicorn-0.35.0-py3-none-any.whl", hash = "sha256:197535216b25ff9b785e29a0b79199f55222193d47f820816e7da751e9bc8d4a", size = 66406 },
924
1101
  ]
925
1102
 
1103
+ [package.optional-dependencies]
1104
+ standard = [
1105
+ { name = "colorama", marker = "sys_platform == 'win32'" },
1106
+ { name = "httptools" },
1107
+ { name = "python-dotenv" },
1108
+ { name = "pyyaml" },
1109
+ { name = "uvloop", marker = "platform_python_implementation != 'PyPy' and sys_platform != 'cygwin' and sys_platform != 'win32'" },
1110
+ { name = "watchfiles" },
1111
+ { name = "websockets" },
1112
+ ]
1113
+
1114
+ [[package]]
1115
+ name = "uvloop"
1116
+ version = "0.21.0"
1117
+ source = { registry = "https://pypi.org/simple" }
1118
+ sdist = { url = "https://files.pythonhosted.org/packages/af/c0/854216d09d33c543f12a44b393c402e89a920b1a0a7dc634c42de91b9cf6/uvloop-0.21.0.tar.gz", hash = "sha256:3bf12b0fda68447806a7ad847bfa591613177275d35b6724b1ee573faa3704e3", size = 2492741 }
1119
+ wheels = [
1120
+ { url = "https://files.pythonhosted.org/packages/8c/4c/03f93178830dc7ce8b4cdee1d36770d2f5ebb6f3d37d354e061eefc73545/uvloop-0.21.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:359ec2c888397b9e592a889c4d72ba3d6befba8b2bb01743f72fffbde663b59c", size = 1471284 },
1121
+ { url = "https://files.pythonhosted.org/packages/43/3e/92c03f4d05e50f09251bd8b2b2b584a2a7f8fe600008bcc4523337abe676/uvloop-0.21.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f7089d2dc73179ce5ac255bdf37c236a9f914b264825fdaacaded6990a7fb4c2", size = 821349 },
1122
+ { url = "https://files.pythonhosted.org/packages/a6/ef/a02ec5da49909dbbfb1fd205a9a1ac4e88ea92dcae885e7c961847cd51e2/uvloop-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:baa4dcdbd9ae0a372f2167a207cd98c9f9a1ea1188a8a526431eef2f8116cc8d", size = 4580089 },
1123
+ { url = "https://files.pythonhosted.org/packages/06/a7/b4e6a19925c900be9f98bec0a75e6e8f79bb53bdeb891916609ab3958967/uvloop-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86975dca1c773a2c9864f4c52c5a55631038e387b47eaf56210f873887b6c8dc", size = 4693770 },
1124
+ { url = "https://files.pythonhosted.org/packages/ce/0c/f07435a18a4b94ce6bd0677d8319cd3de61f3a9eeb1e5f8ab4e8b5edfcb3/uvloop-0.21.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:461d9ae6660fbbafedd07559c6a2e57cd553b34b0065b6550685f6653a98c1cb", size = 4451321 },
1125
+ { url = "https://files.pythonhosted.org/packages/8f/eb/f7032be105877bcf924709c97b1bf3b90255b4ec251f9340cef912559f28/uvloop-0.21.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:183aef7c8730e54c9a3ee3227464daed66e37ba13040bb3f350bc2ddc040f22f", size = 4659022 },
1126
+ { url = "https://files.pythonhosted.org/packages/3f/8d/2cbef610ca21539f0f36e2b34da49302029e7c9f09acef0b1c3b5839412b/uvloop-0.21.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bfd55dfcc2a512316e65f16e503e9e450cab148ef11df4e4e679b5e8253a5281", size = 1468123 },
1127
+ { url = "https://files.pythonhosted.org/packages/93/0d/b0038d5a469f94ed8f2b2fce2434a18396d8fbfb5da85a0a9781ebbdec14/uvloop-0.21.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:787ae31ad8a2856fc4e7c095341cccc7209bd657d0e71ad0dc2ea83c4a6fa8af", size = 819325 },
1128
+ { url = "https://files.pythonhosted.org/packages/50/94/0a687f39e78c4c1e02e3272c6b2ccdb4e0085fda3b8352fecd0410ccf915/uvloop-0.21.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ee4d4ef48036ff6e5cfffb09dd192c7a5027153948d85b8da7ff705065bacc6", size = 4582806 },
1129
+ { url = "https://files.pythonhosted.org/packages/d2/19/f5b78616566ea68edd42aacaf645adbf71fbd83fc52281fba555dc27e3f1/uvloop-0.21.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3df876acd7ec037a3d005b3ab85a7e4110422e4d9c1571d4fc89b0fc41b6816", size = 4701068 },
1130
+ { url = "https://files.pythonhosted.org/packages/47/57/66f061ee118f413cd22a656de622925097170b9380b30091b78ea0c6ea75/uvloop-0.21.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd53ecc9a0f3d87ab847503c2e1552b690362e005ab54e8a48ba97da3924c0dc", size = 4454428 },
1131
+ { url = "https://files.pythonhosted.org/packages/63/9a/0962b05b308494e3202d3f794a6e85abe471fe3cafdbcf95c2e8c713aabd/uvloop-0.21.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a5c39f217ab3c663dc699c04cbd50c13813e31d917642d459fdcec07555cc553", size = 4660018 },
1132
+ ]
1133
+
1134
+ [[package]]
1135
+ name = "watchfiles"
1136
+ version = "1.1.0"
1137
+ source = { registry = "https://pypi.org/simple" }
1138
+ dependencies = [
1139
+ { name = "anyio" },
1140
+ ]
1141
+ sdist = { url = "https://files.pythonhosted.org/packages/2a/9a/d451fcc97d029f5812e898fd30a53fd8c15c7bbd058fd75cfc6beb9bd761/watchfiles-1.1.0.tar.gz", hash = "sha256:693ed7ec72cbfcee399e92c895362b6e66d63dac6b91e2c11ae03d10d503e575", size = 94406 }
1142
+ wheels = [
1143
+ { url = "https://files.pythonhosted.org/packages/f6/b8/858957045a38a4079203a33aaa7d23ea9269ca7761c8a074af3524fbb240/watchfiles-1.1.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9dc001c3e10de4725c749d4c2f2bdc6ae24de5a88a339c4bce32300a31ede179", size = 402339 },
1144
+ { url = "https://files.pythonhosted.org/packages/80/28/98b222cca751ba68e88521fabd79a4fab64005fc5976ea49b53fa205d1fa/watchfiles-1.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d9ba68ec283153dead62cbe81872d28e053745f12335d037de9cbd14bd1877f5", size = 394409 },
1145
+ { url = "https://files.pythonhosted.org/packages/86/50/dee79968566c03190677c26f7f47960aff738d32087087bdf63a5473e7df/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:130fc497b8ee68dce163e4254d9b0356411d1490e868bd8790028bc46c5cc297", size = 450939 },
1146
+ { url = "https://files.pythonhosted.org/packages/40/45/a7b56fb129700f3cfe2594a01aa38d033b92a33dddce86c8dfdfc1247b72/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:50a51a90610d0845a5931a780d8e51d7bd7f309ebc25132ba975aca016b576a0", size = 457270 },
1147
+ { url = "https://files.pythonhosted.org/packages/b5/c8/fa5ef9476b1d02dc6b5e258f515fcaaecf559037edf8b6feffcbc097c4b8/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc44678a72ac0910bac46fa6a0de6af9ba1355669b3dfaf1ce5f05ca7a74364e", size = 483370 },
1148
+ { url = "https://files.pythonhosted.org/packages/98/68/42cfcdd6533ec94f0a7aab83f759ec11280f70b11bfba0b0f885e298f9bd/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a543492513a93b001975ae283a51f4b67973662a375a403ae82f420d2c7205ee", size = 598654 },
1149
+ { url = "https://files.pythonhosted.org/packages/d3/74/b2a1544224118cc28df7e59008a929e711f9c68ce7d554e171b2dc531352/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ac164e20d17cc285f2b94dc31c384bc3aa3dd5e7490473b3db043dd70fbccfd", size = 478667 },
1150
+ { url = "https://files.pythonhosted.org/packages/8c/77/e3362fe308358dc9f8588102481e599c83e1b91c2ae843780a7ded939a35/watchfiles-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7590d5a455321e53857892ab8879dce62d1f4b04748769f5adf2e707afb9d4f", size = 452213 },
1151
+ { url = "https://files.pythonhosted.org/packages/6e/17/c8f1a36540c9a1558d4faf08e909399e8133599fa359bf52ec8fcee5be6f/watchfiles-1.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:37d3d3f7defb13f62ece99e9be912afe9dd8a0077b7c45ee5a57c74811d581a4", size = 626718 },
1152
+ { url = "https://files.pythonhosted.org/packages/26/45/fb599be38b4bd38032643783d7496a26a6f9ae05dea1a42e58229a20ac13/watchfiles-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7080c4bb3efd70a07b1cc2df99a7aa51d98685be56be6038c3169199d0a1c69f", size = 623098 },
1153
+ { url = "https://files.pythonhosted.org/packages/a1/e7/fdf40e038475498e160cd167333c946e45d8563ae4dd65caf757e9ffe6b4/watchfiles-1.1.0-cp312-cp312-win32.whl", hash = "sha256:cbcf8630ef4afb05dc30107bfa17f16c0896bb30ee48fc24bf64c1f970f3b1fd", size = 279209 },
1154
+ { url = "https://files.pythonhosted.org/packages/3f/d3/3ae9d5124ec75143bdf088d436cba39812122edc47709cd2caafeac3266f/watchfiles-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:cbd949bdd87567b0ad183d7676feb98136cde5bb9025403794a4c0db28ed3a47", size = 292786 },
1155
+ { url = "https://files.pythonhosted.org/packages/26/2f/7dd4fc8b5f2b34b545e19629b4a018bfb1de23b3a496766a2c1165ca890d/watchfiles-1.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:0a7d40b77f07be87c6faa93d0951a0fcd8cbca1ddff60a1b65d741bac6f3a9f6", size = 284343 },
1156
+ { url = "https://files.pythonhosted.org/packages/d3/42/fae874df96595556a9089ade83be34a2e04f0f11eb53a8dbf8a8a5e562b4/watchfiles-1.1.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5007f860c7f1f8df471e4e04aaa8c43673429047d63205d1630880f7637bca30", size = 402004 },
1157
+ { url = "https://files.pythonhosted.org/packages/fa/55/a77e533e59c3003d9803c09c44c3651224067cbe7fb5d574ddbaa31e11ca/watchfiles-1.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:20ecc8abbd957046f1fe9562757903f5eaf57c3bce70929fda6c7711bb58074a", size = 393671 },
1158
+ { url = "https://files.pythonhosted.org/packages/05/68/b0afb3f79c8e832e6571022611adbdc36e35a44e14f129ba09709aa4bb7a/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2f0498b7d2a3c072766dba3274fe22a183dbea1f99d188f1c6c72209a1063dc", size = 449772 },
1159
+ { url = "https://files.pythonhosted.org/packages/ff/05/46dd1f6879bc40e1e74c6c39a1b9ab9e790bf1f5a2fe6c08b463d9a807f4/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:239736577e848678e13b201bba14e89718f5c2133dfd6b1f7846fa1b58a8532b", size = 456789 },
1160
+ { url = "https://files.pythonhosted.org/packages/8b/ca/0eeb2c06227ca7f12e50a47a3679df0cd1ba487ea19cf844a905920f8e95/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eff4b8d89f444f7e49136dc695599a591ff769300734446c0a86cba2eb2f9895", size = 482551 },
1161
+ { url = "https://files.pythonhosted.org/packages/31/47/2cecbd8694095647406645f822781008cc524320466ea393f55fe70eed3b/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12b0a02a91762c08f7264e2e79542f76870c3040bbc847fb67410ab81474932a", size = 597420 },
1162
+ { url = "https://files.pythonhosted.org/packages/d9/7e/82abc4240e0806846548559d70f0b1a6dfdca75c1b4f9fa62b504ae9b083/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:29e7bc2eee15cbb339c68445959108803dc14ee0c7b4eea556400131a8de462b", size = 477950 },
1163
+ { url = "https://files.pythonhosted.org/packages/25/0d/4d564798a49bf5482a4fa9416dea6b6c0733a3b5700cb8a5a503c4b15853/watchfiles-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9481174d3ed982e269c090f780122fb59cee6c3796f74efe74e70f7780ed94c", size = 451706 },
1164
+ { url = "https://files.pythonhosted.org/packages/81/b5/5516cf46b033192d544102ea07c65b6f770f10ed1d0a6d388f5d3874f6e4/watchfiles-1.1.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:80f811146831c8c86ab17b640801c25dc0a88c630e855e2bef3568f30434d52b", size = 625814 },
1165
+ { url = "https://files.pythonhosted.org/packages/0c/dd/7c1331f902f30669ac3e754680b6edb9a0dd06dea5438e61128111fadd2c/watchfiles-1.1.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:60022527e71d1d1fda67a33150ee42869042bce3d0fcc9cc49be009a9cded3fb", size = 622820 },
1166
+ { url = "https://files.pythonhosted.org/packages/1b/14/36d7a8e27cd128d7b1009e7715a7c02f6c131be9d4ce1e5c3b73d0e342d8/watchfiles-1.1.0-cp313-cp313-win32.whl", hash = "sha256:32d6d4e583593cb8576e129879ea0991660b935177c0f93c6681359b3654bfa9", size = 279194 },
1167
+ { url = "https://files.pythonhosted.org/packages/25/41/2dd88054b849aa546dbeef5696019c58f8e0774f4d1c42123273304cdb2e/watchfiles-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:f21af781a4a6fbad54f03c598ab620e3a77032c5878f3d780448421a6e1818c7", size = 292349 },
1168
+ { url = "https://files.pythonhosted.org/packages/c8/cf/421d659de88285eb13941cf11a81f875c176f76a6d99342599be88e08d03/watchfiles-1.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:5366164391873ed76bfdf618818c82084c9db7fac82b64a20c44d335eec9ced5", size = 283836 },
1169
+ { url = "https://files.pythonhosted.org/packages/45/10/6faf6858d527e3599cc50ec9fcae73590fbddc1420bd4fdccfebffeedbc6/watchfiles-1.1.0-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:17ab167cca6339c2b830b744eaf10803d2a5b6683be4d79d8475d88b4a8a4be1", size = 400343 },
1170
+ { url = "https://files.pythonhosted.org/packages/03/20/5cb7d3966f5e8c718006d0e97dfe379a82f16fecd3caa7810f634412047a/watchfiles-1.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:328dbc9bff7205c215a7807da7c18dce37da7da718e798356212d22696404339", size = 392916 },
1171
+ { url = "https://files.pythonhosted.org/packages/8c/07/d8f1176328fa9e9581b6f120b017e286d2a2d22ae3f554efd9515c8e1b49/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7208ab6e009c627b7557ce55c465c98967e8caa8b11833531fdf95799372633", size = 449582 },
1172
+ { url = "https://files.pythonhosted.org/packages/66/e8/80a14a453cf6038e81d072a86c05276692a1826471fef91df7537dba8b46/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a8f6f72974a19efead54195bc9bed4d850fc047bb7aa971268fd9a8387c89011", size = 456752 },
1173
+ { url = "https://files.pythonhosted.org/packages/5a/25/0853b3fe0e3c2f5af9ea60eb2e781eade939760239a72c2d38fc4cc335f6/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d181ef50923c29cf0450c3cd47e2f0557b62218c50b2ab8ce2ecaa02bd97e670", size = 481436 },
1174
+ { url = "https://files.pythonhosted.org/packages/fe/9e/4af0056c258b861fbb29dcb36258de1e2b857be4a9509e6298abcf31e5c9/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:adb4167043d3a78280d5d05ce0ba22055c266cf8655ce942f2fb881262ff3cdf", size = 596016 },
1175
+ { url = "https://files.pythonhosted.org/packages/c5/fa/95d604b58aa375e781daf350897aaaa089cff59d84147e9ccff2447c8294/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c5701dc474b041e2934a26d31d39f90fac8a3dee2322b39f7729867f932b1d4", size = 476727 },
1176
+ { url = "https://files.pythonhosted.org/packages/65/95/fe479b2664f19be4cf5ceeb21be05afd491d95f142e72d26a42f41b7c4f8/watchfiles-1.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b067915e3c3936966a8607f6fe5487df0c9c4afb85226613b520890049deea20", size = 451864 },
1177
+ { url = "https://files.pythonhosted.org/packages/d3/8a/3c4af14b93a15ce55901cd7a92e1a4701910f1768c78fb30f61d2b79785b/watchfiles-1.1.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:9c733cda03b6d636b4219625a4acb5c6ffb10803338e437fb614fef9516825ef", size = 625626 },
1178
+ { url = "https://files.pythonhosted.org/packages/da/f5/cf6aa047d4d9e128f4b7cde615236a915673775ef171ff85971d698f3c2c/watchfiles-1.1.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:cc08ef8b90d78bfac66f0def80240b0197008e4852c9f285907377b2947ffdcb", size = 622744 },
1179
+ { url = "https://files.pythonhosted.org/packages/2c/00/70f75c47f05dea6fd30df90f047765f6fc2d6eb8b5a3921379b0b04defa2/watchfiles-1.1.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:9974d2f7dc561cce3bb88dfa8eb309dab64c729de85fba32e98d75cf24b66297", size = 402114 },
1180
+ { url = "https://files.pythonhosted.org/packages/53/03/acd69c48db4a1ed1de26b349d94077cca2238ff98fd64393f3e97484cae6/watchfiles-1.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c68e9f1fcb4d43798ad8814c4c1b61547b014b667216cb754e606bfade587018", size = 393879 },
1181
+ { url = "https://files.pythonhosted.org/packages/2f/c8/a9a2a6f9c8baa4eceae5887fecd421e1b7ce86802bcfc8b6a942e2add834/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95ab1594377effac17110e1352989bdd7bdfca9ff0e5eeccd8c69c5389b826d0", size = 450026 },
1182
+ { url = "https://files.pythonhosted.org/packages/fe/51/d572260d98388e6e2b967425c985e07d47ee6f62e6455cefb46a6e06eda5/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fba9b62da882c1be1280a7584ec4515d0a6006a94d6e5819730ec2eab60ffe12", size = 457917 },
1183
+ { url = "https://files.pythonhosted.org/packages/c6/2d/4258e52917bf9f12909b6ec314ff9636276f3542f9d3807d143f27309104/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3434e401f3ce0ed6b42569128b3d1e3af773d7ec18751b918b89cd49c14eaafb", size = 483602 },
1184
+ { url = "https://files.pythonhosted.org/packages/84/99/bee17a5f341a4345fe7b7972a475809af9e528deba056f8963d61ea49f75/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa257a4d0d21fcbca5b5fcba9dca5a78011cb93c0323fb8855c6d2dfbc76eb77", size = 596758 },
1185
+ { url = "https://files.pythonhosted.org/packages/40/76/e4bec1d59b25b89d2b0716b41b461ed655a9a53c60dc78ad5771fda5b3e6/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7fd1b3879a578a8ec2076c7961076df540b9af317123f84569f5a9ddee64ce92", size = 477601 },
1186
+ { url = "https://files.pythonhosted.org/packages/1f/fa/a514292956f4a9ce3c567ec0c13cce427c158e9f272062685a8a727d08fc/watchfiles-1.1.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62cc7a30eeb0e20ecc5f4bd113cd69dcdb745a07c68c0370cea919f373f65d9e", size = 451936 },
1187
+ { url = "https://files.pythonhosted.org/packages/32/5d/c3bf927ec3bbeb4566984eba8dd7a8eb69569400f5509904545576741f88/watchfiles-1.1.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:891c69e027748b4a73847335d208e374ce54ca3c335907d381fde4e41661b13b", size = 626243 },
1188
+ { url = "https://files.pythonhosted.org/packages/e6/65/6e12c042f1a68c556802a84d54bb06d35577c81e29fba14019562479159c/watchfiles-1.1.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:12fe8eaffaf0faa7906895b4f8bb88264035b3f0243275e0bf24af0436b27259", size = 623073 },
1189
+ { url = "https://files.pythonhosted.org/packages/89/ab/7f79d9bf57329e7cbb0a6fd4c7bd7d0cee1e4a8ef0041459f5409da3506c/watchfiles-1.1.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:bfe3c517c283e484843cb2e357dd57ba009cff351edf45fb455b5fbd1f45b15f", size = 400872 },
1190
+ { url = "https://files.pythonhosted.org/packages/df/d5/3f7bf9912798e9e6c516094db6b8932df53b223660c781ee37607030b6d3/watchfiles-1.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a9ccbf1f129480ed3044f540c0fdbc4ee556f7175e5ab40fe077ff6baf286d4e", size = 392877 },
1191
+ { url = "https://files.pythonhosted.org/packages/0d/c5/54ec7601a2798604e01c75294770dbee8150e81c6e471445d7601610b495/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba0e3255b0396cac3cc7bbace76404dd72b5438bf0d8e7cefa2f79a7f3649caa", size = 449645 },
1192
+ { url = "https://files.pythonhosted.org/packages/0a/04/c2f44afc3b2fce21ca0b7802cbd37ed90a29874f96069ed30a36dfe57c2b/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4281cd9fce9fc0a9dbf0fc1217f39bf9cf2b4d315d9626ef1d4e87b84699e7e8", size = 457424 },
1193
+ { url = "https://files.pythonhosted.org/packages/9f/b0/eec32cb6c14d248095261a04f290636da3df3119d4040ef91a4a50b29fa5/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6d2404af8db1329f9a3c9b79ff63e0ae7131986446901582067d9304ae8aaf7f", size = 481584 },
1194
+ { url = "https://files.pythonhosted.org/packages/d1/e2/ca4bb71c68a937d7145aa25709e4f5d68eb7698a25ce266e84b55d591bbd/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e78b6ed8165996013165eeabd875c5dfc19d41b54f94b40e9fff0eb3193e5e8e", size = 596675 },
1195
+ { url = "https://files.pythonhosted.org/packages/a1/dd/b0e4b7fb5acf783816bc950180a6cd7c6c1d2cf7e9372c0ea634e722712b/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:249590eb75ccc117f488e2fabd1bfa33c580e24b96f00658ad88e38844a040bb", size = 477363 },
1196
+ { url = "https://files.pythonhosted.org/packages/69/c4/088825b75489cb5b6a761a4542645718893d395d8c530b38734f19da44d2/watchfiles-1.1.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d05686b5487cfa2e2c28ff1aa370ea3e6c5accfe6435944ddea1e10d93872147", size = 452240 },
1197
+ { url = "https://files.pythonhosted.org/packages/10/8c/22b074814970eeef43b7c44df98c3e9667c1f7bf5b83e0ff0201b0bd43f9/watchfiles-1.1.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:d0e10e6f8f6dc5762adee7dece33b722282e1f59aa6a55da5d493a97282fedd8", size = 625607 },
1198
+ { url = "https://files.pythonhosted.org/packages/32/fa/a4f5c2046385492b2273213ef815bf71a0d4c1943b784fb904e184e30201/watchfiles-1.1.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:af06c863f152005c7592df1d6a7009c836a247c9d8adb78fef8575a5a98699db", size = 623315 },
1199
+ ]
1200
+
926
1201
  [[package]]
927
1202
  name = "wcwidth"
928
1203
  version = "0.2.13"
@@ -941,6 +1216,37 @@ wheels = [
941
1216
  { url = "https://files.pythonhosted.org/packages/5a/84/44687a29792a70e111c5c477230a72c4b957d88d16141199bf9acb7537a3/websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526", size = 58826 },
942
1217
  ]
943
1218
 
1219
+ [[package]]
1220
+ name = "websockets"
1221
+ version = "15.0.1"
1222
+ source = { registry = "https://pypi.org/simple" }
1223
+ sdist = { url = "https://files.pythonhosted.org/packages/21/e6/26d09fab466b7ca9c7737474c52be4f76a40301b08362eb2dbc19dcc16c1/websockets-15.0.1.tar.gz", hash = "sha256:82544de02076bafba038ce055ee6412d68da13ab47f0c60cab827346de828dee", size = 177016 }
1224
+ wheels = [
1225
+ { url = "https://files.pythonhosted.org/packages/51/6b/4545a0d843594f5d0771e86463606a3988b5a09ca5123136f8a76580dd63/websockets-15.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3e90baa811a5d73f3ca0bcbf32064d663ed81318ab225ee4f427ad4e26e5aff3", size = 175437 },
1226
+ { url = "https://files.pythonhosted.org/packages/f4/71/809a0f5f6a06522af902e0f2ea2757f71ead94610010cf570ab5c98e99ed/websockets-15.0.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:592f1a9fe869c778694f0aa806ba0374e97648ab57936f092fd9d87f8bc03665", size = 173096 },
1227
+ { url = "https://files.pythonhosted.org/packages/3d/69/1a681dd6f02180916f116894181eab8b2e25b31e484c5d0eae637ec01f7c/websockets-15.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0701bc3cfcb9164d04a14b149fd74be7347a530ad3bbf15ab2c678a2cd3dd9a2", size = 173332 },
1228
+ { url = "https://files.pythonhosted.org/packages/a6/02/0073b3952f5bce97eafbb35757f8d0d54812b6174ed8dd952aa08429bcc3/websockets-15.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8b56bdcdb4505c8078cb6c7157d9811a85790f2f2b3632c7d1462ab5783d215", size = 183152 },
1229
+ { url = "https://files.pythonhosted.org/packages/74/45/c205c8480eafd114b428284840da0b1be9ffd0e4f87338dc95dc6ff961a1/websockets-15.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0af68c55afbd5f07986df82831c7bff04846928ea8d1fd7f30052638788bc9b5", size = 182096 },
1230
+ { url = "https://files.pythonhosted.org/packages/14/8f/aa61f528fba38578ec553c145857a181384c72b98156f858ca5c8e82d9d3/websockets-15.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64dee438fed052b52e4f98f76c5790513235efaa1ef7f3f2192c392cd7c91b65", size = 182523 },
1231
+ { url = "https://files.pythonhosted.org/packages/ec/6d/0267396610add5bc0d0d3e77f546d4cd287200804fe02323797de77dbce9/websockets-15.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5f6b181bb38171a8ad1d6aa58a67a6aa9d4b38d0f8c5f496b9e42561dfc62fe", size = 182790 },
1232
+ { url = "https://files.pythonhosted.org/packages/02/05/c68c5adbf679cf610ae2f74a9b871ae84564462955d991178f95a1ddb7dd/websockets-15.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:5d54b09eba2bada6011aea5375542a157637b91029687eb4fdb2dab11059c1b4", size = 182165 },
1233
+ { url = "https://files.pythonhosted.org/packages/29/93/bb672df7b2f5faac89761cb5fa34f5cec45a4026c383a4b5761c6cea5c16/websockets-15.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3be571a8b5afed347da347bfcf27ba12b069d9d7f42cb8c7028b5e98bbb12597", size = 182160 },
1234
+ { url = "https://files.pythonhosted.org/packages/ff/83/de1f7709376dc3ca9b7eeb4b9a07b4526b14876b6d372a4dc62312bebee0/websockets-15.0.1-cp312-cp312-win32.whl", hash = "sha256:c338ffa0520bdb12fbc527265235639fb76e7bc7faafbb93f6ba80d9c06578a9", size = 176395 },
1235
+ { url = "https://files.pythonhosted.org/packages/7d/71/abf2ebc3bbfa40f391ce1428c7168fb20582d0ff57019b69ea20fa698043/websockets-15.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcd5cf9e305d7b8338754470cf69cf81f420459dbae8a3b40cee57417f4614a7", size = 176841 },
1236
+ { url = "https://files.pythonhosted.org/packages/cb/9f/51f0cf64471a9d2b4d0fc6c534f323b664e7095640c34562f5182e5a7195/websockets-15.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee443ef070bb3b6ed74514f5efaa37a252af57c90eb33b956d35c8e9c10a1931", size = 175440 },
1237
+ { url = "https://files.pythonhosted.org/packages/8a/05/aa116ec9943c718905997412c5989f7ed671bc0188ee2ba89520e8765d7b/websockets-15.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5a939de6b7b4e18ca683218320fc67ea886038265fd1ed30173f5ce3f8e85675", size = 173098 },
1238
+ { url = "https://files.pythonhosted.org/packages/ff/0b/33cef55ff24f2d92924923c99926dcce78e7bd922d649467f0eda8368923/websockets-15.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:746ee8dba912cd6fc889a8147168991d50ed70447bf18bcda7039f7d2e3d9151", size = 173329 },
1239
+ { url = "https://files.pythonhosted.org/packages/31/1d/063b25dcc01faa8fada1469bdf769de3768b7044eac9d41f734fd7b6ad6d/websockets-15.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:595b6c3969023ecf9041b2936ac3827e4623bfa3ccf007575f04c5a6aa318c22", size = 183111 },
1240
+ { url = "https://files.pythonhosted.org/packages/93/53/9a87ee494a51bf63e4ec9241c1ccc4f7c2f45fff85d5bde2ff74fcb68b9e/websockets-15.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c714d2fc58b5ca3e285461a4cc0c9a66bd0e24c5da9911e30158286c9b5be7f", size = 182054 },
1241
+ { url = "https://files.pythonhosted.org/packages/ff/b2/83a6ddf56cdcbad4e3d841fcc55d6ba7d19aeb89c50f24dd7e859ec0805f/websockets-15.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f3c1e2ab208db911594ae5b4f79addeb3501604a165019dd221c0bdcabe4db8", size = 182496 },
1242
+ { url = "https://files.pythonhosted.org/packages/98/41/e7038944ed0abf34c45aa4635ba28136f06052e08fc2168520bb8b25149f/websockets-15.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:229cf1d3ca6c1804400b0a9790dc66528e08a6a1feec0d5040e8b9eb14422375", size = 182829 },
1243
+ { url = "https://files.pythonhosted.org/packages/e0/17/de15b6158680c7623c6ef0db361da965ab25d813ae54fcfeae2e5b9ef910/websockets-15.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:756c56e867a90fb00177d530dca4b097dd753cde348448a1012ed6c5131f8b7d", size = 182217 },
1244
+ { url = "https://files.pythonhosted.org/packages/33/2b/1f168cb6041853eef0362fb9554c3824367c5560cbdaad89ac40f8c2edfc/websockets-15.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:558d023b3df0bffe50a04e710bc87742de35060580a293c2a984299ed83bc4e4", size = 182195 },
1245
+ { url = "https://files.pythonhosted.org/packages/86/eb/20b6cdf273913d0ad05a6a14aed4b9a85591c18a987a3d47f20fa13dcc47/websockets-15.0.1-cp313-cp313-win32.whl", hash = "sha256:ba9e56e8ceeeedb2e080147ba85ffcd5cd0711b89576b83784d8605a7df455fa", size = 176393 },
1246
+ { url = "https://files.pythonhosted.org/packages/1b/6c/c65773d6cab416a64d191d6ee8a8b1c68a09970ea6909d16965d26bfed1e/websockets-15.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:e09473f095a819042ecb2ab9465aee615bd9c2028e4ef7d933600a8401c79561", size = 176837 },
1247
+ { url = "https://files.pythonhosted.org/packages/fa/a8/5b41e0da817d64113292ab1f8247140aac61cbf6cfd085d6a0fa77f4984f/websockets-15.0.1-py3-none-any.whl", hash = "sha256:f7a866fbc1e97b5c617ee4116daaa09b722101d4a3c170c787450ba409f9736f", size = 169743 },
1248
+ ]
1249
+
944
1250
  [[package]]
945
1251
  name = "wsproto"
946
1252
  version = "1.2.0"
File without changes
File without changes
File without changes
File without changes
File without changes