dulus 0.2.36__tar.gz → 0.2.38__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. {dulus-0.2.36/dulus.egg-info → dulus-0.2.38}/PKG-INFO +1 -1
  2. {dulus-0.2.36 → dulus-0.2.38/dulus.egg-info}/PKG-INFO +1 -1
  3. {dulus-0.2.36 → dulus-0.2.38}/dulus.py +14 -5
  4. {dulus-0.2.36 → dulus-0.2.38}/dulus_gui.py +28 -5
  5. {dulus-0.2.36 → dulus-0.2.38}/memory/tools.py +42 -3
  6. {dulus-0.2.36 → dulus-0.2.38}/pyproject.toml +1 -1
  7. {dulus-0.2.36 → dulus-0.2.38}/LICENSE +0 -0
  8. {dulus-0.2.36 → dulus-0.2.38}/MANIFEST.in +0 -0
  9. {dulus-0.2.36 → dulus-0.2.38}/README.md +0 -0
  10. {dulus-0.2.36 → dulus-0.2.38}/agent.py +0 -0
  11. {dulus-0.2.36 → dulus-0.2.38}/backend/__init__.py +0 -0
  12. {dulus-0.2.36 → dulus-0.2.38}/backend/compressor.py +0 -0
  13. {dulus-0.2.36 → dulus-0.2.38}/backend/context.py +0 -0
  14. {dulus-0.2.36 → dulus-0.2.38}/backend/githook.py +0 -0
  15. {dulus-0.2.36 → dulus-0.2.38}/backend/marketplace.py +0 -0
  16. {dulus-0.2.36 → dulus-0.2.38}/backend/mempalace_bridge.py +0 -0
  17. {dulus-0.2.36 → dulus-0.2.38}/backend/personas.py +0 -0
  18. {dulus-0.2.36 → dulus-0.2.38}/backend/plugins.py +0 -0
  19. {dulus-0.2.36 → dulus-0.2.38}/backend/server.py +0 -0
  20. {dulus-0.2.36 → dulus-0.2.38}/backend/tasks.py +0 -0
  21. {dulus-0.2.36 → dulus-0.2.38}/batch_api.py +0 -0
  22. {dulus-0.2.36 → dulus-0.2.38}/checkpoint/__init__.py +0 -0
  23. {dulus-0.2.36 → dulus-0.2.38}/checkpoint/hooks.py +0 -0
  24. {dulus-0.2.36 → dulus-0.2.38}/checkpoint/store.py +0 -0
  25. {dulus-0.2.36 → dulus-0.2.38}/checkpoint/types.py +0 -0
  26. {dulus-0.2.36 → dulus-0.2.38}/claude_code_watcher.py +0 -0
  27. {dulus-0.2.36 → dulus-0.2.38}/clipboard_utils.py +0 -0
  28. {dulus-0.2.36 → dulus-0.2.38}/cloudsave.py +0 -0
  29. {dulus-0.2.36 → dulus-0.2.38}/common.py +0 -0
  30. {dulus-0.2.36 → dulus-0.2.38}/compaction.py +0 -0
  31. {dulus-0.2.36 → dulus-0.2.38}/config.py +0 -0
  32. {dulus-0.2.36 → dulus-0.2.38}/context.py +0 -0
  33. {dulus-0.2.36 → dulus-0.2.38}/data/__init__.py +0 -0
  34. {dulus-0.2.36 → dulus-0.2.38}/data/active_persona.json +0 -0
  35. {dulus-0.2.36 → dulus-0.2.38}/data/context.json +0 -0
  36. {dulus-0.2.36 → dulus-0.2.38}/data/marketplace.json +0 -0
  37. {dulus-0.2.36 → dulus-0.2.38}/data/personas.json +0 -0
  38. {dulus-0.2.36 → dulus-0.2.38}/data/plugins/__init__.py +0 -0
  39. {dulus-0.2.36 → dulus-0.2.38}/data/plugins/composio/__init__.py +0 -0
  40. {dulus-0.2.36 → dulus-0.2.38}/data/plugins/composio/composio_plugin/__init__.py +0 -0
  41. {dulus-0.2.36 → dulus-0.2.38}/data/plugins/composio/composio_plugin/session_manager.py +0 -0
  42. {dulus-0.2.36 → dulus-0.2.38}/data/plugins/composio/composio_plugin/tool_generator.py +0 -0
  43. {dulus-0.2.36 → dulus-0.2.38}/data/plugins/composio/plugin.json +0 -0
  44. {dulus-0.2.36 → dulus-0.2.38}/data/plugins/composio/plugin_tool.py +0 -0
  45. {dulus-0.2.36 → dulus-0.2.38}/data/tasks.json +0 -0
  46. {dulus-0.2.36 → dulus-0.2.38}/docs/README.md +0 -0
  47. {dulus-0.2.36 → dulus-0.2.38}/docs/__init__.py +0 -0
  48. {dulus-0.2.36 → dulus-0.2.38}/docs/api.html +0 -0
  49. {dulus-0.2.36 → dulus-0.2.38}/docs/architecture.md +0 -0
  50. {dulus-0.2.36 → dulus-0.2.38}/docs/azure-speech-template.json +0 -0
  51. {dulus-0.2.36 → dulus-0.2.38}/docs/dashboard/index.html +0 -0
  52. {dulus-0.2.36 → dulus-0.2.38}/docs/divider.svg +0 -0
  53. {dulus-0.2.36 → dulus-0.2.38}/docs/generate.py +0 -0
  54. {dulus-0.2.36 → dulus-0.2.38}/docs/hero.svg +0 -0
  55. {dulus-0.2.36 → dulus-0.2.38}/docs/index.html +0 -0
  56. {dulus-0.2.36 → dulus-0.2.38}/docs/news.md +0 -0
  57. {dulus-0.2.36 → dulus-0.2.38}/docs/nvidia-models.svg +0 -0
  58. {dulus-0.2.36 → dulus-0.2.38}/docs/particle-playground.html +0 -0
  59. {dulus-0.2.36 → dulus-0.2.38}/docs/personas/index.html +0 -0
  60. {dulus-0.2.36 → dulus-0.2.38}/docs/poetry-banner.png +0 -0
  61. {dulus-0.2.36 → dulus-0.2.38}/docs/preview.html +0 -0
  62. {dulus-0.2.36 → dulus-0.2.38}/docs/sec-agents.svg +0 -0
  63. {dulus-0.2.36 → dulus-0.2.38}/docs/sec-brainstorm.svg +0 -0
  64. {dulus-0.2.36 → dulus-0.2.38}/docs/sec-bridges.svg +0 -0
  65. {dulus-0.2.36 → dulus-0.2.38}/docs/sec-features.svg +0 -0
  66. {dulus-0.2.36 → dulus-0.2.38}/docs/sec-freetier.svg +0 -0
  67. {dulus-0.2.36 → dulus-0.2.38}/docs/sec-memory.svg +0 -0
  68. {dulus-0.2.36 → dulus-0.2.38}/docs/sec-models.svg +0 -0
  69. {dulus-0.2.36 → dulus-0.2.38}/docs/sec-perms.svg +0 -0
  70. {dulus-0.2.36 → dulus-0.2.38}/docs/sec-plugins.svg +0 -0
  71. {dulus-0.2.36 → dulus-0.2.38}/docs/sec-quickstart.svg +0 -0
  72. {dulus-0.2.36 → dulus-0.2.38}/docs/sec-ssj.svg +0 -0
  73. {dulus-0.2.36 → dulus-0.2.38}/docs/spinners.svg +0 -0
  74. {dulus-0.2.36 → dulus-0.2.38}/docs/split-pane.svg +0 -0
  75. {dulus-0.2.36 → dulus-0.2.38}/docs/terminal-boot.svg +0 -0
  76. {dulus-0.2.36 → dulus-0.2.38}/docs/uploads/particle-playground.html +0 -0
  77. {dulus-0.2.36 → dulus-0.2.38}/dulus.egg-info/SOURCES.txt +0 -0
  78. {dulus-0.2.36 → dulus-0.2.38}/dulus.egg-info/dependency_links.txt +0 -0
  79. {dulus-0.2.36 → dulus-0.2.38}/dulus.egg-info/entry_points.txt +0 -0
  80. {dulus-0.2.36 → dulus-0.2.38}/dulus.egg-info/requires.txt +0 -0
  81. {dulus-0.2.36 → dulus-0.2.38}/dulus.egg-info/top_level.txt +0 -0
  82. {dulus-0.2.36 → dulus-0.2.38}/dulus_mcp/__init__.py +0 -0
  83. {dulus-0.2.36 → dulus-0.2.38}/dulus_mcp/client.py +0 -0
  84. {dulus-0.2.36 → dulus-0.2.38}/dulus_mcp/config.py +0 -0
  85. {dulus-0.2.36 → dulus-0.2.38}/dulus_mcp/tools.py +0 -0
  86. {dulus-0.2.36 → dulus-0.2.38}/dulus_mcp/types.py +0 -0
  87. {dulus-0.2.36 → dulus-0.2.38}/gui/__init__.py +0 -0
  88. {dulus-0.2.36 → dulus-0.2.38}/gui/agent_bridge.py +0 -0
  89. {dulus-0.2.36 → dulus-0.2.38}/gui/chat_widget.py +0 -0
  90. {dulus-0.2.36 → dulus-0.2.38}/gui/main_window.py +0 -0
  91. {dulus-0.2.36 → dulus-0.2.38}/gui/personas.py +0 -0
  92. {dulus-0.2.36 → dulus-0.2.38}/gui/session_utils.py +0 -0
  93. {dulus-0.2.36 → dulus-0.2.38}/gui/settings_dialog.py +0 -0
  94. {dulus-0.2.36 → dulus-0.2.38}/gui/sidebar.py +0 -0
  95. {dulus-0.2.36 → dulus-0.2.38}/gui/tasks_view.py +0 -0
  96. {dulus-0.2.36 → dulus-0.2.38}/gui/themes.py +0 -0
  97. {dulus-0.2.36 → dulus-0.2.38}/gui/tool_panel.py +0 -0
  98. {dulus-0.2.36 → dulus-0.2.38}/input.py +0 -0
  99. {dulus-0.2.36 → dulus-0.2.38}/license_manager.py +0 -0
  100. {dulus-0.2.36 → dulus-0.2.38}/memory/__init__.py +0 -0
  101. {dulus-0.2.36 → dulus-0.2.38}/memory/audit.py +0 -0
  102. {dulus-0.2.36 → dulus-0.2.38}/memory/consolidator.py +0 -0
  103. {dulus-0.2.36 → dulus-0.2.38}/memory/context.py +0 -0
  104. {dulus-0.2.36 → dulus-0.2.38}/memory/offload.py +0 -0
  105. {dulus-0.2.36 → dulus-0.2.38}/memory/palace.py +0 -0
  106. {dulus-0.2.36 → dulus-0.2.38}/memory/scan.py +0 -0
  107. {dulus-0.2.36 → dulus-0.2.38}/memory/sessions.py +0 -0
  108. {dulus-0.2.36 → dulus-0.2.38}/memory/store.py +0 -0
  109. {dulus-0.2.36 → dulus-0.2.38}/memory/types.py +0 -0
  110. {dulus-0.2.36 → dulus-0.2.38}/memory/vector_search.py +0 -0
  111. {dulus-0.2.36 → dulus-0.2.38}/multi_agent/__init__.py +0 -0
  112. {dulus-0.2.36 → dulus-0.2.38}/multi_agent/subagent.py +0 -0
  113. {dulus-0.2.36 → dulus-0.2.38}/multi_agent/tools.py +0 -0
  114. {dulus-0.2.36 → dulus-0.2.38}/offload_helper.py +0 -0
  115. {dulus-0.2.36 → dulus-0.2.38}/plugin/__init__.py +0 -0
  116. {dulus-0.2.36 → dulus-0.2.38}/plugin/autoadapter.py +0 -0
  117. {dulus-0.2.36 → dulus-0.2.38}/plugin/loader.py +0 -0
  118. {dulus-0.2.36 → dulus-0.2.38}/plugin/recommend.py +0 -0
  119. {dulus-0.2.36 → dulus-0.2.38}/plugin/store.py +0 -0
  120. {dulus-0.2.36 → dulus-0.2.38}/plugin/types.py +0 -0
  121. {dulus-0.2.36 → dulus-0.2.38}/providers.py +0 -0
  122. {dulus-0.2.36 → dulus-0.2.38}/setup.cfg +0 -0
  123. {dulus-0.2.36 → dulus-0.2.38}/skill/__init__.py +0 -0
  124. {dulus-0.2.36 → dulus-0.2.38}/skill/builtin.py +0 -0
  125. {dulus-0.2.36 → dulus-0.2.38}/skill/clawhub.py +0 -0
  126. {dulus-0.2.36 → dulus-0.2.38}/skill/executor.py +0 -0
  127. {dulus-0.2.36 → dulus-0.2.38}/skill/loader.py +0 -0
  128. {dulus-0.2.36 → dulus-0.2.38}/skill/tools.py +0 -0
  129. {dulus-0.2.36 → dulus-0.2.38}/skills.py +0 -0
  130. {dulus-0.2.36 → dulus-0.2.38}/spinner.py +0 -0
  131. {dulus-0.2.36 → dulus-0.2.38}/string_utils.py +0 -0
  132. {dulus-0.2.36 → dulus-0.2.38}/subagent.py +0 -0
  133. {dulus-0.2.36 → dulus-0.2.38}/task/__init__.py +0 -0
  134. {dulus-0.2.36 → dulus-0.2.38}/task/store.py +0 -0
  135. {dulus-0.2.36 → dulus-0.2.38}/task/tools.py +0 -0
  136. {dulus-0.2.36 → dulus-0.2.38}/task/types.py +0 -0
  137. {dulus-0.2.36 → dulus-0.2.38}/tests/test_checkpoint.py +0 -0
  138. {dulus-0.2.36 → dulus-0.2.38}/tests/test_compaction.py +0 -0
  139. {dulus-0.2.36 → dulus-0.2.38}/tests/test_diff_view.py +0 -0
  140. {dulus-0.2.36 → dulus-0.2.38}/tests/test_injection_fix.py +0 -0
  141. {dulus-0.2.36 → dulus-0.2.38}/tests/test_license.py +0 -0
  142. {dulus-0.2.36 → dulus-0.2.38}/tests/test_mcp.py +0 -0
  143. {dulus-0.2.36 → dulus-0.2.38}/tests/test_memory.py +0 -0
  144. {dulus-0.2.36 → dulus-0.2.38}/tests/test_plugin.py +0 -0
  145. {dulus-0.2.36 → dulus-0.2.38}/tests/test_skills.py +0 -0
  146. {dulus-0.2.36 → dulus-0.2.38}/tests/test_subagent.py +0 -0
  147. {dulus-0.2.36 → dulus-0.2.38}/tests/test_task.py +0 -0
  148. {dulus-0.2.36 → dulus-0.2.38}/tests/test_telegram_buffer.py +0 -0
  149. {dulus-0.2.36 → dulus-0.2.38}/tests/test_tool_registry.py +0 -0
  150. {dulus-0.2.36 → dulus-0.2.38}/tests/test_voice.py +0 -0
  151. {dulus-0.2.36 → dulus-0.2.38}/tmux_offloader.py +0 -0
  152. {dulus-0.2.36 → dulus-0.2.38}/tmux_tools.py +0 -0
  153. {dulus-0.2.36 → dulus-0.2.38}/tool_registry.py +0 -0
  154. {dulus-0.2.36 → dulus-0.2.38}/tools.py +0 -0
  155. {dulus-0.2.36 → dulus-0.2.38}/ui/__init__.py +0 -0
  156. {dulus-0.2.36 → dulus-0.2.38}/ui/input.py +0 -0
  157. {dulus-0.2.36 → dulus-0.2.38}/ui/render.py +0 -0
  158. {dulus-0.2.36 → dulus-0.2.38}/voice/__init__.py +0 -0
  159. {dulus-0.2.36 → dulus-0.2.38}/voice/keyterms.py +0 -0
  160. {dulus-0.2.36 → dulus-0.2.38}/voice/recorder.py +0 -0
  161. {dulus-0.2.36 → dulus-0.2.38}/voice/stt.py +0 -0
  162. {dulus-0.2.36 → dulus-0.2.38}/voice/tts.py +0 -0
  163. {dulus-0.2.36 → dulus-0.2.38}/webchat.py +0 -0
  164. {dulus-0.2.36 → dulus-0.2.38}/webchat_server.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dulus
