kash-shell 0.3.22__py3-none-any.whl → 0.3.24__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/combine_docs.py +52 -0
- kash/actions/core/concat_docs.py +47 -0
- kash/commands/workspace/workspace_commands.py +2 -2
- kash/config/logger.py +3 -2
- kash/config/settings.py +8 -0
- kash/docs/markdown/topics/a2_installation.md +2 -2
- kash/embeddings/embeddings.py +1 -1
- kash/exec/action_exec.py +1 -1
- kash/exec/fetch_url_items.py +52 -16
- kash/file_storage/file_store.py +3 -3
- kash/llm_utils/llm_completion.py +1 -1
- kash/mcp/mcp_cli.py +2 -2
- kash/utils/api_utils/api_retries.py +348 -14
- kash/utils/api_utils/gather_limited.py +366 -512
- kash/utils/api_utils/http_utils.py +46 -0
- kash/utils/api_utils/progress_protocol.py +49 -56
- kash/utils/rich_custom/multitask_status.py +70 -21
- kash/utils/text_handling/markdown_utils.py +14 -3
- kash/web_content/web_extract.py +13 -9
- kash/web_content/web_fetch.py +289 -60
- kash/web_content/web_page_model.py +5 -0
- {kash_shell-0.3.22.dist-info → kash_shell-0.3.24.dist-info}/METADATA +5 -3
- {kash_shell-0.3.22.dist-info → kash_shell-0.3.24.dist-info}/RECORD +26 -23
- {kash_shell-0.3.22.dist-info → kash_shell-0.3.24.dist-info}/WHEEL +0 -0
- {kash_shell-0.3.22.dist-info → kash_shell-0.3.24.dist-info}/entry_points.txt +0 -0
- {kash_shell-0.3.22.dist-info → kash_shell-0.3.24.dist-info}/licenses/LICENSE +0 -0
kash/web_content/web_fetch.py
CHANGED
|
@@ -2,11 +2,13 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
from dataclasses import dataclass
|
|
5
|
-
from
|
|
5
|
+
from enum import Enum
|
|
6
|
+
from functools import cache, cached_property
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
from typing import TYPE_CHECKING, Any
|
|
8
9
|
from urllib.parse import urlparse
|
|
9
10
|
|
|
11
|
+
from cachetools import TTLCache
|
|
10
12
|
from strif import atomic_output_file, copyfile_atomic
|
|
11
13
|
|
|
12
14
|
from kash.config.env_settings import KashEnv
|
|
@@ -14,59 +16,245 @@ from kash.utils.common.url import Url
|
|
|
14
16
|
from kash.utils.file_utils.file_formats import MimeType
|
|
15
17
|
|
|
16
18
|
if TYPE_CHECKING:
|
|
17
|
-
from
|
|
19
|
+
from curl_cffi.requests import Response as CurlCffiResponse
|
|
20
|
+
from curl_cffi.requests import Session as CurlCffiSession
|
|
21
|
+
from httpx import Client as HttpxClient
|
|
22
|
+
from httpx import Response as HttpxResponse
|
|
18
23
|
|
|
19
24
|
log = logging.getLogger(__name__)
|
|
20
25
|
|
|
21
26
|
|
|
22
27
|
DEFAULT_TIMEOUT = 30
|
|
28
|
+
CURL_CFFI_IMPERSONATE_VERSION = "chrome120"
|
|
23
29
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"Mozilla/5.0 (Macintosh; Intel Mac OS X
|
|
30
|
+
# Header helpers
|
|
31
|
+
_DEFAULT_UA = (
|
|
32
|
+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 14_7_3) "
|
|
33
|
+
"AppleWebKit/537.36 (KHTML, like Gecko) "
|
|
34
|
+
"Chrome/126.0.0.0 Safari/537.36"
|
|
27
35
|
)
|
|
36
|
+
_SIMPLE_HEADERS = {"User-Agent": KashEnv.KASH_USER_AGENT.read_str(default=_DEFAULT_UA)}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class ClientMode(Enum):
|
|
40
|
+
"""
|
|
41
|
+
Defines the web client and settings.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
SIMPLE = "SIMPLE"
|
|
45
|
+
"""httpx with minimal headers"""
|
|
46
|
+
|
|
47
|
+
BROWSER_HEADERS = "BROWSER_HEADERS"
|
|
48
|
+
"""httpx with extensive, manually-set headers"""
|
|
49
|
+
|
|
50
|
+
CURL_CFFI = "CURL_CFFI"
|
|
51
|
+
"""curl_cffi for full browser impersonation (incl. TLS)"""
|
|
52
|
+
|
|
53
|
+
AUTO = "AUTO"
|
|
54
|
+
"""Automatically pick CURL_CFFI if available, otherwise BROWSER_HEADERS"""
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@cache
|
|
58
|
+
def _have_brotli() -> bool:
|
|
59
|
+
"""
|
|
60
|
+
Check if brotli compression is available.
|
|
61
|
+
Warns once if brotli is not installed.
|
|
62
|
+
"""
|
|
63
|
+
try:
|
|
64
|
+
import brotli # noqa: F401
|
|
65
|
+
|
|
66
|
+
return True
|
|
67
|
+
except ImportError:
|
|
68
|
+
log.warning("web_fetch: brotli package not found; install for better download performance")
|
|
69
|
+
return False
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@cache
|
|
73
|
+
def _have_curl_cffi() -> bool:
|
|
74
|
+
"""
|
|
75
|
+
Check if curl_cffi is available.
|
|
76
|
+
Warns once if curl_cffi is not installed.
|
|
77
|
+
"""
|
|
78
|
+
try:
|
|
79
|
+
import curl_cffi.requests # noqa: F401
|
|
80
|
+
|
|
81
|
+
return True
|
|
82
|
+
except ImportError:
|
|
83
|
+
log.warning("web_fetch: curl_cffi package not found; install for browser impersonation")
|
|
84
|
+
return False
|
|
28
85
|
|
|
29
86
|
|
|
30
|
-
|
|
31
|
-
|
|
87
|
+
@cache
|
|
88
|
+
def _get_auto_mode() -> ClientMode:
|
|
89
|
+
"""
|
|
90
|
+
Automatically select the best available client mode.
|
|
91
|
+
Logs the decision once due to caching.
|
|
92
|
+
"""
|
|
93
|
+
if _have_curl_cffi():
|
|
94
|
+
log.info("web_fetch: AUTO mode selected CURL_CFFI (full browser impersonation)")
|
|
95
|
+
return ClientMode.CURL_CFFI
|
|
96
|
+
else:
|
|
97
|
+
log.info("web_fetch: AUTO mode selected BROWSER_HEADERS (httpx with browser headers)")
|
|
98
|
+
return ClientMode.BROWSER_HEADERS
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@cache
|
|
102
|
+
def _browser_like_headers() -> dict[str, str]:
|
|
103
|
+
"""
|
|
104
|
+
Full header set that looks like a 2025-era Chrome GET.
|
|
105
|
+
"""
|
|
106
|
+
ua = KashEnv.KASH_USER_AGENT.read_str(default=_DEFAULT_UA)
|
|
107
|
+
|
|
108
|
+
# Build Accept-Encoding based on available compression support
|
|
109
|
+
encodings = ["gzip", "deflate"]
|
|
110
|
+
if _have_brotli():
|
|
111
|
+
encodings.append("br")
|
|
112
|
+
accept_encoding = ", ".join(encodings)
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
"User-Agent": ua,
|
|
116
|
+
"Accept": (
|
|
117
|
+
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8"
|
|
118
|
+
),
|
|
119
|
+
"Accept-Language": "en-US,en;q=0.9",
|
|
120
|
+
"Accept-Encoding": accept_encoding,
|
|
121
|
+
"Referer": "https://www.google.com/",
|
|
122
|
+
"DNT": "1",
|
|
123
|
+
"Upgrade-Insecure-Requests": "1",
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
# Cookie priming cache - tracks which hosts have been primed
|
|
128
|
+
_primed_hosts = TTLCache(maxsize=10000, ttl=3600)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _prime_host(host: str, client: HttpxClient | CurlCffiSession, timeout: int, **kwargs) -> bool:
|
|
132
|
+
"""
|
|
133
|
+
Prime cookies for a host using the provided client and extra arguments.
|
|
134
|
+
"""
|
|
135
|
+
if host in _primed_hosts:
|
|
136
|
+
log.debug("Cookie priming for %s skipped (cached)", host)
|
|
137
|
+
return True
|
|
138
|
+
|
|
139
|
+
try:
|
|
140
|
+
root = f"https://{host}/"
|
|
141
|
+
# Pass client-specific kwargs like `impersonate`
|
|
142
|
+
client.get(root, timeout=timeout, **kwargs)
|
|
143
|
+
log.debug("Cookie priming completed for %s", host)
|
|
144
|
+
except Exception as exc:
|
|
145
|
+
log.debug("Cookie priming for %s failed (%s); continuing", host, exc)
|
|
146
|
+
|
|
147
|
+
# Mark as primed (both success and failure to avoid immediate retries)
|
|
148
|
+
_primed_hosts[host] = True
|
|
149
|
+
return True
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _get_req_headers(
|
|
153
|
+
mode: ClientMode, user_headers: dict[str, str] | None = None
|
|
154
|
+
) -> dict[str, str]:
|
|
155
|
+
"""
|
|
156
|
+
Build headers based on the selected ClientMode.
|
|
157
|
+
For CURL_CFFI, curl_cffi handles headers automatically.
|
|
158
|
+
"""
|
|
159
|
+
if mode is ClientMode.AUTO:
|
|
160
|
+
mode = _get_auto_mode()
|
|
161
|
+
|
|
162
|
+
base_headers = {}
|
|
163
|
+
if mode is ClientMode.SIMPLE:
|
|
164
|
+
base_headers = _SIMPLE_HEADERS
|
|
165
|
+
elif mode is ClientMode.BROWSER_HEADERS:
|
|
166
|
+
base_headers = _browser_like_headers()
|
|
167
|
+
elif mode is ClientMode.CURL_CFFI:
|
|
168
|
+
# curl_cffi handles the important headers (UA, Accept-*, etc.)
|
|
169
|
+
# We only need to add user-provided ones.
|
|
170
|
+
return user_headers or {}
|
|
171
|
+
|
|
172
|
+
if user_headers:
|
|
173
|
+
return {**base_headers, **user_headers}
|
|
174
|
+
|
|
175
|
+
return base_headers
|
|
32
176
|
|
|
33
177
|
|
|
34
178
|
def fetch_url(
|
|
35
179
|
url: Url,
|
|
180
|
+
*,
|
|
36
181
|
timeout: int = DEFAULT_TIMEOUT,
|
|
37
182
|
auth: Any | None = None,
|
|
38
183
|
headers: dict[str, str] | None = None,
|
|
39
|
-
|
|
184
|
+
mode: ClientMode = ClientMode.AUTO,
|
|
185
|
+
) -> HttpxResponse | CurlCffiResponse:
|
|
40
186
|
"""
|
|
41
|
-
Fetch a URL
|
|
42
|
-
Raise httpx.HTTPError for non-2xx responses.
|
|
187
|
+
Fetch a URL, dispatching to httpx or curl_cffi based on the mode.
|
|
43
188
|
"""
|
|
44
|
-
|
|
189
|
+
if mode is ClientMode.AUTO:
|
|
190
|
+
mode = _get_auto_mode()
|
|
191
|
+
|
|
192
|
+
req_headers = _get_req_headers(mode, headers)
|
|
193
|
+
parsed_url = urlparse(str(url))
|
|
194
|
+
|
|
195
|
+
# Handle curl_cffi mode
|
|
196
|
+
if mode is ClientMode.CURL_CFFI:
|
|
197
|
+
if not _have_curl_cffi():
|
|
198
|
+
raise ValueError("Could not find curl_cffi, which is needed for CURL_CFFI mode")
|
|
45
199
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
200
|
+
from curl_cffi.requests import Session
|
|
201
|
+
|
|
202
|
+
with Session() as client:
|
|
203
|
+
# Set headers on the session - they will be sent with all requests
|
|
204
|
+
client.headers.update(req_headers)
|
|
205
|
+
_prime_host(
|
|
206
|
+
parsed_url.netloc, client, timeout, impersonate=CURL_CFFI_IMPERSONATE_VERSION
|
|
207
|
+
)
|
|
208
|
+
log.debug("fetch_url (curl_cffi): using session headers: %s", client.headers)
|
|
209
|
+
response = client.get(
|
|
210
|
+
url,
|
|
211
|
+
impersonate=CURL_CFFI_IMPERSONATE_VERSION,
|
|
212
|
+
timeout=timeout,
|
|
213
|
+
auth=auth,
|
|
214
|
+
allow_redirects=True,
|
|
215
|
+
)
|
|
216
|
+
log.info(
|
|
217
|
+
"Fetched (curl_cffi): %s (%s bytes): %s",
|
|
218
|
+
response.status_code,
|
|
219
|
+
len(response.content),
|
|
220
|
+
url,
|
|
221
|
+
)
|
|
222
|
+
response.raise_for_status()
|
|
223
|
+
return response
|
|
224
|
+
|
|
225
|
+
# Handle httpx modes
|
|
226
|
+
else:
|
|
227
|
+
import httpx
|
|
228
|
+
|
|
229
|
+
with httpx.Client(
|
|
230
|
+
follow_redirects=True,
|
|
231
|
+
timeout=timeout,
|
|
232
|
+
auth=auth,
|
|
233
|
+
headers=req_headers,
|
|
234
|
+
) as client:
|
|
235
|
+
log.debug("fetch_url (httpx): using headers: %s", client.headers)
|
|
236
|
+
|
|
237
|
+
# Cookie priming only makes sense for the browser-like mode
|
|
238
|
+
if mode is ClientMode.BROWSER_HEADERS:
|
|
239
|
+
_prime_host(parsed_url.netloc, client, timeout)
|
|
240
|
+
|
|
241
|
+
response = client.get(url)
|
|
242
|
+
log.info(
|
|
243
|
+
"Fetched (httpx): %s (%s bytes): %s",
|
|
244
|
+
response.status_code,
|
|
245
|
+
len(response.content),
|
|
246
|
+
url,
|
|
247
|
+
)
|
|
248
|
+
response.raise_for_status()
|
|
249
|
+
return response
|
|
57
250
|
|
|
58
251
|
|
|
59
252
|
@dataclass(frozen=True)
|
|
60
253
|
class HttpHeaders:
|
|
61
|
-
"""
|
|
62
|
-
HTTP response headers.
|
|
63
|
-
"""
|
|
64
|
-
|
|
65
254
|
headers: dict[str, str]
|
|
66
255
|
|
|
67
256
|
@cached_property
|
|
68
257
|
def mime_type(self) -> MimeType | None:
|
|
69
|
-
"""Get content type header, if available."""
|
|
70
258
|
for key, value in self.headers.items():
|
|
71
259
|
if key.lower() == "content-type":
|
|
72
260
|
return MimeType(value)
|
|
@@ -76,11 +264,12 @@ class HttpHeaders:
|
|
|
76
264
|
def download_url(
|
|
77
265
|
url: Url,
|
|
78
266
|
target_filename: str | Path,
|
|
79
|
-
|
|
267
|
+
*,
|
|
80
268
|
show_progress: bool = False,
|
|
81
269
|
timeout: int = DEFAULT_TIMEOUT,
|
|
82
270
|
auth: Any | None = None,
|
|
83
271
|
headers: dict[str, str] | None = None,
|
|
272
|
+
mode: ClientMode = ClientMode.AUTO,
|
|
84
273
|
) -> HttpHeaders | None:
|
|
85
274
|
"""
|
|
86
275
|
Download given file, optionally with progress bar, streaming to a target file.
|
|
@@ -88,8 +277,8 @@ def download_url(
|
|
|
88
277
|
Raise httpx.HTTPError for non-2xx responses.
|
|
89
278
|
Returns response headers for HTTP/HTTPS requests, None for other URL types.
|
|
90
279
|
"""
|
|
91
|
-
|
|
92
|
-
|
|
280
|
+
if mode is ClientMode.AUTO:
|
|
281
|
+
mode = _get_auto_mode()
|
|
93
282
|
|
|
94
283
|
target_filename = str(target_filename)
|
|
95
284
|
parsed_url = urlparse(url)
|
|
@@ -106,39 +295,79 @@ def download_url(
|
|
|
106
295
|
s3_path = parsed_url.path.lstrip("/")
|
|
107
296
|
s3.Bucket(parsed_url.netloc).download_file(s3_path, target_filename)
|
|
108
297
|
return None
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
298
|
+
|
|
299
|
+
req_headers = _get_req_headers(mode, headers)
|
|
300
|
+
response_headers = None
|
|
301
|
+
|
|
302
|
+
def stream_to_file(response_iterator, total_size):
|
|
303
|
+
with atomic_output_file(target_filename, make_parents=True) as temp_filename:
|
|
304
|
+
with open(temp_filename, "wb") as f:
|
|
305
|
+
if not show_progress:
|
|
306
|
+
for chunk in response_iterator:
|
|
307
|
+
if chunk: # Skip empty chunks
|
|
308
|
+
f.write(chunk)
|
|
309
|
+
else:
|
|
310
|
+
from tqdm import tqdm
|
|
311
|
+
|
|
312
|
+
with tqdm(
|
|
313
|
+
total=total_size,
|
|
314
|
+
unit="B",
|
|
315
|
+
unit_scale=True,
|
|
316
|
+
desc=f"Downloading {Path(target_filename).name}",
|
|
317
|
+
) as progress:
|
|
318
|
+
for chunk in response_iterator:
|
|
319
|
+
if chunk: # Skip empty chunks
|
|
320
|
+
f.write(chunk)
|
|
321
|
+
progress.update(len(chunk))
|
|
322
|
+
|
|
323
|
+
# Handle curl_cffi mode
|
|
324
|
+
if mode is ClientMode.CURL_CFFI:
|
|
325
|
+
if not _have_curl_cffi():
|
|
326
|
+
raise ValueError("Could not find curl_cffi, which is needed for CURL_CFFI mode")
|
|
327
|
+
|
|
328
|
+
from curl_cffi.requests import Session
|
|
329
|
+
|
|
330
|
+
with Session() as client:
|
|
331
|
+
# Set headers on the session; they will be sent with all requests
|
|
332
|
+
client.headers.update(req_headers)
|
|
333
|
+
_prime_host(
|
|
334
|
+
parsed_url.netloc, client, timeout, impersonate=CURL_CFFI_IMPERSONATE_VERSION
|
|
335
|
+
)
|
|
336
|
+
log.debug("download_url (curl_cffi): using session headers: %s", client.headers)
|
|
337
|
+
|
|
338
|
+
response = client.get(
|
|
118
339
|
url,
|
|
119
|
-
|
|
340
|
+
impersonate=CURL_CFFI_IMPERSONATE_VERSION,
|
|
120
341
|
timeout=timeout,
|
|
121
342
|
auth=auth,
|
|
122
|
-
|
|
123
|
-
|
|
343
|
+
allow_redirects=True,
|
|
344
|
+
stream=True,
|
|
345
|
+
)
|
|
346
|
+
response.raise_for_status()
|
|
347
|
+
response_headers = dict(response.headers)
|
|
348
|
+
total = int(response.headers.get("content-length", "0"))
|
|
349
|
+
|
|
350
|
+
# Use iter_content for streaming; this is the standard method for curl_cffi
|
|
351
|
+
chunk_iterator = response.iter_content(chunk_size=8192)
|
|
352
|
+
stream_to_file(chunk_iterator, total)
|
|
353
|
+
|
|
354
|
+
# Handle httpx modes
|
|
355
|
+
else:
|
|
356
|
+
import httpx
|
|
357
|
+
|
|
358
|
+
with httpx.Client(follow_redirects=True, timeout=timeout, headers=req_headers) as client:
|
|
359
|
+
if mode is ClientMode.BROWSER_HEADERS:
|
|
360
|
+
_prime_host(parsed_url.netloc, client, timeout)
|
|
361
|
+
|
|
362
|
+
log.debug("download_url (httpx): using headers: %s", client.headers)
|
|
363
|
+
with client.stream("GET", url, auth=auth, follow_redirects=True) as response:
|
|
124
364
|
response.raise_for_status()
|
|
125
365
|
response_headers = dict(response.headers)
|
|
126
|
-
|
|
366
|
+
total = int(response.headers.get("content-length", "0"))
|
|
367
|
+
stream_to_file(response.iter_bytes(), total)
|
|
127
368
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
else:
|
|
134
|
-
with tqdm(total=total_size, unit="B", unit_scale=True) as progress:
|
|
135
|
-
for chunk in response.iter_bytes():
|
|
136
|
-
f.write(chunk)
|
|
137
|
-
progress.update(len(chunk))
|
|
138
|
-
finally:
|
|
139
|
-
if not session: # Only close if we created the client
|
|
140
|
-
client.close()
|
|
141
|
-
if response:
|
|
142
|
-
response.raise_for_status() # In case of errors during streaming
|
|
143
|
-
|
|
144
|
-
return HttpHeaders(response_headers) if response_headers else None
|
|
369
|
+
# Filter out None values from headers for HttpHeaders type compatibility
|
|
370
|
+
if response_headers:
|
|
371
|
+
clean_headers = {k: v for k, v in response_headers.items() if v is not None}
|
|
372
|
+
return HttpHeaders(clean_headers)
|
|
373
|
+
return None
|
|
@@ -6,6 +6,7 @@ from pydantic.dataclasses import dataclass
|
|
|
6
6
|
|
|
7
7
|
from kash.utils.common.url import Url
|
|
8
8
|
from kash.utils.file_utils.file_formats_model import FileFormatInfo
|
|
9
|
+
from kash.web_content.local_file_cache import CacheResult
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
@dataclass
|
|
@@ -18,6 +19,9 @@ class WebPageData:
|
|
|
18
19
|
The `clean_html` field should be a clean HTML version of the page, if available.
|
|
19
20
|
The `saved_content` is optional but can be used to reference the original content,
|
|
20
21
|
especially for large or non-text content.
|
|
22
|
+
|
|
23
|
+
Optionally exposes the cache result for the content, so the client can have
|
|
24
|
+
information about headers and whether it was cached.
|
|
21
25
|
"""
|
|
22
26
|
|
|
23
27
|
locator: Url | Path
|
|
@@ -29,6 +33,7 @@ class WebPageData:
|
|
|
29
33
|
saved_content: Path | None = None
|
|
30
34
|
format_info: FileFormatInfo | None = None
|
|
31
35
|
thumbnail_url: Url | None = None
|
|
36
|
+
cache_result: CacheResult | None = None
|
|
32
37
|
|
|
33
38
|
def __repr__(self):
|
|
34
39
|
return abbrev_obj(self)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kash-shell
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.24
|
|
4
4
|
Summary: The knowledge agent shell (core)
|
|
5
5
|
Project-URL: Repository, https://github.com/jlevy/kash-shell
|
|
6
6
|
Author-email: Joshua Levy <joshua@cal.berkeley.edu>
|
|
@@ -24,12 +24,14 @@ Requires-Dist: chopdiff>=0.2.3
|
|
|
24
24
|
Requires-Dist: clideps>=0.1.4
|
|
25
25
|
Requires-Dist: colour>=0.1.5
|
|
26
26
|
Requires-Dist: cssselect>=1.2.0
|
|
27
|
+
Requires-Dist: curl-cffi>=0.11.4
|
|
27
28
|
Requires-Dist: deepgram-sdk>=3.10.1
|
|
28
29
|
Requires-Dist: dunamai>=1.23.0
|
|
29
30
|
Requires-Dist: fastapi>=0.115.11
|
|
30
31
|
Requires-Dist: flowmark>=0.4.6
|
|
31
32
|
Requires-Dist: frontmatter-format>=0.2.1
|
|
32
33
|
Requires-Dist: funlog>=0.2.0
|
|
34
|
+
Requires-Dist: httpx[brotli]>=0.28.1
|
|
33
35
|
Requires-Dist: humanfriendly>=10.0
|
|
34
36
|
Requires-Dist: inquirerpy>=0.3.4
|
|
35
37
|
Requires-Dist: jinja2>=3.1.6
|
|
@@ -329,7 +331,7 @@ These are for `kash-media` but you can use a `kash-shell` for a more basic setup
|
|
|
329
331
|
|
|
330
332
|
You can use kash from your MCP client (such as Anthropic Desktop or Cursor).
|
|
331
333
|
|
|
332
|
-
You do this by running the the `
|
|
334
|
+
You do this by running the the `kash-mcp` binary to make kash actions available as MCP
|
|
333
335
|
tools.
|
|
334
336
|
|
|
335
337
|
For Claude Desktop, my config looks like this:
|
|
@@ -338,7 +340,7 @@ For Claude Desktop, my config looks like this:
|
|
|
338
340
|
{
|
|
339
341
|
"mcpServers": {
|
|
340
342
|
"kash": {
|
|
341
|
-
"command": "/Users/levy/.local/bin/
|
|
343
|
+
"command": "/Users/levy/.local/bin/kash-mcp",
|
|
342
344
|
"args": ["--proxy"]
|
|
343
345
|
}
|
|
344
346
|
}
|
|
@@ -3,6 +3,8 @@ kash/__main__.py,sha256=83JiHvCNK7BAwEkaTD0kVekj7PEMucvCyquSl4p-1QA,78
|
|
|
3
3
|
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
|
+
kash/actions/core/combine_docs.py,sha256=5bTU7n_ICavvTXfC7fs5BDMeZYn7Xh5FkU7DVQqDHAQ,1536
|
|
7
|
+
kash/actions/core/concat_docs.py,sha256=Umx3VzFiHJGY-76AEs4ju_1HnB9SbQsBux03Mkeig24,1345
|
|
6
8
|
kash/actions/core/format_markdown_template.py,sha256=ZJbtyTSypPo2ewLiGRSyIpVf711vQMhI_-Ng-FgCs80,2991
|
|
7
9
|
kash/actions/core/markdownify_html.py,sha256=luMXwwaY6E8oidgdrwV6KSgzdG0iX2JKS5aTNYDdZpA,1810
|
|
8
10
|
kash/actions/core/minify_html.py,sha256=99r3SjpI2NQP7e5MnMixAiT5lxPx7t2nyJvJi6Yps6w,1365
|
|
@@ -35,18 +37,18 @@ kash/commands/help/help_commands.py,sha256=eJTpIhXck123PAUq2k-D3Q6UL6IQ8atOVYurL
|
|
|
35
37
|
kash/commands/help/logo.py,sha256=4K3pPIiNlPIgdNeOgdIGbJQCKmqCEsDOhuWPH51msWo,3377
|
|
36
38
|
kash/commands/help/welcome.py,sha256=uM2UMtHG4oXTxU8IFlFbTOTmR5_XTCUtT828V_tKMsk,920
|
|
37
39
|
kash/commands/workspace/selection_commands.py,sha256=nZzA-H7Pk8kqSJVRlX7j1m6cZX-e0X8isOryDU41vqU,8156
|
|
38
|
-
kash/commands/workspace/workspace_commands.py,sha256=
|
|
40
|
+
kash/commands/workspace/workspace_commands.py,sha256=xJ3Agg_tzGvH55705ovoZDYiTEO4FejWclwcsMWKitY,21965
|
|
39
41
|
kash/config/__init__.py,sha256=ytly9Typ1mWV4CXfV9G3CIPtPQ02u2rpZ304L3GlFro,148
|
|
40
42
|
kash/config/capture_output.py,sha256=ud3uUVNuDicHj3mI_nBUBO-VmOrxtBdA3z-I3D1lSCU,2398
|
|
41
43
|
kash/config/colors.py,sha256=osHrR1rLrFV5j2kTdN4z-DDM-HmbnBfMaOj4NiBdFzg,13527
|
|
42
44
|
kash/config/env_settings.py,sha256=uhCdfs9-TzJ15SzbuIQP1yIORaLUqYXCxh9qq_Z8cJc,996
|
|
43
45
|
kash/config/init.py,sha256=aE4sZ6DggBmmoZEx9C5mQKrEbcDiswX--HF7pfCFKzc,526
|
|
44
46
|
kash/config/lazy_imports.py,sha256=MCZXLnKvNyfHi0k7MU5rNwcdJtUF28naCixuogsAOAA,805
|
|
45
|
-
kash/config/logger.py,sha256=
|
|
47
|
+
kash/config/logger.py,sha256=M5_w7LTxHO60VqLVM0yqu8pK6FHU-Twt4wfoJwuxWbY,12102
|
|
46
48
|
kash/config/logger_basic.py,sha256=Gsxitz7xeMGCUr5qjWK6y72qeMsIz4mcDZP5xyzK0WU,1598
|
|
47
49
|
kash/config/logo.txt,sha256=P4RO1cJ9HRF1BavTp3Kae9iByDNhzhEC-qLAa6ww1RA,217
|
|
48
50
|
kash/config/server_config.py,sha256=eQ1yxDk031QI0Efp0I1VetqQd9wG7MrLVBCHFm4gp2g,1790
|
|
49
|
-
kash/config/settings.py,sha256=
|
|
51
|
+
kash/config/settings.py,sha256=Tja5MNZJDjo5SRaCngfQQFTTjmVGUlmkCZ7UEAhvXqw,9087
|
|
50
52
|
kash/config/setup.py,sha256=zFxfTPG1cbsozuwUkIyAYebxuHhpYCiaexHnYnYJG1c,3524
|
|
51
53
|
kash/config/suppress_warnings.py,sha256=yty5ZodMLIpmjphRtcVmRamXfiWbyfga9annve6qxb0,1475
|
|
52
54
|
kash/config/text_styles.py,sha256=QCidaUPRbelZjBnI6Q_Dtw-g_4VHdr99g4KHvDyQAo4,13810
|
|
@@ -62,7 +64,7 @@ kash/docs/markdown/readme_template.md,sha256=iGx9IjSni1t_9BuYD5d2GgkxkNIkqvE3k78
|
|
|
62
64
|
kash/docs/markdown/warning.md,sha256=ipTFWQc1F0cPGrIG0epX5RqQH6PmshyZOoCQDz0zDos,88
|
|
63
65
|
kash/docs/markdown/welcome.md,sha256=uNI6CyZOuPUe6FZgKd9MpO_pumHxlGN7Lfhb52XJRa0,611
|
|
64
66
|
kash/docs/markdown/topics/a1_what_is_kash.md,sha256=rgVrv6tRXEwdqQ54DAfHP3BSAuq8Ux4wCNeluTwpkhU,6758
|
|
65
|
-
kash/docs/markdown/topics/a2_installation.md,sha256=
|
|
67
|
+
kash/docs/markdown/topics/a2_installation.md,sha256=MHZN0xBsNRYllg7H-Y4mribzXCMN_zweyE2CVELbhf0,5736
|
|
66
68
|
kash/docs/markdown/topics/a3_getting_started.md,sha256=xOMevEXMIpVJvTGuuwI9Cc9sun3tQM3OqCgynSgMpeM,9376
|
|
67
69
|
kash/docs/markdown/topics/a4_elements.md,sha256=XNJRw-iqnytiIHOAshp1YnUpHM5KBgFAhuOdp_fekxQ,4615
|
|
68
70
|
kash/docs/markdown/topics/a5_tips_for_use_with_other_tools.md,sha256=VGUdq8dm78E8PFbNR9BmV8Gj-r8zP-vOQz8TibQmGw0,3259
|
|
@@ -79,16 +81,16 @@ kash/docs_base/recipes/general_system_commands.sh,sha256=rFNPuLj3Md09L5yzWx6ILja
|
|
|
79
81
|
kash/docs_base/recipes/python_dev_commands.sh,sha256=9vJsQiDZKJ7ShokFnzc2Jsto6n87MNRkbgcc2Ee9Kro,179
|
|
80
82
|
kash/docs_base/recipes/tldr_standard_commands.sh,sha256=7nPES55aT45HF3eDhQRrEUiWRpPdvvp40Sg88uADa80,60491
|
|
81
83
|
kash/embeddings/cosine.py,sha256=QTWPWUHivXjxCM6APSqij_-4mywM2BVVm0xb0hu7FHA,1587
|
|
82
|
-
kash/embeddings/embeddings.py,sha256=
|
|
84
|
+
kash/embeddings/embeddings.py,sha256=l1mDCTi296_hP_jvY8lJ8-WTBq43FWS4jy3kNvKJy44,4448
|
|
83
85
|
kash/embeddings/text_similarity.py,sha256=BOo9Vcs5oi2Zs5La56uTkPMHo65XSd4qz_yr6GTfUA4,1924
|
|
84
86
|
kash/exec/__init__.py,sha256=Najls8No143yoj_KAaOQgo8ufC2LWCB_DwwEQ-8nDM0,1277
|
|
85
87
|
kash/exec/action_decorators.py,sha256=kCEqQFN1MIhRbFeIEGux956LzsiwonhyIIrJ8Pq9zPk,16765
|
|
86
|
-
kash/exec/action_exec.py,sha256=
|
|
88
|
+
kash/exec/action_exec.py,sha256=tLL3ESIuxqS_gSOD4eGRYLuTzesRMTyKb_Uy3i_28VU,19022
|
|
87
89
|
kash/exec/action_registry.py,sha256=numU9pH_W5RgIrYmfi0iYMYy_kLJl6vup8PMrhxAfdc,2627
|
|
88
90
|
kash/exec/combiners.py,sha256=AJ6wgPUHsmwanObsUw64B83XzU26yuh5t4l7igLn82I,4291
|
|
89
91
|
kash/exec/command_exec.py,sha256=zc-gWm7kyB5J5Kp8xhULQ9Jj9AL927KkDPXXk-Yr1Bw,1292
|
|
90
92
|
kash/exec/command_registry.py,sha256=1s2ogU8b8nqK_AEtslbr1eYrXCGDkeT30UrB7L0BRoM,2027
|
|
91
|
-
kash/exec/fetch_url_items.py,sha256=
|
|
93
|
+
kash/exec/fetch_url_items.py,sha256=onKoAOg747r5T9fAIO8sTjJcllxKh3XLzh59FU5EFE8,5466
|
|
92
94
|
kash/exec/history.py,sha256=l2XwHGBR1UgTGSFPSBE9mltmxvjR_5qFFO6d-Z008nc,1208
|
|
93
95
|
kash/exec/importing.py,sha256=xunmBapeUMNc6Zox7y6e_DZkidyWeouiFZpphajwSzc,1843
|
|
94
96
|
kash/exec/llm_transforms.py,sha256=n7S-Dew8z_BoAwp-14lY4LueFeUtG117eK_8msbn02c,4375
|
|
@@ -104,7 +106,7 @@ kash/exec_model/commands_model.py,sha256=iM8QhzA0tAas5OwF5liUfHtm45XIH1LcvCviuh3
|
|
|
104
106
|
kash/exec_model/script_model.py,sha256=1VG3LhkTmlKzHOYouZ92ZpOSKSCcsz3-tHNcFMQF788,5031
|
|
105
107
|
kash/exec_model/shell_model.py,sha256=LUhQivbpXlerM-DUzNY7BtctNBbn08Wto8CSSxQDxRU,568
|
|
106
108
|
kash/file_storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
107
|
-
kash/file_storage/file_store.py,sha256=
|
|
109
|
+
kash/file_storage/file_store.py,sha256=GeAHIvAFcIj_cKssqzHajmpJmZQ8sFNDbFDvy4fETys,30877
|
|
108
110
|
kash/file_storage/item_file_format.py,sha256=_o2CjWstk_Z__qMr-Inct9wJm2VEUK0GZvF-fDZ8bcc,5377
|
|
109
111
|
kash/file_storage/metadata_dirs.py,sha256=9AqO3S3SSY1dtvP2iLX--E4ui0VIzXttG8R040otfyg,3820
|
|
110
112
|
kash/file_storage/persisted_yaml.py,sha256=4-4RkFqdlBUkTOwkdA4vRKUywEE9TaDo13OGaDUyU9M,1309
|
|
@@ -128,7 +130,7 @@ kash/llm_utils/custom_sliding_transforms.py,sha256=z07WCdBoiywBIGk2EXK3xY4t_6g8I
|
|
|
128
130
|
kash/llm_utils/fuzzy_parsing.py,sha256=bbG2Y7i5w6kxAVPAixyluv3MDS2hW_pkhnJpVOLHZQc,3278
|
|
129
131
|
kash/llm_utils/init_litellm.py,sha256=5Fn9uW4P7lfEO8Rk1EJJUzDEGNjw-PDvxFgHlKDf-Ok,409
|
|
130
132
|
kash/llm_utils/llm_api_keys.py,sha256=nTB9wSFfHTOXvqshSQCQGCPxUwOW1U7oslngya8nHkw,1168
|
|
131
|
-
kash/llm_utils/llm_completion.py,sha256=
|
|
133
|
+
kash/llm_utils/llm_completion.py,sha256=SzeWGRrsjuN1TXdPwscYG6whLQkHclITtwTvQK19GE0,5436
|
|
132
134
|
kash/llm_utils/llm_features.py,sha256=tgcz8qQByZ80noybXS2144K9eCw1iOFxPYkCK2kJJOw,1861
|
|
133
135
|
kash/llm_utils/llm_messages.py,sha256=70QwIIvdwo-h4Jfn_6MbEHb3LTUjUmzg_v_dU_Ey__g,1174
|
|
134
136
|
kash/llm_utils/llm_names.py,sha256=VZbdKnoeBx_luB5YQ-Rz37gMt3_FcueJdp40ZaQbpUA,3620
|
|
@@ -141,7 +143,7 @@ kash/local_server/local_url_formatters.py,sha256=SqHjGMEufvm43n34SCa_8Asdwm7utx9
|
|
|
141
143
|
kash/local_server/port_tools.py,sha256=oFfOvO6keqS5GowTpVg2FTu5KqkPHBq-dWAEomUIgGo,2008
|
|
142
144
|
kash/local_server/rich_html_template.py,sha256=O9CnkMYkWuMvKJkqD0P8jaZqfUe6hMP4LXFvcLpwN8Q,196
|
|
143
145
|
kash/mcp/__init__.py,sha256=HA_aFskEchpAwA0wOKi5nasytx2JZfH8z9oCVXUI7MQ,160
|
|
144
|
-
kash/mcp/mcp_cli.py,sha256=
|
|
146
|
+
kash/mcp/mcp_cli.py,sha256=UC0Jwd7DLxx2zEFj0hK1UWPkXXXGntV1te33CensyHM,3849
|
|
145
147
|
kash/mcp/mcp_main.py,sha256=6PhjKwp631mezDTUAd-pL8lUZx9Gl7yCrCQFW61pqJU,3167
|
|
146
148
|
kash/mcp/mcp_server_commands.py,sha256=C-gKLbUVEpF76SmW3NgjlEWoM3YaA3ARrCos5UH1Zh8,4345
|
|
147
149
|
kash/mcp/mcp_server_routes.py,sha256=radxy5cc0yBmcbqAbB1LpZ0qcNtfDbtS5RTGwvgliDM,10617
|
|
@@ -199,10 +201,11 @@ kash/shell/utils/native_utils.py,sha256=pAiuqqrjfNTesdArSya6CVavKVsuAXOcX3_XAIQr
|
|
|
199
201
|
kash/shell/utils/shell_function_wrapper.py,sha256=fgUuVhocYMKLkGJJQJOER5nFMAvM0ZVpfGu7iJPJI9s,7385
|
|
200
202
|
kash/utils/__init__.py,sha256=4Jl_AtgRADdGORimWhYZwbSfQSpQ6SiexNIZzmbcngI,111
|
|
201
203
|
kash/utils/errors.py,sha256=2lPL0fxI8pPOiDvjl0j-rvwY8uhmWetsrYYIc2-x1WY,3906
|
|
202
|
-
kash/utils/api_utils/api_retries.py,sha256=
|
|
204
|
+
kash/utils/api_utils/api_retries.py,sha256=TtgxLxoMnXIzYMKbMUzsnVcPf-aKFm3cJ95zOcSeae8,21844
|
|
203
205
|
kash/utils/api_utils/cache_requests_limited.py,sha256=TA5buZ9Dgbj4I1zHhwerTXre018i0TCACGsezsjX9Uc,3140
|
|
204
|
-
kash/utils/api_utils/gather_limited.py,sha256=
|
|
205
|
-
kash/utils/api_utils/
|
|
206
|
+
kash/utils/api_utils/gather_limited.py,sha256=CV-9YjZakbKZ5AN3xIZSOyHwGvtvMljpl5Dq_SuDfNs,32945
|
|
207
|
+
kash/utils/api_utils/http_utils.py,sha256=Ou6QNiba5w7n71cgNmV168OFTLmMDNxWW5MM-XkFEME,1461
|
|
208
|
+
kash/utils/api_utils/progress_protocol.py,sha256=6cT5URY6cScHYd6UZoTT_rHI0mbsE52joBf88regEN8,8816
|
|
206
209
|
kash/utils/common/__init__.py,sha256=ggeWw1xmbl1mgCQD3c4CNN2h5WXFCsN2wXlCWurEUEI,161
|
|
207
210
|
kash/utils/common/format_utils.py,sha256=83FhAwbMnOQIFudpnOGMuCqCiyoAlWGS6cc8q6xgZus,2072
|
|
208
211
|
kash/utils/common/function_inspect.py,sha256=KE5QgIPx16iupCZrMlcvU6MuA5fUFboPEkqmltpFw3E,19824
|
|
@@ -235,14 +238,14 @@ kash/utils/lang_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
|
|
|
235
238
|
kash/utils/lang_utils/capitalization.py,sha256=5XbqBvjkzlxsm1Ue5AQP3P1J1IG0PubMVmGnoKVTF-c,3903
|
|
236
239
|
kash/utils/rich_custom/__init__.py,sha256=_g2F3Bqc1UnLTdAdCwkvzXmW7OvmqXrA8DpfT1dKy6w,75
|
|
237
240
|
kash/utils/rich_custom/ansi_cell_len.py,sha256=oQlNrqWB0f6pmigkbRRyeK6oWlGHMPbV_YLO_qmDH5E,2356
|
|
238
|
-
kash/utils/rich_custom/multitask_status.py,sha256=
|
|
241
|
+
kash/utils/rich_custom/multitask_status.py,sha256=ViNx35akEaV-fihef57_b3l8FnLytNomV6t2I_peJv8,23676
|
|
239
242
|
kash/utils/rich_custom/rich_char_transform.py,sha256=3M89tViKM0y31VHsDoHi5eHFWlv5ME7F4p35IdDxnrw,2616
|
|
240
243
|
kash/utils/rich_custom/rich_indent.py,sha256=nz72yNpUuYjOsaPNVmxM81oEQm-GKEfQkNsuWmv16G0,2286
|
|
241
244
|
kash/utils/rich_custom/rich_markdown_fork.py,sha256=M_JRaSAyHrSg-wuLv9C9P7SkehSim3lwkqQPuMIFkVw,26551
|
|
242
245
|
kash/utils/text_handling/doc_normalization.py,sha256=C211eSof8PUDVCqQtShuC4AMJpTZeBK8GHlGATp3c9E,2976
|
|
243
246
|
kash/utils/text_handling/escape_html_tags.py,sha256=8pC3JgoKRtdnbnOu8DiWrlvNR6GAqjwhGbQgl3jiFG4,6441
|
|
244
247
|
kash/utils/text_handling/markdown_render.py,sha256=LHPdJc__2ejBx7iwkp_P9wIePNmiVSgwu4-uhamVjms,3791
|
|
245
|
-
kash/utils/text_handling/markdown_utils.py,sha256=
|
|
248
|
+
kash/utils/text_handling/markdown_utils.py,sha256=tAPMZulEcJOmWQHnZVKRqstThmsLB0mm7Fey5DHMFvc,31985
|
|
246
249
|
kash/utils/text_handling/markdownify_utils.py,sha256=xNMlBX36BJ5VK5kxY2Ofo-Q84R2kBSM91u1grkQ-5As,2925
|
|
247
250
|
kash/utils/text_handling/unified_diffs.py,sha256=JfHSakISkT_GuBPBI4fTooHrp2aenWzDKiVvDewVfMk,2655
|
|
248
251
|
kash/web_content/canon_url.py,sha256=Zv2q7xQdIHBFkxxwyJn3_ME-qqMFRi_fKxE_IgV2Z50,742
|
|
@@ -250,11 +253,11 @@ kash/web_content/dir_store.py,sha256=BJc-s-RL5CC-GwhFTC_lhLXSMWluPPnLVmVBx-66DiM
|
|
|
250
253
|
kash/web_content/file_cache_utils.py,sha256=JRXUCAmrc83iAgdiICU2EYGWcoORflWNl6GAVq-O80I,5529
|
|
251
254
|
kash/web_content/file_processing.py,sha256=cQC-MnJMM5qG9-y0S4yobkmRi6A75qhHjV6xTwbtYDY,1904
|
|
252
255
|
kash/web_content/local_file_cache.py,sha256=PEDKU5VIwhCnSC-HXG4EkO2OzrOUDuuDBMuo3lP2EN0,9466
|
|
253
|
-
kash/web_content/web_extract.py,sha256=
|
|
256
|
+
kash/web_content/web_extract.py,sha256=TrGOohn99LF1F0zbycgXqYiIR-V3leefFFEVk3tnW6Y,2979
|
|
254
257
|
kash/web_content/web_extract_justext.py,sha256=74HLJBKDGKatwxyRDX6za70bZG9LrVmtj9jLX7UJzg4,2540
|
|
255
258
|
kash/web_content/web_extract_readabilipy.py,sha256=IT7ET5IoU2-Nf37-Neh6CkKMvLL3WTNVJjq7ZMOx6OM,808
|
|
256
|
-
kash/web_content/web_fetch.py,sha256=
|
|
257
|
-
kash/web_content/web_page_model.py,sha256=
|
|
259
|
+
kash/web_content/web_fetch.py,sha256=hC4kijKVez4_QtfwdE0OPrEkfEGT2hVcB1vVZko8o78,12129
|
|
260
|
+
kash/web_content/web_page_model.py,sha256=TWgWreGOoOvl7lUkjMSd0rxoDf2VuaKOq7WijwydW0Q,1387
|
|
258
261
|
kash/web_gen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
259
262
|
kash/web_gen/simple_webpage.py,sha256=ks_0ljxCeS2-gAAEaUc1JEnzY3JY0nzqGFiyyqyRuZs,1537
|
|
260
263
|
kash/web_gen/tabbed_webpage.py,sha256=e0GGG1bQ4CQegpJgOIT2KpyM6cmwN_BQ9eJSsi4BjgY,4945
|
|
@@ -291,8 +294,8 @@ kash/xonsh_custom/xonsh_modern_tools.py,sha256=mj_b34LZXfE8MJe9EpDmp5JZ0tDM1biYN
|
|
|
291
294
|
kash/xonsh_custom/xonsh_ranking_completer.py,sha256=ZRGiAfoEgqgnlq2-ReUVEaX5oOgW1DQ9WxIv2OJLuTo,5620
|
|
292
295
|
kash/xontrib/fnm.py,sha256=V2tsOdmIDgbFbZSfMLpsvDIwwJJqiYnOkOySD1cXNXw,3700
|
|
293
296
|
kash/xontrib/kash_extension.py,sha256=FLIMlgR3C_6A1fwKE-Ul0nmmpJSszVPbAriinUyQ8Zg,1896
|
|
294
|
-
kash_shell-0.3.
|
|
295
|
-
kash_shell-0.3.
|
|
296
|
-
kash_shell-0.3.
|
|
297
|
-
kash_shell-0.3.
|
|
298
|
-
kash_shell-0.3.
|
|
297
|
+
kash_shell-0.3.24.dist-info/METADATA,sha256=zgoOSgrSaBs5rmbe9RaoUHai2XJTLc3YFLTdM73MwWU,32726
|
|
298
|
+
kash_shell-0.3.24.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
299
|
+
kash_shell-0.3.24.dist-info/entry_points.txt,sha256=SQraWDAo8SqYpthLXThei0mf_hGGyhYBUO-Er_0HcwI,85
|
|
300
|
+
kash_shell-0.3.24.dist-info/licenses/LICENSE,sha256=rCh2PsfYeiU6FK_0wb58kHGm_Fj5c43fdcHEexiVzIo,34562
|
|
301
|
+
kash_shell-0.3.24.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|