dulus 0.2.56__tar.gz → 0.2.60__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 (367) hide show
  1. {dulus-0.2.56 → dulus-0.2.60}/MANIFEST.in +1 -0
  2. {dulus-0.2.56/dulus.egg-info → dulus-0.2.60}/PKG-INFO +4 -1
  3. {dulus-0.2.56 → dulus-0.2.60}/data/context.json +19 -23
  4. {dulus-0.2.56 → dulus-0.2.60/dulus.egg-info}/PKG-INFO +4 -1
  5. {dulus-0.2.56 → dulus-0.2.60}/dulus.egg-info/SOURCES.txt +7 -1
  6. {dulus-0.2.56 → dulus-0.2.60}/dulus.egg-info/requires.txt +4 -0
  7. {dulus-0.2.56 → dulus-0.2.60}/dulus.egg-info/top_level.txt +1 -0
  8. {dulus-0.2.56 → dulus-0.2.60}/dulus.py +136 -0
  9. {dulus-0.2.56 → dulus-0.2.60}/pyproject.toml +4 -2
  10. {dulus-0.2.56 → dulus-0.2.60}/tools.py +14 -0
  11. {dulus-0.2.56 → dulus-0.2.60}/voice/stt.py +18 -1
  12. {dulus-0.2.56 → dulus-0.2.60}/voice/wake_word.py +20 -0
  13. dulus-0.2.60/webbridge/__init__.py +5 -0
  14. dulus-0.2.60/webbridge/__pycache__/__init__.cpython-314.pyc +0 -0
  15. dulus-0.2.60/webbridge/__pycache__/core.cpython-314.pyc +0 -0
  16. dulus-0.2.60/webbridge/__pycache__/tools.cpython-314.pyc +0 -0
  17. dulus-0.2.60/webbridge/core.py +538 -0
  18. dulus-0.2.60/webbridge/tools.py +261 -0
  19. {dulus-0.2.56 → dulus-0.2.60}/LICENSE +0 -0
  20. {dulus-0.2.56 → dulus-0.2.60}/README.md +0 -0
  21. {dulus-0.2.56 → dulus-0.2.60}/agent.py +0 -0
  22. {dulus-0.2.56 → dulus-0.2.60}/backend/__init__.py +0 -0
  23. {dulus-0.2.56 → dulus-0.2.60}/backend/agents_bridge.py +0 -0
  24. {dulus-0.2.56 → dulus-0.2.60}/backend/compressor.py +0 -0
  25. {dulus-0.2.56 → dulus-0.2.60}/backend/context.py +0 -0
  26. {dulus-0.2.56 → dulus-0.2.60}/backend/githook.py +0 -0
  27. {dulus-0.2.56 → dulus-0.2.60}/backend/marketplace.py +0 -0
  28. {dulus-0.2.56 → dulus-0.2.60}/backend/mempalace_bridge.py +0 -0
  29. {dulus-0.2.56 → dulus-0.2.60}/backend/personas.py +0 -0
  30. {dulus-0.2.56 → dulus-0.2.60}/backend/plugins.py +0 -0
  31. {dulus-0.2.56 → dulus-0.2.60}/backend/server.py +0 -0
  32. {dulus-0.2.56 → dulus-0.2.60}/backend/tasks.py +0 -0
  33. {dulus-0.2.56 → dulus-0.2.60}/batch_api.py +0 -0
  34. {dulus-0.2.56 → dulus-0.2.60}/checkpoint/__init__.py +0 -0
  35. {dulus-0.2.56 → dulus-0.2.60}/checkpoint/hooks.py +0 -0
  36. {dulus-0.2.56 → dulus-0.2.60}/checkpoint/store.py +0 -0
  37. {dulus-0.2.56 → dulus-0.2.60}/checkpoint/types.py +0 -0
  38. {dulus-0.2.56 → dulus-0.2.60}/claude_code_watcher.py +0 -0
  39. {dulus-0.2.56 → dulus-0.2.60}/clipboard_utils.py +0 -0
  40. {dulus-0.2.56 → dulus-0.2.60}/cloudsave.py +0 -0
  41. {dulus-0.2.56 → dulus-0.2.60}/common.py +0 -0
  42. {dulus-0.2.56 → dulus-0.2.60}/compaction.py +0 -0
  43. {dulus-0.2.56 → dulus-0.2.60}/config.py +0 -0
  44. {dulus-0.2.56 → dulus-0.2.60}/context.py +0 -0
  45. {dulus-0.2.56 → dulus-0.2.60}/data/__init__.py +0 -0
  46. {dulus-0.2.56 → dulus-0.2.60}/data/active_persona.json +0 -0
  47. {dulus-0.2.56 → dulus-0.2.60}/data/marketplace.json +0 -0
  48. {dulus-0.2.56 → dulus-0.2.60}/data/personas.json +0 -0
  49. {dulus-0.2.56 → dulus-0.2.60}/data/plugins/__init__.py +0 -0
  50. {dulus-0.2.56 → dulus-0.2.60}/data/plugins/composio/__init__.py +0 -0
  51. {dulus-0.2.56 → dulus-0.2.60}/data/plugins/composio/composio_plugin/__init__.py +0 -0
  52. {dulus-0.2.56 → dulus-0.2.60}/data/plugins/composio/composio_plugin/session_manager.py +0 -0
  53. {dulus-0.2.56 → dulus-0.2.60}/data/plugins/composio/composio_plugin/tool_generator.py +0 -0
  54. {dulus-0.2.56 → dulus-0.2.60}/data/plugins/composio/plugin.json +0 -0
  55. {dulus-0.2.56 → dulus-0.2.60}/data/plugins/composio/plugin_tool.py +0 -0
  56. {dulus-0.2.56 → dulus-0.2.60}/data/tasks.json +0 -0
  57. {dulus-0.2.56 → dulus-0.2.60}/docs/README.md +0 -0
  58. {dulus-0.2.56 → dulus-0.2.60}/docs/__init__.py +0 -0
  59. {dulus-0.2.56 → dulus-0.2.60}/docs/api.html +0 -0
  60. {dulus-0.2.56 → dulus-0.2.60}/docs/architecture.md +0 -0
  61. {dulus-0.2.56 → dulus-0.2.60}/docs/azure-speech-template.json +0 -0
  62. {dulus-0.2.56 → dulus-0.2.60}/docs/dashboard/index.html +0 -0
  63. {dulus-0.2.56 → dulus-0.2.60}/docs/divider.svg +0 -0
  64. {dulus-0.2.56 → dulus-0.2.60}/docs/generate.py +0 -0
  65. {dulus-0.2.56 → dulus-0.2.60}/docs/hero.svg +0 -0
  66. {dulus-0.2.56 → dulus-0.2.60}/docs/index.html +0 -0
  67. {dulus-0.2.56 → dulus-0.2.60}/docs/news.md +0 -0
  68. {dulus-0.2.56 → dulus-0.2.60}/docs/nvidia-models.svg +0 -0
  69. {dulus-0.2.56 → dulus-0.2.60}/docs/particle-playground.html +0 -0
  70. {dulus-0.2.56 → dulus-0.2.60}/docs/personas/index.html +0 -0
  71. {dulus-0.2.56 → dulus-0.2.60}/docs/poetry-banner.png +0 -0
  72. {dulus-0.2.56 → dulus-0.2.60}/docs/preview.html +0 -0
  73. {dulus-0.2.56 → dulus-0.2.60}/docs/sec-agents.svg +0 -0
  74. {dulus-0.2.56 → dulus-0.2.60}/docs/sec-brainstorm.svg +0 -0
  75. {dulus-0.2.56 → dulus-0.2.60}/docs/sec-bridges.svg +0 -0
  76. {dulus-0.2.56 → dulus-0.2.60}/docs/sec-features.svg +0 -0
  77. {dulus-0.2.56 → dulus-0.2.60}/docs/sec-freetier.svg +0 -0
  78. {dulus-0.2.56 → dulus-0.2.60}/docs/sec-memory.svg +0 -0
  79. {dulus-0.2.56 → dulus-0.2.60}/docs/sec-models.svg +0 -0
  80. {dulus-0.2.56 → dulus-0.2.60}/docs/sec-perms.svg +0 -0
  81. {dulus-0.2.56 → dulus-0.2.60}/docs/sec-plugins.svg +0 -0
  82. {dulus-0.2.56 → dulus-0.2.60}/docs/sec-quickstart.svg +0 -0
  83. {dulus-0.2.56 → dulus-0.2.60}/docs/sec-ssj.svg +0 -0
  84. {dulus-0.2.56 → dulus-0.2.60}/docs/spinners.svg +0 -0
  85. {dulus-0.2.56 → dulus-0.2.60}/docs/split-pane.svg +0 -0
  86. {dulus-0.2.56 → dulus-0.2.60}/docs/terminal-boot.svg +0 -0
  87. {dulus-0.2.56 → dulus-0.2.60}/docs/uploads/particle-playground.html +0 -0
  88. {dulus-0.2.56 → dulus-0.2.60}/dulus.egg-info/dependency_links.txt +0 -0
  89. {dulus-0.2.56 → dulus-0.2.60}/dulus.egg-info/entry_points.txt +0 -0
  90. {dulus-0.2.56 → dulus-0.2.60}/dulus_gui.py +0 -0
  91. {dulus-0.2.56 → dulus-0.2.60}/dulus_mcp/__init__.py +0 -0
  92. {dulus-0.2.56 → dulus-0.2.60}/dulus_mcp/client.py +0 -0
  93. {dulus-0.2.56 → dulus-0.2.60}/dulus_mcp/config.py +0 -0
  94. {dulus-0.2.56 → dulus-0.2.60}/dulus_mcp/tools.py +0 -0
  95. {dulus-0.2.56 → dulus-0.2.60}/dulus_mcp/types.py +0 -0
  96. {dulus-0.2.56 → dulus-0.2.60}/gui/__init__.py +0 -0
  97. {dulus-0.2.56 → dulus-0.2.60}/gui/agent_bridge.py +0 -0
  98. {dulus-0.2.56 → dulus-0.2.60}/gui/chat_widget.py +0 -0
  99. {dulus-0.2.56 → dulus-0.2.60}/gui/main_window.py +0 -0
  100. {dulus-0.2.56 → dulus-0.2.60}/gui/personas.py +0 -0
  101. {dulus-0.2.56 → dulus-0.2.60}/gui/session_utils.py +0 -0
  102. {dulus-0.2.56 → dulus-0.2.60}/gui/settings_dialog.py +0 -0
  103. {dulus-0.2.56 → dulus-0.2.60}/gui/sidebar.py +0 -0
  104. {dulus-0.2.56 → dulus-0.2.60}/gui/tasks_view.py +0 -0
  105. {dulus-0.2.56 → dulus-0.2.60}/gui/themes.py +0 -0
  106. {dulus-0.2.56 → dulus-0.2.60}/gui/tool_panel.py +0 -0
  107. {dulus-0.2.56 → dulus-0.2.60}/input.py +0 -0
  108. {dulus-0.2.56 → dulus-0.2.60}/license_manager.py +0 -0
  109. {dulus-0.2.56 → dulus-0.2.60}/memory/__init__.py +0 -0
  110. {dulus-0.2.56 → dulus-0.2.60}/memory/audit.py +0 -0
  111. {dulus-0.2.56 → dulus-0.2.60}/memory/consolidator.py +0 -0
  112. {dulus-0.2.56 → dulus-0.2.60}/memory/context.py +0 -0
  113. {dulus-0.2.56 → dulus-0.2.60}/memory/offload.py +0 -0
  114. {dulus-0.2.56 → dulus-0.2.60}/memory/palace.py +0 -0
  115. {dulus-0.2.56 → dulus-0.2.60}/memory/scan.py +0 -0
  116. {dulus-0.2.56 → dulus-0.2.60}/memory/sessions.py +0 -0
  117. {dulus-0.2.56 → dulus-0.2.60}/memory/store.py +0 -0
  118. {dulus-0.2.56 → dulus-0.2.60}/memory/tools.py +0 -0
  119. {dulus-0.2.56 → dulus-0.2.60}/memory/types.py +0 -0
  120. {dulus-0.2.56 → dulus-0.2.60}/memory/vector_search.py +0 -0
  121. {dulus-0.2.56 → dulus-0.2.60}/multi_agent/__init__.py +0 -0
  122. {dulus-0.2.56 → dulus-0.2.60}/multi_agent/subagent.py +0 -0
  123. {dulus-0.2.56 → dulus-0.2.60}/multi_agent/tools.py +0 -0
  124. {dulus-0.2.56 → dulus-0.2.60}/offload_helper.py +0 -0
  125. {dulus-0.2.56 → dulus-0.2.60}/plugin/__init__.py +0 -0
  126. {dulus-0.2.56 → dulus-0.2.60}/plugin/autoadapter.py +0 -0
  127. {dulus-0.2.56 → dulus-0.2.60}/plugin/loader.py +0 -0
  128. {dulus-0.2.56 → dulus-0.2.60}/plugin/recommend.py +0 -0
  129. {dulus-0.2.56 → dulus-0.2.60}/plugin/store.py +0 -0
  130. {dulus-0.2.56 → dulus-0.2.60}/plugin/types.py +0 -0
  131. {dulus-0.2.56 → dulus-0.2.60}/providers.py +0 -0
  132. {dulus-0.2.56 → dulus-0.2.60}/sandbox/README.md +0 -0
  133. {dulus-0.2.56 → dulus-0.2.60}/sandbox/__init__.py +0 -0
  134. {dulus-0.2.56 → dulus-0.2.60}/sandbox/components.json +0 -0
  135. {dulus-0.2.56 → dulus-0.2.60}/sandbox/dist/assets/index-DE51D6wI.css +0 -0
  136. {dulus-0.2.56 → dulus-0.2.60}/sandbox/dist/assets/index-DMCCNE9Y.js +0 -0
  137. {dulus-0.2.56 → dulus-0.2.60}/sandbox/dist/index.html +0 -0
  138. {dulus-0.2.56 → dulus-0.2.60}/sandbox/dist/wallpaper-default.jpg +0 -0
  139. {dulus-0.2.56 → dulus-0.2.60}/sandbox/dist/wallpapers/default.jpeg +0 -0
  140. {dulus-0.2.56 → dulus-0.2.60}/sandbox/dist/wallpapers/light.jpeg +0 -0
  141. {dulus-0.2.56 → dulus-0.2.60}/sandbox/dist/wallpapers/nature.jpeg +0 -0
  142. {dulus-0.2.56 → dulus-0.2.60}/sandbox/dist/wallpapers/tech.jpeg +0 -0
  143. {dulus-0.2.56 → dulus-0.2.60}/sandbox/eslint.config.js +0 -0
  144. {dulus-0.2.56 → dulus-0.2.60}/sandbox/index.html +0 -0
  145. {dulus-0.2.56 → dulus-0.2.60}/sandbox/info.md +0 -0
  146. {dulus-0.2.56 → dulus-0.2.60}/sandbox/package-lock.json +0 -0
  147. {dulus-0.2.56 → dulus-0.2.60}/sandbox/package.json +0 -0
  148. {dulus-0.2.56 → dulus-0.2.60}/sandbox/postcss.config.js +0 -0
  149. {dulus-0.2.56 → dulus-0.2.60}/sandbox/public/wallpaper-default.jpg +0 -0
  150. {dulus-0.2.56 → dulus-0.2.60}/sandbox/public/wallpapers/default.jpeg +0 -0
  151. {dulus-0.2.56 → dulus-0.2.60}/sandbox/public/wallpapers/light.jpeg +0 -0
  152. {dulus-0.2.56 → dulus-0.2.60}/sandbox/public/wallpapers/nature.jpeg +0 -0
  153. {dulus-0.2.56 → dulus-0.2.60}/sandbox/public/wallpapers/tech.jpeg +0 -0
  154. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/App.tsx +0 -0
  155. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/AgentMonitor.tsx +0 -0
  156. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/ApiTester.tsx +0 -0
  157. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/AppRouter.tsx +0 -0
  158. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/ArchiveManager.tsx +0 -0
  159. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/AsciiArt.tsx +0 -0
  160. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/Base64Tool.tsx +0 -0
  161. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/Browser.tsx +0 -0
  162. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/Calculator.tsx +0 -0
  163. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/Calendar.tsx +0 -0
  164. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/Chat.tsx +0 -0
  165. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/Chess.tsx +0 -0
  166. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/Clock.tsx +0 -0
  167. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/CodeEditor.tsx +0 -0
  168. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/ColorPalette.tsx +0 -0
  169. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/ColorPicker.tsx +0 -0
  170. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/Contacts.tsx +0 -0
  171. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/DocumentViewer.tsx +0 -0
  172. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/Drawing.tsx +0 -0
  173. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/Email.tsx +0 -0
  174. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/FileManager.tsx +0 -0
  175. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/FlappyBird.tsx +0 -0
  176. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/FtpClient.tsx +0 -0
  177. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/Game2048.tsx +0 -0
  178. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/GitClient.tsx +0 -0
  179. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/ImageGallery.tsx +0 -0
  180. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/ImageViewer.tsx +0 -0
  181. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/JsonFormatter.tsx +0 -0
  182. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/MarkdownPreview.tsx +0 -0
  183. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/MatrixRain.tsx +0 -0
  184. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/MediaConverter.tsx +0 -0
  185. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/Memory.tsx +0 -0
  186. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/MemoryManager.tsx +0 -0
  187. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/Minesweeper.tsx +0 -0
  188. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/MusicPlayer.tsx +0 -0
  189. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/NetworkTools.tsx +0 -0
  190. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/Notes.tsx +0 -0
  191. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/PasswordManager.tsx +0 -0
  192. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/PhotoEditor.tsx +0 -0
  193. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/Pong.tsx +0 -0
  194. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/RegexTester.tsx +0 -0
  195. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/Reminders.tsx +0 -0
  196. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/RssReader.tsx +0 -0
  197. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/ScreenRecorder.tsx +0 -0
  198. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/Settings.tsx +0 -0
  199. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/SkillsLauncher.tsx +0 -0
  200. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/Snake.tsx +0 -0
  201. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/Solitaire.tsx +0 -0
  202. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/Spreadsheet.tsx +0 -0
  203. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/Sudoku.tsx +0 -0
  204. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/SystemMonitor.tsx +0 -0
  205. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/TaskManager.tsx +0 -0
  206. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/Terminal.tsx +0 -0
  207. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/Tetris.tsx +0 -0
  208. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/TextEditor.tsx +0 -0
  209. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/TicTacToe.tsx +0 -0
  210. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/Todo.tsx +0 -0
  211. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/VideoPlayer.tsx +0 -0
  212. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/VoiceRecorder.tsx +0 -0
  213. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/Weather.tsx +0 -0
  214. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/Whiteboard.tsx +0 -0
  215. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/apps/registry.ts +0 -0
  216. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/AppContainer.tsx +0 -0
  217. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/AppLauncher.tsx +0 -0
  218. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/BootSequence.tsx +0 -0
  219. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ContextMenu.tsx +0 -0
  220. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/Desktop.tsx +0 -0
  221. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/Dock.tsx +0 -0
  222. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/LoginScreen.tsx +0 -0
  223. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/NotImplemented.tsx +0 -0
  224. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/NotificationCenter.tsx +0 -0
  225. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/NotificationSystem.tsx +0 -0
  226. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/TopPanel.tsx +0 -0
  227. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/WindowFrame.tsx +0 -0
  228. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/WindowManager.tsx +0 -0
  229. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/accordion.tsx +0 -0
  230. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/alert-dialog.tsx +0 -0
  231. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/alert.tsx +0 -0
  232. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/aspect-ratio.tsx +0 -0
  233. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/avatar.tsx +0 -0
  234. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/badge.tsx +0 -0
  235. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/breadcrumb.tsx +0 -0
  236. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/button-group.tsx +0 -0
  237. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/button.tsx +0 -0
  238. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/calendar.tsx +0 -0
  239. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/card.tsx +0 -0
  240. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/carousel.tsx +0 -0
  241. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/chart.tsx +0 -0
  242. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/checkbox.tsx +0 -0
  243. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/collapsible.tsx +0 -0
  244. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/command.tsx +0 -0
  245. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/context-menu.tsx +0 -0
  246. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/dialog.tsx +0 -0
  247. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/drawer.tsx +0 -0
  248. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/dropdown-menu.tsx +0 -0
  249. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/empty.tsx +0 -0
  250. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/field.tsx +0 -0
  251. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/form.tsx +0 -0
  252. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/hover-card.tsx +0 -0
  253. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/input-group.tsx +0 -0
  254. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/input-otp.tsx +0 -0
  255. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/input.tsx +0 -0
  256. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/item.tsx +0 -0
  257. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/kbd.tsx +0 -0
  258. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/label.tsx +0 -0
  259. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/menubar.tsx +0 -0
  260. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/navigation-menu.tsx +0 -0
  261. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/pagination.tsx +0 -0
  262. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/popover.tsx +0 -0
  263. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/progress.tsx +0 -0
  264. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/radio-group.tsx +0 -0
  265. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/resizable.tsx +0 -0
  266. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/scroll-area.tsx +0 -0
  267. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/select.tsx +0 -0
  268. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/separator.tsx +0 -0
  269. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/sheet.tsx +0 -0
  270. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/sidebar.tsx +0 -0
  271. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/skeleton.tsx +0 -0
  272. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/slider.tsx +0 -0
  273. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/sonner.tsx +0 -0
  274. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/spinner.tsx +0 -0
  275. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/switch.tsx +0 -0
  276. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/table.tsx +0 -0
  277. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/tabs.tsx +0 -0
  278. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/textarea.tsx +0 -0
  279. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/toggle-group.tsx +0 -0
  280. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/toggle.tsx +0 -0
  281. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/components/ui/tooltip.tsx +0 -0
  282. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/hooks/index.ts +0 -0
  283. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/hooks/use-mobile.ts +0 -0
  284. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/hooks/useAutoOpenChat.ts +0 -0
  285. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/hooks/useDulusAgents.ts +0 -0
  286. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/hooks/useDulusChat.ts +0 -0
  287. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/hooks/useDulusEvents.ts +0 -0
  288. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/hooks/useDulusHealth.ts +0 -0
  289. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/hooks/useDulusMemory.ts +0 -0
  290. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/hooks/useDulusSkills.ts +0 -0
  291. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/hooks/useDulusTasks.ts +0 -0
  292. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/hooks/useFileSystem.ts +0 -0
  293. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/hooks/useOSStore.tsx +0 -0
  294. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/hooks/useSkillBridge.ts +0 -0
  295. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/hooks/useSystemBattery.ts +0 -0
  296. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/hooks/useSystemNetwork.ts +0 -0
  297. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/hooks/useSystemVolume.ts +0 -0
  298. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/index.css +0 -0
  299. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/lib/dulus-api.ts +0 -0
  300. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/lib/utils.ts +0 -0
  301. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/main.tsx +0 -0
  302. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/types/index.ts +0 -0
  303. {dulus-0.2.56 → dulus-0.2.60}/sandbox/src/utils/assets.ts +0 -0
  304. {dulus-0.2.56 → dulus-0.2.60}/sandbox/tailwind.config.js +0 -0
  305. {dulus-0.2.56 → dulus-0.2.60}/sandbox/tsconfig.app.json +0 -0
  306. {dulus-0.2.56 → dulus-0.2.60}/sandbox/tsconfig.json +0 -0
  307. {dulus-0.2.56 → dulus-0.2.60}/sandbox/tsconfig.node.json +0 -0
  308. {dulus-0.2.56 → dulus-0.2.60}/sandbox/vite.config.ts +0 -0
  309. {dulus-0.2.56 → dulus-0.2.60}/setup.cfg +0 -0
  310. {dulus-0.2.56 → dulus-0.2.60}/skill/__init__.py +0 -0
  311. {dulus-0.2.56 → dulus-0.2.60}/skill/builtin.py +0 -0
  312. {dulus-0.2.56 → dulus-0.2.60}/skill/clawhub.py +0 -0
  313. {dulus-0.2.56 → dulus-0.2.60}/skill/executor.py +0 -0
  314. {dulus-0.2.56 → dulus-0.2.60}/skill/loader.py +0 -0
  315. {dulus-0.2.56 → dulus-0.2.60}/skill/tools.py +0 -0
  316. {dulus-0.2.56 → dulus-0.2.60}/skills.py +0 -0
  317. {dulus-0.2.56 → dulus-0.2.60}/spinner.py +0 -0
  318. {dulus-0.2.56 → dulus-0.2.60}/string_utils.py +0 -0
  319. {dulus-0.2.56 → dulus-0.2.60}/subagent.py +0 -0
  320. {dulus-0.2.56 → dulus-0.2.60}/task/__init__.py +0 -0
  321. {dulus-0.2.56 → dulus-0.2.60}/task/store.py +0 -0
  322. {dulus-0.2.56 → dulus-0.2.60}/task/tools.py +0 -0
  323. {dulus-0.2.56 → dulus-0.2.60}/task/types.py +0 -0
  324. {dulus-0.2.56 → dulus-0.2.60}/tests/test_afk_yolo.py +0 -0
  325. {dulus-0.2.56 → dulus-0.2.60}/tests/test_approval_runtime.py +0 -0
  326. {dulus-0.2.56 → dulus-0.2.60}/tests/test_background_task_tools.py +0 -0
  327. {dulus-0.2.56 → dulus-0.2.60}/tests/test_background_tasks.py +0 -0
  328. {dulus-0.2.56 → dulus-0.2.60}/tests/test_checkpoint.py +0 -0
  329. {dulus-0.2.56 → dulus-0.2.60}/tests/test_clipboard_utils.py +0 -0
  330. {dulus-0.2.56 → dulus-0.2.60}/tests/test_compaction.py +0 -0
  331. {dulus-0.2.56 → dulus-0.2.60}/tests/test_diff_view.py +0 -0
  332. {dulus-0.2.56 → dulus-0.2.60}/tests/test_diff_visualization.py +0 -0
  333. {dulus-0.2.56 → dulus-0.2.60}/tests/test_display_blocks.py +0 -0
  334. {dulus-0.2.56 → dulus-0.2.60}/tests/test_export_import.py +0 -0
  335. {dulus-0.2.56 → dulus-0.2.60}/tests/test_hook_engine.py +0 -0
  336. {dulus-0.2.56 → dulus-0.2.60}/tests/test_injection_fix.py +0 -0
  337. {dulus-0.2.56 → dulus-0.2.60}/tests/test_license.py +0 -0
  338. {dulus-0.2.56 → dulus-0.2.60}/tests/test_mcp.py +0 -0
  339. {dulus-0.2.56 → dulus-0.2.60}/tests/test_memory.py +0 -0
  340. {dulus-0.2.56 → dulus-0.2.60}/tests/test_notification_manager.py +0 -0
  341. {dulus-0.2.56 → dulus-0.2.60}/tests/test_plugin.py +0 -0
  342. {dulus-0.2.56 → dulus-0.2.60}/tests/test_session_fork.py +0 -0
  343. {dulus-0.2.56 → dulus-0.2.60}/tests/test_shell_mode.py +0 -0
  344. {dulus-0.2.56 → dulus-0.2.60}/tests/test_skills.py +0 -0
  345. {dulus-0.2.56 → dulus-0.2.60}/tests/test_steer_input.py +0 -0
  346. {dulus-0.2.56 → dulus-0.2.60}/tests/test_subagent.py +0 -0
  347. {dulus-0.2.56 → dulus-0.2.60}/tests/test_task.py +0 -0
  348. {dulus-0.2.56 → dulus-0.2.60}/tests/test_telegram_buffer.py +0 -0
  349. {dulus-0.2.56 → dulus-0.2.60}/tests/test_think_tool.py +0 -0
  350. {dulus-0.2.56 → dulus-0.2.60}/tests/test_todo_tool.py +0 -0
  351. {dulus-0.2.56 → dulus-0.2.60}/tests/test_todo_visualization.py +0 -0
  352. {dulus-0.2.56 → dulus-0.2.60}/tests/test_tool_registry.py +0 -0
  353. {dulus-0.2.56 → dulus-0.2.60}/tests/test_voice.py +0 -0
  354. {dulus-0.2.56 → dulus-0.2.60}/tests/test_wire_events.py +0 -0
  355. {dulus-0.2.56 → dulus-0.2.60}/tmux_offloader.py +0 -0
  356. {dulus-0.2.56 → dulus-0.2.60}/tmux_tools.py +0 -0
  357. {dulus-0.2.56 → dulus-0.2.60}/tool_registry.py +0 -0
  358. {dulus-0.2.56 → dulus-0.2.60}/ui/__init__.py +0 -0
  359. {dulus-0.2.56 → dulus-0.2.60}/ui/input.py +0 -0
  360. {dulus-0.2.56 → dulus-0.2.60}/ui/render.py +0 -0
  361. {dulus-0.2.56 → dulus-0.2.60}/voice/__init__.py +0 -0
  362. {dulus-0.2.56 → dulus-0.2.60}/voice/audio_utils.py +0 -0
  363. {dulus-0.2.56 → dulus-0.2.60}/voice/keyterms.py +0 -0
  364. {dulus-0.2.56 → dulus-0.2.60}/voice/recorder.py +0 -0
  365. {dulus-0.2.56 → dulus-0.2.60}/voice/tts.py +0 -0
  366. {dulus-0.2.56 → dulus-0.2.60}/webchat.py +0 -0
  367. {dulus-0.2.56 → dulus-0.2.60}/webchat_server.py +0 -0
