dulus 0.2.29__tar.gz → 0.2.30__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.29/dulus.egg-info → dulus-0.2.30}/PKG-INFO +2 -2
  2. {dulus-0.2.29 → dulus-0.2.30}/README.md +1 -1
  3. {dulus-0.2.29 → dulus-0.2.30}/docs/news.md +2 -0
  4. {dulus-0.2.29 → dulus-0.2.30/dulus.egg-info}/PKG-INFO +2 -2
  5. {dulus-0.2.29 → dulus-0.2.30}/dulus.py +54 -17
  6. {dulus-0.2.29 → dulus-0.2.30}/pyproject.toml +1 -1
  7. {dulus-0.2.29 → dulus-0.2.30}/LICENSE +0 -0
  8. {dulus-0.2.29 → dulus-0.2.30}/MANIFEST.in +0 -0
  9. {dulus-0.2.29 → dulus-0.2.30}/agent.py +0 -0
  10. {dulus-0.2.29 → dulus-0.2.30}/backend/__init__.py +0 -0
  11. {dulus-0.2.29 → dulus-0.2.30}/backend/compressor.py +0 -0
  12. {dulus-0.2.29 → dulus-0.2.30}/backend/context.py +0 -0
  13. {dulus-0.2.29 → dulus-0.2.30}/backend/githook.py +0 -0
  14. {dulus-0.2.29 → dulus-0.2.30}/backend/marketplace.py +0 -0
  15. {dulus-0.2.29 → dulus-0.2.30}/backend/mempalace_bridge.py +0 -0
  16. {dulus-0.2.29 → dulus-0.2.30}/backend/personas.py +0 -0
  17. {dulus-0.2.29 → dulus-0.2.30}/backend/plugins.py +0 -0
  18. {dulus-0.2.29 → dulus-0.2.30}/backend/server.py +0 -0
  19. {dulus-0.2.29 → dulus-0.2.30}/backend/tasks.py +0 -0
  20. {dulus-0.2.29 → dulus-0.2.30}/batch_api.py +0 -0
  21. {dulus-0.2.29 → dulus-0.2.30}/checkpoint/__init__.py +0 -0
  22. {dulus-0.2.29 → dulus-0.2.30}/checkpoint/hooks.py +0 -0
  23. {dulus-0.2.29 → dulus-0.2.30}/checkpoint/store.py +0 -0
  24. {dulus-0.2.29 → dulus-0.2.30}/checkpoint/types.py +0 -0
  25. {dulus-0.2.29 → dulus-0.2.30}/claude_code_watcher.py +0 -0
  26. {dulus-0.2.29 → dulus-0.2.30}/clipboard_utils.py +0 -0
  27. {dulus-0.2.29 → dulus-0.2.30}/cloudsave.py +0 -0
  28. {dulus-0.2.29 → dulus-0.2.30}/common.py +0 -0
  29. {dulus-0.2.29 → dulus-0.2.30}/compaction.py +0 -0
  30. {dulus-0.2.29 → dulus-0.2.30}/config.py +0 -0
  31. {dulus-0.2.29 → dulus-0.2.30}/context.py +0 -0
  32. {dulus-0.2.29 → dulus-0.2.30}/data/__init__.py +0 -0
  33. {dulus-0.2.29 → dulus-0.2.30}/data/active_persona.json +0 -0
  34. {dulus-0.2.29 → dulus-0.2.30}/data/context.json +0 -0
  35. {dulus-0.2.29 → dulus-0.2.30}/data/marketplace.json +0 -0
  36. {dulus-0.2.29 → dulus-0.2.30}/data/personas.json +0 -0
  37. {dulus-0.2.29 → dulus-0.2.30}/data/plugins/__init__.py +0 -0
  38. {dulus-0.2.29 → dulus-0.2.30}/data/plugins/composio/__init__.py +0 -0
  39. {dulus-0.2.29 → dulus-0.2.30}/data/plugins/composio/composio_plugin/__init__.py +0 -0
  40. {dulus-0.2.29 → dulus-0.2.30}/data/plugins/composio/composio_plugin/session_manager.py +0 -0
  41. {dulus-0.2.29 → dulus-0.2.30}/data/plugins/composio/composio_plugin/tool_generator.py +0 -0
  42. {dulus-0.2.29 → dulus-0.2.30}/data/plugins/composio/plugin.json +0 -0
  43. {dulus-0.2.29 → dulus-0.2.30}/data/plugins/composio/plugin_tool.py +0 -0
  44. {dulus-0.2.29 → dulus-0.2.30}/data/tasks.json +0 -0
  45. {dulus-0.2.29 → dulus-0.2.30}/docs/README.md +0 -0
  46. {dulus-0.2.29 → dulus-0.2.30}/docs/__init__.py +0 -0
  47. {dulus-0.2.29 → dulus-0.2.30}/docs/api.html +0 -0
  48. {dulus-0.2.29 → dulus-0.2.30}/docs/architecture.md +0 -0
  49. {dulus-0.2.29 → dulus-0.2.30}/docs/azure-speech-template.json +0 -0
  50. {dulus-0.2.29 → dulus-0.2.30}/docs/dashboard/index.html +0 -0
  51. {dulus-0.2.29 → dulus-0.2.30}/docs/divider.svg +0 -0
  52. {dulus-0.2.29 → dulus-0.2.30}/docs/generate.py +0 -0
  53. {dulus-0.2.29 → dulus-0.2.30}/docs/hero.svg +0 -0
  54. {dulus-0.2.29 → dulus-0.2.30}/docs/index.html +0 -0
  55. {dulus-0.2.29 → dulus-0.2.30}/docs/nvidia-models.svg +0 -0
  56. {dulus-0.2.29 → dulus-0.2.30}/docs/particle-playground.html +0 -0
  57. {dulus-0.2.29 → dulus-0.2.30}/docs/personas/index.html +0 -0
  58. {dulus-0.2.29 → dulus-0.2.30}/docs/poetry-banner.png +0 -0
  59. {dulus-0.2.29 → dulus-0.2.30}/docs/preview.html +0 -0
  60. {dulus-0.2.29 → dulus-0.2.30}/docs/sec-agents.svg +0 -0
  61. {dulus-0.2.29 → dulus-0.2.30}/docs/sec-brainstorm.svg +0 -0
  62. {dulus-0.2.29 → dulus-0.2.30}/docs/sec-bridges.svg +0 -0
  63. {dulus-0.2.29 → dulus-0.2.30}/docs/sec-features.svg +0 -0
  64. {dulus-0.2.29 → dulus-0.2.30}/docs/sec-freetier.svg +0 -0
  65. {dulus-0.2.29 → dulus-0.2.30}/docs/sec-memory.svg +0 -0
  66. {dulus-0.2.29 → dulus-0.2.30}/docs/sec-models.svg +0 -0
  67. {dulus-0.2.29 → dulus-0.2.30}/docs/sec-perms.svg +0 -0
  68. {dulus-0.2.29 → dulus-0.2.30}/docs/sec-plugins.svg +0 -0
  69. {dulus-0.2.29 → dulus-0.2.30}/docs/sec-quickstart.svg +0 -0
  70. {dulus-0.2.29 → dulus-0.2.30}/docs/sec-ssj.svg +0 -0
  71. {dulus-0.2.29 → dulus-0.2.30}/docs/spinners.svg +0 -0
  72. {dulus-0.2.29 → dulus-0.2.30}/docs/split-pane.svg +0 -0
  73. {dulus-0.2.29 → dulus-0.2.30}/docs/terminal-boot.svg +0 -0
  74. {dulus-0.2.29 → dulus-0.2.30}/docs/uploads/particle-playground.html +0 -0
  75. {dulus-0.2.29 → dulus-0.2.30}/dulus.egg-info/SOURCES.txt +0 -0
  76. {dulus-0.2.29 → dulus-0.2.30}/dulus.egg-info/dependency_links.txt +0 -0
  77. {dulus-0.2.29 → dulus-0.2.30}/dulus.egg-info/entry_points.txt +0 -0
  78. {dulus-0.2.29 → dulus-0.2.30}/dulus.egg-info/requires.txt +0 -0
  79. {dulus-0.2.29 → dulus-0.2.30}/dulus.egg-info/top_level.txt +0 -0
  80. {dulus-0.2.29 → dulus-0.2.30}/dulus_gui.py +0 -0
  81. {dulus-0.2.29 → dulus-0.2.30}/dulus_mcp/__init__.py +0 -0
  82. {dulus-0.2.29 → dulus-0.2.30}/dulus_mcp/client.py +0 -0
  83. {dulus-0.2.29 → dulus-0.2.30}/dulus_mcp/config.py +0 -0
  84. {dulus-0.2.29 → dulus-0.2.30}/dulus_mcp/tools.py +0 -0
  85. {dulus-0.2.29 → dulus-0.2.30}/dulus_mcp/types.py +0 -0
  86. {dulus-0.2.29 → dulus-0.2.30}/gui/__init__.py +0 -0
  87. {dulus-0.2.29 → dulus-0.2.30}/gui/agent_bridge.py +0 -0
  88. {dulus-0.2.29 → dulus-0.2.30}/gui/chat_widget.py +0 -0
  89. {dulus-0.2.29 → dulus-0.2.30}/gui/main_window.py +0 -0
  90. {dulus-0.2.29 → dulus-0.2.30}/gui/personas.py +0 -0
  91. {dulus-0.2.29 → dulus-0.2.30}/gui/session_utils.py +0 -0
  92. {dulus-0.2.29 → dulus-0.2.30}/gui/settings_dialog.py +0 -0
  93. {dulus-0.2.29 → dulus-0.2.30}/gui/sidebar.py +0 -0
  94. {dulus-0.2.29 → dulus-0.2.30}/gui/tasks_view.py +0 -0
  95. {dulus-0.2.29 → dulus-0.2.30}/gui/themes.py +0 -0
  96. {dulus-0.2.29 → dulus-0.2.30}/gui/tool_panel.py +0 -0
  97. {dulus-0.2.29 → dulus-0.2.30}/input.py +0 -0
  98. {dulus-0.2.29 → dulus-0.2.30}/license_manager.py +0 -0
  99. {dulus-0.2.29 → dulus-0.2.30}/memory/__init__.py +0 -0
  100. {dulus-0.2.29 → dulus-0.2.30}/memory/audit.py +0 -0
  101. {dulus-0.2.29 → dulus-0.2.30}/memory/consolidator.py +0 -0
  102. {dulus-0.2.29 → dulus-0.2.30}/memory/context.py +0 -0
  103. {dulus-0.2.29 → dulus-0.2.30}/memory/offload.py +0 -0
  104. {dulus-0.2.29 → dulus-0.2.30}/memory/palace.py +0 -0
  105. {dulus-0.2.29 → dulus-0.2.30}/memory/scan.py +0 -0
  106. {dulus-0.2.29 → dulus-0.2.30}/memory/sessions.py +0 -0
  107. {dulus-0.2.29 → dulus-0.2.30}/memory/store.py +0 -0
  108. {dulus-0.2.29 → dulus-0.2.30}/memory/tools.py +0 -0
  109. {dulus-0.2.29 → dulus-0.2.30}/memory/types.py +0 -0
  110. {dulus-0.2.29 → dulus-0.2.30}/memory/vector_search.py +0 -0
  111. {dulus-0.2.29 → dulus-0.2.30}/multi_agent/__init__.py +0 -0
  112. {dulus-0.2.29 → dulus-0.2.30}/multi_agent/subagent.py +0 -0
  113. {dulus-0.2.29 → dulus-0.2.30}/multi_agent/tools.py +0 -0
  114. {dulus-0.2.29 → dulus-0.2.30}/offload_helper.py +0 -0
  115. {dulus-0.2.29 → dulus-0.2.30}/plugin/__init__.py +0 -0
  116. {dulus-0.2.29 → dulus-0.2.30}/plugin/autoadapter.py +0 -0
  117. {dulus-0.2.29 → dulus-0.2.30}/plugin/loader.py +0 -0
  118. {dulus-0.2.29 → dulus-0.2.30}/plugin/recommend.py +0 -0
  119. {dulus-0.2.29 → dulus-0.2.30}/plugin/store.py +0 -0
  120. {dulus-0.2.29 → dulus-0.2.30}/plugin/types.py +0 -0
  121. {dulus-0.2.29 → dulus-0.2.30}/providers.py +0 -0
  122. {dulus-0.2.29 → dulus-0.2.30}/setup.cfg +0 -0
  123. {dulus-0.2.29 → dulus-0.2.30}/skill/__init__.py +0 -0
  124. {dulus-0.2.29 → dulus-0.2.30}/skill/builtin.py +0 -0
  125. {dulus-0.2.29 → dulus-0.2.30}/skill/clawhub.py +0 -0
  126. {dulus-0.2.29 → dulus-0.2.30}/skill/executor.py +0 -0
  127. {dulus-0.2.29 → dulus-0.2.30}/skill/loader.py +0 -0
  128. {dulus-0.2.29 → dulus-0.2.30}/skill/tools.py +0 -0
  129. {dulus-0.2.29 → dulus-0.2.30}/skills.py +0 -0
  130. {dulus-0.2.29 → dulus-0.2.30}/spinner.py +0 -0
  131. {dulus-0.2.29 → dulus-0.2.30}/string_utils.py +0 -0
  132. {dulus-0.2.29 → dulus-0.2.30}/subagent.py +0 -0
  133. {dulus-0.2.29 → dulus-0.2.30}/task/__init__.py +0 -0
  134. {dulus-0.2.29 → dulus-0.2.30}/task/store.py +0 -0
  135. {dulus-0.2.29 → dulus-0.2.30}/task/tools.py +0 -0
  136. {dulus-0.2.29 → dulus-0.2.30}/task/types.py +0 -0
  137. {dulus-0.2.29 → dulus-0.2.30}/tests/test_checkpoint.py +0 -0
  138. {dulus-0.2.29 → dulus-0.2.30}/tests/test_compaction.py +0 -0
  139. {dulus-0.2.29 → dulus-0.2.30}/tests/test_diff_view.py +0 -0
  140. {dulus-0.2.29 → dulus-0.2.30}/tests/test_injection_fix.py +0 -0
  141. {dulus-0.2.29 → dulus-0.2.30}/tests/test_license.py +0 -0
  142. {dulus-0.2.29 → dulus-0.2.30}/tests/test_mcp.py +0 -0
  143. {dulus-0.2.29 → dulus-0.2.30}/tests/test_memory.py +0 -0
  144. {dulus-0.2.29 → dulus-0.2.30}/tests/test_plugin.py +0 -0
  145. {dulus-0.2.29 → dulus-0.2.30}/tests/test_skills.py +0 -0
  146. {dulus-0.2.29 → dulus-0.2.30}/tests/test_subagent.py +0 -0
  147. {dulus-0.2.29 → dulus-0.2.30}/tests/test_task.py +0 -0
  148. {dulus-0.2.29 → dulus-0.2.30}/tests/test_telegram_buffer.py +0 -0
  149. {dulus-0.2.29 → dulus-0.2.30}/tests/test_tool_registry.py +0 -0
  150. {dulus-0.2.29 → dulus-0.2.30}/tests/test_voice.py +0 -0
  151. {dulus-0.2.29 → dulus-0.2.30}/tmux_offloader.py +0 -0
  152. {dulus-0.2.29 → dulus-0.2.30}/tmux_tools.py +0 -0
  153. {dulus-0.2.29 → dulus-0.2.30}/tool_registry.py +0 -0
  154. {dulus-0.2.29 → dulus-0.2.30}/tools.py +0 -0
  155. {dulus-0.2.29 → dulus-0.2.30}/ui/__init__.py +0 -0
  156. {dulus-0.2.29 → dulus-0.2.30}/ui/input.py +0 -0
  157. {dulus-0.2.29 → dulus-0.2.30}/ui/render.py +0 -0
  158. {dulus-0.2.29 → dulus-0.2.30}/voice/__init__.py +0 -0
  159. {dulus-0.2.29 → dulus-0.2.30}/voice/keyterms.py +0 -0
  160. {dulus-0.2.29 → dulus-0.2.30}/voice/recorder.py +0 -0
  161. {dulus-0.2.29 → dulus-0.2.30}/voice/stt.py +0 -0
  162. {dulus-0.2.29 → dulus-0.2.30}/voice/tts.py +0 -0
  163. {dulus-0.2.29 → dulus-0.2.30}/webchat.py +0 -0
  164. {dulus-0.2.29 → dulus-0.2.30}/webchat_server.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dulus