3
- Version: 0.2.36
3
+ Version: 0.2.38
4
4
  Summary: Spanish-first multi-provider AI CLI — 14 NVIDIA models free, Mesa Redonda, voice, TTS, RTK token reducer, MemPalace
5
5
  Author: KevRojo
6
6
  License: GPL-3.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dulus
3
- Version: 0.2.36
3
+ Version: 0.2.38
4
4
  Summary: Spanish-first multi-provider AI CLI — 14 NVIDIA models free, Mesa Redonda, voice, TTS, RTK token reducer, MemPalace
5
5
  Author: KevRojo
6
6
  License: GPL-3.0
@@ -1459,11 +1459,20 @@ def cmd_context(_args: str, state, config) -> bool:
1459
1459
 
1460
1460
  def cmd_cost(_args: str, state, config) -> bool:
1461
1461
  from config import calc_cost
1462
- cost = calc_cost(config["model"],
1463
- state.total_input_tokens,
1464
- state.total_output_tokens)
1465
- info(f"Input tokens: {state.total_input_tokens:,}")
1466
- info(f"Output tokens: {state.total_output_tokens:,}")
1462
+ from compaction import estimate_tokens
1463
+
1464
+ # The accumulated counters (total_input_tokens etc.) sum the provider's
1465
+ # reported "tokens in this request" which includes the ENTIRE context
1466
+ # window every turn. That creates a quadratic explosion and is useless
1467
+ # for cost estimation. Instead, estimate from the actual message list.
1468
+ est_input = estimate_tokens(state.messages, model=config.get("model", ""), config=config)
1469
+ # Output is trickier to estimate from history alone; use the accumulated
1470
+ # counter as a lower-bound fallback but cap it to something sane.
1471
+ est_output = min(state.total_output_tokens, est_input)
1472
+
1473
+ cost = calc_cost(config["model"], est_input, est_output)
1474
+ info(f"Input tokens: ~{est_input:,} (estimated from {len(state.messages)} messages)")
1475
+ info(f"Output tokens: ~{est_output:,}")
1467
1476
  c_read = getattr(state, "total_cache_read_tokens", 0)
