kash-shell 0.3.13__py3-none-any.whl → 0.3.15__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.
- kash/actions/core/markdownify.py +7 -4
- kash/actions/core/readability.py +4 -3
- kash/actions/core/render_as_html.py +2 -2
- kash/actions/core/show_webpage.py +2 -2
- kash/commands/workspace/selection_commands.py +22 -0
- kash/commands/workspace/workspace_commands.py +45 -22
- kash/exec/action_exec.py +25 -7
- kash/exec/precondition_registry.py +5 -3
- kash/exec/preconditions.py +31 -6
- kash/exec/resolve_args.py +2 -2
- kash/file_storage/file_store.py +48 -12
- kash/model/items_model.py +13 -8
- kash/model/operations_model.py +14 -0
- kash/shell/utils/native_utils.py +2 -2
- kash/utils/common/url.py +80 -3
- kash/utils/file_utils/file_formats.py +3 -2
- kash/utils/file_utils/file_formats_model.py +37 -49
- kash/utils/text_handling/doc_normalization.py +7 -0
- kash/web_content/local_file_cache.py +28 -5
- kash/web_gen/templates/base_styles.css.jinja +7 -3
- {kash_shell-0.3.13.dist-info → kash_shell-0.3.15.dist-info}/METADATA +1 -1
- {kash_shell-0.3.13.dist-info → kash_shell-0.3.15.dist-info}/RECORD +25 -26
- kash/workspaces/workspace_importing.py +0 -56
- {kash_shell-0.3.13.dist-info → kash_shell-0.3.15.dist-info}/WHEEL +0 -0
- {kash_shell-0.3.13.dist-info → kash_shell-0.3.15.dist-info}/entry_points.txt +0 -0
- {kash_shell-0.3.13.dist-info → kash_shell-0.3.15.dist-info}/licenses/LICENSE +0 -0
kash/model/items_model.py
CHANGED
|
@@ -181,7 +181,7 @@ class ItemId:
|
|
|
181
181
|
item_id = ItemId(item.type, IdType.url, canonicalize_url(item.url))
|
|
182
182
|
elif item.type == ItemType.concept and item.title:
|
|
183
183
|
item_id = ItemId(item.type, IdType.concept, canonicalize_concept(item.title))
|
|
184
|
-
elif item.source and item.source.cacheable:
|
|
184
|
+
elif item.source and item.source.cacheable and item.source.operation.has_known_inputs:
|
|
185
185
|
# We know the source of this and if the action was cacheable, we can create
|
|
186
186
|
# an identity based on the source.
|
|
187
187
|
item_id = ItemId(item.type, IdType.source, item.source.as_str())
|
|
@@ -363,21 +363,24 @@ class Item:
|
|
|
363
363
|
*,
|
|
364
364
|
title: str | None = None,
|
|
365
365
|
original_filename: str | None = None,
|
|
366
|
+
url: Url | None = None,
|
|
366
367
|
mime_type: MimeType | None = None,
|
|
367
368
|
) -> Item:
|
|
368
369
|
"""
|
|
369
370
|
Create a resource Item for a file with a format inferred from the file extension
|
|
370
371
|
or the content. Only sets basic metadata. Does not read the content. Will set
|
|
371
372
|
`format` and `file_ext` if possible but will leave them as None if unrecognized.
|
|
372
|
-
If `mime_type` is provided, it can help determine the file extension
|
|
373
|
+
If `mime_type` is provided, it can help determine the file extension if the
|
|
374
|
+
extension isn't recognized from the filename or URL.
|
|
373
375
|
"""
|
|
374
376
|
from kash.file_storage.store_filenames import parse_item_filename
|
|
375
|
-
from kash.utils.file_utils.file_formats_model import
|
|
377
|
+
from kash.utils.file_utils.file_formats_model import file_format_info
|
|
376
378
|
|
|
377
379
|
# Will raise error for unrecognized file ext.
|
|
378
380
|
_name, filename_item_type, format, file_ext = parse_item_filename(path)
|
|
381
|
+
format_info = file_format_info(path, suggested_mime_type=mime_type)
|
|
379
382
|
if not format:
|
|
380
|
-
format =
|
|
383
|
+
format = format_info.format
|
|
381
384
|
if not item_type and filename_item_type:
|
|
382
385
|
item_type = filename_item_type
|
|
383
386
|
if not item_type:
|
|
@@ -385,9 +388,10 @@ class Item:
|
|
|
385
388
|
item_type = (
|
|
386
389
|
ItemType.doc if format and format.supports_frontmatter else ItemType.resource
|
|
387
390
|
)
|
|
388
|
-
|
|
389
|
-
if not
|
|
390
|
-
|
|
391
|
+
|
|
392
|
+
# Try to determine a good file extension if it's not already on the filename.
|
|
393
|
+
if not file_ext:
|
|
394
|
+
file_ext = format_info.suggested_file_ext
|
|
391
395
|
|
|
392
396
|
item = cls(
|
|
393
397
|
type=item_type,
|
|
@@ -396,6 +400,7 @@ class Item:
|
|
|
396
400
|
format=format,
|
|
397
401
|
external_path=str(path),
|
|
398
402
|
original_filename=original_filename,
|
|
403
|
+
url=url,
|
|
399
404
|
)
|
|
400
405
|
|
|
401
406
|
# Update modified time from the file system.
|
|
@@ -623,7 +628,7 @@ class Item:
|
|
|
623
628
|
if not display_title and self.store_path:
|
|
624
629
|
display_title = Path(self.store_path).name
|
|
625
630
|
if not display_title:
|
|
626
|
-
display_title = self.abbrev_title()
|
|
631
|
+
display_title = self.abbrev_title(pull_body_heading=True)
|
|
627
632
|
return display_title
|
|
628
633
|
|
|
629
634
|
def abbrev_description(self, max_len: int = 1000) -> str:
|
kash/model/operations_model.py
CHANGED
|
@@ -66,6 +66,13 @@ class Input:
|
|
|
66
66
|
else:
|
|
67
67
|
return "[input info missing]"
|
|
68
68
|
|
|
69
|
+
@property
|
|
70
|
+
def is_known(self) -> bool:
|
|
71
|
+
"""
|
|
72
|
+
Whether the input is known, i.e. we had saved inputs with hashes.
|
|
73
|
+
"""
|
|
74
|
+
return bool(self.path and self.hash)
|
|
75
|
+
|
|
69
76
|
# Inputs are equal if the hashes match (even if the paths have changed).
|
|
70
77
|
|
|
71
78
|
def __hash__(self):
|
|
@@ -117,6 +124,13 @@ class Operation:
|
|
|
117
124
|
|
|
118
125
|
return d
|
|
119
126
|
|
|
127
|
+
@property
|
|
128
|
+
def has_known_inputs(self) -> bool:
|
|
129
|
+
"""
|
|
130
|
+
Whether the operation has known inputs, i.e. all inputs have hashes.
|
|
131
|
+
"""
|
|
132
|
+
return all(arg.is_known for arg in self.arguments)
|
|
133
|
+
|
|
120
134
|
def summary(self) -> OperationSummary:
|
|
121
135
|
return OperationSummary(self.action_name)
|
|
122
136
|
|
kash/shell/utils/native_utils.py
CHANGED
|
@@ -23,7 +23,7 @@ from kash.shell.output.shell_output import cprint
|
|
|
23
23
|
from kash.utils.common.format_utils import fmt_loc
|
|
24
24
|
from kash.utils.common.url import as_file_url, is_file_url, is_url
|
|
25
25
|
from kash.utils.errors import FileNotFound, SetupError
|
|
26
|
-
from kash.utils.file_utils.file_formats import
|
|
26
|
+
from kash.utils.file_utils.file_formats import is_fullpage_html, read_partial_text
|
|
27
27
|
from kash.utils.file_utils.file_formats_model import file_format_info
|
|
28
28
|
|
|
29
29
|
log = get_logger(__name__)
|
|
@@ -88,7 +88,7 @@ def _detect_view_mode(file_or_url: str) -> ViewMode:
|
|
|
88
88
|
path = Path(file_or_url)
|
|
89
89
|
if path.is_file(): # File or symlink.
|
|
90
90
|
content = read_partial_text(path)
|
|
91
|
-
if content and
|
|
91
|
+
if content and is_fullpage_html(content):
|
|
92
92
|
return ViewMode.browser
|
|
93
93
|
|
|
94
94
|
info = file_format_info(path)
|
kash/utils/common/url.py
CHANGED
|
@@ -47,7 +47,9 @@ def check_if_url(
|
|
|
47
47
|
if only_schemes:
|
|
48
48
|
return result if result.scheme in only_schemes else None
|
|
49
49
|
else:
|
|
50
|
-
|
|
50
|
+
# Consider it a URL if the scheme is present and longer than a single character.
|
|
51
|
+
# This helps avoid misinterpreting Windows drive letters (e.g., "C:\foo") as schemes.
|
|
52
|
+
return result if result.scheme and len(result.scheme) > 1 else None
|
|
51
53
|
except ValueError:
|
|
52
54
|
return None
|
|
53
55
|
|
|
@@ -145,6 +147,41 @@ def normalize_url(
|
|
|
145
147
|
return Url(normalized_url)
|
|
146
148
|
|
|
147
149
|
|
|
150
|
+
def is_valid_path(text: UnresolvedLocator) -> bool:
|
|
151
|
+
"""
|
|
152
|
+
Sanity check if the input is plausibly a file path, i.e. not a URL or malformed in
|
|
153
|
+
an obvious way. Does not check for existence or OS-specific naming restrictions.
|
|
154
|
+
For a more thorough check there are other more complex options like:
|
|
155
|
+
https://github.com/thombashi/pathvalidate
|
|
156
|
+
"""
|
|
157
|
+
if isinstance(text, Path):
|
|
158
|
+
return True
|
|
159
|
+
elif isinstance(text, str):
|
|
160
|
+
path_str = text
|
|
161
|
+
else:
|
|
162
|
+
return False
|
|
163
|
+
|
|
164
|
+
# Check for empty or whitespace-only strings or null characters
|
|
165
|
+
# (never acceptable paths).
|
|
166
|
+
if not path_str or path_str.isspace():
|
|
167
|
+
return False
|
|
168
|
+
if "\0" in path_str:
|
|
169
|
+
return False
|
|
170
|
+
|
|
171
|
+
# Explicitly disallow URLs.
|
|
172
|
+
if is_url(path_str):
|
|
173
|
+
return False
|
|
174
|
+
|
|
175
|
+
# As a final lightweight check, ensure it can be instantiated as a Path object
|
|
176
|
+
# This doesn't validate existence or character restrictions.
|
|
177
|
+
try:
|
|
178
|
+
_ = Path(path_str)
|
|
179
|
+
except (TypeError, ValueError):
|
|
180
|
+
return False
|
|
181
|
+
|
|
182
|
+
return True
|
|
183
|
+
|
|
184
|
+
|
|
148
185
|
## Tests
|
|
149
186
|
|
|
150
187
|
|
|
@@ -155,13 +192,19 @@ def test_is_url():
|
|
|
155
192
|
assert is_url("ftp://example.com") == True
|
|
156
193
|
assert is_url("file:///path/to/file") == True
|
|
157
194
|
assert is_url("file://hostname/path/to/file") == True
|
|
158
|
-
assert is_url("invalid-url") == False
|
|
159
|
-
assert is_url("www.example.com") == False
|
|
160
195
|
assert is_url("http://example.com", only_schemes=HTTP_ONLY) == True
|
|
161
196
|
assert is_url("https://example.com", only_schemes=HTTP_ONLY) == True
|
|
197
|
+
|
|
198
|
+
assert is_url("invalid-url") == False
|
|
199
|
+
assert is_url("www.example.com") == False
|
|
162
200
|
assert is_url("ftp://example.com", only_schemes=HTTP_ONLY) == False
|
|
163
201
|
assert is_url("file:///path/to/file", only_schemes=HTTP_ONLY) == False
|
|
164
202
|
|
|
203
|
+
assert is_url("www.example.com") is False
|
|
204
|
+
assert is_url("c:\\path\\to\\file") is False
|
|
205
|
+
assert is_url("/foo/bar") is False
|
|
206
|
+
assert is_url("//foo") is False
|
|
207
|
+
|
|
165
208
|
|
|
166
209
|
def test_as_file_url():
|
|
167
210
|
assert as_file_url("file:///path/to/file") == "file:///path/to/file"
|
|
@@ -205,3 +248,37 @@ def test_normalize_url():
|
|
|
205
248
|
str(e)
|
|
206
249
|
== "Scheme 'ftp' not in allowed schemes: ['http', 'https', 'file']: ftp://example.com"
|
|
207
250
|
)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def test_is_path():
|
|
254
|
+
assert is_valid_path("foo/bar") is True
|
|
255
|
+
assert is_valid_path("/foo/bar") is True
|
|
256
|
+
assert is_valid_path("./foo/bar") is True
|
|
257
|
+
assert is_valid_path("../foo/bar") is True
|
|
258
|
+
assert is_valid_path("foo.txt") is True
|
|
259
|
+
assert is_valid_path(Path("foo/bar")) is True
|
|
260
|
+
assert is_valid_path(Path()) is True
|
|
261
|
+
assert is_valid_path(".") is True
|
|
262
|
+
assert is_valid_path("..") is True
|
|
263
|
+
assert is_valid_path("C:\\Users\\name") is True # Windows-style
|
|
264
|
+
assert is_valid_path("file_with:colon.txt") is True # Valid on POSIX
|
|
265
|
+
assert is_valid_path(Url("relative/path")) is True # Url type with relative content
|
|
266
|
+
|
|
267
|
+
assert is_valid_path("http://example.com") is False
|
|
268
|
+
assert is_valid_path("https://example.com/path") is False
|
|
269
|
+
assert is_valid_path("file:///path/to/file") is False
|
|
270
|
+
assert is_valid_path(Url("http://example.com")) is False
|
|
271
|
+
assert is_valid_path("") is False
|
|
272
|
+
assert is_valid_path(" ") is False
|
|
273
|
+
assert is_valid_path("foo\0bar.txt") is False
|
|
274
|
+
assert is_valid_path(None) is False # pyright: ignore
|
|
275
|
+
assert is_valid_path(123) is False # pyright: ignore
|
|
276
|
+
|
|
277
|
+
# Edge cases
|
|
278
|
+
assert is_valid_path("www.example.com") is True # No scheme
|
|
279
|
+
assert str(Path("")) == "."
|
|
280
|
+
assert str(Path(" ")) == " "
|
|
281
|
+
assert is_valid_path(Path(" ")) is True # A bad idea but allowed
|
|
282
|
+
assert is_valid_path(Path("")) is True
|
|
283
|
+
assert is_valid_path(" ") is False
|
|
284
|
+
assert is_valid_path("") is False
|
|
@@ -11,9 +11,10 @@ from kash.config.logger import get_logger
|
|
|
11
11
|
log = get_logger(__name__)
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
def
|
|
14
|
+
def is_fullpage_html(content: str) -> bool:
|
|
15
15
|
"""
|
|
16
|
-
A full HTML document that is
|
|
16
|
+
A full HTML document that is a full page (headers, footers, etc.) and
|
|
17
|
+
so probably best rendered in a browser.
|
|
17
18
|
"""
|
|
18
19
|
return bool(re.search(r"<!DOCTYPE html>|<html>|<body>|<head>", content[:2048], re.IGNORECASE))
|
|
19
20
|
|
|
@@ -4,7 +4,7 @@ from dataclasses import dataclass
|
|
|
4
4
|
from enum import Enum
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
|
|
7
|
-
from kash.utils.common.url import
|
|
7
|
+
from kash.utils.common.url import is_valid_path
|
|
8
8
|
from kash.utils.file_utils.file_ext import FileExt
|
|
9
9
|
from kash.utils.file_utils.file_formats import (
|
|
10
10
|
MIME_EMPTY,
|
|
@@ -143,11 +143,23 @@ class Format(Enum):
|
|
|
143
143
|
|
|
144
144
|
@property
|
|
145
145
|
def is_markdown(self) -> bool:
|
|
146
|
-
|
|
146
|
+
"""Is this pure Markdown? Does not include Markdown mixed with HTML."""
|
|
147
|
+
return self in [self.markdown]
|
|
148
|
+
|
|
149
|
+
@property
|
|
150
|
+
def is_markdown_with_html(self) -> bool:
|
|
151
|
+
"""Is this Markdown mixed with HTML?"""
|
|
152
|
+
return self in [self.md_html]
|
|
147
153
|
|
|
148
154
|
@property
|
|
149
155
|
def is_html(self) -> bool:
|
|
150
|
-
|
|
156
|
+
"""Is this format HTML? Does not include Markdown mixed with HTML."""
|
|
157
|
+
return self in [self.html]
|
|
158
|
+
|
|
159
|
+
@property
|
|
160
|
+
def is_html_compatible(self) -> bool:
|
|
161
|
+
"""Is this format directly compatible with HTML (any combination of text, markdown, or HTML)?"""
|
|
162
|
+
return self in [self.plaintext, self.markdown, self.md_html, self.html]
|
|
151
163
|
|
|
152
164
|
@property
|
|
153
165
|
def is_data(self) -> bool:
|
|
@@ -406,15 +418,6 @@ class FileFormatInfo:
|
|
|
406
418
|
return self.as_str()
|
|
407
419
|
|
|
408
420
|
|
|
409
|
-
def _guess_format(file_ext: FileExt | None, mime_type: MimeType | None) -> Format | None:
|
|
410
|
-
format = None
|
|
411
|
-
if file_ext:
|
|
412
|
-
format = Format.guess_by_file_ext(file_ext)
|
|
413
|
-
if not format and mime_type:
|
|
414
|
-
format = Format.from_mime_type(mime_type)
|
|
415
|
-
return format
|
|
416
|
-
|
|
417
|
-
|
|
418
421
|
def guess_format_by_name(path: str | Path) -> Format | None:
|
|
419
422
|
"""
|
|
420
423
|
Fast guess of file format by the file name only.
|
|
@@ -423,22 +426,39 @@ def guess_format_by_name(path: str | Path) -> Format | None:
|
|
|
423
426
|
return Format.guess_by_file_ext(file_ext) if file_ext else None
|
|
424
427
|
|
|
425
428
|
|
|
426
|
-
def file_format_info(
|
|
429
|
+
def file_format_info(
|
|
430
|
+
path: str | Path,
|
|
431
|
+
suggested_mime_type: MimeType | None = None,
|
|
432
|
+
) -> FileFormatInfo:
|
|
427
433
|
"""
|
|
428
434
|
Get info on the file format path and content (file extension and file content).
|
|
429
435
|
Looks at the file extension first and then the file content if needed.
|
|
430
|
-
If `
|
|
431
|
-
|
|
436
|
+
If `suggested_mime_type` is provided, it will be used as the detected mime type
|
|
437
|
+
instead of detecting it from the file content.
|
|
432
438
|
"""
|
|
439
|
+
if not is_valid_path(path):
|
|
440
|
+
raise ValueError(f"Expected a file path but got: {path!r}")
|
|
441
|
+
|
|
433
442
|
path = Path(path)
|
|
434
443
|
file_ext = parse_file_ext(path)
|
|
435
|
-
if
|
|
444
|
+
if not suggested_mime_type and not file_ext:
|
|
436
445
|
# Look at the file content.
|
|
437
446
|
detected_mime_type = detect_mime_type(path)
|
|
447
|
+
elif suggested_mime_type:
|
|
448
|
+
detected_mime_type = suggested_mime_type
|
|
438
449
|
else:
|
|
439
450
|
detected_mime_type = None
|
|
440
|
-
|
|
451
|
+
|
|
452
|
+
# Pick format first by file extension, then by detected mime type.
|
|
453
|
+
format = None
|
|
454
|
+
if file_ext:
|
|
455
|
+
format = Format.guess_by_file_ext(file_ext)
|
|
456
|
+
if not format and detected_mime_type:
|
|
457
|
+
format = Format.from_mime_type(detected_mime_type)
|
|
458
|
+
|
|
459
|
+
# Attempt to canonicalize the mime type to match the format.
|
|
441
460
|
final_mime_type = format.mime_type if format else detected_mime_type
|
|
461
|
+
|
|
442
462
|
return FileFormatInfo(file_ext, format, final_mime_type)
|
|
443
463
|
|
|
444
464
|
|
|
@@ -456,35 +476,3 @@ def detect_media_type(filename: str | Path) -> MediaType:
|
|
|
456
476
|
fmt = detect_file_format(filename)
|
|
457
477
|
media_type = fmt.media_type if fmt else MediaType.binary
|
|
458
478
|
return media_type
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
def choose_file_ext(
|
|
462
|
-
url_or_path: Url | Path | str, mime_type: MimeType | None = None
|
|
463
|
-
) -> FileExt | None:
|
|
464
|
-
"""
|
|
465
|
-
Pick a file extension to reflect the type of the content. First tries from any
|
|
466
|
-
provided content type (e.g. if this item was just downloaded). Then
|
|
467
|
-
recognizes known file extensions on the filename or URL, then tries looking
|
|
468
|
-
at the content with libmagic and heuristics, then gives up.
|
|
469
|
-
"""
|
|
470
|
-
if mime_type:
|
|
471
|
-
fmt = Format.from_mime_type(mime_type)
|
|
472
|
-
if fmt:
|
|
473
|
-
return fmt.file_ext
|
|
474
|
-
|
|
475
|
-
# First check if it's a known standard extension.
|
|
476
|
-
filename_ext = parse_file_ext(url_or_path)
|
|
477
|
-
if filename_ext:
|
|
478
|
-
return filename_ext
|
|
479
|
-
|
|
480
|
-
local_path = None
|
|
481
|
-
if isinstance(url_or_path, str) and is_file_url(url_or_path):
|
|
482
|
-
local_path = parse_file_url(url_or_path)
|
|
483
|
-
elif not is_url(url_or_path):
|
|
484
|
-
local_path = Path(url_or_path)
|
|
485
|
-
|
|
486
|
-
# If it's local based the extension on the file content.
|
|
487
|
-
if local_path:
|
|
488
|
-
return file_format_info(local_path).suggested_file_ext
|
|
489
|
-
|
|
490
|
-
return None
|
|
@@ -9,6 +9,13 @@ from kash.utils.file_utils.file_formats_model import Format, detect_file_format
|
|
|
9
9
|
from kash.utils.rich_custom.ansi_cell_len import ansi_cell_len
|
|
10
10
|
|
|
11
11
|
|
|
12
|
+
def can_normalize(format: Format) -> bool:
|
|
13
|
+
"""
|
|
14
|
+
True for Markdown (the only format we currently normalize).
|
|
15
|
+
"""
|
|
16
|
+
return format == Format.markdown or format == Format.md_html
|
|
17
|
+
|
|
18
|
+
|
|
12
19
|
def normalize_formatting(
|
|
13
20
|
text: str,
|
|
14
21
|
format: Format | None,
|
|
@@ -10,10 +10,17 @@ from funlog import log_if_modifies
|
|
|
10
10
|
from prettyfmt import fmt_path
|
|
11
11
|
from strif import atomic_output_file, copyfile_atomic
|
|
12
12
|
|
|
13
|
-
from kash.utils.common.url import
|
|
13
|
+
from kash.utils.common.url import (
|
|
14
|
+
Url,
|
|
15
|
+
is_file_url,
|
|
16
|
+
is_url,
|
|
17
|
+
is_valid_path,
|
|
18
|
+
normalize_url,
|
|
19
|
+
parse_file_url,
|
|
20
|
+
)
|
|
14
21
|
from kash.utils.errors import FileNotFound
|
|
15
|
-
from kash.utils.file_utils.
|
|
16
|
-
from kash.utils.file_utils.
|
|
22
|
+
from kash.utils.file_utils.file_formats_model import file_format_info
|
|
23
|
+
from kash.utils.file_utils.filename_parsing import parse_file_ext
|
|
17
24
|
from kash.web_content.dir_store import DirStore
|
|
18
25
|
from kash.web_content.web_fetch import HttpHeaders, download_url
|
|
19
26
|
|
|
@@ -91,9 +98,25 @@ class CacheResult:
|
|
|
91
98
|
was_cached: bool
|
|
92
99
|
|
|
93
100
|
|
|
94
|
-
def _suffix_for(cacheable: Cacheable
|
|
101
|
+
def _suffix_for(cacheable: Cacheable) -> str | None:
|
|
95
102
|
key = cacheable.key if isinstance(cacheable, Loadable) else cacheable
|
|
96
|
-
|
|
103
|
+
|
|
104
|
+
# Check for recognized file extensions on URLs and Paths.
|
|
105
|
+
filename_ext = parse_file_ext(str(key))
|
|
106
|
+
if filename_ext:
|
|
107
|
+
return filename_ext.dot_ext
|
|
108
|
+
|
|
109
|
+
# Handle local paths
|
|
110
|
+
if is_file_url(str(key)):
|
|
111
|
+
path = parse_file_url(str(key))
|
|
112
|
+
elif is_valid_path(str(key)):
|
|
113
|
+
path = Path(str(key))
|
|
114
|
+
else:
|
|
115
|
+
# A non-local path with no recognized extension.
|
|
116
|
+
return None
|
|
117
|
+
|
|
118
|
+
# If it's a local file, check the file content too.
|
|
119
|
+
file_ext = file_format_info(path).suggested_file_ext
|
|
97
120
|
return file_ext.dot_ext if file_ext else None
|
|
98
121
|
|
|
99
122
|
|
|
@@ -121,13 +121,17 @@ h1 {
|
|
|
121
121
|
|
|
122
122
|
h2 {
|
|
123
123
|
font-size: 1.4rem;
|
|
124
|
-
margin-top:
|
|
124
|
+
margin-top: 2rem;
|
|
125
125
|
margin-bottom: 1rem;
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
+
h1 + h2 {
|
|
129
|
+
margin-top: 2rem;
|
|
130
|
+
}
|
|
131
|
+
|
|
128
132
|
h3 {
|
|
129
133
|
font-size: 1.03rem;
|
|
130
|
-
margin-top: 1.
|
|
134
|
+
margin-top: 1.6rem;
|
|
131
135
|
margin-bottom: 0.5rem;
|
|
132
136
|
}
|
|
133
137
|
|
|
@@ -340,7 +344,7 @@ tbody tr:nth-child(even) {
|
|
|
340
344
|
left: 50%;
|
|
341
345
|
transform: translateX(-50%);
|
|
342
346
|
box-sizing: border-box;
|
|
343
|
-
margin
|
|
347
|
+
margin: 2rem 0;
|
|
344
348
|
background-color: var(--color-bg-solid);
|
|
345
349
|
}
|
|
346
350
|
{% endblock table_styles %}
|
|
@@ -4,10 +4,10 @@ kash/actions/__init__.py,sha256=a4pQw8O-Y3q5N4Qg2jUV0xEZLX6d164FQhZ6zizY9fE,1357
|
|
|
4
4
|
kash/actions/core/assistant_chat.py,sha256=28G20cSr7Z94cltouTPve5TXY3km0lACrRvpLE27fK8,1837
|
|
5
5
|
kash/actions/core/chat.py,sha256=yCannBFa0cSpR_in-XSSuMm1x2ZZQUCKmlqzhsUfpOo,2696
|
|
6
6
|
kash/actions/core/format_markdown_template.py,sha256=ZJbtyTSypPo2ewLiGRSyIpVf711vQMhI_-Ng-FgCs80,2991
|
|
7
|
-
kash/actions/core/markdownify.py,sha256=
|
|
8
|
-
kash/actions/core/readability.py,sha256=
|
|
9
|
-
kash/actions/core/render_as_html.py,sha256=
|
|
10
|
-
kash/actions/core/show_webpage.py,sha256=
|
|
7
|
+
kash/actions/core/markdownify.py,sha256=KjdUeY4c9EhZ5geQrn22IoBv0P_p62q4zyyOYE0NRHM,1270
|
|
8
|
+
kash/actions/core/readability.py,sha256=ljdB2rOpzfKU2FpEJ2UELIzcdOAWvdUjFsxoHRTE3xo,989
|
|
9
|
+
kash/actions/core/render_as_html.py,sha256=bSyZdX9nZnP33QBdGSzWhInRREWXWayMG2oyiKn4rxw,1824
|
|
10
|
+
kash/actions/core/show_webpage.py,sha256=Ggba9jkx9U-FZOcuL0lkS-SwtPNUyxVsGdeQrqwWs1s,887
|
|
11
11
|
kash/actions/core/strip_html.py,sha256=FDLN_4CKB11q5cU4NixTf7PGrAq92AjQNbKAdvQDwCY,849
|
|
12
12
|
kash/actions/core/summarize_as_bullets.py,sha256=Zwr8lNzL77pwpnW_289LQjNBijNDpTPANfFdOJA-PZ4,2070
|
|
13
13
|
kash/actions/core/tabbed_webpage_config.py,sha256=rIbzEhBTmnkbSiRZC-Rj46T1J6c0jOztiKE9Usa4nsc,980
|
|
@@ -33,8 +33,8 @@ kash/commands/help/doc_commands.py,sha256=7lKR7mzue2N7pSkNqblpFJy892fS5N6jWVOHqe
|
|
|
33
33
|
kash/commands/help/help_commands.py,sha256=eJTpIhXck123PAUq2k-D3Q6UL6IQ8atOVYurLi2GD0A,4229
|
|
34
34
|
kash/commands/help/logo.py,sha256=W8SUach9FjoTqpHZwTGS582ry4ZluxbBp86ZCiAtDkY,3505
|
|
35
35
|
kash/commands/help/welcome.py,sha256=F4QBgj3e1dM9Pf0H4TSzCrkVfXQVKUIl0b6Qmofbdo4,905
|
|
36
|
-
kash/commands/workspace/selection_commands.py,sha256=
|
|
37
|
-
kash/commands/workspace/workspace_commands.py,sha256=
|
|
36
|
+
kash/commands/workspace/selection_commands.py,sha256=nZzA-H7Pk8kqSJVRlX7j1m6cZX-e0X8isOryDU41vqU,8156
|
|
37
|
+
kash/commands/workspace/workspace_commands.py,sha256=ZJ3aPsnQ0FOkaA6stpV4YPEOQRCOKTazbMCIQkk9Cmk,25119
|
|
38
38
|
kash/config/__init__.py,sha256=ytly9Typ1mWV4CXfV9G3CIPtPQ02u2rpZ304L3GlFro,148
|
|
39
39
|
kash/config/capture_output.py,sha256=ud3uUVNuDicHj3mI_nBUBO-VmOrxtBdA3z-I3D1lSCU,2398
|
|
40
40
|
kash/config/colors.py,sha256=6lqrB2RQYF2OLw-njfOqVHO9Bwiq7bW6K1ROCOAd1EM,9949
|
|
@@ -82,7 +82,7 @@ kash/embeddings/embeddings.py,sha256=v6RmrEHsx5PuE3fPrY15RK4fgW0K_VlNWDTjCVr11zY
|
|
|
82
82
|
kash/embeddings/text_similarity.py,sha256=BOo9Vcs5oi2Zs5La56uTkPMHo65XSd4qz_yr6GTfUA4,1924
|
|
83
83
|
kash/exec/__init__.py,sha256=rdSsKzTaXfSZmD5JvmUSSwmpfvl-moNv9PUgtE_WUpQ,1148
|
|
84
84
|
kash/exec/action_decorators.py,sha256=VOSCnFiev2_DuFoSk0i_moejwM4wJ1j6QfsQd93uetI,16480
|
|
85
|
-
kash/exec/action_exec.py,sha256=
|
|
85
|
+
kash/exec/action_exec.py,sha256=UHs-gKbu63d313MwfKsbVoRoTq7LbY8Vs0u_R4QZMh8,19025
|
|
86
86
|
kash/exec/action_registry.py,sha256=numU9pH_W5RgIrYmfi0iYMYy_kLJl6vup8PMrhxAfdc,2627
|
|
87
87
|
kash/exec/combiners.py,sha256=AJ6wgPUHsmwanObsUw64B83XzU26yuh5t4l7igLn82I,4291
|
|
88
88
|
kash/exec/command_exec.py,sha256=zc-gWm7kyB5J5Kp8xhULQ9Jj9AL927KkDPXXk-Yr1Bw,1292
|
|
@@ -92,9 +92,9 @@ kash/exec/history.py,sha256=l2XwHGBR1UgTGSFPSBE9mltmxvjR_5qFFO6d-Z008nc,1208
|
|
|
92
92
|
kash/exec/importing.py,sha256=xunmBapeUMNc6Zox7y6e_DZkidyWeouiFZpphajwSzc,1843
|
|
93
93
|
kash/exec/llm_transforms.py,sha256=p_aLp70VoIgheW4v8uoweeuEVWj06AzQekvn_jM3B-g,4378
|
|
94
94
|
kash/exec/precondition_checks.py,sha256=HymxL7qm4Yz8V76Um5pKdIRnQ2N-p9rpQQi1fI38bNA,2139
|
|
95
|
-
kash/exec/precondition_registry.py,sha256
|
|
96
|
-
kash/exec/preconditions.py,sha256=
|
|
97
|
-
kash/exec/resolve_args.py,sha256=
|
|
95
|
+
kash/exec/precondition_registry.py,sha256=-vYWN2wduq-hau2aDShQ5tglTF-cHC1vaZqU-peir9A,1384
|
|
96
|
+
kash/exec/preconditions.py,sha256=6bJ76AVFo0TLoykrGBNB0rrpIDWD0umfb3F5sMcmoKo,5131
|
|
97
|
+
kash/exec/resolve_args.py,sha256=gu65epzVrwWHdHA-KwAwYssncJIB84oHOJeoXXrQ2mM,4428
|
|
98
98
|
kash/exec/runtime_settings.py,sha256=aK6nGbZhKSIDVmV6AqV68hQkiaIGWnCiNzHtwwZ5V0w,3960
|
|
99
99
|
kash/exec/shell_callable_action.py,sha256=x-Hs4EqpsZfKEcwhWkhc27HCIfoI91b-DrbG40BLxRY,4350
|
|
100
100
|
kash/exec_model/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -103,7 +103,7 @@ kash/exec_model/commands_model.py,sha256=iM8QhzA0tAas5OwF5liUfHtm45XIH1LcvCviuh3
|
|
|
103
103
|
kash/exec_model/script_model.py,sha256=1VG3LhkTmlKzHOYouZ92ZpOSKSCcsz3-tHNcFMQF788,5031
|
|
104
104
|
kash/exec_model/shell_model.py,sha256=LUhQivbpXlerM-DUzNY7BtctNBbn08Wto8CSSxQDxRU,568
|
|
105
105
|
kash/file_storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
106
|
-
kash/file_storage/file_store.py,sha256=
|
|
106
|
+
kash/file_storage/file_store.py,sha256=c9Vt40JwleHOVqk-2b7yayW80RRiH-AApD-HZ3gdebo,30021
|
|
107
107
|
kash/file_storage/item_file_format.py,sha256=YAz7VqyfIoiSLQOoFdWsp-FI_2tTLXAPi8V8QXbo5ag,5475
|
|
108
108
|
kash/file_storage/metadata_dirs.py,sha256=9AqO3S3SSY1dtvP2iLX--E4ui0VIzXttG8R040otfyg,3820
|
|
109
109
|
kash/file_storage/persisted_yaml.py,sha256=4-4RkFqdlBUkTOwkdA4vRKUywEE9TaDo13OGaDUyU9M,1309
|
|
@@ -164,11 +164,11 @@ kash/model/compound_actions_model.py,sha256=HiDK5wwCu3WwZYHATZoLEguiqwR9V6V296wi
|
|
|
164
164
|
kash/model/concept_model.py,sha256=we2qOcy9Mv1q7XPfkDLp_CyO_-8DwAUfUYlpgy_jrFs,1011
|
|
165
165
|
kash/model/exec_model.py,sha256=IlfvtQyoFRRWhWju7vdXp9J-w_NGcGtL5DhDLy9gRd8,2250
|
|
166
166
|
kash/model/graph_model.py,sha256=jnctrPiBZ0xwAR8D54JMAJPanA1yZdaxSFQoIpe8anA,2662
|
|
167
|
-
kash/model/items_model.py,sha256=
|
|
167
|
+
kash/model/items_model.py,sha256=RLbRTo36AZR5QLHotcYo4s6na8u40rcLXA0F6POUHyw,34913
|
|
168
168
|
kash/model/language_list.py,sha256=I3RIbxTseVmPdhExQimimEv18Gmy2ImMbpXe0-_t1Qw,450
|
|
169
169
|
kash/model/llm_actions_model.py,sha256=a29uXVNfS2CiqvM7HPdC6H9A23rSQQihAideuBLMH8g,2110
|
|
170
170
|
kash/model/media_model.py,sha256=64Zic4cRjQpgf_-tOuZlZZe59mz_qu0s6OQSU0YlDUI,3357
|
|
171
|
-
kash/model/operations_model.py,sha256=
|
|
171
|
+
kash/model/operations_model.py,sha256=WmU-xeWGsqMLVN369dQEyVGU8T7G_KyLLsj6YFc5sVw,6517
|
|
172
172
|
kash/model/params_model.py,sha256=qGhsGvtDQoSqWkrKk9QZZfEh-jO1q2V-s-p6X-F37_M,14939
|
|
173
173
|
kash/model/paths_model.py,sha256=KDFm7wan7hjObHbnV2rR8-jsyLTVqbKcwFdKeLFRtdM,15889
|
|
174
174
|
kash/model/preconditions_model.py,sha256=-IfsVR0NkQhq_3hUTXzK2bFYAd--3YjSwUiDKHVQQqk,2887
|
|
@@ -195,7 +195,7 @@ kash/shell/ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
195
195
|
kash/shell/ui/shell_results.py,sha256=mvFHxK_oz3bNfF5_Twt6VqDO44TA1b256Bjf5oco804,4130
|
|
196
196
|
kash/shell/ui/shell_syntax.py,sha256=1fuDqcCV16AAWwWS4w4iT-tlSnl-Ywdrf68Ge8XIfmQ,751
|
|
197
197
|
kash/shell/utils/exception_printing.py,sha256=UizjOkBPhW6YbkiFP965BE5FrCwn04MXGDbxyTuyvOk,1908
|
|
198
|
-
kash/shell/utils/native_utils.py,sha256=
|
|
198
|
+
kash/shell/utils/native_utils.py,sha256=pAiuqqrjfNTesdArSya6CVavKVsuAXOcX3_XAIQrWtE,9151
|
|
199
199
|
kash/shell/utils/shell_function_wrapper.py,sha256=fgUuVhocYMKLkGJJQJOER5nFMAvM0ZVpfGu7iJPJI9s,7385
|
|
200
200
|
kash/utils/__init__.py,sha256=4Jl_AtgRADdGORimWhYZwbSfQSpQ6SiexNIZzmbcngI,111
|
|
201
201
|
kash/utils/errors.py,sha256=2lPL0fxI8pPOiDvjl0j-rvwY8uhmWetsrYYIc2-x1WY,3906
|
|
@@ -211,13 +211,13 @@ kash/utils/common/stack_traces.py,sha256=a2NwlK_0xxnjMCDC4LrQu7ueFylF-OImFG3bAAH
|
|
|
211
211
|
kash/utils/common/task_stack.py,sha256=XkeBz3BwYY1HxxTqd3f7CulV0s61PePAKw1Irrtvf5o,4536
|
|
212
212
|
kash/utils/common/type_utils.py,sha256=SJirXhPilQom_-OKkFToDLm_82ZwpjcNjRy8U1HaQ0Q,3829
|
|
213
213
|
kash/utils/common/uniquifier.py,sha256=75OY4KIVF8u1eoO0FCPbEGTyVpPOtM-0ctoG_s_jahM,3082
|
|
214
|
-
kash/utils/common/url.py,sha256=
|
|
214
|
+
kash/utils/common/url.py,sha256=R_P-CkOUiFxVdo9COcaL7YFvFIoAULj5-XxvmlFLvzo,9416
|
|
215
215
|
kash/utils/file_formats/chat_format.py,sha256=Onby7Zany1UQSUo_JzLs6MIfmoXViZeOAacRTMVe92M,11818
|
|
216
216
|
kash/utils/file_utils/__init__.py,sha256=loL_iW0oOZs0mJ5GelBPptBcqzYKSWdsGcHrpRyxitQ,43
|
|
217
217
|
kash/utils/file_utils/dir_info.py,sha256=HamMr58k_DanTLifj7A2JDxTGWXEZZx2pQuE6Hjcm8g,1856
|
|
218
218
|
kash/utils/file_utils/file_ext.py,sha256=-H63vlrVI3pfE2Cn_9qF7-QLDaUIu_njc4TieNgAHSY,1860
|
|
219
|
-
kash/utils/file_utils/file_formats.py,sha256=
|
|
220
|
-
kash/utils/file_utils/file_formats_model.py,sha256=
|
|
219
|
+
kash/utils/file_utils/file_formats.py,sha256=vnihRFLl85G1uzpqDc_uiGH9SIvbFTYVszz3srdSSz0,4949
|
|
220
|
+
kash/utils/file_utils/file_formats_model.py,sha256=M1KTGJdwC91SiQkBbEkext-hjMcjYpSnoNGUIuWiJKo,15448
|
|
221
221
|
kash/utils/file_utils/file_sort_filter.py,sha256=_k1chT3dJl5lSmKA2PW90KaoG4k4zftGdtwWoNEljP4,7136
|
|
222
222
|
kash/utils/file_utils/file_walk.py,sha256=cpwVDPuaVm95_ZwFJiAdIuZAGhASI3gJ3ZUsCGP75b8,5527
|
|
223
223
|
kash/utils/file_utils/filename_parsing.py,sha256=drHrH2B9W_5yAbXURNGJxNqj9GmTe8FayH6Gjw9e4-U,4194
|
|
@@ -231,7 +231,7 @@ kash/utils/rich_custom/ansi_cell_len.py,sha256=oQlNrqWB0f6pmigkbRRyeK6oWlGHMPbV_
|
|
|
231
231
|
kash/utils/rich_custom/rich_char_transform.py,sha256=3M89tViKM0y31VHsDoHi5eHFWlv5ME7F4p35IdDxnrw,2616
|
|
232
232
|
kash/utils/rich_custom/rich_indent.py,sha256=nz72yNpUuYjOsaPNVmxM81oEQm-GKEfQkNsuWmv16G0,2286
|
|
233
233
|
kash/utils/rich_custom/rich_markdown_fork.py,sha256=M_JRaSAyHrSg-wuLv9C9P7SkehSim3lwkqQPuMIFkVw,26551
|
|
234
|
-
kash/utils/text_handling/doc_normalization.py,sha256=
|
|
234
|
+
kash/utils/text_handling/doc_normalization.py,sha256=C211eSof8PUDVCqQtShuC4AMJpTZeBK8GHlGATp3c9E,2976
|
|
235
235
|
kash/utils/text_handling/escape_html_tags.py,sha256=7ZNQw3wfGzATVBBKmJMWmBTuznEPGzS6utjrH9HmmlQ,6361
|
|
236
236
|
kash/utils/text_handling/markdown_render.py,sha256=Ea-QiBND0kp4Dc9rYr8Z3dB_CRpAxneGHBOlTDWsDo0,3751
|
|
237
237
|
kash/utils/text_handling/markdown_utils.py,sha256=ndEd5ai80ZjSeMR104KLmAj0LOCPZln0ntd9tP5mf-E,9783
|
|
@@ -241,7 +241,7 @@ kash/web_content/canon_url.py,sha256=Zv2q7xQdIHBFkxxwyJn3_ME-qqMFRi_fKxE_IgV2Z50
|
|
|
241
241
|
kash/web_content/dir_store.py,sha256=BJc-s-RL5CC-GwhFTC_lhLXSMWluPPnLVmVBx-66DiM,3425
|
|
242
242
|
kash/web_content/file_cache_utils.py,sha256=JRXUCAmrc83iAgdiICU2EYGWcoORflWNl6GAVq-O80I,5529
|
|
243
243
|
kash/web_content/file_processing.py,sha256=cQC-MnJMM5qG9-y0S4yobkmRi6A75qhHjV6xTwbtYDY,1904
|
|
244
|
-
kash/web_content/local_file_cache.py,sha256=
|
|
244
|
+
kash/web_content/local_file_cache.py,sha256=PEDKU5VIwhCnSC-HXG4EkO2OzrOUDuuDBMuo3lP2EN0,9466
|
|
245
245
|
kash/web_content/web_extract.py,sha256=LbuG4AFEeIiXyUrN9CAxX0ret41Fqu_iTJSjIWyk3Bg,2296
|
|
246
246
|
kash/web_content/web_extract_justext.py,sha256=74HLJBKDGKatwxyRDX6za70bZG9LrVmtj9jLX7UJzg4,2540
|
|
247
247
|
kash/web_content/web_extract_readabilipy.py,sha256=IT7ET5IoU2-Nf37-Neh6CkKMvLL3WTNVJjq7ZMOx6OM,808
|
|
@@ -251,7 +251,7 @@ kash/web_gen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
251
251
|
kash/web_gen/simple_webpage.py,sha256=c_kLXAsjP9wB9-ppF7MMJMw5VHXzVwkcHymfo5YOfS0,1402
|
|
252
252
|
kash/web_gen/tabbed_webpage.py,sha256=Q_Htw2QO0O9H3A9OFrWw9GBD73cbwB6hOKF-W6mO6YE,4807
|
|
253
253
|
kash/web_gen/template_render.py,sha256=aypo6UanouftV4RpxgNm6JdquelI52fV0IlihdA3yjE,1908
|
|
254
|
-
kash/web_gen/templates/base_styles.css.jinja,sha256=
|
|
254
|
+
kash/web_gen/templates/base_styles.css.jinja,sha256=xrA7567wXHp9oMRqXhiMcEjH_IMeo6Ny1_pThYWpe4o,9202
|
|
255
255
|
kash/web_gen/templates/base_webpage.html.jinja,sha256=Nvdd8pLSG2OdbrDQRvYcexYIZoMdr1G_7MdUVHNDoA8,7945
|
|
256
256
|
kash/web_gen/templates/content_styles.css.jinja,sha256=3qcIwIt3DipCDJa9z6oIM_BMxmwoT7E_loTK0F3L9Vo,3629
|
|
257
257
|
kash/web_gen/templates/explain_view.html.jinja,sha256=DNw5Iw5SrhIUFRGB4qNvfcKXsBHVbEJVURGdhvyC75Q,949
|
|
@@ -263,7 +263,6 @@ kash/workspaces/param_state.py,sha256=vT_eGWqg2SRviIM5jqEAauznX2B5Xt2nHHu2oRxTcI
|
|
|
263
263
|
kash/workspaces/selections.py,sha256=rEUuQlrQ3C_54bzBSKDTTptgX8oZPqN0Ao4uaXSWA-Q,12003
|
|
264
264
|
kash/workspaces/source_items.py,sha256=Pwnw3OhjR2IJEMEeHf6hpKloj-ellM5vsY7LgkGevRY,2861
|
|
265
265
|
kash/workspaces/workspace_dirs.py,sha256=kjuY4t7mSSXq00fZmln7p9TWq4kAZoPTCDM0DG7uEaI,1545
|
|
266
|
-
kash/workspaces/workspace_importing.py,sha256=4IJo713Kuoynhd_lcZF9M_DZ0rrMK_IDfhTVgwKmVyQ,1934
|
|
267
266
|
kash/workspaces/workspace_output.py,sha256=MMg_KumkHKFGc0DOUFaW5ImpgqIfdlsLtvXbLEt1hwI,5692
|
|
268
267
|
kash/workspaces/workspace_registry.py,sha256=SQt2DZgBEu95Zj9fpy67XdJPgJyKFDCU2laSuiZswNo,2200
|
|
269
268
|
kash/workspaces/workspaces.py,sha256=kQyS3F57Y9A9xVT_Ss7HzJhDGlI-UXHKvRDnEVkBnik,6764
|
|
@@ -280,8 +279,8 @@ kash/xonsh_custom/xonsh_modern_tools.py,sha256=mj_b34LZXfE8MJe9EpDmp5JZ0tDM1biYN
|
|
|
280
279
|
kash/xonsh_custom/xonsh_ranking_completer.py,sha256=ZRGiAfoEgqgnlq2-ReUVEaX5oOgW1DQ9WxIv2OJLuTo,5620
|
|
281
280
|
kash/xontrib/fnm.py,sha256=V2tsOdmIDgbFbZSfMLpsvDIwwJJqiYnOkOySD1cXNXw,3700
|
|
282
281
|
kash/xontrib/kash_extension.py,sha256=JRRJC3cZSMOl4sSWEdKAQ_dVRMubWaOltKr8G0dWt6Y,1876
|
|
283
|
-
kash_shell-0.3.
|
|
284
|
-
kash_shell-0.3.
|
|
285
|
-
kash_shell-0.3.
|
|
286
|
-
kash_shell-0.3.
|
|
287
|
-
kash_shell-0.3.
|
|
282
|
+
kash_shell-0.3.15.dist-info/METADATA,sha256=nxMDgU5r65l0fnmvMfJkeIXQeUMxXKdvjk6EFTpgyQY,31258
|
|
283
|
+
kash_shell-0.3.15.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
284
|
+
kash_shell-0.3.15.dist-info/entry_points.txt,sha256=SQraWDAo8SqYpthLXThei0mf_hGGyhYBUO-Er_0HcwI,85
|
|
285
|
+
kash_shell-0.3.15.dist-info/licenses/LICENSE,sha256=rCh2PsfYeiU6FK_0wb58kHGm_Fj5c43fdcHEexiVzIo,34562
|
|
286
|
+
kash_shell-0.3.15.dist-info/RECORD,,
|