@@ -1,3 +1,4 @@
1
1
  recursive-include docs *
2
2
  recursive-include data *
3
3
  recursive-include sandbox/dist *
4
+ recursive-include webbridge *
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dulus
3
- Version: 0.2.56
3
+ Version: 0.2.60
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
@@ -40,9 +40,12 @@ Provides-Extra: memory
40
40
  Requires-Dist: mempalace>=3.3.4; extra == "memory"
41
41
  Provides-Extra: voice
42
42
  Requires-Dist: sounddevice; extra == "voice"
43
+ Provides-Extra: webbridge
44
+ Requires-Dist: playwright>=1.40.0; extra == "webbridge"
43
45
  Provides-Extra: full
44
46
  Requires-Dist: mempalace>=3.3.4; extra == "full"
45
47
  Requires-Dist: sounddevice; extra == "full"
48
+ Requires-Dist: playwright>=1.40.0; extra == "full"
46
49
  Dynamic: license-file
47
50
 
48
51
  # ▲ DULUS
@@ -10,21 +10,21 @@
10
10
  "name": "Dulus Command Center",
11
11
  "repo_stats": {
12
12
  "files": 439,
13
- "lines": 206577,
13
+ "lines": 206190,
14
14
  "languages": {
15
15
  ".example": 5,
16
16
  "no_ext": 1471,
17
- ".py": 61567,
17
+ ".py": 61619,
18
18
  ".html": 14944,
19
19
  ".in": 3,
20
20
  ".toml": 129,
21
21
  ".md": 2170,
22
22
  ".txt": 473,
23
23
  ".lock": 1,
24
- ".json": 9515,
24
+ ".json": 9524,
25
25
  ".TAG": 4,
26
- ".whl": 21792,
27
- ".gz": 21068,
26
+ ".whl": 21634,
27
+ ".gz": 20778,
28
28
  ".svg": 1123,
29
29
  ".png": 31639,
30
30
  ".sh": 144,
@@ -39,42 +39,38 @@
39
39
  },
40
40
  "recent_commits": [
41
41
  {
42
- "hash": "d9c870b",
43
- "subject": "bump: 0.2.53 → 0.2.54",
42
+ "hash": "73887e7",
43
+ "subject": "bump: 0.2.55 → 0.2.56",
44
44
  "author": "KevRojo",
45
45
  "date": "2026-05-13"
46
46
  },
47
47
  {
48
- "hash": "3c7daf7",
49
- "subject": "feat(voice): add wake word detection, audio utils, and voice module improvements",
48
+ "hash": "70a1b1d",
49
+ "subject": "fix: eliminar notificaciones intermedias de Telegram/wake/TTS que se acumulaban en CLI",
50
50
  "author": "KevRojo",
51
51
  "date": "2026-05-13"
52
52
  },
53
53
  {
54
- "hash": "14c4f42",
55
- "subject": "bump: 0.2.53",
54
+ "hash": "cf4128b",
55
+ "subject": "feat: voice module polish — wake word UI, toolbar status, TTY beep fix",
56
56
  "author": "KevRojo",
57
- "date": "2026-05-12"
57
+ "date": "2026-05-13"
58
58
  },
59
59
  {
60
- "hash": "979df03",
61
- "subject": "feat: skill system overhaul — awesome-remote install, interactive use, descriptions",
60
+ "hash": "d9c870b",
61
+ "subject": "bump: 0.2.53 → 0.2.54",
62
62
  "author": "KevRojo",
63
- "date": "2026-05-12"
63
+ "date": "2026-05-13"
64
64
  },
65
65
  {
66
- "hash": "39ee671",
67
- "subject": "chore(release): bump v0.2.52",
66
+ "hash": "3c7daf7",
67
+ "subject": "feat(voice): add wake word detection, audio utils, and voice module improvements",
68
68
  "author": "KevRojo",
69
- "date": "2026-05-12"
69
+ "date": "2026-05-13"
70
70
  }
71
71
  ],
72
72
  "recent_changes": [
73
- "input.py",
74
- "pyproject.toml",
75
- "requirements.txt",
76
- "voice/audio_utils.py",
77
- "voice/tts.py"
73
+ "pyproject.toml"
78
74
  ]
79
75
  },