1468
1477
  c_write = getattr(state, "total_cache_creation_tokens", 0)
1469
1478
  if c_read > 0 or c_write > 0:
@@ -127,6 +127,26 @@ def launch_gui(config: dict | None = None, initial_prompt: str | None = None) ->
127
127
  """
128
128
  cfg = config or load_config()
129
129
 
130
+ # ── Ensure MemPalace is initialized for fresh installs ──────────────────
131
+ try:
132
+ from pathlib import Path as _Path
133
+ import subprocess as _sp, sys as _sys, os as _os
134
+ _mp_cfg = _Path.home() / ".mempalace" / "config.json"
135
+ if not _mp_cfg.exists():
136
+ _mem_dir = _Path.home() / ".dulus" / "memory"
137
+ _mem_dir.mkdir(parents=True, exist_ok=True)
138
+ _env = {**_os.environ, "PYTHONIOENCODING": "utf-8", "PYTHONUTF8": "1"}
139
+ _sp.run(
140
+ [_sys.executable, "-X", "utf8", "-m", "mempalace", "init",
141
+ str(_mem_dir), "--yes", "--no-llm"],
142
+ stdout=_sp.DEVNULL, stderr=_sp.DEVNULL,
143
+ env=_env,
144
+ creationflags=getattr(_sp, "CREATE_NO_WINDOW", 0),
145
+ check=False,
146
+ )
147
+ except Exception:
148
+ pass # best-effort; don't block GUI startup
149
+
130
150
  # Theme
131
151
  ctk.set_appearance_mode(cfg.get("appearance", "dark"))
132
152
  ctk.set_default_color_theme("dark-blue")
@@ -266,11 +286,14 @@ def launch_gui(config: dict | None = None, initial_prompt: str | None = None) ->
266
286
  itok = event.get("input_tokens", 0)
267
287
  otok = event.get("output_tokens", 0)
268
288
  app.set_status(f"Listo (+{itok}/{otok} tok)", t["success"])
269
-
270
- # Refresh sessions list to show the newly saved session (with its title)
271
- app.set_sessions(scan_sessions())
272
- if event.get("session_id"):
273
- app.set_active_session(event.get("session_id"))
289
+
290
+ # Only rebuild sidebar if this is a brand-new session not yet in the list.
291
+ # Rebuilding after every message causes annoying flicker.
292
+ sid = event.get("session_id")
293
+ if sid and sid not in app.sidebar._session_buttons:
294
+ app.set_sessions(scan_sessions())
295
+ if sid:
296
+ app.set_active_session(sid)
274
297
 
275
298
  elif etype == "permission":
276
299
  _show_perm(event.get("description", ""))
@@ -13,6 +13,45 @@ from .scan import scan_all_memories, format_memory_manifest
13
13
  from .sessions import search_session_history
14
14
 
15
15
 
16
+ # ── MemPalace auto-init helper ─────────────────────────────────────────────
17
+
18
+ def _mempalace_env() -> dict:
19
+ """Return environment dict with UTF-8 overrides for Windows safety."""
20
+ import os as _os
21
+ return {**_os.environ, "PYTHONIOENCODING": "utf-8", "PYTHONUTF8": "1"}
22
+
23
+
24
+ def _is_mempalace_initialized() -> bool:
25
+ """Check whether MemPalace has been init'd (config dir exists)."""
26
+ try:
27
+ from pathlib import Path as _Path
28
+ return (_Path.home() / ".mempalace" / "config.json").exists()
29
+ except Exception:
30
+ return False
31
+
32
+
33
+ def _ensure_mempalace_initialized(mem_dir: "Path") -> None:
34
+ """Run ``mempalace init`` if the global palace has never been set up.
35
+
36
+ This is a no-op for existing installations so it is safe to call on every
37
+ startup / save.
38
+ """
39
+ if _is_mempalace_initialized():
40
+ return
41
+ try:
42
+ import subprocess as _sp, sys as _sys
43
+ _sp.run(
44
+ [_sys.executable, "-X", "utf8", "-m", "mempalace", "init",
45
+ str(mem_dir), "--yes", "--no-llm"],
46
+ stdout=_sp.DEVNULL, stderr=_sp.DEVNULL,
47
+ env=_mempalace_env(),
48
+ creationflags=getattr(_sp, "CREATE_NO_WINDOW", 0),
49
+ check=False,
50
+ )
51
+ except Exception:
52
+ pass # best-effort; don't crash the UI on init failure
53
+
54
+
16
55
  # ── Tool implementations ───────────────────────────────────────────────────