3
- Version: 0.2.29
3
+ Version: 0.2.30
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
@@ -69,7 +69,7 @@ SET /sticky_input ON since the first run for the best experience!
69
69
  <a href="https://pypi.org/project/dulus/"><img src="https://static.pepy.tech/badge/dulus?style=flat-square" alt="downloads"/></a>
70
70
  <img src="https://img.shields.io/badge/python-3.11+-ff6b1f?style=flat-square&labelColor=07070a" alt="python"/>
71
71
  <img src="https://img.shields.io/badge/license-GPLv3-ff6b1f?style=flat-square&labelColor=07070a" alt="license"/>
72
- <img src="https://img.shields.io/badge/version-v0.2.29-ff6b1f?style=flat-square&labelColor=07070a" alt="version"/>
72
+ <img src="https://img.shields.io/badge/version-v0.2.30-ff6b1f?style=flat-square&labelColor=07070a" alt="version"/>
73
73
  <img src="https://img.shields.io/badge/providers-11-ff6b1f?style=flat-square&labelColor=07070a" alt="providers"/>
74
74
  <img src="https://img.shields.io/badge/tools-27-ff6b1f?style=flat-square&labelColor=07070a" alt="tools"/>
75
75
  <img src="https://img.shields.io/badge/tests-263+-ff6b1f?style=flat-square&labelColor=07070a" alt="tests"/>
@@ -22,7 +22,7 @@ SET /sticky_input ON since the first run for the best experience!
22
22
  <a href="https://pypi.org/project/dulus/"><img src="https://static.pepy.tech/badge/dulus?style=flat-square" alt="downloads"/></a>
23
23
  <img src="https://img.shields.io/badge/python-3.11+-ff6b1f?style=flat-square&labelColor=07070a" alt="python"/>
24
24
  <img src="https://img.shields.io/badge/license-GPLv3-ff6b1f?style=flat-square&labelColor=07070a" alt="license"/>
25
- <img src="https://img.shields.io/badge/version-v0.2.29-ff6b1f?style=flat-square&labelColor=07070a" alt="version"/>
25
+ <img src="https://img.shields.io/badge/version-v0.2.30-ff6b1f?style=flat-square&labelColor=07070a" alt="version"/>
26
26
  <img src="https://img.shields.io/badge/providers-11-ff6b1f?style=flat-square&labelColor=07070a" alt="providers"/>
27
27
  <img src="https://img.shields.io/badge/tools-27-ff6b1f?style=flat-square&labelColor=07070a" alt="tools"/>
28
28
  <img src="https://img.shields.io/badge/tests-263+-ff6b1f?style=flat-square&labelColor=07070a" alt="tests"/>
@@ -3,6 +3,8 @@
3
3
  ## 🔥🔥🔥 News (Pacific Time)
4
4
 
5
5
 
6
+ - May 09, 2026 (**v0.2.30**): **`/bg start` daemon is now truly windowless on Windows** — `python.exe` is a console-subsystem binary, so even with `DETACHED_PROCESS` Windows still spun up a visible console window for the daemon. Closing that window killed the daemon. Switched to `pythonw.exe` (the GUI-subsystem variant) + `CREATE_NO_WINDOW` so the daemon spawns with NO console window at all. Verified: `Get-Process` reports `MainWindowHandle = 0` after spawn — there's literally nothing to close. Telegram + WebChat + IPC keep running in background until `/bg stop` or `/bg kill`.
7
+
6
8
  - May 09, 2026 (**v0.2.29**): **`/bg start` actually works from inside a REPL + daemon-mode webchat default-on + tested end-to-end this time**
7
9
  - **The whole point of `/bg start` was broken from day one.** A REPL itself binds `127.0.0.1:5151` to serve `dulus "..."` shell calls, so the moment you typed `/bg start` from inside that REPL, the duplicate-detection check saw "port in use" and refused — by the very REPL invoking the command. `/bg kill` then killed the only thing on the port: your own REPL. Pure logic flaw on me.
