lkr-dev-cli 0.0.29__py3-none-any.whl → 0.0.31__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lkr/main.py +4 -1
- lkr/tools/classes.py +41 -49
- lkr/tools/main.py +2 -3
- {lkr_dev_cli-0.0.29.dist-info → lkr_dev_cli-0.0.31.dist-info}/METADATA +204 -7
- {lkr_dev_cli-0.0.29.dist-info → lkr_dev_cli-0.0.31.dist-info}/RECORD +8 -8
- {lkr_dev_cli-0.0.29.dist-info → lkr_dev_cli-0.0.31.dist-info}/WHEEL +0 -0
- {lkr_dev_cli-0.0.29.dist-info → lkr_dev_cli-0.0.31.dist-info}/entry_points.txt +0 -0
- {lkr_dev_cli-0.0.29.dist-info → lkr_dev_cli-0.0.31.dist-info}/licenses/LICENSE +0 -0
lkr/main.py
CHANGED
@@ -12,7 +12,10 @@ from lkr.observability.main import group as observability_group
|
|
12
12
|
from lkr.tools.main import group as tools_group
|
13
13
|
|
14
14
|
app = typer.Typer(
|
15
|
-
name="lkr",
|
15
|
+
name="lkr",
|
16
|
+
help="A CLI for Looker with helpful tools",
|
17
|
+
add_completion=True,
|
18
|
+
no_args_is_help=True,
|
16
19
|
)
|
17
20
|
|
18
21
|
app.add_typer(auth_group, name="auth")
|
lkr/tools/classes.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
from typing import Literal, Optional, Self, cast
|
2
2
|
|
3
|
-
from fastapi import Request
|
4
3
|
from looker_sdk.sdk.api40.methods import Looker40SDK
|
5
4
|
from looker_sdk.sdk.api40.models import (
|
6
5
|
UserAttributeGroupValue,
|
@@ -46,10 +45,12 @@ class UserAttributeUpdater(BaseModel):
|
|
46
45
|
)
|
47
46
|
return self
|
48
47
|
|
49
|
-
def get_request_authorization_for_value(self,
|
50
|
-
authorization_token =
|
48
|
+
def get_request_authorization_for_value(self, headers: list[tuple[str, str]]):
|
49
|
+
authorization_token = next(
|
50
|
+
(header for header in headers if header[0] == "Authorization"), None
|
51
|
+
)
|
51
52
|
if authorization_token:
|
52
|
-
self.value = authorization_token
|
53
|
+
self.value = authorization_token[1]
|
53
54
|
else:
|
54
55
|
logger.error("No authorization token found")
|
55
56
|
|
@@ -68,6 +69,17 @@ class UserAttributeUpdater(BaseModel):
|
|
68
69
|
return None
|
69
70
|
return init_api_key_sdk(api_key, True)
|
70
71
|
|
72
|
+
def _get_looker_user_id(self, sdk: Looker40SDK) -> str | None:
|
73
|
+
if self.looker_user_id:
|
74
|
+
return self.looker_user_id
|
75
|
+
elif self.email:
|
76
|
+
user = sdk.user_for_credential("email", self.email)
|
77
|
+
return user.id if user else None
|
78
|
+
elif self.external_user_id:
|
79
|
+
user = sdk.user_for_credential("embed", self.external_user_id)
|
80
|
+
return user.id if user else None
|
81
|
+
return None
|
82
|
+
|
71
83
|
def _get_group_id(self, sdk: Looker40SDK) -> str | None:
|
72
84
|
if self.group_id:
|
73
85
|
return self.group_id
|
@@ -107,25 +119,24 @@ class UserAttributeUpdater(BaseModel):
|
|
107
119
|
else:
|
108
120
|
raise ValueError("Group not found")
|
109
121
|
elif self.update_type == "default":
|
110
|
-
sdk.
|
122
|
+
user_attribute = sdk.user_attribute(user_attribute_id, "name,label,type")
|
123
|
+
sdk.update_user_attribute(
|
124
|
+
user_attribute_id,
|
125
|
+
WriteUserAttribute(
|
126
|
+
default_value=None,
|
127
|
+
name=user_attribute.name,
|
128
|
+
label=user_attribute.label,
|
129
|
+
type=user_attribute.type,
|
130
|
+
),
|
131
|
+
)
|
111
132
|
elif self.update_type == "user":
|
112
|
-
|
113
|
-
|
114
|
-
user_id=self.looker_user_id,
|
115
|
-
user_attribute_id=user_attribute_id,
|
116
|
-
)
|
117
|
-
elif self.external_user_id:
|
118
|
-
sdk.delete_user_attribute_user_value(
|
119
|
-
user_id=self.external_user_id,
|
120
|
-
user_attribute_id=user_attribute_id,
|
121
|
-
)
|
122
|
-
elif self.email:
|
123
|
-
sdk.delete_user_attribute_user_value(
|
124
|
-
user_id=self.email,
|
125
|
-
user_attribute_id=user_attribute_id,
|
126
|
-
)
|
127
|
-
else:
|
133
|
+
looker_user_id = self._get_looker_user_id(sdk)
|
134
|
+
if not looker_user_id:
|
128
135
|
raise ValueError("User not found")
|
136
|
+
sdk.delete_user_attribute_user_value(
|
137
|
+
user_id=looker_user_id,
|
138
|
+
user_attribute_id=user_attribute_id,
|
139
|
+
)
|
129
140
|
|
130
141
|
def update_user_attribute_value(self):
|
131
142
|
if not self.value:
|
@@ -170,35 +181,16 @@ class UserAttributeUpdater(BaseModel):
|
|
170
181
|
),
|
171
182
|
)
|
172
183
|
elif self.update_type == "user":
|
173
|
-
|
174
|
-
|
175
|
-
sdk.set_user_attribute_user_value(
|
176
|
-
user_id=user_id,
|
177
|
-
user_attribute_id=user_attribute_id,
|
178
|
-
body=WriteUserAttributeWithValue(
|
179
|
-
value=self.value,
|
180
|
-
),
|
181
|
-
)
|
182
|
-
|
183
|
-
if self.looker_user_id:
|
184
|
-
set_user_attribute_user_value(self.looker_user_id)
|
185
|
-
|
186
|
-
elif self.external_user_id:
|
187
|
-
user = sdk.user_for_credential("embed", self.external_user_id)
|
188
|
-
if not (user and user.id):
|
189
|
-
raise ValueError("User not found")
|
190
|
-
set_user_attribute_user_value(user.id)
|
191
|
-
|
192
|
-
elif self.email:
|
193
|
-
user = sdk.user_for_credential("email", self.email)
|
194
|
-
if not (user and user.id):
|
195
|
-
raise ValueError("User not found")
|
196
|
-
set_user_attribute_user_value(user.id)
|
197
|
-
|
198
|
-
else:
|
184
|
+
looker_user_id = self._get_looker_user_id(sdk)
|
185
|
+
if not looker_user_id:
|
199
186
|
raise ValueError("User not found")
|
200
|
-
|
201
|
-
|
187
|
+
sdk.set_user_attribute_user_value(
|
188
|
+
user_id=looker_user_id,
|
189
|
+
user_attribute_id=user_attribute_id,
|
190
|
+
body=WriteUserAttributeWithValue(
|
191
|
+
value=self.value,
|
192
|
+
),
|
193
|
+
)
|
202
194
|
|
203
195
|
|
204
196
|
class AttributeUpdaterResponse(BaseModel):
|
lkr/tools/main.py
CHANGED
@@ -14,16 +14,15 @@ group = typer.Typer()
|
|
14
14
|
|
15
15
|
@group.command()
|
16
16
|
def user_attribute_updater(
|
17
|
-
ctx: typer.Context,
|
18
17
|
host: str = typer.Option(default="127.0.0.1", envvar="HOST"),
|
19
|
-
port: int = typer.Option(default=8080, envvar="
|
18
|
+
port: int = typer.Option(default=8080, envvar="PORT"),
|
20
19
|
):
|
21
20
|
api = FastAPI()
|
22
21
|
|
23
22
|
@api.post("/identity_token")
|
24
23
|
def identity_token(request: Request, body: UserAttributeUpdater):
|
25
24
|
try:
|
26
|
-
body.get_request_authorization_for_value(request)
|
25
|
+
body.get_request_authorization_for_value(request.headers.items())
|
27
26
|
body.update_user_attribute_value()
|
28
27
|
raw_urls = os.getenv("LOOKER_WHITELISTED_BASE_URLS", "")
|
29
28
|
whitelisted_base_urls = (
|
@@ -1,23 +1,32 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: lkr-dev-cli
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.31
|
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
9
|
Requires-Dist: cryptography>=42.0.0
|
10
|
-
Requires-Dist: duckdb>=1.2.2
|
11
|
-
Requires-Dist: fastapi>=0.115.12
|
12
10
|
Requires-Dist: looker-sdk>=25.4.0
|
13
|
-
Requires-Dist: mcp[cli]>=1.9.2
|
14
11
|
Requires-Dist: pydantic>=2.11.4
|
15
12
|
Requires-Dist: pydash>=8.0.5
|
16
13
|
Requires-Dist: questionary>=2.1.0
|
17
14
|
Requires-Dist: requests>=2.31.0
|
18
|
-
Requires-Dist: selenium>=4.32.0
|
19
15
|
Requires-Dist: structlog>=25.3.0
|
20
16
|
Requires-Dist: typer>=0.15.2
|
17
|
+
Provides-Extra: all
|
18
|
+
Requires-Dist: duckdb>=1.2.2; extra == 'all'
|
19
|
+
Requires-Dist: fastapi>=0.115.12; extra == 'all'
|
20
|
+
Requires-Dist: mcp[cli]>=1.9.2; extra == 'all'
|
21
|
+
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'
|
25
|
+
Provides-Extra: mcp
|
26
|
+
Requires-Dist: duckdb>=1.2.2; extra == 'mcp'
|
27
|
+
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'
|
21
30
|
Description-Content-Type: text/markdown
|
22
31
|
|
23
32
|
# lkr cli
|
@@ -129,7 +138,7 @@ The observability command provides tools for monitoring and interacting with Loo
|
|
129
138
|
- `GET /health`: Launches a headless browser to simulate embedding a dashboard, waits for a completion indicator, and logs the process for health checking. This endpoint accepts query parameters to help login users with custom attributes.
|
130
139
|
|
131
140
|
> [!IMPORTANT]
|
132
|
-
> Make sure you add the http://host:port to your domain allowlist in Admin Embed. [docs](https://cloud.google.com/looker/docs/embedded-javascript-events#adding_the_embed_domain_to_the_allowlist) Unless overridden, the default would be http://0.0.0.0:8080. These can also set via cli arguments. E.g., `lkr observability embed --host localhost --port 7777` or by setting the environment variables `HOST` and `PORT`. You can check the embed_domain by sending a request to the `/settings` endpoint.
|
141
|
+
> Make sure you add the `http://host:port` to your domain allowlist in Admin Embed. [docs](https://cloud.google.com/looker/docs/embedded-javascript-events#adding_the_embed_domain_to_the_allowlist) Unless overridden, the default would be http://0.0.0.0:8080. These can also set via cli arguments. E.g., `lkr observability embed --host localhost --port 7777` or by setting the environment variables `HOST` and `PORT`. You can check the embed_domain by sending a request to the `/settings` endpoint.
|
133
142
|
|
134
143
|
|
135
144
|
For example:
|
@@ -209,4 +218,192 @@ gcloud monitoring uptime create lkr-observability-health-check \
|
|
209
218
|
```
|
210
219
|
|
211
220
|
### Alternative Usage
|
212
|
-
This can also be used to stress test your Looker environment as it serves an API that logs into a Looker embedded dashboard and runs queries like a user would within Chromium. If you wrote a script to repeatedly call this API with different parameters, you could use it to stress test your Looker environment and/or your database.
|
221
|
+
This can also be used to stress test your Looker environment as it serves an API that logs into a Looker embedded dashboard and runs queries like a user would within Chromium. If you wrote a script to repeatedly call this API with different parameters, you could use it to stress test your Looker environment and/or your database.
|
222
|
+
|
223
|
+
## User Attribute Updater (OIDC Token)
|
224
|
+
|
225
|
+
1. Create a new cloud run using the `lkr-cli` public docker image `us-central1-docker.pkg.dev/lkr-dev-production/lkr-cli/cli:latest`
|
226
|
+
2. Put in the environment variables LOOKERSDK_CLIENT_ID, LOOKERSDK_CLIENT_SECRET, LOOKERSDK_BASE_URL, LOOKER_WHITELISTED_BASE_URLS. The `LOOKER_WHITELISTED_BASE_URLS` would be the same url as the `LOOKERSDK_BASE_URL` if you are only using this for a single Looker instance. For more advanced use cases, you can set the `LOOKER_WHITELISTED_BASE_URLS` to a comma separated list of urls. The body of the request also accepts a `base_url`, `client_id`, and `client_secret` key that will override these settings. See example [`gcloud` command](#example-gcloud-command)
|
227
|
+
3. For the command and arguments use:
|
228
|
+
- command: `lkr`
|
229
|
+
- args: `tools` `user-attribute-updater`
|
230
|
+
4. Deploy the cloud run
|
231
|
+
5. Retrieve the URL of the cloud run
|
232
|
+
6. Create the user attribute
|
233
|
+
- Name: cloud_run_access_token
|
234
|
+
- Data Type: String
|
235
|
+
- User Access: None
|
236
|
+
- Hide values: Yes
|
237
|
+
- Domain Allowlist: The URL of the cloud run from step 4. Looker will only allow the user attribute to be set if the request is going to this URL
|
238
|
+
|
239
|
+
> [!NOTE]
|
240
|
+
> The user attribute name can be anything you want. Typically you will be using this with an extension, so you should follow the naming convention of the type of user attribute you will be using with the extension. I would recommend using a `scoped user attributes` for this. See [Extension User Attributes](https://www.npmjs.com/package/@looker/extension-sdk#user-attributes). If you are using a `global_user_attribute`, then you can just use the name of it like `cloud_run_access_token`.
|
241
|
+
|
242
|
+
6. Create a new cloud scheduler
|
243
|
+
- cron: `0 * * * *`
|
244
|
+
- Target Type: Cloud Run
|
245
|
+
- URL: The URL of the cloud run from step 4 with a path of `/identity_token`. E.g. `https://your-cloud-run-url.com/identity_token`
|
246
|
+
- HTTP Method: POST
|
247
|
+
- Headers: `Content-Type: application/json`
|
248
|
+
- Body: Use the user attribute_name from step 5, or use the user_attribute_id found in the Looker URL after you created it or are editing it
|
249
|
+
|
250
|
+
```json
|
251
|
+
{
|
252
|
+
"user_attribute": "cloud_run_access_token",
|
253
|
+
"update_type": "default"
|
254
|
+
}
|
255
|
+
```
|
256
|
+
- Auth Header: OIDC Token
|
257
|
+
- Service Account: Choose the service account you want to use to run the cloud scheduler
|
258
|
+
- Audience: The URL of the cloud run
|
259
|
+
- Max Retries: >0
|
260
|
+
7. Make sure the Service Account has the `Cloud Run Invoker` role
|
261
|
+
8. Navigate the the cloud scheduler page, select the one you just created, and click Force Run
|
262
|
+
9. Check the logs of the cloud run to see if there was a 200 response
|
263
|
+
|
264
|
+
|
265
|
+
### Example `gcloud` command
|
266
|
+
```bash
|
267
|
+
export REGION=<your region>
|
268
|
+
export PROJECT=<your project id>
|
269
|
+
|
270
|
+
gcloud run deploy lkr-access-token-updater \
|
271
|
+
--image us-central1-docker.pkg.dev/lkr-dev-production/lkr-cli/cli:latest \
|
272
|
+
--command lkr \
|
273
|
+
--args tools,user-attribute-updater \
|
274
|
+
--platform managed \
|
275
|
+
--region $REGION \
|
276
|
+
--project $PROJECT \
|
277
|
+
--cpu 1 \
|
278
|
+
--memory 2Gi \
|
279
|
+
--set-env-vars LOOKERSDK_CLIENT_ID=<your client id>,LOOKERSDK_CLIENT_SECRET=<your client secret>,LOOKERSDK_BASE_URL=<your instance url>,LOOKER_WHITELISTED_BASE_URLS=<your instance url>
|
280
|
+
```
|
281
|
+
|
282
|
+
## UserAttributeUpdater `lkr-dev-cli`
|
283
|
+
|
284
|
+
Exported from the `lkr-dev-cli` package is the `UserAttributeUpdater` pydantic class. This class has all the necessary logic to update a user attribute value.
|
285
|
+
|
286
|
+
It supports the following operations:
|
287
|
+
- Updating a default value
|
288
|
+
- Updating a group value
|
289
|
+
- Updating a user value
|
290
|
+
- Deleting a default value
|
291
|
+
- Deleting a group value
|
292
|
+
- Deleting a user value
|
293
|
+
|
294
|
+
It can also support looking up looker ids. It will lookup the following if the id is not provided:
|
295
|
+
- user_attribute_id by the name
|
296
|
+
- user_id by the email or external_user_id
|
297
|
+
- group_id by the name
|
298
|
+
|
299
|
+
|
300
|
+
### Example Usage
|
301
|
+
|
302
|
+
```python
|
303
|
+
from lkr import UserAttributeUpdater
|
304
|
+
|
305
|
+
# without credentials
|
306
|
+
updater = UserAttributeUpdater(
|
307
|
+
user_attribute="cloud_run_access_token",
|
308
|
+
update_type="default",
|
309
|
+
value="123",
|
310
|
+
)
|
311
|
+
|
312
|
+
|
313
|
+
# with credentials
|
314
|
+
updater = UserAttributeUpdater(
|
315
|
+
user_attribute="cloud_run_access_token",
|
316
|
+
update_type="default",
|
317
|
+
value="123",
|
318
|
+
base_url="https://your-looker-instance.com",
|
319
|
+
client_id="your-client-id",
|
320
|
+
client_secret="your-client-secret",
|
321
|
+
)
|
322
|
+
|
323
|
+
updater.update_user_attribute_value()
|
324
|
+
|
325
|
+
# Getting authorization header from a FastAPI request
|
326
|
+
from fastapi import Request
|
327
|
+
from lkr import UserAttributeUpdater
|
328
|
+
|
329
|
+
@app.post("/request_authorization")
|
330
|
+
def request_authorization(request: Request):
|
331
|
+
body = await request.json()
|
332
|
+
updater = UserAttributeUpdater.model_validate(body)
|
333
|
+
updater.get_request_authorization_for_value(request.headers.items())
|
334
|
+
updater.update_user_attribute_value()
|
335
|
+
|
336
|
+
@app.post("/as_body")
|
337
|
+
def as_body(request: Request, body: UserAttributeUpdater):
|
338
|
+
body.get_request_authorization_for_value(request.headers.items())
|
339
|
+
body.update_user_attribute_value()
|
340
|
+
|
341
|
+
@app.post("/assigning_value")
|
342
|
+
def assigning_value(request: Request):
|
343
|
+
updater = UserAttributeUpdater(
|
344
|
+
user_attribute="cloud_run_access_token",
|
345
|
+
update_type="default"
|
346
|
+
)
|
347
|
+
updater.value = request.headers.get("my_custom_header")
|
348
|
+
updater.update_user_attribute_value()
|
349
|
+
|
350
|
+
@app.delete("/:user_attribute_name/:email")
|
351
|
+
def delete_user_attribute(user_attribute_name: str, email: str):
|
352
|
+
updater = UserAttributeUpdater(
|
353
|
+
user_attribute=user_attribute_name,
|
354
|
+
update_type="user",
|
355
|
+
email=email,
|
356
|
+
)
|
357
|
+
updater.delete_user_attribute_value()
|
358
|
+
|
359
|
+
## Optional Dependencies
|
360
|
+
|
361
|
+
The `lkr` CLI supports optional dependencies that enable additional functionality. You can install these individually or all at once.
|
362
|
+
|
363
|
+
### Available Extras
|
364
|
+
|
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`
|
371
|
+
|
372
|
+
### Installing Optional Dependencies
|
373
|
+
|
374
|
+
**Install all optional dependencies:**
|
375
|
+
```bash
|
376
|
+
uv sync --extra all
|
377
|
+
```
|
378
|
+
|
379
|
+
**Install specific extras:**
|
380
|
+
```bash
|
381
|
+
# Install MCP functionality
|
382
|
+
uv sync --extra mcp
|
383
|
+
|
384
|
+
# Install observability features
|
385
|
+
uv sync --extra embed-observability
|
386
|
+
|
387
|
+
# Install user attribute updater
|
388
|
+
uv sync --extra user-attribute-updater
|
389
|
+
|
390
|
+
# Install multiple extras
|
391
|
+
uv sync --extra mcp --extra embed-observability
|
392
|
+
```
|
393
|
+
|
394
|
+
**Using pip:**
|
395
|
+
```bash
|
396
|
+
# Install all optional dependencies
|
397
|
+
pip install lkr-dev-cli[all]
|
398
|
+
|
399
|
+
# Install specific extras
|
400
|
+
pip install lkr-dev-cli[mcp,embed-observability,user-attribute-updater]
|
401
|
+
```
|
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.
|
@@ -5,7 +5,7 @@ lkr/constants.py,sha256=DdCfsV6q8wgs2iHpIQeb6oDP_2XejusEHyPvCbaM3yY,108
|
|
5
5
|
lkr/custom_types.py,sha256=feJ-W2U61PJTiotMLuZJqxrotA53er95kO1O30mooy4,323
|
6
6
|
lkr/exceptions.py,sha256=M_aR4YaCZtY4wyxhcoqJCVkxVu9z3Wwo5KgSDyOoEnI,210
|
7
7
|
lkr/logger.py,sha256=vKlJZqiMzJbYBzmiiD0HzJp-J-rHd4nWX-7P4ZKgh78,2033
|
8
|
-
lkr/main.py,sha256=
|
8
|
+
lkr/main.py,sha256=TZR6GQ0Yc5Tp6XlUAVHi6Zpj3JcQ_DSZp9YuGhymu-c,2491
|
9
9
|
lkr/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
10
|
lkr/auth/main.py,sha256=7tWGPWokzbBnrX1enZ9YP4rdDJqYBlGfuYe0Wg-fXT4,7532
|
11
11
|
lkr/auth/oauth.py,sha256=n2yAcccdBZaloGVtFRTwCPBfh1cvVYNbXLsFCxmWc5M,7207
|
@@ -16,10 +16,10 @@ lkr/observability/classes.py,sha256=LgGuUnY-J1csPrlAKnw4PPOqOfbvaOx2cxENlQgJYcE,
|
|
16
16
|
lkr/observability/embed_container.html,sha256=IcDG-QVsYYNGQGrkDrx9OMZ2Pmo4C8oAjRHddFQ7Tlw,2939
|
17
17
|
lkr/observability/main.py,sha256=XbejIdqhNNUMqHVezb5EnLaJ32dO9-Bt0o5d8lc0kyw,9544
|
18
18
|
lkr/observability/utils.py,sha256=UpaBrp_ufaXLoz4p3xG3K6lHKBpP9wBhvP8rDmeGoWg,2148
|
19
|
-
lkr/tools/classes.py,sha256=
|
20
|
-
lkr/tools/main.py,sha256=
|
21
|
-
lkr_dev_cli-0.0.
|
22
|
-
lkr_dev_cli-0.0.
|
23
|
-
lkr_dev_cli-0.0.
|
24
|
-
lkr_dev_cli-0.0.
|
25
|
-
lkr_dev_cli-0.0.
|
19
|
+
lkr/tools/classes.py,sha256=ZyRRCQjjwV4WVWGmKlTfXiLiOGUf67XgrboYhOLuLts,7508
|
20
|
+
lkr/tools/main.py,sha256=u9O5JgGVFscav9wFcrHd5ZPUUue_KpeK0QukYKqzros,2683
|
21
|
+
lkr_dev_cli-0.0.31.dist-info/METADATA,sha256=8QimDm78NO_wvLUt3XJ_SlwdMkCY3pk5Jt8KY1FuGxo,18527
|
22
|
+
lkr_dev_cli-0.0.31.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
23
|
+
lkr_dev_cli-0.0.31.dist-info/entry_points.txt,sha256=nn2sFMGDpwUVE61ZUpbDPnQZkW7Gc08nV-tyLGo8q34,37
|
24
|
+
lkr_dev_cli-0.0.31.dist-info/licenses/LICENSE,sha256=hKnCOORW1JRE_M2vStz8dblS5u1iR-2VpqS9xagKNa0,1063
|
25
|
+
lkr_dev_cli-0.0.31.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|