17
56
 
18
57
  def _memory_save(params: dict, config: dict) -> str:
@@ -37,15 +76,15 @@ def _memory_save(params: dict, config: dict) -> str:
37
76
  # mempalace skips already-filed files, so only the new MD gets indexed.
38
77
  if config.get("mem_palace", True) and scope == "user":
39
78
  try:
40
- import subprocess as _sp, sys as _sys, os as _os
79
+ import subprocess as _sp, sys as _sys
41
80
  from pathlib import Path as _Path
42
81
  _mem_dir = _Path.home() / ".dulus" / "memory"
43
- _env = {**_os.environ, "PYTHONIOENCODING": "utf-8", "PYTHONUTF8": "1"}
82
+ _ensure_mempalace_initialized(_mem_dir)
44
83
  _sp.Popen(
45
84
  [_sys.executable, "-X", "utf8", "-m", "mempalace", "mine",
46
85
  str(_mem_dir), "--wing", "memory", "--agent", "dulus"],
47
86
  stdout=_sp.DEVNULL, stderr=_sp.DEVNULL,
48
- env=_env,
87
+ env=_mempalace_env(),
49
88
  creationflags=getattr(_sp, "CREATE_NO_WINDOW", 0),
50
89
  )
51
90
  except Exception:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "dulus"
7
- version = "0.2.36"
7
+ version = "0.2.38"
8
8
  description = "Spanish-first multi-provider AI CLI — 14 NVIDIA models free, Mesa Redonda, voice, TTS, RTK token reducer, MemPalace"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes