lkr-dev-cli 0.0.33__tar.gz → 0.0.34__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.
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/.github/workflows/release.yml +6 -0
- lkr_dev_cli-0.0.34/Makefile +26 -0
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/PKG-INFO +25 -17
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/README.md +4 -0
- lkr_dev_cli-0.0.34/codemode.md +51 -0
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/lkr/auth/main.py +15 -9
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/lkr/auth_service.py +19 -2
- lkr_dev_cli-0.0.34/lkr/codemode/__init__.py +3 -0
- lkr_dev_cli-0.0.34/lkr/codemode/main.py +104 -0
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/lkr/main.py +1 -0
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/lkr.md +30 -0
- lkr_dev_cli-0.0.34/pyproject.toml +67 -0
- lkr_dev_cli-0.0.34/tests/test_codemode.py +18 -0
- lkr_dev_cli-0.0.34/tests/test_codemode2.py +26 -0
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/uv.lock +80 -37
- lkr_dev_cli-0.0.33/Makefile +0 -7
- lkr_dev_cli-0.0.33/pyproject.toml +0 -56
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/.github/workflows/test-dependencies.yml +0 -0
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/.gitignore +0 -0
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/.python-version +0 -0
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/.vscode/launch.json +0 -0
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/.vscode/settings.json +0 -0
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/Dockerfile +0 -0
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/LICENSE +0 -0
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/cloudbuild.yaml +0 -0
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/lkr/__init__.py +0 -0
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/lkr/auth/__init__.py +0 -0
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/lkr/auth/oauth.py +0 -0
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/lkr/classes.py +0 -0
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/lkr/constants.py +0 -0
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/lkr/custom_types.py +0 -0
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/lkr/exceptions.py +0 -0
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/lkr/logger.py +0 -0
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/lkr/mcp/classes.py +0 -0
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/lkr/mcp/main.py +0 -0
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/lkr/mcp/utils.py +0 -0
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/lkr/observability/classes.py +0 -0
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/lkr/observability/embed_container.html +0 -0
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/lkr/observability/main.py +0 -0
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/lkr/observability/utils.py +0 -0
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/lkr/tools/classes.py +0 -0
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/lkr/tools/main.py +0 -0
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/lkr/tools/permission_deprecation.py +0 -0
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/tests/TESTING.md +0 -0
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/tests/test_dependency_resolution.py +0 -0
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/tests/test_deps.sh +0 -0
- {lkr_dev_cli-0.0.33 → lkr_dev_cli-0.0.34}/tests/test_permission_deprecation.py +0 -0
|
@@ -52,6 +52,12 @@ jobs:
|
|
|
52
52
|
${{ env.IMAGE_NAME }}:latest \
|
|
53
53
|
--project ${{ secrets.GCP_PROJECT_ID }}
|
|
54
54
|
|
|
55
|
+
- name: Trigger Website Deploy
|
|
56
|
+
run: |
|
|
57
|
+
curl -fSL "$DEPLOY_URL"
|
|
58
|
+
env:
|
|
59
|
+
DEPLOY_URL: ${{ secrets.DEPLOY_URL }}
|
|
60
|
+
|
|
55
61
|
publish:
|
|
56
62
|
name: python
|
|
57
63
|
runs-on: ubuntu-latest
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
.PHONY: docs test-deps codemode-test codemode-test2 codemode-start
|
|
2
|
+
|
|
3
|
+
docs:
|
|
4
|
+
uv run typer lkr/main.py utils docs --output lkr.md
|
|
5
|
+
|
|
6
|
+
test-deps:
|
|
7
|
+
python tests/test_dependency_resolution.py
|
|
8
|
+
|
|
9
|
+
codemode-test:
|
|
10
|
+
uv run python tests/test_codemode.py
|
|
11
|
+
|
|
12
|
+
codemode-test2:
|
|
13
|
+
uv run python tests/test_codemode2.py
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
codemode-start:
|
|
17
|
+
@echo "Add this to your mcpServers config:"
|
|
18
|
+
@echo "{"
|
|
19
|
+
@echo " \"mcpServers\": {"
|
|
20
|
+
@echo " \"lkr-codemode\": {"
|
|
21
|
+
@echo " \"command\": \"uvx\","
|
|
22
|
+
@echo " \"args\": [\"--from\", \"lkr-dev-cli[codemode]\", \"lkr\", \"code-mode\", \"run\"]"
|
|
23
|
+
@echo " }"
|
|
24
|
+
@echo " }"
|
|
25
|
+
@echo "}"
|
|
26
|
+
uv run lkr code-mode run
|
|
@@ -1,33 +1,37 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lkr-dev-cli
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.34
|
|
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>=
|
|
10
|
-
Requires-Dist: looker-sdk>=25.
|
|
11
|
-
Requires-Dist: pydantic>=2.11.
|
|
9
|
+
Requires-Dist: cryptography>=45.0.4
|
|
10
|
+
Requires-Dist: looker-sdk>=25.10.0
|
|
11
|
+
Requires-Dist: pydantic>=2.11.7
|
|
12
12
|
Requires-Dist: pydash>=8.0.5
|
|
13
13
|
Requires-Dist: questionary>=2.1.0
|
|
14
|
-
Requires-Dist: requests>=2.
|
|
15
|
-
Requires-Dist: structlog>=25.
|
|
16
|
-
Requires-Dist: typer>=0.
|
|
14
|
+
Requires-Dist: requests>=2.32.4
|
|
15
|
+
Requires-Dist: structlog>=25.4.0
|
|
16
|
+
Requires-Dist: typer>=0.16.0
|
|
17
17
|
Provides-Extra: all
|
|
18
|
-
Requires-Dist: duckdb>=1.
|
|
19
|
-
Requires-Dist: fastapi[standard]>=0.115.
|
|
20
|
-
Requires-Dist: mcp[cli]>=1.
|
|
21
|
-
Requires-Dist:
|
|
18
|
+
Requires-Dist: duckdb>=1.3.1; extra == 'all'
|
|
19
|
+
Requires-Dist: fastapi[standard]>=0.115.14; extra == 'all'
|
|
20
|
+
Requires-Dist: mcp[cli]>=1.10.1; extra == 'all'
|
|
21
|
+
Requires-Dist: pydantic-monty; extra == 'all'
|
|
22
|
+
Requires-Dist: selenium>=4.34.0; extra == 'all'
|
|
23
|
+
Provides-Extra: codemode
|
|
24
|
+
Requires-Dist: mcp[cli]>=1.10.1; extra == 'codemode'
|
|
25
|
+
Requires-Dist: pydantic-monty; extra == 'codemode'
|
|
22
26
|
Provides-Extra: mcp
|
|
23
|
-
Requires-Dist: duckdb>=1.
|
|
24
|
-
Requires-Dist: fastapi[standard]>=0.115.
|
|
25
|
-
Requires-Dist: mcp[cli]>=1.
|
|
27
|
+
Requires-Dist: duckdb>=1.3.1; extra == 'mcp'
|
|
28
|
+
Requires-Dist: fastapi[standard]>=0.115.14; extra == 'mcp'
|
|
29
|
+
Requires-Dist: mcp[cli]>=1.10.1; extra == 'mcp'
|
|
26
30
|
Provides-Extra: observability
|
|
27
|
-
Requires-Dist: fastapi[standard]>=0.115.
|
|
28
|
-
Requires-Dist: selenium>=4.
|
|
31
|
+
Requires-Dist: fastapi[standard]>=0.115.14; extra == 'observability'
|
|
32
|
+
Requires-Dist: selenium>=4.34.0; extra == 'observability'
|
|
29
33
|
Provides-Extra: tools
|
|
30
|
-
Requires-Dist: fastapi[standard]>=0.115.
|
|
34
|
+
Requires-Dist: fastapi[standard]>=0.115.14; extra == 'tools'
|
|
31
35
|
Description-Content-Type: text/markdown
|
|
32
36
|
|
|
33
37
|
# lkr cli
|
|
@@ -105,6 +109,10 @@ Go to the Looker API Explorer for Register OAuth App (https://your.looker.instan
|
|
|
105
109
|
- This only needs to be done once per instance
|
|
106
110
|
|
|
107
111
|
|
|
112
|
+
## Code-Mode
|
|
113
|
+
|
|
114
|
+
Execute Python code safely with full Looker SDK coverage within a secure sandbox environment. Constructed as an MCP tool, it dynamically inspects the Looker SDK for all public methods and injects them into the Monty sandbox safely. For detailed options, safe primitives transformations, and PKCE configurations, view the full [Code-Mode Docs](./codemode.md).
|
|
115
|
+
|
|
108
116
|
## MCP
|
|
109
117
|
Built into the `lkr` is an MCP server. Right now its tools are based on helping you work within an IDE. To use it a tool like [Cursor](https://www.cursor.com/), add this to your mcp.json
|
|
110
118
|
|
|
@@ -73,6 +73,10 @@ Go to the Looker API Explorer for Register OAuth App (https://your.looker.instan
|
|
|
73
73
|
- This only needs to be done once per instance
|
|
74
74
|
|
|
75
75
|
|
|
76
|
+
## Code-Mode
|
|
77
|
+
|
|
78
|
+
Execute Python code safely with full Looker SDK coverage within a secure sandbox environment. Constructed as an MCP tool, it dynamically inspects the Looker SDK for all public methods and injects them into the Monty sandbox safely. For detailed options, safe primitives transformations, and PKCE configurations, view the full [Code-Mode Docs](./codemode.md).
|
|
79
|
+
|
|
76
80
|
## MCP
|
|
77
81
|
Built into the `lkr` is an MCP server. Right now its tools are based on helping you work within an IDE. To use it a tool like [Cursor](https://www.cursor.com/), add this to your mcp.json
|
|
78
82
|
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Looker Code-Mode MCP Server
|
|
2
|
+
|
|
3
|
+
`lkr code-mode` allows you to invoke a Python-based Model Context Protocol (MCP) server. It offers an AI agent the unique capacity to batched-execute Python commands securely within the Monty sandbox against your active Looker instance!
|
|
4
|
+
|
|
5
|
+
## How It Works
|
|
6
|
+
|
|
7
|
+
Instead of dumping hundreds of explicit tool declarations onto your AI agent (token bloat), `lkr code-mode` exposes **exactly one tool**: `run_python_code(code: str)`.
|
|
8
|
+
|
|
9
|
+
The tool instantiates Looker SDK natively, searches all bound methods, and passes them safely onto Monty's environment as global functions. When the LLM writes standard Python code (e.g., `me()`, `folder(id)`), Monty will process it correctly locally!
|
|
10
|
+
|
|
11
|
+
### Key Features:
|
|
12
|
+
- **100% Tool Coverage:** Accesses all Looker SDK public operations smoothly without token limits.
|
|
13
|
+
- **Recursive Translation:** Complex Looker models like User, Folder, Dashboard get string-converted into dictionaries immediately before ingesting them into Monty.
|
|
14
|
+
- **Automatic PKCE Restarter:** Caught an invalid token? Code-Mode immediately catches `InvalidRefreshTokenError` and safely opens up your PKCE authentication browser automatically.
|
|
15
|
+
- **Extremely Secure:** Monty interpreter ensures isolated sandbox processing. No local filesystem accesses are exposed.
|
|
16
|
+
|
|
17
|
+
## Continuous Usage
|
|
18
|
+
|
|
19
|
+
### 1. Starting the Server
|
|
20
|
+
To immediately trigger the stdio listener, use:
|
|
21
|
+
```bash
|
|
22
|
+
uvx --from lkr-dev-cli[codemode] lkr code-mode run
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### 2. Client Configuration
|
|
26
|
+
To hook this server into Cursor or Claude Desktop natively over stdio, append the following onto your `mcpServers` configuration JSON. Make sure to pass your Looker instance credentials as environment variables (see standard API requirements in [README.md](./README.md#using-api-key)):
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"mcpServers": {
|
|
31
|
+
"looker-codemode": {
|
|
32
|
+
"command": "uvx",
|
|
33
|
+
"args": ["--from", "lkr-dev-cli[codemode]", "lkr", "code-mode", "run"],
|
|
34
|
+
"env": {
|
|
35
|
+
"LOOKERSDK_BASE_URL": "https://your.looker.instance",
|
|
36
|
+
"LOOKERSDK_CLIENT_ID": "your-client-id",
|
|
37
|
+
"LOOKERSDK_CLIENT_SECRET": "your-client-secret"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 3. Visual Inspector
|
|
45
|
+
To check things out on a web panel:
|
|
46
|
+
```bash
|
|
47
|
+
npx @modelcontextprotocol/inspector uvx --from lkr-dev-cli[codemode] lkr code-mode run
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
|
|
@@ -191,16 +191,22 @@ def whoami(ctx: typer.Context):
|
|
|
191
191
|
Check current authentication
|
|
192
192
|
"""
|
|
193
193
|
auth = get_auth(ctx)
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
194
|
+
try:
|
|
195
|
+
sdk = auth.get_current_sdk(prompt_refresh_invalid_token=True)
|
|
196
|
+
if not sdk:
|
|
197
|
+
logger.error(
|
|
198
|
+
"Not currently authenticated - use `lkr auth login` or `lkr auth switch` to authenticate"
|
|
199
|
+
)
|
|
200
|
+
raise typer.Exit(1)
|
|
201
|
+
user = sdk.me()
|
|
202
|
+
logger.info(
|
|
203
|
+
f"Currently authenticated as {user.first_name} {user.last_name} ({user.email}) to {sdk.auth.settings.base_url}"
|
|
198
204
|
)
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
205
|
+
except Exception as e:
|
|
206
|
+
if "invalid_grant" in str(e) or "token expired" in str(e):
|
|
207
|
+
logger.error("Your Looker OAuth session has expired. Please run 'lkr auth login' to re-authenticate.")
|
|
208
|
+
raise typer.Exit(1)
|
|
209
|
+
raise e
|
|
204
210
|
|
|
205
211
|
|
|
206
212
|
@group.command()
|
|
@@ -23,7 +23,12 @@ from lkr.constants import LOOKER_API_VERSION, OAUTH_CLIENT_ID, OAUTH_REDIRECT_UR
|
|
|
23
23
|
from lkr.custom_types import NewTokenCallback
|
|
24
24
|
from lkr.logger import logger
|
|
25
25
|
|
|
26
|
-
__all__ = ["get_auth", "ApiKeyAuthSession", "DbOAuthSession"]
|
|
26
|
+
__all__ = ["get_auth", "ApiKeyAuthSession", "DbOAuthSession", "is_auth_expired"]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def is_auth_expired(e: Exception) -> bool:
|
|
30
|
+
return "invalid_grant" in str(e) or "token expired" in str(e)
|
|
31
|
+
|
|
27
32
|
|
|
28
33
|
|
|
29
34
|
def get_auth(ctx: Union["typer.Context", LkrCtxObj]) -> Union["SqlLiteAuth", "ApiKeyAuth"]:
|
|
@@ -465,11 +470,23 @@ class SqlLiteAuth:
|
|
|
465
470
|
def refresh_current_token(token: Union[AccessToken, AuthToken]):
|
|
466
471
|
current_auth.set_token(self.conn, new_token=token, commit=True)
|
|
467
472
|
|
|
468
|
-
|
|
473
|
+
sdk = init_oauth_sdk(
|
|
469
474
|
current_auth.base_url,
|
|
470
475
|
new_token_callback=refresh_current_token,
|
|
471
476
|
access_token=current_auth.to_access_token(),
|
|
472
477
|
)
|
|
478
|
+
if prompt_refresh_invalid_token:
|
|
479
|
+
import sys
|
|
480
|
+
try:
|
|
481
|
+
sdk.auth.authenticate({})
|
|
482
|
+
except Exception as e:
|
|
483
|
+
if is_auth_expired(e):
|
|
484
|
+
if sys.stdin.isatty():
|
|
485
|
+
self._cli_confirm_refresh_token(current_auth, quiet=False)
|
|
486
|
+
return self.get_current_sdk(prompt_refresh_invalid_token=False)
|
|
487
|
+
raise e
|
|
488
|
+
|
|
489
|
+
return sdk
|
|
473
490
|
|
|
474
491
|
else:
|
|
475
492
|
logger.error("No current instance found, please login")
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
import typer
|
|
5
|
+
import pydantic_monty
|
|
6
|
+
from mcp.server.fastmcp import FastMCP
|
|
7
|
+
|
|
8
|
+
from lkr.auth_service import get_auth, is_auth_expired
|
|
9
|
+
from lkr.classes import LkrCtxObj
|
|
10
|
+
from lkr.logger import logger
|
|
11
|
+
|
|
12
|
+
__all__ = ["group"]
|
|
13
|
+
|
|
14
|
+
mcp = FastMCP("lkr:codemode")
|
|
15
|
+
group = typer.Typer()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def get_mcp_sdk(ctx: LkrCtxObj):
|
|
20
|
+
sdk = get_auth(ctx).get_current_sdk(prompt_refresh_invalid_token=True)
|
|
21
|
+
sdk.auth.settings.agent_tag += "-codemode"
|
|
22
|
+
return sdk
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def to_primitive(obj):
|
|
27
|
+
seen = set()
|
|
28
|
+
|
|
29
|
+
def _to_primitive(o):
|
|
30
|
+
if isinstance(o, (str, int, float, bool, type(None))):
|
|
31
|
+
return o
|
|
32
|
+
|
|
33
|
+
obj_id = id(o)
|
|
34
|
+
if obj_id in seen:
|
|
35
|
+
return f"<Circular reference to {type(o).__name__}>"
|
|
36
|
+
seen.add(obj_id)
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
if isinstance(o, list):
|
|
40
|
+
return [_to_primitive(item) for item in o]
|
|
41
|
+
elif isinstance(o, dict):
|
|
42
|
+
return {k: _to_primitive(v) for k, v in o.items()}
|
|
43
|
+
else:
|
|
44
|
+
try:
|
|
45
|
+
return _to_primitive(vars(o))
|
|
46
|
+
except TypeError:
|
|
47
|
+
return str(o)
|
|
48
|
+
except Exception:
|
|
49
|
+
return str(o)
|
|
50
|
+
finally:
|
|
51
|
+
seen.remove(obj_id)
|
|
52
|
+
|
|
53
|
+
return _to_primitive(obj)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@mcp.tool()
|
|
58
|
+
def run_python_code(code: str) -> str:
|
|
59
|
+
"""
|
|
60
|
+
Execute Python code safely with access to all Looker SDK methods as global functions.
|
|
61
|
+
Capture the result and any print outputs.
|
|
62
|
+
"""
|
|
63
|
+
try:
|
|
64
|
+
ctx = LkrCtxObj(force_oauth=False)
|
|
65
|
+
sdk = get_mcp_sdk(ctx)
|
|
66
|
+
|
|
67
|
+
external_funcs = {}
|
|
68
|
+
for name, method in inspect.getmembers(sdk, predicate=inspect.ismethod):
|
|
69
|
+
if not name.startswith('_'):
|
|
70
|
+
# Wrap in a lambda to recursively convert output to primitives
|
|
71
|
+
def make_wrapper(m):
|
|
72
|
+
def wrapper(*args, **kwargs):
|
|
73
|
+
res = m(*args, **kwargs)
|
|
74
|
+
return to_primitive(res)
|
|
75
|
+
return wrapper
|
|
76
|
+
external_funcs[name] = make_wrapper(method)
|
|
77
|
+
|
|
78
|
+
m = pydantic_monty.Monty(code)
|
|
79
|
+
result = m.run(external_functions=external_funcs)
|
|
80
|
+
|
|
81
|
+
# Monty run returns a MontyComplete or None
|
|
82
|
+
output = str(getattr(result, "output", "")) if result is not None else ""
|
|
83
|
+
|
|
84
|
+
# Try to append captured stdout if available on the object
|
|
85
|
+
stdout = getattr(result, "stdout", None) if result is not None else None
|
|
86
|
+
if stdout:
|
|
87
|
+
return f"Output:\n{output}\nStdout:\n{stdout}"
|
|
88
|
+
return output
|
|
89
|
+
except Exception as e:
|
|
90
|
+
logger.error(f"Error executing Monty: {e}")
|
|
91
|
+
if is_auth_expired(e):
|
|
92
|
+
return "Error: Your Looker OAuth session has expired. Please run 'lkr auth login' to re-authenticate."
|
|
93
|
+
return f"Error: {str(e)}"
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@group.command(name="run")
|
|
97
|
+
def run(
|
|
98
|
+
ctx: typer.Context,
|
|
99
|
+
debug: bool = typer.Option(False, help="Debug mode"),
|
|
100
|
+
):
|
|
101
|
+
mcp.run()
|
|
102
|
+
|
|
103
|
+
if __name__ == "__main__":
|
|
104
|
+
mcp.run("sse")
|
|
@@ -42,6 +42,7 @@ def add_optional_typer_group(app, import_path, group_name, extra_message=None):
|
|
|
42
42
|
add_optional_typer_group(app, "lkr.mcp.main.group", "mcp")
|
|
43
43
|
add_optional_typer_group(app, "lkr.observability.main.group", "observability")
|
|
44
44
|
add_optional_typer_group(app, "lkr.tools.main.group", "tools")
|
|
45
|
+
add_optional_typer_group(app, "lkr.codemode.main.group", "code-mode")
|
|
45
46
|
|
|
46
47
|
@app.callback()
|
|
47
48
|
def callback(
|
|
@@ -27,6 +27,7 @@ $ lkr [OPTIONS] COMMAND [ARGS]...
|
|
|
27
27
|
* `mcp`
|
|
28
28
|
* `observability`
|
|
29
29
|
* `tools`
|
|
30
|
+
* `code-mode`
|
|
30
31
|
|
|
31
32
|
## `lkr auth`
|
|
32
33
|
|
|
@@ -221,3 +222,32 @@ $ lkr tools schedule-download-deprecation [OPTIONS]
|
|
|
221
222
|
* `--unfiltered`: Show all rows, including those with no missing permissions
|
|
222
223
|
* `--email`: Use Email instead of Name
|
|
223
224
|
* `--help`: Show this message and exit.
|
|
225
|
+
|
|
226
|
+
## `lkr code-mode`
|
|
227
|
+
|
|
228
|
+
**Usage**:
|
|
229
|
+
|
|
230
|
+
```console
|
|
231
|
+
$ lkr code-mode [OPTIONS] COMMAND [ARGS]...
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
**Options**:
|
|
235
|
+
|
|
236
|
+
* `--help`: Show this message and exit.
|
|
237
|
+
|
|
238
|
+
**Commands**:
|
|
239
|
+
|
|
240
|
+
* `run`
|
|
241
|
+
|
|
242
|
+
### `lkr code-mode run`
|
|
243
|
+
|
|
244
|
+
**Usage**:
|
|
245
|
+
|
|
246
|
+
```console
|
|
247
|
+
$ lkr code-mode run [OPTIONS]
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
**Options**:
|
|
251
|
+
|
|
252
|
+
* `--debug / --no-debug`: Debug mode [default: no-debug]
|
|
253
|
+
* `--help`: Show this message and exit.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "lkr-dev-cli"
|
|
3
|
+
version = "0.0.34"
|
|
4
|
+
description = "lkr: a command line interface for looker"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
authors = [
|
|
8
|
+
{ name = "bwebs"}
|
|
9
|
+
]
|
|
10
|
+
requires-python = ">=3.12"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"looker-sdk>=25.10.0",
|
|
13
|
+
"pydantic>=2.11.7",
|
|
14
|
+
"pydash>=8.0.5",
|
|
15
|
+
"typer>=0.16.0",
|
|
16
|
+
"requests>=2.32.4",
|
|
17
|
+
"cryptography>=45.0.4",
|
|
18
|
+
"structlog>=25.4.0",
|
|
19
|
+
"questionary>=2.1.0",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[project.optional-dependencies]
|
|
23
|
+
mcp = [
|
|
24
|
+
"mcp[cli]>=1.10.1",
|
|
25
|
+
"duckdb>=1.3.1",
|
|
26
|
+
"fastapi[standard]>=0.115.14"
|
|
27
|
+
]
|
|
28
|
+
codemode = [
|
|
29
|
+
"pydantic-monty",
|
|
30
|
+
"mcp[cli]>=1.10.1"
|
|
31
|
+
]
|
|
32
|
+
observability = [
|
|
33
|
+
"fastapi[standard]>=0.115.14",
|
|
34
|
+
"selenium>=4.34.0"
|
|
35
|
+
]
|
|
36
|
+
tools = [
|
|
37
|
+
"fastapi[standard]>=0.115.14"
|
|
38
|
+
]
|
|
39
|
+
all = [
|
|
40
|
+
"mcp[cli]>=1.10.1",
|
|
41
|
+
"fastapi[standard]>=0.115.14",
|
|
42
|
+
"selenium>=4.34.0",
|
|
43
|
+
"duckdb>=1.3.1",
|
|
44
|
+
"pydantic-monty"
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
[project.scripts]
|
|
48
|
+
lkr = "lkr.main:app"
|
|
49
|
+
|
|
50
|
+
[build-system]
|
|
51
|
+
requires = ["hatchling"]
|
|
52
|
+
build-backend = "hatchling.build"
|
|
53
|
+
|
|
54
|
+
[dependency-groups]
|
|
55
|
+
dev = [
|
|
56
|
+
"pytest>=8.4.1",
|
|
57
|
+
"ruff>=0.12.1",
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
[tool.hatch.build.targets.wheel]
|
|
61
|
+
packages = ["lkr"]
|
|
62
|
+
|
|
63
|
+
[[tool.uv.index]]
|
|
64
|
+
name = "pypi"
|
|
65
|
+
url = "https://pypi.org/simple"
|
|
66
|
+
default = true
|
|
67
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from lkr.codemode.main import run_python_code
|
|
2
|
+
|
|
3
|
+
code = """
|
|
4
|
+
me_obj = me()
|
|
5
|
+
print("Name:", me_obj["first_name"], me_obj["last_name"])
|
|
6
|
+
personal_folder = folder(me_obj["personal_folder_id"])
|
|
7
|
+
print("Folders:")
|
|
8
|
+
for f in folder_children(personal_folder["id"]):
|
|
9
|
+
print(" - " + f["name"])
|
|
10
|
+
print("Dashboards:")
|
|
11
|
+
for d in personal_folder["dashboards"]:
|
|
12
|
+
print(" - " + d["title"])
|
|
13
|
+
print("Looks:")
|
|
14
|
+
for l in personal_folder["looks"]:
|
|
15
|
+
print(" - " + l["title"])
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
print(run_python_code(code))
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from lkr.codemode.main import run_python_code
|
|
2
|
+
|
|
3
|
+
code = """
|
|
4
|
+
me_obj = me()
|
|
5
|
+
personal_folder_id = me_obj["personal_folder_id"]
|
|
6
|
+
|
|
7
|
+
def print_folder(folder_id, indent):
|
|
8
|
+
f = folder(folder_id)
|
|
9
|
+
print(indent + "+ Folder: " + f["name"])
|
|
10
|
+
|
|
11
|
+
if "dashboards" in f and f["dashboards"]:
|
|
12
|
+
for d in f["dashboards"]:
|
|
13
|
+
print(indent + " - Dashboard: " + d["title"])
|
|
14
|
+
|
|
15
|
+
if "looks" in f and f["looks"]:
|
|
16
|
+
for l in f["looks"]:
|
|
17
|
+
print(indent + " - Look: " + l["title"])
|
|
18
|
+
|
|
19
|
+
children = folder_children(folder_id)
|
|
20
|
+
for child in children:
|
|
21
|
+
print_folder(child["id"], indent + " ")
|
|
22
|
+
|
|
23
|
+
print_folder(personal_folder_id, "")
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
print(run_python_code(code))
|
|
@@ -397,29 +397,27 @@ name = "lkr-dev-cli"
|
|
|
397
397
|
version = "0.0.0"
|
|
398
398
|
source = { editable = "." }
|
|
399
399
|
dependencies = [
|
|
400
|
+
{ name = "cryptography" },
|
|
400
401
|
{ name = "looker-sdk" },
|
|
401
402
|
{ name = "pydantic" },
|
|
402
403
|
{ name = "pydash" },
|
|
404
|
+
{ name = "questionary" },
|
|
405
|
+
{ name = "requests" },
|
|
406
|
+
{ name = "structlog" },
|
|
407
|
+
{ name = "typer" },
|
|
403
408
|
]
|
|
404
409
|
|
|
405
410
|
[package.optional-dependencies]
|
|
406
411
|
all = [
|
|
407
|
-
{ name = "cryptography" },
|
|
408
412
|
{ name = "duckdb" },
|
|
409
413
|
{ name = "fastapi", extra = ["standard"] },
|
|
410
414
|
{ name = "mcp", extra = ["cli"] },
|
|
411
|
-
{ name = "
|
|
412
|
-
{ name = "requests" },
|
|
415
|
+
{ name = "pydantic-monty" },
|
|
413
416
|
{ name = "selenium" },
|
|
414
|
-
{ name = "structlog" },
|
|
415
|
-
{ name = "typer" },
|
|
416
417
|
]
|
|
417
|
-
|
|
418
|
-
{ name = "
|
|
419
|
-
{ name = "
|
|
420
|
-
{ name = "requests" },
|
|
421
|
-
{ name = "structlog" },
|
|
422
|
-
{ name = "typer" },
|
|
418
|
+
codemode = [
|
|
419
|
+
{ name = "mcp", extra = ["cli"] },
|
|
420
|
+
{ name = "pydantic-monty" },
|
|
423
421
|
]
|
|
424
422
|
mcp = [
|
|
425
423
|
{ name = "duckdb" },
|
|
@@ -442,36 +440,34 @@ dev = [
|
|
|
442
440
|
|
|
443
441
|
[package.metadata]
|
|
444
442
|
requires-dist = [
|
|
445
|
-
{ name = "cryptography",
|
|
446
|
-
{ name = "
|
|
447
|
-
{ name = "duckdb", marker = "extra == '
|
|
448
|
-
{ name = "
|
|
449
|
-
{ name = "fastapi", extras = ["standard"], marker = "extra == '
|
|
450
|
-
{ name = "fastapi", extras = ["standard"], marker = "extra == '
|
|
451
|
-
{ name = "fastapi", extras = ["standard"], marker = "extra == '
|
|
452
|
-
{ name = "
|
|
453
|
-
{ name = "
|
|
454
|
-
{ name = "mcp", extras = ["cli"], marker = "extra == '
|
|
455
|
-
{ name = "mcp", extras = ["cli"], marker = "extra == 'mcp'", specifier = ">=1.
|
|
456
|
-
{ name = "pydantic", specifier = ">=2.11.
|
|
443
|
+
{ name = "cryptography", specifier = ">=45.0.4" },
|
|
444
|
+
{ name = "duckdb", marker = "extra == 'all'", specifier = ">=1.3.1" },
|
|
445
|
+
{ name = "duckdb", marker = "extra == 'mcp'", specifier = ">=1.3.1" },
|
|
446
|
+
{ name = "fastapi", extras = ["standard"], marker = "extra == 'all'", specifier = ">=0.115.14" },
|
|
447
|
+
{ name = "fastapi", extras = ["standard"], marker = "extra == 'mcp'", specifier = ">=0.115.14" },
|
|
448
|
+
{ name = "fastapi", extras = ["standard"], marker = "extra == 'observability'", specifier = ">=0.115.14" },
|
|
449
|
+
{ name = "fastapi", extras = ["standard"], marker = "extra == 'tools'", specifier = ">=0.115.14" },
|
|
450
|
+
{ name = "looker-sdk", specifier = ">=25.10.0" },
|
|
451
|
+
{ name = "mcp", extras = ["cli"], marker = "extra == 'all'", specifier = ">=1.10.1" },
|
|
452
|
+
{ name = "mcp", extras = ["cli"], marker = "extra == 'codemode'", specifier = ">=1.10.1" },
|
|
453
|
+
{ name = "mcp", extras = ["cli"], marker = "extra == 'mcp'", specifier = ">=1.10.1" },
|
|
454
|
+
{ name = "pydantic", specifier = ">=2.11.7" },
|
|
455
|
+
{ name = "pydantic-monty", marker = "extra == 'all'" },
|
|
456
|
+
{ name = "pydantic-monty", marker = "extra == 'codemode'" },
|
|
457
457
|
{ name = "pydash", specifier = ">=8.0.5" },
|
|
458
|
-
{ name = "questionary",
|
|
459
|
-
{ name = "
|
|
460
|
-
{ name = "
|
|
461
|
-
{ name = "
|
|
462
|
-
{ name = "
|
|
463
|
-
{ name = "
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
{ name = "typer", marker = "extra == 'all'", specifier = ">=0.15.2" },
|
|
467
|
-
{ name = "typer", marker = "extra == 'cli'", specifier = ">=0.15.2" },
|
|
468
|
-
]
|
|
469
|
-
provides-extras = ["cli", "mcp", "observability", "tools", "all"]
|
|
458
|
+
{ name = "questionary", specifier = ">=2.1.0" },
|
|
459
|
+
{ name = "requests", specifier = ">=2.32.4" },
|
|
460
|
+
{ name = "selenium", marker = "extra == 'all'", specifier = ">=4.34.0" },
|
|
461
|
+
{ name = "selenium", marker = "extra == 'observability'", specifier = ">=4.34.0" },
|
|
462
|
+
{ name = "structlog", specifier = ">=25.4.0" },
|
|
463
|
+
{ name = "typer", specifier = ">=0.16.0" },
|
|
464
|
+
]
|
|
465
|
+
provides-extras = ["mcp", "codemode", "observability", "tools", "all"]
|
|
470
466
|
|
|
471
467
|
[package.metadata.requires-dev]
|
|
472
468
|
dev = [
|
|
473
|
-
{ name = "pytest", specifier = ">=8.
|
|
474
|
-
{ name = "ruff", specifier = ">=0.
|
|
469
|
+
{ name = "pytest", specifier = ">=8.4.1" },
|
|
470
|
+
{ name = "ruff", specifier = ">=0.12.1" },
|
|
475
471
|
]
|
|
476
472
|
|
|
477
473
|
[[package]]
|
|
@@ -683,6 +679,53 @@ wheels = [
|
|
|
683
679
|
{ url = "https://files.pythonhosted.org/packages/6f/9a/e73262f6c6656262b5fdd723ad90f518f579b7bc8622e43a942eec53c938/pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", size = 1935777 },
|
|
684
680
|
]
|
|
685
681
|
|
|
682
|
+
[[package]]
|
|
683
|
+
name = "pydantic-monty"
|
|
684
|
+
version = "0.0.14"
|
|
685
|
+
source = { registry = "https://pypi.org/simple" }
|
|
686
|
+
dependencies = [
|
|
687
|
+
{ name = "typing-extensions" },
|
|
688
|
+
]
|
|
689
|
+
sdist = { url = "https://files.pythonhosted.org/packages/45/ef/e192b10c20ca616098e694c3d890d34390aad709dcee432b1467a2187566/pydantic_monty-0.0.14.tar.gz", hash = "sha256:6c2529f307fc20f0bd97f0c12ad2d746086ac4c98b62d7dc789dd0b4fbbd0fa6", size = 981576 }
|
|
690
|
+
wheels = [
|
|
691
|
+
{ url = "https://files.pythonhosted.org/packages/ec/fe/9702e8714d2a3bfa50cc72b5148513c08e1ebbaaed9e7f01d0e0075ac7f2/pydantic_monty-0.0.14-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:8d6f10621888e58dab658e68be9957218164ffe8948d015fefeafc8ba9facc08", size = 7312431 },
|
|
692
|
+
{ url = "https://files.pythonhosted.org/packages/1f/bd/22dab0664939b45748972df4ed3665a1e67d0276be48afd55b5c72aa064f/pydantic_monty-0.0.14-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5615e1b04bc0875d4f302965b9acfe878b4b3a3563aff30beab077daf29c74d8", size = 7307878 },
|
|
693
|
+
{ url = "https://files.pythonhosted.org/packages/23/29/97d33ff5c95a559cf2a6d55426026de16b3b9feec6e6352545b63c0cac9d/pydantic_monty-0.0.14-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:dfe535449b2a0b3257b87d66cfe9b3f4488a0d6e002424720f5b5592e88e412b", size = 7818636 },
|
|
694
|
+
{ url = "https://files.pythonhosted.org/packages/1e/c1/7ea63a9997ddcff20240936308222c908ed410dc2eb01eeb3976a00004ec/pydantic_monty-0.0.14-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae9be17e9a8b4fd20c0e43b0bea9f7d83ed3e3800cde866cc490a077c17fbbbf", size = 7112021 },
|
|
695
|
+
{ url = "https://files.pythonhosted.org/packages/8a/49/62f38c595100e483ec0aa0001f4eedc025818eed8fc892b1d34344017b80/pydantic_monty-0.0.14-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8a282d30d0a0c309dca2d4ccf9b5258878958e49c096c6396522703a6d21c8ef", size = 7403717 },
|
|
696
|
+
{ url = "https://files.pythonhosted.org/packages/2b/5b/ae6ca0987ee4676c34145401d417c7d375e918e73d41f5ed35a741aa85b4/pydantic_monty-0.0.14-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:63e6339c89e33ea3d91e32d1e454277eb1f9f5687148cd97f9975ec07599ab5e", size = 7923295 },
|
|
697
|
+
{ url = "https://files.pythonhosted.org/packages/c9/9a/58829ce58a2dc89ea02c0ac25b3b93b60fdd70a855c1dd055574585d9f7d/pydantic_monty-0.0.14-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fa566cf8926ad12290d5fc5d75e63f87e36e683b756e868510d5542f255966f8", size = 8146698 },
|
|
698
|
+
{ url = "https://files.pythonhosted.org/packages/13/5d/f0d95397fd9a52d8841a7971c4de18950517f89e769219911fdad1e0baa1/pydantic_monty-0.0.14-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a84930c1c67c0b5783adc17eb9f7aaabbbf85f0eda261eb0b39a7cab3d9b4d4", size = 7703857 },
|
|
699
|
+
{ url = "https://files.pythonhosted.org/packages/fa/6b/dcfbe9c590d571869d8df718444347d995d91423b13c77af3f42a88e2253/pydantic_monty-0.0.14-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e042f8eaaddf2b08202766e1ac20ea2032f3ed0714ad5d64f2b4d104e4842501", size = 7292375 },
|
|
700
|
+
{ url = "https://files.pythonhosted.org/packages/b5/5e/9c85dbfaa7c29de82e85f605ae9be7ff0d0439b04ec1fad5fd139e8c5623/pydantic_monty-0.0.14-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:40d1e659fdf76a259cc4a70eaaf081556d3a34a34191a3d1fccadeb8196478dc", size = 7735590 },
|
|
701
|
+
{ url = "https://files.pythonhosted.org/packages/74/13/da8c76d8e96070068adf947df731187c2c856b1a26faf74106601b9c29b9/pydantic_monty-0.0.14-cp312-cp312-win32.whl", hash = "sha256:cb86a22f70942118c6e1ae89304f14ba804f2460de9b33a3c4773c08ed1fce2b", size = 7189537 },
|
|
702
|
+
{ url = "https://files.pythonhosted.org/packages/68/80/c584d2fa2c2cfcae81504435ee592c2d8ed34965061c816ce4a3ed3925b9/pydantic_monty-0.0.14-cp312-cp312-win_amd64.whl", hash = "sha256:d811554f3d46652c941cb839d07290eaebca4b771fecf149aa61d47fa77fbaf4", size = 7952530 },
|
|
703
|
+
{ url = "https://files.pythonhosted.org/packages/01/24/279f8a2e6f7af7b05f52e37806c060078e8c4e3a81de6a57e1f2952a62cb/pydantic_monty-0.0.14-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:43298234981ad055ba80238229f1e86a6be957e9c703275f716c766a2c60f28e", size = 7311901 },
|
|
704
|
+
{ url = "https://files.pythonhosted.org/packages/09/18/474cf9e663bdcd241b3b089144ab939b91182e86fa553dc5949d7aa55fdc/pydantic_monty-0.0.14-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:281e5a0367faaccbbdfc6d2335cce37068f6d1a1ec443237d26e1e21268f9e9a", size = 7307967 },
|
|
705
|
+
{ url = "https://files.pythonhosted.org/packages/43/99/17987aeeabb3f22cbc260c0f75ca6b74de82ed6613ac56e1b231af8d2ac0/pydantic_monty-0.0.14-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c3d18ab1b5997edc1a081576518b87d08d94f02d1483a0fed648282eba304fe4", size = 7818387 },
|
|
706
|
+
{ url = "https://files.pythonhosted.org/packages/5c/6d/7c1d5da18da51c45eac520ae6d05859c3bfad37fd548e5da09ce461081f5/pydantic_monty-0.0.14-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a3bbb594aa33e45589283078c7ee72aeacf66ccfd3714800b0f0b75ce5ffc1e", size = 7111299 },
|
|
707
|
+
{ url = "https://files.pythonhosted.org/packages/02/56/90c1c354c0c5a6266625122b419aa7cdd447a907c794001c5abd234ae7f4/pydantic_monty-0.0.14-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f1fdf67a21d0d66fb8063d253220dc79536f6ff192058440fa30956bfdd5581b", size = 7403680 },
|
|
708
|
+
{ url = "https://files.pythonhosted.org/packages/21/98/75f6da7405f4927c772b4a98cae7aea0cafc7247892000b5fbcd107fe16a/pydantic_monty-0.0.14-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ec5a2305b6b38756edf7be6a67e650efcc4c56a8e93138602ad11ddb43d5036b", size = 7922586 },
|
|
709
|
+
{ url = "https://files.pythonhosted.org/packages/0f/e9/dfff51f7cd405a7a6bbdbc707752693dd82d67d94cb4da22d42b8dbf85c3/pydantic_monty-0.0.14-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e6dfcbc3363d93dfbfc7fcacd740f2d7e652f4b22c179816795007b85a0a0ff6", size = 8146441 },
|
|
710
|
+
{ url = "https://files.pythonhosted.org/packages/a0/e9/5a4b4e6ff73b73f116661ae4e2d7343f4e6f29940584949950a3e84bf467/pydantic_monty-0.0.14-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3b5c5eaf7a6b4620488fe11c211f0a34c6b600857470f3dccd9097d7c52b371", size = 7703282 },
|
|
711
|
+
{ url = "https://files.pythonhosted.org/packages/ce/4e/9c41abc7646f20c0bb90814276052939e43eb24692b125f634b6620d732a/pydantic_monty-0.0.14-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:f90496ab30bc5910def0cb3c55b53b6c5a98ccffdbbc6766661552cf64579ab7", size = 7291952 },
|
|
712
|
+
{ url = "https://files.pythonhosted.org/packages/de/34/8766903b8ea6b95173e5b2525eadf011d153e941a346af8b289d0167c8ca/pydantic_monty-0.0.14-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:1f6740d535578a828ce72a664bd1f3a6e4f906f156b06c3735b9bdcdf3104f40", size = 7733595 },
|
|
713
|
+
{ url = "https://files.pythonhosted.org/packages/36/59/af632d8f7f38224ac04eb76b49152c7c378ce0d0966144737698713d0a77/pydantic_monty-0.0.14-cp313-cp313-win32.whl", hash = "sha256:2bb211fadba4ab898d5cf8a1622d88179d5b07633d00be0ac7168913d76c1cd9", size = 7188687 },
|
|
714
|
+
{ url = "https://files.pythonhosted.org/packages/08/6f/5da7e21c79bf93004d26b1a1fd61407bee3852d7a398e33738a9ac72a079/pydantic_monty-0.0.14-cp313-cp313-win_amd64.whl", hash = "sha256:679978dccd92551111061cfbee7f7b475447217a7656afcb3944095c0c1f0cfb", size = 7952562 },
|
|
715
|
+
{ url = "https://files.pythonhosted.org/packages/c2/59/838b5c98ce25a1eb845c83cfd9aa70f3d22da60da5eb72b27cb1ebed5ae1/pydantic_monty-0.0.14-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:8c11d75377fae113a42c7a0fec2bc31a4896f2452f283e5866f76e4dd9996950", size = 7313228 },
|
|
716
|
+
{ url = "https://files.pythonhosted.org/packages/fd/88/7862783b3dc1de24390226ea31d5383cab011918865474681e16abede32b/pydantic_monty-0.0.14-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0e8d0105672b034e32f1bb5010669e4b4d0b4412f77ceb120868d9e8836a23ee", size = 7321140 },
|
|
717
|
+
{ url = "https://files.pythonhosted.org/packages/74/2f/a330b2fa485870eb35be655b13d18ae6e06bbefadf062a55ee96ac16d065/pydantic_monty-0.0.14-cp314-cp314-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:85b3779579eebb0425954fc4c4d33b38a007c8b432503678073787494d79f185", size = 7818570 },
|
|
718
|
+
{ url = "https://files.pythonhosted.org/packages/5a/fd/d951afa215575c7bd43d2a6e6dc7c209d2de5be69e0966a8d8e67fdba6d2/pydantic_monty-0.0.14-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e0e493b08b9f154400c4ede998a5e41a54258a6ace7cb99b55827c354e11077", size = 7111698 },
|
|
719
|
+
{ url = "https://files.pythonhosted.org/packages/3b/e6/c136182b6bff61b4e37b257bf893fd4f4e036fae42625d9163425cef4228/pydantic_monty-0.0.14-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c6e3ed73986606c3f3ee330ff53a615dbdc0840b48960b547d0b74754376e7e7", size = 7405251 },
|
|
720
|
+
{ url = "https://files.pythonhosted.org/packages/09/83/4e65cce2d178e61a658e0c182946e6d93277c6cb8b2428db927ee740ac31/pydantic_monty-0.0.14-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bad627a02fc0e6b69888e609915c6ffe814d7225f3effcb201bae9e0c2a6023f", size = 7923563 },
|
|
721
|
+
{ url = "https://files.pythonhosted.org/packages/9a/7d/f5195d701098f9da604ff26d80dc86d00f6a644df6c57a91dd452ffd3dad/pydantic_monty-0.0.14-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:34691bf8f558208cf513ea30bd76b0ef7821a06ba32525024fd28d1a5bf7ed4b", size = 8147677 },
|
|
722
|
+
{ url = "https://files.pythonhosted.org/packages/bf/a4/564b603d8494d971ffebec3806fa17e51e3bec1c2849f3898bc1955f6d64/pydantic_monty-0.0.14-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18bd7e61a12fde23f3dde5561f3d8da101a33bd53621a3ccd40686dc827a98f3", size = 7715534 },
|
|
723
|
+
{ url = "https://files.pythonhosted.org/packages/61/8e/639fb9179f39fa2e6de88b85146ab987f70e5f7cc5e8588368337537d9e4/pydantic_monty-0.0.14-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:a7d0911d260347266015edde71d804fdd48f5dc5002f8e22546e8b4d5436d588", size = 7291939 },
|
|
724
|
+
{ url = "https://files.pythonhosted.org/packages/3b/8e/950e42985689900d77f4df51213171adfcbffaf4137b322dbfd96ed3a6f9/pydantic_monty-0.0.14-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:aca052a3acf9b66fd713b44c43be4944b92c0c2eac2641dd230ea4258758bb6c", size = 7735299 },
|
|
725
|
+
{ url = "https://files.pythonhosted.org/packages/59/40/de590213a28c769c46d78843aed8fbe9665eceabb275cf7cc414183659a6/pydantic_monty-0.0.14-cp314-cp314-win32.whl", hash = "sha256:c913b2a99fef193b3ee948885d04bb9c44035514125dc3e765167fa65d349303", size = 7191124 },
|
|
726
|
+
{ url = "https://files.pythonhosted.org/packages/bd/a5/b5d589aa2d9d7ed1fa1c181beacaa6bdf8c13be0117f610947723236c2b2/pydantic_monty-0.0.14-cp314-cp314-win_amd64.whl", hash = "sha256:9b59d0534cca8b75a53e4386c6098116aaf969e9c4164e093696519f9382d5e9", size = 7967367 },
|
|
727
|
+
]
|
|
728
|
+
|
|
686
729
|
[[package]]
|
|
687
730
|
name = "pydantic-settings"
|
|
688
731
|
version = "2.10.1"
|
lkr_dev_cli-0.0.33/Makefile
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
[project]
|
|
2
|
-
name = "lkr-dev-cli"
|
|
3
|
-
version = "0.0.33"
|
|
4
|
-
description = "lkr: a command line interface for looker"
|
|
5
|
-
readme = "README.md"
|
|
6
|
-
license = "MIT"
|
|
7
|
-
authors = [
|
|
8
|
-
{ name = "bwebs"}
|
|
9
|
-
]
|
|
10
|
-
requires-python = ">=3.12"
|
|
11
|
-
dependencies = [
|
|
12
|
-
"looker-sdk>=25.4.0",
|
|
13
|
-
"pydantic>=2.11.4",
|
|
14
|
-
"pydash>=8.0.5",
|
|
15
|
-
"typer>=0.15.2",
|
|
16
|
-
"requests>=2.31.0",
|
|
17
|
-
"cryptography>=42.0.0",
|
|
18
|
-
"structlog>=25.3.0",
|
|
19
|
-
"questionary>=2.1.0"
|
|
20
|
-
]
|
|
21
|
-
|
|
22
|
-
[project.optional-dependencies]
|
|
23
|
-
mcp = [
|
|
24
|
-
"mcp[cli]>=1.9.2",
|
|
25
|
-
"duckdb>=1.2.2",
|
|
26
|
-
"fastapi[standard]>=0.115.12"
|
|
27
|
-
]
|
|
28
|
-
observability = [
|
|
29
|
-
"fastapi[standard]>=0.115.12",
|
|
30
|
-
"selenium>=4.32.0"
|
|
31
|
-
]
|
|
32
|
-
tools = [
|
|
33
|
-
"fastapi[standard]>=0.115.12"
|
|
34
|
-
]
|
|
35
|
-
all = [
|
|
36
|
-
"mcp[cli]>=1.9.2",
|
|
37
|
-
"fastapi[standard]>=0.115.12",
|
|
38
|
-
"selenium>=4.32.0",
|
|
39
|
-
"duckdb>=1.2.2"
|
|
40
|
-
]
|
|
41
|
-
|
|
42
|
-
[project.scripts]
|
|
43
|
-
lkr = "lkr.main:app"
|
|
44
|
-
|
|
45
|
-
[build-system]
|
|
46
|
-
requires = ["hatchling"]
|
|
47
|
-
build-backend = "hatchling.build"
|
|
48
|
-
|
|
49
|
-
[dependency-groups]
|
|
50
|
-
dev = [
|
|
51
|
-
"pytest>=8.3.4",
|
|
52
|
-
"ruff>=0.11.7",
|
|
53
|
-
]
|
|
54
|
-
|
|
55
|
-
[tool.hatch.build.targets.wheel]
|
|
56
|
-
packages = ["lkr"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|