8
10
  - **Now `/bg start` releases the REPL's own IPC first.** When invoked from inside a REPL, the command stops the REPL's IPC thread, force-closes the socket with `SO_LINGER {1, 0}` (skips TIME_WAIT), waits ~600ms for the OS to free the port, and only then spawns the daemon. The REPL keeps running — it just becomes a normal client whose `dulus "..."` dispatches now go to the daemon.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dulus
3
- Version: 0.2.29
3
+ Version: 0.2.30
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
@@ -69,7 +69,7 @@ SET /sticky_input ON since the first run for the best experience!
69
69
  <a href="https://pypi.org/project/dulus/"><img src="https://static.pepy.tech/badge/dulus?style=flat-square" alt="downloads"/></a>
70
70
  <img src="https://img.shields.io/badge/python-3.11+-ff6b1f?style=flat-square&labelColor=07070a" alt="python"/>
71
71
  <img src="https://img.shields.io/badge/license-GPLv3-ff6b1f?style=flat-square&labelColor=07070a" alt="license"/>
72
- <img src="https://img.shields.io/badge/version-v0.2.29-ff6b1f?style=flat-square&labelColor=07070a" alt="version"/>
72
+ <img src="https://img.shields.io/badge/version-v0.2.30-ff6b1f?style=flat-square&labelColor=07070a" alt="version"/>
73
73
  <img src="https://img.shields.io/badge/providers-11-ff6b1f?style=flat-square&labelColor=07070a" alt="providers"/>
