kash-shell 0.3.34__py3-none-any.whl → 0.3.36__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.
@@ -1,6 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import logging
4
+ import ssl
5
+ from collections.abc import Iterable
4
6
  from dataclasses import dataclass
5
7
  from enum import Enum
6
8
  from functools import cache, cached_property
@@ -12,17 +14,145 @@ from cachetools import TTLCache
12
14
  from strif import atomic_output_file, copyfile_atomic
13
15
 
14
16
  from kash.config.env_settings import KashEnv
17
+ from kash.utils.common.s3_utils import s3_download_file
15
18
  from kash.utils.common.url import Url
16
19
  from kash.utils.file_utils.file_formats import MimeType
17
20
 
21
+ log = logging.getLogger(__name__)
22
+
23
+
24
+ def _httpx_verify_context() -> ssl.SSLContext | bool:
25
+ """
26
+ Return an SSLContext that uses the system trust store via truststore, if available.
27
+ Falls back to certifi bundle; otherwise True to use httpx defaults.
28
+ """
29
+ try:
30
+ import truststore
31
+
32
+ return truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
33
+ except Exception:
34
+ try:
35
+ import certifi
36
+
37
+ return ssl.create_default_context(cafile=certifi.where())
38
+ except Exception:
39
+ return True
40
+
41
+
42
+ def _stream_to_file(
43
+ target_filename: str | Path,
44
+ response_iterator: Iterable[bytes],
45
+ total_size: int,
46
+ show_progress: bool,
47
+ ) -> None:
48
+ with atomic_output_file(target_filename, make_parents=True) as temp_filename:
49
+ with open(temp_filename, "wb") as f:
50
+ if not show_progress:
51
+ for chunk in response_iterator:
52
+ if chunk:
53
+ f.write(chunk)
54
+ else:
55
+ from tqdm import tqdm
56
+
57
+ with tqdm(
58
+ total=total_size,
59
+ unit="B",
60
+ unit_scale=True,
61
+ desc=f"Downloading {Path(str(target_filename)).name}",
62
+ ) as progress:
63
+ for chunk in response_iterator:
64
+ if chunk:
65
+ f.write(chunk)
66
+ progress.update(len(chunk))
67
+
68
+
69
+ def _httpx_fetch(
70
+ url: Url,
71
+ *,
72
+ timeout: int,
73
+ auth: Any | None,
74
+ headers: dict[str, str] | None,
75
+ mode: ClientMode,
76
+ log_label: str,
77
+ ):
78
+ import httpx
79
+
80
+ req_headers = _get_req_headers(mode, headers)
81
+ parsed_url = urlparse(str(url))
82
+ with httpx.Client(
83
+ follow_redirects=True,
84
+ timeout=timeout,
85
+ auth=auth,
86
+ headers=req_headers,
87
+ verify=_httpx_verify_context(),
88
+ ) as client:
89
+ log.debug("fetch_url (%s): using headers: %s", log_label, client.headers)
90
+ if mode is ClientMode.BROWSER_HEADERS:
91
+ _prime_host(parsed_url.netloc, client, timeout)
92
+ response = client.get(url)
93
+ log.info(
94
+ "Fetched (%s): %s (%s bytes): %s",
95
+ log_label,
96
+ response.status_code,
97
+ len(response.content),
98
+ url,
99
+ )
100
+ response.raise_for_status()
101
+ return response
102
+
103
+
104
+ def _httpx_download(
105
+ url: Url,
106
+ target_filename: str | Path,
107
+ *,
108
+ show_progress: bool,
109
+ timeout: int,
110
+ auth: Any | None,
111
+ headers: dict[str, str] | None,
112
+ mode: ClientMode,
113
+ log_label: str,
114
+ ) -> dict[str, str]:
115
+ import httpx
116
+
117
+ req_headers = _get_req_headers(mode, headers)
118
+ parsed_url = urlparse(str(url))
119
+ with httpx.Client(
120
+ follow_redirects=True,
121
+ timeout=timeout,
122
+ headers=req_headers,
123
+ verify=_httpx_verify_context(),
124
+ ) as client:
125
+ if mode is ClientMode.BROWSER_HEADERS:
126
+ _prime_host(parsed_url.netloc, client, timeout)
127
+ log.debug("download_url (%s): using headers: %s", log_label, client.headers)
128
+ with client.stream("GET", url, auth=auth, follow_redirects=True) as response:
129
+ response.raise_for_status()
130
+ response_headers = dict(response.headers)
131
+ total = int(response.headers.get("content-length", "0"))
132
+ _stream_to_file(target_filename, response.iter_bytes(), total, show_progress)
133
+ return response_headers
134
+
135
+
136
+ def _is_tls_cert_error(exc: Exception) -> bool:
137
+ """
138
+ Heuristic detection of TLS/certificate verification errors coming from curl_cffi/libcurl.
139
+ """
140
+ s = str(exc).lower()
141
+ if "curl: (60)" in s:
142
+ return True
143
+ if "certificate verify failed" in s:
144
+ return True
145
+ if "ssl" in s and ("certificate" in s or "cert" in s or "handshake" in s):
146
+ return True
147
+ return False
148
+
149
+
18
150
  if TYPE_CHECKING:
19
151
  from curl_cffi.requests import Response as CurlCffiResponse
20
152
  from curl_cffi.requests import Session as CurlCffiSession
21
153
  from httpx import Client as HttpxClient
22
154
  from httpx import Response as HttpxResponse
23
155
 
24
- log = logging.getLogger(__name__)
25
-
26
156
 
27
157
  DEFAULT_TIMEOUT = 30