80
76
  "tasks": {
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dulus
3
- Version: 0.2.56
3
+ Version: 0.2.60
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
@@ -40,9 +40,12 @@ Provides-Extra: memory
40
40
  Requires-Dist: mempalace>=3.3.4; extra == "memory"
41
41
  Provides-Extra: voice
42
42
  Requires-Dist: sounddevice; extra == "voice"
43
+ Provides-Extra: webbridge
44
+ Requires-Dist: playwright>=1.40.0; extra == "webbridge"
43
45
  Provides-Extra: full
44
46
  Requires-Dist: mempalace>=3.3.4; extra == "full"
45
47
  Requires-Dist: sounddevice; extra == "full"
48
+ Requires-Dist: playwright>=1.40.0; extra == "full"
46
49
  Dynamic: license-file
47
50
 
48
51
  # ▲ DULUS
@@ -356,4 +356,10 @@ voice/keyterms.py
356
356
  voice/recorder.py
357
357
  voice/stt.py
358
358
  voice/tts.py
359
- voice/wake_word.py
359
+ voice/wake_word.py
360
+ webbridge/__init__.py
361
+ webbridge/core.py
362
+ webbridge/tools.py
363
+ webbridge/__pycache__/__init__.cpython-314.pyc
364
+ webbridge/__pycache__/core.cpython-314.pyc
365
+ webbridge/__pycache__/tools.cpython-314.pyc
@@ -15,9 +15,13 @@ beautifulsoup4>=4.12.0
15
15
  [full]
16
16
  mempalace>=3.3.4
17
17
  sounddevice
18
+ playwright>=1.40.0
18
19
 
19
20
  [memory]
20
21
  mempalace>=3.3.4
21
22
 
22
23
  [voice]
23
24
  sounddevice
25
+
26
+ [webbridge]
27
+ playwright>=1.40.0
@@ -36,5 +36,6 @@ tool_registry
36
36
  tools
37
37
  ui
38
38
  voice
39
+ webbridge
39
40
  webchat
40
41
  webchat_server
@@ -7714,6 +7714,141 @@ def cmd_batch(args: str, _state, config) -> bool:
7714
7714
  return True
7715
7715
 
7716
7716
 
7717
+ def cmd_webbridge(args: str, state, config) -> bool:
7718
+ """Control the Dulus WebBridge browser automation."""
7719
+ from os import makedirs, path, getenv
7720
+ from datetime import datetime
7721
+ try:
7722
+ from webbridge.core import DulusWebBridge
7723
+ except ImportError:
7724
+ err("WebBridge not available. Install dependencies: pip install playwright")
7725
+ return True
7726
+
7727
+ bridge = DulusWebBridge()
7728
+ parts = (args or "").strip().split()
7729
+ sub = parts[0].lower() if parts else "status"
7730
+
7731
+ if sub in ("status", ""):
7732
+ try:
7733
+ st = bridge.status()
7734
+ if st.get("browser_open"):
7735
+ ok("Browser: OPEN")
7736
+ info(f" URL: {st.get('url', 'N/A')}")
7737
+ else:
7738
+ info("Browser: CLOSED")
7739
+ except Exception as e:
7740
+ err(f"Status check failed: {e}")
7741
+ return True
7742
+
7743
+ if sub == "open":
7744
+ if len(parts) < 2:
7745
+ err("Usage: /webbridge open <url>")
7746
+ return True
7747
+ url = parts[1]
7748
+ if not url.startswith(("http://", "https://")):
7749
+ url = "https://" + url
7750
+ try:
7751
+ bridge.navigate_sync(url)
7752
+ ok(f"Opened: {url}")
7753
+ except Exception as e:
7754
+ err(f"Failed to open URL: {e}")
7755
+ return True
7756
+
7757
+ if sub == "click":
7758
+ if len(parts) < 2:
7759
+ err("Usage: /webbridge click <selector>")
7760
+ return True
7761
+ selector = parts[1]
7762
+ try:
7763
+ bridge.click_sync(selector)
7764
+ ok(f"Clicked: {selector}")
7765
+ except Exception as e:
7766
+ err(f"Click failed: {e}")
7767
+ return True
7768
+
7769
+ if sub == "type":
7770
+ if len(parts) < 3:
7771
+ err("Usage: /webbridge type <selector> <text>")
7772
+ return True
7773
+ selector = parts[1]
7774
+ text = " ".join(parts[2:])
7775
+ try:
7776
+ bridge.type_sync(selector, text)
7777
+ ok(f"Typed into: {selector}")
7778
+ except Exception as e:
7779
+ err(f"Type failed: {e}")
7780
+ return True
7781
+
7782
+ if sub == "screenshot":
7783
+ save_path = None
7784
+ if len(parts) >= 2:
7785
+ save_path = parts[1]
7786
+ else:
7787
+ screenshots_dir = path.join(getenv("USERPROFILE", getenv("HOME", ".")), ".dulus", "screenshots")
7788
+ makedirs(screenshots_dir, exist_ok=True)
7789
+ ts = datetime.now().strftime("%Y%m%d_%H%M%S")
7790
+ save_path = path.join(screenshots_dir, f"webbridge_{ts}.png")
7791
+ try:
7792
+ bridge.screenshot_sync(save_path)
7793
+ ok(f"Screenshot saved: {save_path}")
7794
+ except Exception as e:
7795
+ err(f"Screenshot failed: {e}")
7796
+ return True
7797
+
7798
+ if sub == "extract":
7799
+ mode = parts[1].lower() if len(parts) >= 2 else "text"
7800
+ if mode not in ("text", "dom"):
7801
+ mode = "text"
7802
+ try:
7803
+ if mode == "text":
7804
+ content = bridge.get_text_sync()
7805
+ else:
7806
+ content = bridge.get_dom_sync()
7807
+ preview = content[:2000]
7808
+ ok(f"Extracted ({mode}) — {len(content)} chars")
7809
+ info(preview)
7810
+ if len(content) > 2000:
7811
+ info(f"... ({len(content) - 2000} more chars)")
7812
+ except Exception as e:
7813
+ err(f"Extract failed: {e}")
7814
+ return True
7815
+
7816
+ if sub == "scroll":
7817
+ direction = parts[1].lower() if len(parts) >= 2 else "down"
7818
+ if direction not in ("up", "down"):
7819
+ direction = "down"
7820
+ try:
7821
+ bridge.scroll_sync(direction)
7822
+ ok(f"Scrolled {direction}")
7823
+ except Exception as e:
7824
+ err(f"Scroll failed: {e}")
7825
+ return True
7826
+
7827
+ if sub == "close":
7828
+ try:
7829
+ bridge.close_sync()
7830
+ ok("Browser closed")
7831
+ except Exception as e:
7832
+ err(f"Close failed: {e}")
7833
+ return True
7834
+
7835
+ if sub == "help":
7836
+ info("WebBridge commands:")
7837
+ info(" /webbridge status — Show browser status")
7838
+ info(" /webbridge open <url> — Open URL")
7839
+ info(" /webbridge click <sel> — Click element")
7840
+ info(" /webbridge type <sel> <t> — Type text")
7841
+ info(" /webbridge screenshot [p] — Take screenshot")
7842
+ info(" /webbridge extract [t|d] — Extract text or DOM")
7843
+ info(" /webbridge scroll [u|d] — Scroll page")
7844
+ info(" /webbridge close — Close browser")
7845
+ info(" /webbridge help — Show this help")
7846
+ return True
7847
+
7848
+ err(f"Unknown subcommand: {sub}. Use /webbridge help")
7849
+ return True
7850
+
7851
+
7717
7852
  COMMANDS = {
7718
7853
  "tts": cmd_tts,
7719
7854
  "say": cmd_say,
@@ -7776,6 +7911,7 @@ COMMANDS = {
7776
7911
  "wake": cmd_wake,
7777
7912
  "git": cmd_git,
7778
7913
  "webchat": cmd_webchat,
7914
+ "webbridge": cmd_webbridge,
7779
7915
  "sandbox": cmd_sandbox,
7780
7916
  "gui": cmd_gui,
7781
7917
  "brave": cmd_brave,
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "dulus"
7
- version = "0.2.56"
7
+ version = "0.2.60"
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"
@@ -56,7 +56,8 @@ dependencies = [
56
56
  [project.optional-dependencies]
57
57
  memory = ["mempalace>=3.3.4"]
58
58
  voice = ["sounddevice"]
59
- full = ["mempalace>=3.3.4", "sounddevice"]
59
+ webbridge = ["playwright>=1.40.0"]
60
+ full = ["mempalace>=3.3.4", "sounddevice", "playwright>=1.40.0"]
60
61
 
61
62
  [project.scripts]
62
63
  dulus = "dulus:main"
@@ -117,6 +118,7 @@ include = [
117
118
  "task*",
118
119
  "ui*",
119
120
  "voice*",
121
+ "webbridge*",
120
122
  ]
121
123
  exclude = [
122
124
  "tests*",
@@ -2259,6 +2259,13 @@ except Exception as _plugin_err:
2259
2259
  # task/tools.py registers all four tools into the central registry on import.
2260
2260
  import task.tools as _task_tools # noqa: F401
2261
2261
 
2262
+ # ── WebBridge tools (browser automation via Playwright) ────────────────────────
2263
+ # webbridge/tools.py registers WebBridgeNavigate, WebBridgeClick, etc.
2264
+ try:
2265
+ import webbridge.tools as _webbridge_tools # noqa: F401
2266
+ except Exception:
2267
+ pass # Playwright may not be installed; skip gracefully
2268
+
2262
2269
 
2263
2270
  # ── Checkpoint hooks (backup files before Write/Edit/NotebookEdit) ───────────
2264
2271
  from checkpoint.hooks import install_hooks as _install_checkpoint_hooks
@@ -2738,4 +2745,11 @@ except Exception:
2738
2745
  _plugin_count = 0
2739
2746
 
2740
2747
 
2748
+ # ── WebBridge tool schemas (append to TOOL_SCHEMAS so AI sees them) ──────────
2749
+ try:
2750
+ from webbridge.tools import _TOOL_SCHEMAS as _WEBBRIDGE_SCHEMAS
2751
+ TOOL_SCHEMAS.extend(_WEBBRIDGE_SCHEMAS)
2752
+ except Exception:
2753
+ pass # Playwright not installed; skip gracefully
2754
+
2741
2755
 
@@ -258,10 +258,18 @@ def _transcribe_faster_whisper(
258
258
  initial_prompt = _keyterms_to_prompt(keyterms)
259
259
  lang = None if not language or language == "auto" else language
260
260
 
261
+ # Force Spanish context when language is set to "es" — prevents
262
+ # the model from hallucinating English on short wake-word utterances.
263
+ if lang == "es" and not initial_prompt:
264
+ initial_prompt = "Transcripción en español."
265
+ elif lang == "es":
266
+ initial_prompt = "Transcripción en español: " + initial_prompt
267
+
261
268
  segments, _info = model.transcribe(
262
269
  audio,
263
270
  language=lang,
264
271
  initial_prompt=initial_prompt,
272
+ condition_on_previous_text=False, # avoid language drift on short audio
265
273
  vad_filter=True, # skip silent regions
266
274
  vad_parameters=dict(
267
275
  min_silence_duration_ms=300,
@@ -291,6 +299,13 @@ def _transcribe_openai_whisper(
291
299
  audio = np.frombuffer(pcm_bytes, dtype=np.int16).astype(np.float32) / 32768.0
292
300
 
293
301
  initial_prompt = _keyterms_to_prompt(keyterms)
302
+
303
+ # Force Spanish context when language is set to "es"
304
+ if language == "es" and not initial_prompt:
305
+ initial_prompt = "Transcripción en español."
306
+ elif language == "es":
307
+ initial_prompt = "Transcripción en español: " + initial_prompt
308
+
294
309
  options: dict = {"initial_prompt": initial_prompt} if initial_prompt else {}
295
310
  if language and language != "auto":
296
311
  options["language"] = language
@@ -356,7 +371,9 @@ def transcribe(
356
371
  lang = None if language == "auto" else language
357
372
 
358
373
  # NVIDIA Riva (whisper-large-v3, cloud) — preferred when configured
359
- if _riva_available():
374
+ # Skip Riva for wake-word detection if DULUS_WAKE_FORCE_LOCAL is set
375
+ # (avoids cloud latency / 502 errors on hotword path)
376
+ if _riva_available() and not os.environ.get("DULUS_WAKE_FORCE_LOCAL"):
360
377
  try:
361
378
  return _transcribe_nvidia_riva(pcm_bytes, lang)
362
379
  except Exception as e:
@@ -31,6 +31,7 @@ Usage
31
31
 
32
32
  from __future__ import annotations
33
33
 
34
+ import os
34
35
  import sys
35
36
  import threading
36
37
  import time
@@ -44,8 +45,23 @@ from .stt import transcribe
44
45
  # ── Config ────────────────────────────────────────────────────────────────
45
46
 
46
47
  WAKE_PHRASES: list[str] = [
48
+ # --- Variantes con "dulus" (S) ---
47
49
  "hey dulus", "okey dulus", "ok dulus", "dale dulus",
48
50
  "oye dulus", "escucha dulus", "dulus",
51
+ "dolus", "daulus", "doiulus",
52
+ # --- Variantes con "duluz" (Z) ---
53
+ "hey duluz", "okey duluz", "ok duluz", "dale duluz",
54
+ "oye duluz", "escucha duluz", "duluz",
55
+ "dolus", "dauluz", "doiuluz",
56
+ # --- Variantes con signos de exclamación (como transcribe Whisper) ---
57
+ "¡oye! ¡dulus!", "¡oye! ¡duluz!",
58
+ "¡okey dulus!", "¡okey duluz!",
59
+ "¡ok dulus!", "¡ok duluz!",
60
+ "¡dale dulus!", "¡dale duluz!",
61
+ "¡hey dulus!", "¡hey duluz!",
62
+ # --- Variantes cortas / slang ---
63
+ "dulus", "duluz", "dolus", "dulús", "dulúz",
64
+ "oye", "okey", "ok", "dale", "escucha",
49
65
  ]
50
66
 
51
67
  # VAD: chunk size and energy threshold.
@@ -126,6 +142,10 @@ class WakeWordListener:
126
142
  # like "Hey Dulus" don't get mis-detected by Whisper's auto-lang.
127
143
  self._wake_lang = "es" if language in ("auto", "") else language
128
144
 
145
+ # Force local STT (Whisper) for wake-word — avoids cloud latency
146
+ # and 502 errors from NVIDIA Riva on the hotword path.
147
+ os.environ["DULUS_WAKE_FORCE_LOCAL"] = "1"
148
+
129
149
  self._thread: threading.Thread | None = None
130
150
  self._stop_evt = threading.Event()
131
151
  self._running = False
@@ -0,0 +1,5 @@
1
+ """Dulus WebBridge — Browser automation for AI agents via Playwright."""
2
+
3
+ from .core import DulusWebBridge
4
+
5
+ __all__ = ["DulusWebBridge"]