74
74
  <img src="https://img.shields.io/badge/tools-27-ff6b1f?style=flat-square&labelColor=07070a" alt="tools"/>
75
75
  <img src="https://img.shields.io/badge/tests-263+-ff6b1f?style=flat-square&labelColor=07070a" alt="tests"/>
@@ -122,6 +122,26 @@ if sys.platform == "win32":
122
122
  sys.stdout.reconfigure(encoding="utf-8", errors="replace")
123
123
  if hasattr(sys.stderr, "reconfigure"):
124
124
  sys.stderr.reconfigure(encoding="utf-8", errors="replace")
125
+
126
+ # ── Suppress noisy third-party startup warnings ──────────────────────────
127
+ # These don't affect functionality but pollute every Dulus boot (REPL,
128
+ # daemon, --print, every shell call). Filtered globally so logs stay clean.
129
+ import warnings as _warnings
130
+ # requests >= 2.32 nags about urllib3/chardet version pins on Python 3.13+.
131
+ _warnings.filterwarnings("ignore", message=r".*urllib3.*")
132
+ _warnings.filterwarnings("ignore", message=r".*chardet.*charset_normalizer.*")
133
+ _warnings.filterwarnings("ignore", message=r".*RequestsDependencyWarning.*")
134
+ # Dulus's own dev-license warning — only relevant if you're building
135
+ # license keys for production, not noise we want on every boot.
136
+ _warnings.filterwarnings("ignore", message=r".*DULUS_LICENSE_SECRET.*")
137
+ # Catch-all: any RequestsDependencyWarning by category, regardless of msg.
138
+ try:
139
+ from requests.exceptions import RequestsDependencyWarning as _RDW # type: ignore
140
+ _warnings.filterwarnings("ignore", category=_RDW)
141
+ except Exception:
142
+ pass
143
+ # pkg_resources / setuptools-based deprecations from optional plugins.
144
+ _warnings.filterwarnings("ignore", category=DeprecationWarning, module=r"pkg_resources.*")
125
145
  from pathlib import Path
126
146
 
127
147
  # ── Global Import Hook ───────────────────────────────────────────────────────
@@ -218,7 +238,7 @@ try:
218
238
  from importlib.metadata import version as _pkg_version
219
239
  VERSION = _pkg_version("dulus")