28
158
  CURL_CFFI_IMPERSONATE_VERSION = "chrome120"
@@ -199,54 +329,57 @@ def fetch_url(
199
329
 
200
330
  from curl_cffi.requests import Session
201
331
 
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
332
+ exc: Exception | None = None
333
+ try:
334
+ with Session() as client:
335
+ # Set headers on the session - they will be sent with all requests
336
+ client.headers.update(req_headers)
337
+ _prime_host(
338
+ parsed_url.netloc, client, timeout, impersonate=CURL_CFFI_IMPERSONATE_VERSION
339
+ )
340
+ log.debug("fetch_url (curl_cffi): using session headers: %s", client.headers)
341
+ response = client.get(
342
+ url,
343
+ impersonate=CURL_CFFI_IMPERSONATE_VERSION,
344
+ timeout=timeout,
345
+ auth=auth,
346
+ allow_redirects=True,
347
+ )
348
+ log.info(
349
+ "Fetched (curl_cffi): %s (%s bytes): %s",
350
+ response.status_code,
351
+ len(response.content),
352
+ url,
353
+ )
354
+ response.raise_for_status()
355
+ return response
356
+ except Exception as e:
357
+ exc = e
358
+
359
+ if exc and _is_tls_cert_error(exc):
360
+ log.warning(
361
+ "TLS/SSL verification failed with curl_cffi for %s: %s; falling back to httpx",
362
+ url,
363
+ exc,
207
364
  )
208
- log.debug("fetch_url (curl_cffi): using session headers: %s", client.headers)
209
- response = client.get(
365
+ # Fallback to httpx with browser-like headers (uses system trust if available)
366
+ return _httpx_fetch(
210
367
  url,
211
- impersonate=CURL_CFFI_IMPERSONATE_VERSION,
212
368
  timeout=timeout,
213
369
  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,
370
+ headers=headers,
371
+ mode=ClientMode.BROWSER_HEADERS,
372
+ log_label="httpx fallback",
221
373
  )
222
- response.raise_for_status()
223
- return response
374
+
375
+ if exc:
376
+ raise exc
224
377
 
225
378
  # Handle httpx modes
226
379
  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
380
+ return _httpx_fetch(
381
+ url, timeout=timeout, auth=auth, headers=headers, mode=mode, log_label="httpx"
382
+ )
250
383
 
251
384
 
252
385
  @dataclass(frozen=True)
@@ -289,38 +422,13 @@ def download_url(
289
422
  copyfile_atomic(parsed_url.netloc + parsed_url.path, target_filename, make_parents=True)
290
423
  return None
291
424
  elif parsed_url.scheme == "s3":
292
- import boto3 # pyright: ignore
293
-
294
- s3 = boto3.resource("s3")
295
- s3_path = parsed_url.path.lstrip("/")
296
425
  with atomic_output_file(target_filename, make_parents=True) as temp_filename:
297
- s3.Bucket(parsed_url.netloc).download_file(s3_path, temp_filename)
426
+ s3_download_file(url, temp_filename)
298
427
  return None
299
428
 
300
429
  req_headers = _get_req_headers(mode, headers)
301
430
  response_headers = None
302
431
 
303
- def stream_to_file(response_iterator, total_size):
304
- with atomic_output_file(target_filename, make_parents=True) as temp_filename:
305
- with open(temp_filename, "wb") as f:
306
- if not show_progress:
307
- for chunk in response_iterator:
308
- if chunk: # Skip empty chunks
309
- f.write(chunk)
310
- else:
311
- from tqdm import tqdm
312
-
313
- with tqdm(
314
- total=total_size,
315
- unit="B",
316
- unit_scale=True,
317
- desc=f"Downloading {Path(target_filename).name}",
318
- ) as progress:
319
- for chunk in response_iterator:
320
- if chunk: # Skip empty chunks
321
- f.write(chunk)
322
- progress.update(len(chunk))
323
-
324
432
  # Handle curl_cffi mode
325
433
  if mode is ClientMode.CURL_CFFI:
326
434
  if not _have_curl_cffi():
@@ -328,47 +436,111 @@ def download_url(
328
436
 
329
437
  from curl_cffi.requests import Session
330
438
 
331
- with Session() as client:
332
- # Set headers on the session; they will be sent with all requests
333
- client.headers.update(req_headers)
334
- _prime_host(
335
- parsed_url.netloc, client, timeout, impersonate=CURL_CFFI_IMPERSONATE_VERSION
336
- )
337
- log.debug("download_url (curl_cffi): using session headers: %s", client.headers)
439
+ exc: Exception | None = None
440
+ try:
441
+ with Session() as client:
442
+ # Set headers on the session; they will be sent with all requests
443
+ client.headers.update(req_headers)
444
+ _prime_host(
445
+ parsed_url.netloc, client, timeout, impersonate=CURL_CFFI_IMPERSONATE_VERSION
446
+ )
447
+ log.debug("download_url (curl_cffi): using session headers: %s", client.headers)
448
+ response = client.get(
449
+ url,
450
+ impersonate=CURL_CFFI_IMPERSONATE_VERSION,
451
+ timeout=timeout,
452
+ auth=auth,
453
+ allow_redirects=True,
454
+ stream=True,
455
+ )
456
+ response.raise_for_status()
457
+ response_headers = dict(response.headers)
458
+ total = int(response.headers.get("content-length", "0"))
459
+
460
+ # Use iter_content for streaming; this is the standard method for curl_cffi
461
+ chunk_iterator = response.iter_content(chunk_size=8192)
462
+ _stream_to_file(target_filename, chunk_iterator, total, show_progress)
463
+ except Exception as e:
464
+ exc = e
338
465
 
339
- response = client.get(
466
+ if exc and _is_tls_cert_error(exc):
467
+ log.warning(
468
+ "TLS/SSL verification failed with curl_cffi for %s: %s; falling back to httpx",
340
469
  url,
341
- impersonate=CURL_CFFI_IMPERSONATE_VERSION,
470
+ exc,
471
+ )
472
+ # Fallback to httpx streaming with browser-like headers (system trust store if available)
473
+ response_headers = _httpx_download(
474
+ url,
475
+ target_filename,
476
+ show_progress=show_progress,
342
477
  timeout=timeout,
343
478
  auth=auth,
344
- allow_redirects=True,
345
- stream=True,
479
+ headers=headers,
480
+ mode=ClientMode.BROWSER_HEADERS,
481
+ log_label="httpx fallback",
346
482
  )
347
- response.raise_for_status()
348
- response_headers = dict(response.headers)
349
- total = int(response.headers.get("content-length", "0"))
350
-
351
- # Use iter_content for streaming; this is the standard method for curl_cffi
352
- chunk_iterator = response.iter_content(chunk_size=8192)
353
- stream_to_file(chunk_iterator, total)
483
+ elif exc:
484
+ raise exc
354
485
 
355
486
  # Handle httpx modes
356
487
  else:
357
- import httpx
358
-
359
- with httpx.Client(follow_redirects=True, timeout=timeout, headers=req_headers) as client:
360
- if mode is ClientMode.BROWSER_HEADERS:
361
- _prime_host(parsed_url.netloc, client, timeout)
362
-
363
- log.debug("download_url (httpx): using headers: %s", client.headers)
364
- with client.stream("GET", url, auth=auth, follow_redirects=True) as response:
365
- response.raise_for_status()
366
- response_headers = dict(response.headers)
367
- total = int(response.headers.get("content-length", "0"))
368
- stream_to_file(response.iter_bytes(), total)
488
+ response_headers = _httpx_download(
489
+ url,
490
+ target_filename,
491
+ show_progress=show_progress,
492
+ timeout=timeout,
493
+ auth=auth,
494
+ headers=headers,
495
+ mode=mode,
496
+ log_label="httpx",
497
+ )
369
498
 
370
499
  # Filter out None values from headers for HttpHeaders type compatibility
371
500
  if response_headers:
372
501
  clean_headers = {k: v for k, v in response_headers.items() if v is not None}
373
502
  return HttpHeaders(clean_headers)
374
503
  return None
504
+
505
+
506
+ def main() -> None:
507
+ """
508
+ Simple CLI test harness for fetch and download.
509
+
510
+ Usage examples:
511
+ uv run python -m kash.web_content.web_fetch
512
+ uv run python -m kash.web_content.web_fetch https://www.example.com
513
+ """
514
+ import sys
515
+ import traceback
516
+
517
+ # Try to use the system trust store for TLS like command-line curl
518
+ try:
519
+ import truststore # type: ignore
520
+
521
+ truststore.inject_into_ssl()
522
+ log.warning("truststore initialized for test harness: using system TLS trust store")
523
+ except Exception as exc:
524
+ log.info("truststore not available for test harness; using default TLS trust (%s)", exc)
525
+
526
+ urls = [
527
+ "https://www.example.com",
528
+ "https://www.businessdefense.gov/ibr/mceip/dpai/dpat3/index.html",
529
+ ]
530
+
531
+ args = [a for a in sys.argv[1:] if a and a.strip()]
532
+ if args:
533
+ urls = args
534
+
535
+ for u in urls:
536
+ try:
537
+ log.warning("Testing fetch_url: %s", u)
538
+ r = fetch_url(Url(u))
539
+ log.warning("fetch_url OK: %s -> %s bytes", u, len(r.content))
540
+ except Exception as exc:
541
+ log.exception("fetch_url FAILED for %s: %s", u, exc)
542
+ traceback.print_exc()
543
+
544
+
545
+ if __name__ == "__main__":
546
+ main()
@@ -1,8 +1,9 @@
1
1
  {% extends "simple_webpage.html.jinja" %}
2
2
 
3
3
  {#
4
- Extends the simple page with a right-side YouTube popover player.
5
- Usage: pass page_template="youtube_webpage.html.jinja" to simple_webpage_render.
4
+ Extends the simple page with YouTube popover player.
5
+ Safe to use instead of simple_webpage.html.jinja since it only adds
6
+ functionality for YouTube if applicable.
6
7
  #}
7
8
 
8
9
  {% block custom_styles %}
@@ -95,6 +95,8 @@ def get_ws(name_or_path: str | Path, auto_init: bool = True) -> FileStore:
95
95
  Get a workspace by name or path. Adds to the in-memory registry so we reuse it.
96
96
  With `auto_init` true, will initialize the workspace if it is not already initialized.
97
97
  """
98
+ if isinstance(name_or_path, Path):
99
+ name_or_path = name_or_path.expanduser().absolute()
98
100
  name = Path(name_or_path).name
99
101
  name = check_strict_workspace_name(name)
100
102
  info = resolve_ws(name_or_path)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kash-shell
3
- Version: 0.3.34
3
+ Version: 0.3.36
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>
@@ -72,6 +72,7 @@ Requires-Dist: thefuzz>=0.22.1
72
72
  Requires-Dist: tiktoken>=0.9.0
73
73
  Requires-Dist: tldr>=3.3.0
74
74
  Requires-Dist: tminify>=0.1.6
75
+ Requires-Dist: truststore>=0.10.4
75
76
  Requires-Dist: typing-extensions>=4.12.2
76
77
  Requires-Dist: uvicorn>=0.34.0
77
78
  Requires-Dist: xonsh>=0.19.3
@@ -43,18 +43,19 @@ kash/commands/workspace/workspace_commands.py,sha256=GE_wHdeYFJ1ruRi4spZtyyTjYXJ
43
43
  kash/config/__init__.py,sha256=ytly9Typ1mWV4CXfV9G3CIPtPQ02u2rpZ304L3GlFro,148
44
44
  kash/config/capture_output.py,sha256=ud3uUVNuDicHj3mI_nBUBO-VmOrxtBdA3z-I3D1lSCU,2398
45
45
  kash/config/colors.py,sha256=cV3LRBegDZxZXjV-XEjc3fystFY_defhz1LN8QPdaKc,13727
46
- kash/config/env_settings.py,sha256=uhCdfs9-TzJ15SzbuIQP1yIORaLUqYXCxh9qq_Z8cJc,996
46
+ kash/config/env_settings.py,sha256=HmXmlOQ8i4b3vqEaRHF2PnHHNOjITPN7U27hiG4dsZ4,904
47
47
  kash/config/init.py,sha256=aE4sZ6DggBmmoZEx9C5mQKrEbcDiswX--HF7pfCFKzc,526
48
48
  kash/config/lazy_imports.py,sha256=MCZXLnKvNyfHi0k7MU5rNwcdJtUF28naCixuogsAOAA,805
49
- kash/config/logger.py,sha256=Wrhau5gPYwGSmA1V2IbMhyApTRgB8p4Pyk8JL7OCTaE,12189
50
- kash/config/logger_basic.py,sha256=Gsxitz7xeMGCUr5qjWK6y72qeMsIz4mcDZP5xyzK0WU,1598
49
+ kash/config/logger.py,sha256=gkE3yV6WMawduxbQpiKwmIqglThXv9qEJ8_YR1uKy3Y,12226
50
+ kash/config/logger_basic.py,sha256=_a42XrXOGzuHEIU0LkXcBiZ3u4JTnMmCVChXZCv9U68,1870
51
51
  kash/config/logo.txt,sha256=P4RO1cJ9HRF1BavTp3Kae9iByDNhzhEC-qLAa6ww1RA,217
52
52
  kash/config/server_config.py,sha256=eQ1yxDk031QI0Efp0I1VetqQd9wG7MrLVBCHFm4gp2g,1790
53
- kash/config/settings.py,sha256=Tja5MNZJDjo5SRaCngfQQFTTjmVGUlmkCZ7UEAhvXqw,9087
54
- kash/config/setup.py,sha256=SepMs7CvWdIXCjs03_noA16PETW_YOxH33F8cdP8kYc,2643
53
+ kash/config/settings.py,sha256=XfD5xgxREMAnJ5YMGmwmpN6dmRJ8tw0aQV398fvHj1s,8770
54
+ kash/config/setup.py,sha256=D9hvg-Y5X9mXiDoIzqNPJIgOleA6nDZyANgxBYWewYk,3135
55
55
  kash/config/suppress_warnings.py,sha256=yty5ZodMLIpmjphRtcVmRamXfiWbyfga9annve6qxb0,1475
56
- kash/config/text_styles.py,sha256=pQ0FKJW7eSXU6ol5LhyaGZewxF40L6Q0o92mfsNNCYQ,13843
56
+ kash/config/text_styles.py,sha256=hDIxP-uTio6FYVz8xI1AwyS5XpRRwjU3A-V7AdDF_ug,13841
57
57
  kash/config/unified_live.py,sha256=tUEnTWMSYKYSJkEoAPbaJET-bJ1HnfU2Ar-XeyBOtlo,8420
58
+ kash/config/warm_slow_imports.py,sha256=65nUsVBMCp6VmKqPPidxrYLXkWixU2RTr86VlGYRVP4,2241
58
59
  kash/docs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
59
60
  kash/docs/all_docs.py,sha256=NutfgU8VOA7_K2HX18FoOHVvvLL14dALoCxK9qDbQ04,3157
60
61
  kash/docs/load_actions_info.py,sha256=D9uv8gdBtkBmdbFZeyIigsupPF9_WfidHVwWQrlggI0,937
@@ -87,16 +88,16 @@ kash/embeddings/cosine.py,sha256=QTWPWUHivXjxCM6APSqij_-4mywM2BVVm0xb0hu7FHA,158
87
88
  kash/embeddings/embeddings.py,sha256=DirB5DjowEQADfxtp1BTROINyIxU2QEUj79_tXipRfo,6664
88
89
  kash/embeddings/text_similarity.py,sha256=2jp0A27dOto6kItzqe95-f-lx6Mop68d-MJevyCHj9o,1817
89
90
  kash/exec/__init__.py,sha256=Najls8No143yoj_KAaOQgo8ufC2LWCB_DwwEQ-8nDM0,1277
90
- kash/exec/action_decorators.py,sha256=omCr3oxB7LRBWksRrB8nif1bSgFv2ZBKhfMGQuTBp2k,16956
91
- kash/exec/action_exec.py,sha256=HZ7ZtWhpkRxjzai_ZIeTf33Ak_4HmmLujjvSud8yGsw,20943
91
+ kash/exec/action_decorators.py,sha256=bb4O8TcCFw3Oe5bFfnUZ1AeBwEwnNCL0GLwrMDv_UDI,16954
92
+ kash/exec/action_exec.py,sha256=KWvJdD9Ndelr-wEqZF3pJRujKO4n4gBLdwNz4S39Q5A,20942
92
93
  kash/exec/action_registry.py,sha256=numU9pH_W5RgIrYmfi0iYMYy_kLJl6vup8PMrhxAfdc,2627
93
94
  kash/exec/combiners.py,sha256=AJ6wgPUHsmwanObsUw64B83XzU26yuh5t4l7igLn82I,4291
94
95
  kash/exec/command_exec.py,sha256=zc-gWm7kyB5J5Kp8xhULQ9Jj9AL927KkDPXXk-Yr1Bw,1292
95
96
  kash/exec/command_registry.py,sha256=1s2ogU8b8nqK_AEtslbr1eYrXCGDkeT30UrB7L0BRoM,2027
96
- kash/exec/fetch_url_items.py,sha256=BNqAPL4VaXUGZ_q3pNt5QLNKMfU0PMLDrdw8f6xQRo8,6211
97
+ kash/exec/fetch_url_items.py,sha256=Bb8i9YW_XfJHwIAh5yf2_zLXagyESkQF1LL1uNaQQx0,6345
97
98
  kash/exec/history.py,sha256=l2XwHGBR1UgTGSFPSBE9mltmxvjR_5qFFO6d-Z008nc,1208
98
99
  kash/exec/importing.py,sha256=xunmBapeUMNc6Zox7y6e_DZkidyWeouiFZpphajwSzc,1843
99
- kash/exec/llm_transforms.py,sha256=jQAnxHhcvF3oILjpSHx0YRw8Qw-ZnioukXaJuaqjNVk,4626
100
+ kash/exec/llm_transforms.py,sha256=2fxas6K2YcixVGpXKt0-nxlN2qc1SR3-O1KbE0sFKIg,4834
100
101
  kash/exec/precondition_checks.py,sha256=HymxL7qm4Yz8V76Um5pKdIRnQ2N-p9rpQQi1fI38bNA,2139
101
102
  kash/exec/precondition_registry.py,sha256=9O-01d2OrsYLuhqodVuWjouLR2ABJbUotAmfJNBCRzs,1439
102
103
  kash/exec/preconditions.py,sha256=bwNuuPEnkymj24ySPTl8K5DEgTtB2NPGAm9cWaomhAk,5262
@@ -109,7 +110,7 @@ kash/exec_model/commands_model.py,sha256=iM8QhzA0tAas5OwF5liUfHtm45XIH1LcvCviuh3
109
110
  kash/exec_model/script_model.py,sha256=1VG3LhkTmlKzHOYouZ92ZpOSKSCcsz3-tHNcFMQF788,5031
110
111
  kash/exec_model/shell_model.py,sha256=LUhQivbpXlerM-DUzNY7BtctNBbn08Wto8CSSxQDxRU,568
111
112
  kash/file_storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
112
- kash/file_storage/file_store.py,sha256=rWFJD_PVzNgBtFBUD8Ltj9GK-am5zFj9vrMdyIy9pjQ,31442
113
+ kash/file_storage/file_store.py,sha256=0re_Gp0NoN3KxsMPtU8RSrG3Jw_PelC2wqju07AGxTU,31634
113
114
  kash/file_storage/item_file_format.py,sha256=QNNpvMDDNyBLLrIwy3yhNgN0Ktsvjh-_Uzkf2XQpI98,7531
114
115
  kash/file_storage/item_id_index.py,sha256=aiDHomizFLO_Ui2i6n-YIF4RUosIsxRIQca35yCdIIk,4589
115
116
  kash/file_storage/metadata_dirs.py,sha256=9AqO3S3SSY1dtvP2iLX--E4ui0VIzXttG8R040otfyg,3820
@@ -134,10 +135,10 @@ kash/llm_utils/custom_sliding_transforms.py,sha256=z07WCdBoiywBIGk2EXK3xY4t_6g8I
134
135
  kash/llm_utils/fuzzy_parsing.py,sha256=bbG2Y7i5w6kxAVPAixyluv3MDS2hW_pkhnJpVOLHZQc,3278
135
136
  kash/llm_utils/init_litellm.py,sha256=5Fn9uW4P7lfEO8Rk1EJJUzDEGNjw-PDvxFgHlKDf-Ok,409
136
137
  kash/llm_utils/llm_api_keys.py,sha256=nTB9wSFfHTOXvqshSQCQGCPxUwOW1U7oslngya8nHkw,1168
137
- kash/llm_utils/llm_completion.py,sha256=SzeWGRrsjuN1TXdPwscYG6whLQkHclITtwTvQK19GE0,5436
138
+ kash/llm_utils/llm_completion.py,sha256=UNFNgJbgU8sAWXmSrMV9DGrQ_1x-0IeEbtvrDG_P4NE,9251
138
139
  kash/llm_utils/llm_messages.py,sha256=70QwIIvdwo-h4Jfn_6MbEHb3LTUjUmzg_v_dU_Ey__g,1174
139
140
  kash/llm_utils/llm_names.py,sha256=VZbdKnoeBx_luB5YQ-Rz37gMt3_FcueJdp40ZaQbpUA,3620
140
- kash/llm_utils/llms.py,sha256=S4hBDeOraKOp9e99M8Ga61u6xAkocxs02jXgJRdFNSA,3983
141
+ kash/llm_utils/llms.py,sha256=sGDrTnYAqjg_keKhXa7JR3HHkp5Klt-OhO3t9YjuhVQ,4036
141
142
  kash/local_server/__init__.py,sha256=AyNpvCOJlQF6A4DnlYKRMbbfRNzdizEA-ytp-F2SLZU,162
142
143
  kash/local_server/local_server.py,sha256=EugjL30VM0pWdZDsiQxU-o6EdEa082qlGd_7RHvI5tk,5863
143
144
  kash/local_server/local_server_commands.py,sha256=T5wN-Xty3IbwbyeJxTULPB2NqssWLJcuV8IoqMu9bus,1668
@@ -146,10 +147,10 @@ kash/local_server/local_url_formatters.py,sha256=SqHjGMEufvm43n34SCa_8Asdwm7utx9
146
147
  kash/local_server/port_tools.py,sha256=oFfOvO6keqS5GowTpVg2FTu5KqkPHBq-dWAEomUIgGo,2008
147
148
  kash/local_server/rich_html_template.py,sha256=O9CnkMYkWuMvKJkqD0P8jaZqfUe6hMP4LXFvcLpwN8Q,196
148
149
  kash/mcp/__init__.py,sha256=HA_aFskEchpAwA0wOKi5nasytx2JZfH8z9oCVXUI7MQ,160
149
- kash/mcp/mcp_cli.py,sha256=UC0Jwd7DLxx2zEFj0hK1UWPkXXXGntV1te33CensyHM,3849
150
+ kash/mcp/mcp_cli.py,sha256=lWV7mbsj7qMgA42aW5MMkXxLwb66kGrjuaXLsDsTRTo,4300
150
151
  kash/mcp/mcp_main.py,sha256=6PhjKwp631mezDTUAd-pL8lUZx9Gl7yCrCQFW61pqJU,3167
151
152
  kash/mcp/mcp_server_commands.py,sha256=sKPBD4DptUArxo833eJRRYYRt32E5Dwpr0k8Qe6U8ec,4349
152
- kash/mcp/mcp_server_routes.py,sha256=gWsr9WkEVzSslF-XaiO7wMUD9bJeabXj4xNy6WwqHaM,11757
153
+ kash/mcp/mcp_server_routes.py,sha256=dfeCFK6FedRG3OXVd-53Hk5uX2TkWWQ0mAxllm8gJc8,11803
153
154
  kash/mcp/mcp_server_sse.py,sha256=7zAOJodrCVfLtChUTRxzgxWGymQnmj1e9fRG7H_yqXg,5678
154
155
  kash/mcp/mcp_server_stdio.py,sha256=u-oeIZKwzIAGlKmBpipC-hfNQKx36Md9hST11y3AZUY,1174
155
156
  kash/media_base/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -163,22 +164,22 @@ kash/media_base/transcription_format.py,sha256=rOVPTpwvW22c27BRwYF-Tc_xzqK_wOtUZ
163
164
  kash/media_base/transcription_whisper.py,sha256=GqvroW9kBAH4-gcbYkMgNCfs2MpMIgm1ip3NMWtJ0IE,1169
164
165
  kash/media_base/services/local_file_media.py,sha256=_NV-T90rShJ8ucUjQXMPCKKJ50GSFE9PyyVzhXp5z9w,5624
165
166
  kash/model/__init__.py,sha256=kFfBKb5N70NWYUfpRRxn_Sb9p_vXlB6BBaTCqWmSReo,2978
166
- kash/model/actions_model.py,sha256=D-q-eZO_yOug3fuvDUWB1AI_MNwhpElSomaPS7PybyU,23362
167
+ kash/model/actions_model.py,sha256=I87Gb2nU6xelEuM8WFuuHWDqczjnlbpBYkw6wWIYkbo,23825
167
168
  kash/model/assistant_response_model.py,sha256=6eDfC27nyuBDFjv5nCYMa_Qb2mPbKwDzZy7uLOIyskI,2653
168
169
  kash/model/compound_actions_model.py,sha256=oYEtVKtQv-mA1abZkK7PvaM9xazVBUuk1z0geKBulak,6965
169
170
  kash/model/concept_model.py,sha256=we2qOcy9Mv1q7XPfkDLp_CyO_-8DwAUfUYlpgy_jrFs,1011
170
171
  kash/model/exec_model.py,sha256=3Su3NEmEtDoSuQSxvg75FYY_EdClSM5pwQK1i7_S88A,3131
171
172
  kash/model/graph_model.py,sha256=T034y0E9OJtITd1g9zp9vll5pLscdatq6JoT08KvPZE,2724
172
- kash/model/items_model.py,sha256=--n5PzDHuX0zp0cleCAEhigqzj9g4uSIoF58iIpv2AI,39720
173
+ kash/model/items_model.py,sha256=e-KCVhC6poWjocsxZVBa0A8gmehtsZRcIqhLVIna5Ho,40152
173
174
  kash/model/language_list.py,sha256=I3RIbxTseVmPdhExQimimEv18Gmy2ImMbpXe0-_t1Qw,450
174
175
  kash/model/llm_actions_model.py,sha256=a29uXVNfS2CiqvM7HPdC6H9A23rSQQihAideuBLMH8g,2110
175
176
  kash/model/media_model.py,sha256=ZnlZ-FkswbAIGpUAuNqLce1WDZK-WbnwHn2ipg8x7-0,3511
176
177
  kash/model/operations_model.py,sha256=WmU-xeWGsqMLVN369dQEyVGU8T7G_KyLLsj6YFc5sVw,6517
177
- kash/model/params_model.py,sha256=NHW-74_sFCY58spf9bzU1EaVxLDpdbqmiw8SCSKASCw,15079
178
+ kash/model/params_model.py,sha256=PWyx2P9tt1zj8NpBnvFkn61_KCegFtTJ_FSPYSLnsiY,15121
178
179
  kash/model/paths_model.py,sha256=KDFm7wan7hjObHbnV2rR8-jsyLTVqbKcwFdKeLFRtdM,15889
179
180
  kash/model/preconditions_model.py,sha256=-IfsVR0NkQhq_3hUTXzK2bFYAd--3YjSwUiDKHVQQqk,2887
180
181
  kash/shell/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
181
- kash/shell/shell_main.py,sha256=thpGEN2kiruTiMaocdwnzfIAs-WCX-Pvi8jiea3V2q0,3896
182
+ kash/shell/shell_main.py,sha256=mG_QoyOeHjcXpPUARjmHEtCjPGpc3IT28kdu8i8NkwM,3401
182
183
  kash/shell/version.py,sha256=Si8MgIwAwTOSdXY7znWHTsvzdR2vlZdkmcF5n_oUigE,1400
183
184
  kash/shell/completions/completion_scoring.py,sha256=-svesm2cR1AA86jYcxlynXCBZON26eUJce93FlL2nQo,10209
184
185
  kash/shell/completions/completion_types.py,sha256=FocRXd6Df3Df0nL2Y1GevMx3FsljJwbQdVgWsIngpaQ,4793
@@ -213,13 +214,13 @@ kash/utils/api_utils/progress_protocol.py,sha256=6cT5URY6cScHYd6UZoTT_rHI0mbsE52
213
214
  kash/utils/common/__init__.py,sha256=ggeWw1xmbl1mgCQD3c4CNN2h5WXFCsN2wXlCWurEUEI,161
214
215
  kash/utils/common/format_utils.py,sha256=83FhAwbMnOQIFudpnOGMuCqCiyoAlWGS6cc8q6xgZus,2072
215
216
  kash/utils/common/function_inspect.py,sha256=KE5QgIPx16iupCZrMlcvU6MuA5fUFboPEkqmltpFw3E,19824
216
- kash/utils/common/import_utils.py,sha256=zyCa5sG_vTxzgIgjOS98xAwqkSeCQzN-8UkM6k9ZZOI,4615
217
+ kash/utils/common/import_utils.py,sha256=j3_xZS__647YjlszmKCwtS2agZPOi9ZaQ7SNbz99RhM,8988
217
218
  kash/utils/common/lazyobject.py,sha256=9dmOfSheblOXgo2RRakMwgfPIKdTgtyrlm6dCKAze04,5157
218
219
  kash/utils/common/obj_replace.py,sha256=AuiXptUOnuDNcWDgAJ3jEHkLh89XIqCP_SOkgaVyFIQ,2075
219
220
  kash/utils/common/parse_docstring.py,sha256=oM1ecGGySRA3L_poddjReJ_qPY5506Le7E8_CDUrU8k,10922
220
221
  kash/utils/common/parse_key_vals.py,sha256=yZRZIa5GD9SlnBSn2YNZm8PRVKoSJMY8DCmdGujQj_I,2418
221
222
  kash/utils/common/parse_shell_args.py,sha256=UZXTZDbV5m5Jy39jdAQ6W8uilr1TNa0__RqnE8UmQ_M,10604
222
- kash/utils/common/s3_utils.py,sha256=YZ_qTKtuUtb_oJd8Ke0PgVdg8sYa47PwUkTZVsJ0rWM,3311
223
+ kash/utils/common/s3_utils.py,sha256=wt-ieT9DPwsqe8_w-KCeyUAbDCvsIaCiD9lsOfHvd1A,5534
223
224
  kash/utils/common/stack_traces.py,sha256=a2NwlK_0xxnjMCDC4LrQu7ueFylF-OImFG3bAAHpPwY,1392
224
225
  kash/utils/common/task_stack.py,sha256=XkeBz3BwYY1HxxTqd3f7CulV0s61PePAKw1Irrtvf5o,4536
225
226
  kash/utils/common/testing.py,sha256=9at6m_2YwRYitQoo-KeGsd2aoA59YUPs-x7cKcmy1C4,1802
@@ -260,10 +261,10 @@ kash/web_content/dir_store.py,sha256=BJc-s-RL5CC-GwhFTC_lhLXSMWluPPnLVmVBx-66DiM
260
261
  kash/web_content/file_cache_utils.py,sha256=JRXUCAmrc83iAgdiICU2EYGWcoORflWNl6GAVq-O80I,5529
261
262
  kash/web_content/file_processing.py,sha256=cQC-MnJMM5qG9-y0S4yobkmRi6A75qhHjV6xTwbtYDY,1904
262
263
  kash/web_content/local_file_cache.py,sha256=PEDKU5VIwhCnSC-HXG4EkO2OzrOUDuuDBMuo3lP2EN0,9466
263
- kash/web_content/web_extract.py,sha256=TrGOohn99LF1F0zbycgXqYiIR-V3leefFFEVk3tnW6Y,2979
264
+ kash/web_content/web_extract.py,sha256=2CTxNJTYRGJ5ZfBvNVKxNGwNlbUyTnfHRPL6qRIrYxI,2978
264
265
  kash/web_content/web_extract_justext.py,sha256=74HLJBKDGKatwxyRDX6za70bZG9LrVmtj9jLX7UJzg4,2540
265
266
  kash/web_content/web_extract_readabilipy.py,sha256=IT7ET5IoU2-Nf37-Neh6CkKMvLL3WTNVJjq7ZMOx6OM,808
266
- kash/web_content/web_fetch.py,sha256=TGzGRyG9KW26V8WhQJkK-MwFN2MJBtMX-j4iY72tVtg,12217
267
+ kash/web_content/web_fetch.py,sha256=xBTd63oi3j2kwwTUYTF5mJ7ZXEEHa51wZB60KzCgG5k,17046
267
268
  kash/web_content/web_page_model.py,sha256=TWgWreGOoOvl7lUkjMSd0rxoDf2VuaKOq7WijwydW0Q,1387
268
269
  kash/web_gen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
269
270
  kash/web_gen/tabbed_webpage.py,sha256=e0GGG1bQ4CQegpJgOIT2KpyM6cmwN_BQ9eJSsi4BjgY,4945
@@ -276,7 +277,7 @@ kash/web_gen/templates/explain_view.html.jinja,sha256=DNw5Iw5SrhIUFRGB4qNvfcKXsB
276
277
  kash/web_gen/templates/item_view.html.jinja,sha256=_b51RuoBmYu7nxVq-S_zw_tv8mfQXHICGqfDWNIB9Xg,7304
277
278
  kash/web_gen/templates/simple_webpage.html.jinja,sha256=_RVu0AvrN5fK_Kz-tEi4AlPQ_wuu8-S9oFIYg_sdcec,2556
278
279
  kash/web_gen/templates/tabbed_webpage.html.jinja,sha256=umkipUXW-lDGnbV-tlDroCfCx_385PFnpbzsvwmityo,1843
279
- kash/web_gen/templates/youtube_webpage.html.jinja,sha256=uUdu3HWv0lwwgEh094QbzzesxAdYv9rCxCkiuuIr1MY,1380
280
+ kash/web_gen/templates/youtube_webpage.html.jinja,sha256=eEvWXQsqEIapH0-0nXfG1SGh4BAmbxKIMmvZMcteYQE,1397
280
281
  kash/web_gen/templates/components/toc_scripts.js.jinja,sha256=9AanLJaVormGi52h-k2tKJTRT4BiBGGNnw3Kmrnr40Q,10481
281
282
  kash/web_gen/templates/components/toc_styles.css.jinja,sha256=SM99C9bOu11qdZ1U6hN7lLe2w_Hk6u9qNZcx2knSkgg,7553
282
283
  kash/web_gen/templates/components/tooltip_scripts.js.jinja,sha256=63HUSXtT07Vh2ndp90ViuPHTdzslrkGO5it4s-5EheM,40181
@@ -290,7 +291,7 @@ kash/workspaces/source_items.py,sha256=Pwnw3OhjR2IJEMEeHf6hpKloj-ellM5vsY7LgkGev
290
291
  kash/workspaces/workspace_dirs.py,sha256=kjuY4t7mSSXq00fZmln7p9TWq4kAZoPTCDM0DG7uEaI,1545
291
292
  kash/workspaces/workspace_output.py,sha256=MMg_KumkHKFGc0DOUFaW5ImpgqIfdlsLtvXbLEt1hwI,5692
292
293
  kash/workspaces/workspace_registry.py,sha256=SQt2DZgBEu95Zj9fpy67XdJPgJyKFDCU2laSuiZswNo,2200
293
- kash/workspaces/workspaces.py,sha256=H-VpigO2zvWvWCZIT2G-cqEqcZATiwCLFX2jwFW08bw,6781
294
+ kash/workspaces/workspaces.py,sha256=LBqrl_NVpXb7Lnjg_aIhQ0BZxNzEWK7p0jv0VEy0L7A,6880
294
295
  kash/xonsh_custom/command_nl_utils.py,sha256=6Xcx98HXL5HywziHi0XskwFx6kfvz7oCmMX-siJib1A,3392
295
296
  kash/xonsh_custom/custom_shell.py,sha256=wTYwDt6zJzVtljcDh1j__cJXkoMy9w687vhgDk6nlqU,19024
296
297
  kash/xonsh_custom/customize_prompt.py,sha256=u-jRY-ftXYflBkbJVetEg6GYelPUvqZEpjrPRUTfy8w,6896
@@ -304,8 +305,8 @@ kash/xonsh_custom/xonsh_modern_tools.py,sha256=mj_b34LZXfE8MJe9EpDmp5JZ0tDM1biYN
304
305
  kash/xonsh_custom/xonsh_ranking_completer.py,sha256=ZRGiAfoEgqgnlq2-ReUVEaX5oOgW1DQ9WxIv2OJLuTo,5620
305
306
  kash/xontrib/fnm.py,sha256=V2tsOdmIDgbFbZSfMLpsvDIwwJJqiYnOkOySD1cXNXw,3700
306
307
  kash/xontrib/kash_extension.py,sha256=FLIMlgR3C_6A1fwKE-Ul0nmmpJSszVPbAriinUyQ8Zg,1896
307
- kash_shell-0.3.34.dist-info/METADATA,sha256=ubXLWP8kfglDx7f2zvpr1TKaah6BZ6ye-B7hPYorfYc,33547
308
- kash_shell-0.3.34.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
309
- kash_shell-0.3.34.dist-info/entry_points.txt,sha256=SQraWDAo8SqYpthLXThei0mf_hGGyhYBUO-Er_0HcwI,85
310
- kash_shell-0.3.34.dist-info/licenses/LICENSE,sha256=rCh2PsfYeiU6FK_0wb58kHGm_Fj5c43fdcHEexiVzIo,34562
311
- kash_shell-0.3.34.dist-info/RECORD,,
308
+ kash_shell-0.3.36.dist-info/METADATA,sha256=LK3nnR0TWHtw_F8qG9WSihteRlQZXggGY8f-DwtUjBQ,33581
309
+ kash_shell-0.3.36.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
310
+ kash_shell-0.3.36.dist-info/entry_points.txt,sha256=SQraWDAo8SqYpthLXThei0mf_hGGyhYBUO-Er_0HcwI,85
311
+ kash_shell-0.3.36.dist-info/licenses/LICENSE,sha256=rCh2PsfYeiU6FK_0wb58kHGm_Fj5c43fdcHEexiVzIo,34562
312
+ kash_shell-0.3.36.dist-info/RECORD,,