220
240
  except Exception:
221
- VERSION = "0.2.29" # dev fallback — keep in sync with pyproject.toml
241
+ VERSION = "0.2.30" # dev fallback — keep in sync with pyproject.toml
222
242
 
223
243
  # ── ANSI helpers (used even with rich for non-markdown output) ─────────────
224
244
  from common import C, clr, info, ok, warn, err, stream_thinking, print_tool_start, print_tool_end, sanitize_text
@@ -1766,20 +1786,32 @@ def cmd_bg(args: str, _state, config) -> bool:
1766
1786
  from config import save_config
1767
1787
  save_config(config)
1768
1788
 
1769
- # Build the spawn command. Prefer the installed `dulus` shim; fall
1770
- # back to `python dulus.py` when running from source.
1771
- dulus_bin = None
1772
- for cand in ["dulus", "dulus.exe"]:
1773
- from shutil import which
1774
- p = which(cand)
1775
- if p:
1776
- dulus_bin = p
1777
- break
1778
- if dulus_bin:
1779
- cmd = [dulus_bin, "--daemon"]
1780
- else:
1789
+ # Build the spawn command. On Windows we MUST use pythonw.exe (windowless
1790
+ # variant) instead of the console-subsystem python.exe / dulus shim,
1791
+ # otherwise Windows creates a visible console window for the daemon
1792
+ # and closing it kills the process. The shim itself runs python.exe,
1793
+ # so we go around it by invoking pythonw -m dulus directly.
1794
+ if _sys.platform == "win32":
1795
+ pythonw = _sys.executable.replace("python.exe", "pythonw.exe")
1796
+ if not _os.path.exists(pythonw):
1797
+ # Fall back to python.exe if pythonw isn't shipped (rare;
1798
+ # mostly happens on stripped embeddable distributions).
1799
+ pythonw = _sys.executable
1781
1800
  dulus_script = _os.path.abspath(__file__)
1782
- cmd = [_sys.executable, dulus_script, "--daemon"]
1801
+ cmd = [pythonw, dulus_script, "--daemon"]
1802
+ else:
1803
+ from shutil import which
1804
+ dulus_bin = None
1805
+ for cand in ["dulus", "dulus.exe"]:
1806
+ p = which(cand)
1807
+ if p:
1808
+ dulus_bin = p
1809
+ break
1810
+ if dulus_bin:
1811
+ cmd = [dulus_bin, "--daemon"]
1812
+ else:
1813
+ dulus_script = _os.path.abspath(__file__)
1814
+ cmd = [_sys.executable, dulus_script, "--daemon"]
1783
1815
 
1784
1816
  # Pass the auto-webchat hint via env so the daemon picks it up.
1785
1817
  env = _os.environ.copy()
@@ -1790,13 +1822,18 @@ def cmd_bg(args: str, _state, config) -> bool:
1790
1822
  log_fp = open(BG_LOG, "ab")
1791
1823
  try:
1792
1824
  if _sys.platform == "win32":
1793
- # CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS
1794
- DETACHED = 0x00000008
1825
+ # CREATE_NO_WINDOW (0x08000000) suppresses the console window
1826
+ # entirely — cannot be combined with DETACHED_PROCESS, but
1827
+ # because we're invoking pythonw.exe (a GUI-subsystem binary)
1828
+ # there is no console to inherit from in the first place.
1829
+ # CREATE_NEW_PROCESS_GROUP keeps Ctrl+C in the parent shell
1830
+ # from killing the daemon when the parent later exits.
1831
+ CREATE_NO_WINDOW = 0x08000000
1795
1832
  NEW_GROUP = 0x00000200
1796
1833
  proc = _sp.Popen(
1797
1834
  cmd,
1798
1835
  stdout=log_fp, stderr=log_fp, stdin=_sp.DEVNULL,
1799
- creationflags=DETACHED | NEW_GROUP,
1836
+ creationflags=CREATE_NO_WINDOW | NEW_GROUP,
1800
1837
  close_fds=True,
1801
1838
  env=env,
1802
1839
  )
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "dulus"
7
- version = "0.2.29"
7
+ version = "0.2.30"
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