dulus 3.7.0__tar.gz → 3.8.0__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.
- {dulus-3.7.0/dulus.egg-info → dulus-3.8.0}/PKG-INFO +3 -2
- {dulus-3.7.0 → dulus-3.8.0}/README.md +2 -1
- {dulus-3.7.0 → dulus-3.8.0}/analytics.py +164 -164
- {dulus-3.7.0 → dulus-3.8.0}/common.py +21 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/news.md +2 -0
- {dulus-3.7.0 → dulus-3.8.0/dulus.egg-info}/PKG-INFO +3 -2
- {dulus-3.7.0 → dulus-3.8.0}/dulus.egg-info/SOURCES.txt +2 -2
- {dulus-3.7.0 → dulus-3.8.0}/dulus.py +6 -6
- {dulus-3.7.0 → dulus-3.8.0}/dulus_gui_web.py +825 -825
- dulus-3.8.0/dulus_tools/tree_ls.py +156 -0
- {dulus-3.7.0 → dulus-3.8.0}/gui/dulus_orchestrator/App.tsx +196 -196
- {dulus-3.7.0 → dulus-3.8.0}/gui/dulus_orchestrator/THIRD_PARTY_NOTICES.md +10 -10
- {dulus-3.7.0 → dulus-3.8.0}/gui/dulus_orchestrator/dist/index.html +188 -188
- {dulus-3.7.0 → dulus-3.8.0}/gui/dulus_orchestrator/dist/standalone.html +188 -188
- {dulus-3.7.0 → dulus-3.8.0}/gui/dulus_orchestrator/index.html +120 -120
- {dulus-3.7.0 → dulus-3.8.0}/gui/dulus_orchestrator/index.tsx +16 -16
- {dulus-3.7.0 → dulus-3.8.0}/gui/dulus_orchestrator/metadata.json +6 -6
- {dulus-3.7.0 → dulus-3.8.0}/gui/dulus_orchestrator/package-lock.json +3143 -3143
- {dulus-3.7.0 → dulus-3.8.0}/gui/dulus_orchestrator/package.json +30 -30
- {dulus-3.7.0 → dulus-3.8.0}/gui/dulus_orchestrator/types.ts +169 -169
- {dulus-3.7.0 → dulus-3.8.0}/gui/dulus_orchestrator/utils.ts +74 -74
- {dulus-3.7.0 → dulus-3.8.0}/gui/dulus_orchestrator/vertex-ai-proxy-interceptor.js +154 -154
- {dulus-3.7.0 → dulus-3.8.0}/gui/dulus_orchestrator/vite.config.ts +44 -44
- {dulus-3.7.0 → dulus-3.8.0}/plugin/store.py +2 -1
- {dulus-3.7.0 → dulus-3.8.0}/pyproject.toml +1 -1
- dulus-3.7.0/tests/test_fs_compass.py → dulus-3.8.0/tests/test_tree_ls.py +48 -40
- {dulus-3.7.0 → dulus-3.8.0}/tools.py +3 -3
- {dulus-3.7.0 → dulus-3.8.0}/voice/stt.py +80 -5
- {dulus-3.7.0 → dulus-3.8.0}/voice/tts.py +101 -11
- {dulus-3.7.0 → dulus-3.8.0}/welcome.py +1 -1
- dulus-3.7.0/dulus_tools/fs_compass.py +0 -472
- {dulus-3.7.0 → dulus-3.8.0}/LICENSE +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/MANIFEST.in +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/agent.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/backend/__init__.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/backend/agents_bridge.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/backend/compressor.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/backend/context.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/backend/githook.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/backend/marketplace.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/backend/mempalace_bridge.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/backend/personas.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/backend/plugins.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/backend/server.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/backend/tasks.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/batch_api.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/checkpoint/__init__.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/checkpoint/hooks.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/checkpoint/store.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/checkpoint/types.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/claude_code_watcher.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/clipboard_utils.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/cloudsave.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/compaction.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/config.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/context.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/data/__init__.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/data/active_persona.json +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/data/marketplace.json +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/data/personas.json +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/data/plugins/__init__.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/data/plugins/composio/__init__.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/data/plugins/composio/composio_plugin/__init__.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/data/plugins/composio/composio_plugin/session_manager.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/data/plugins/composio/composio_plugin/tool_generator.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/data/plugins/composio/plugin.json +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/data/plugins/composio/plugin_tool.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/data/tasks.json +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/API.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/BRAND_GUIDE.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/CHANGELOG.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/CONTRIBUTING.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/DEPLOYMENT.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/Dulus_AI_Whitepaper_(v2.0).pdf +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/FAQ.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/GETTING_STARTED.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/INVESTOR_BRIEF.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/README.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/README_AR.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/README_EN.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/README_ES.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/README_FR.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/README_JA.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/README_KO.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/README_PT.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/README_RU.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/README_ZH.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/SECURITY.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/__init__.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/api.html +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/architecture.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/azure-speech-template.json +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/dashboard/index.html +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/divider.svg +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/dulus-bird.png +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/generate.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/hero.svg +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/index.html +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/nvidia-models.svg +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/particle-playground.html +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/personas/index.html +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/poetry-banner.png +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/preview.html +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/sec-agents.svg +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/sec-brainstorm.svg +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/sec-bridges.svg +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/sec-features.svg +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/sec-freetier.svg +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/sec-memory.svg +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/sec-models.svg +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/sec-perms.svg +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/sec-plugins.svg +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/sec-quickstart.svg +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/sec-ssj.svg +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/spinners.svg +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/split-pane.svg +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/docs/terminal-boot.svg +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/dulus.egg-info/dependency_links.txt +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/dulus.egg-info/entry_points.txt +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/dulus.egg-info/requires.txt +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/dulus.egg-info/top_level.txt +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/dulus_gui.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/dulus_mcp/__init__.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/dulus_mcp/client.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/dulus_mcp/config.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/dulus_mcp/tools.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/dulus_mcp/types.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/dulus_tools/__init__.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/dulus_tools/add_dir_manager.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/dulus_tools/afk_mode.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/dulus_tools/approval_runtime.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/dulus_tools/background_tasks.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/dulus_tools/clipboard_utils.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/dulus_tools/diff_visualizer.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/dulus_tools/export_import.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/dulus_tools/hook_engine.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/dulus_tools/notification_manager.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/dulus_tools/session_fork.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/dulus_tools/shell_mode.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/dulus_tools/todo_visualizer.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/dulus_tools/wire_events.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/dulus_tools/yolo_mode.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/governance.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/gui/__init__.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/gui/agent_bridge.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/gui/chat_widget.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/gui/main_window.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/gui/personas.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/gui/sandbox_server.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/gui/session_utils.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/gui/settings_dialog.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/gui/sidebar.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/gui/tasks_view.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/gui/themes.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/gui/tool_panel.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/input.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/license_manager.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/memory/__init__.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/memory/audit.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/memory/consolidator.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/memory/context.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/memory/offload.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/memory/palace.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/memory/scan.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/memory/sessions.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/memory/store.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/memory/tools.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/memory/types.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/memory/vector_search.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/multi_agent/__init__.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/multi_agent/subagent.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/multi_agent/tools.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/offload_helper.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/personality_config.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/plugin/__init__.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/plugin/autoadapter.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/plugin/loader.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/plugin/recommend.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/plugin/types.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/providers.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/sandbox/README.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/sandbox/__init__.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/sandbox/components.json +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/sandbox/dist/assets/index-DE51D6wI.css +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/sandbox/dist/assets/index-DMCCNE9Y.js +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/sandbox/dist/index.html +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/sandbox/dist/wallpaper-default.jpg +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/sandbox/dist/wallpapers/default.jpeg +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/sandbox/dist/wallpapers/light.jpeg +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/sandbox/dist/wallpapers/nature.jpeg +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/sandbox/dist/wallpapers/tech.jpeg +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/sandbox/eslint.config.js +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/sandbox/index.html +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/sandbox/info.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/sandbox/package-lock.json +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/sandbox/package.json +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/sandbox/postcss.config.js +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/sandbox/tailwind.config.js +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/sandbox/tsconfig.app.json +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/sandbox/tsconfig.json +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/sandbox/tsconfig.node.json +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/sandbox/vite.config.ts +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/sandbox_bootstrap.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/setup.cfg +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/__init__.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/builtin.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/LICENSE-obsidian-skills +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/README.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/defuddle/SKILL.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/json-canvas/SKILL.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/json-canvas/references/EXAMPLES.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/obsidian-bases/SKILL.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/obsidian-bases/references/FUNCTIONS_REFERENCE.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/obsidian-cli/SKILL.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/obsidian-markdown/SKILL.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/obsidian-markdown/references/CALLOUTS.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/obsidian-markdown/references/EMBEDS.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/obsidian-markdown/references/PROPERTIES.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/README.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/SKILL.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/references/brokers/addresses.json +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/references/brokers/advancedbackgroundchecks.json +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/references/brokers/beenverified.json +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/references/brokers/clustal.json +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/references/brokers/clustrmaps.json +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/references/brokers/cyberbackgroundchecks.json +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/references/brokers/familytreenow.json +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/references/brokers/fastpeoplesearch.json +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/references/brokers/intelius.json +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/references/brokers/mylife.json +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/references/brokers/nuwber.json +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/references/brokers/peekyou.json +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/references/brokers/peoplefinders.json +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/references/brokers/radaris.json +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/references/brokers/rehold.json +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/references/brokers/searchpeoplefree.json +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/references/brokers/socialcatfish.json +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/references/brokers/spokeo.json +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/references/brokers/thatsthem.json +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/references/brokers/truepeoplesearch.json +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/references/brokers/usphonebook.json +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/references/brokers/whitepages.json +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/references/legal/ccpa.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/references/legal/drop.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/references/legal/gdpr.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/references/methods.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/references/site-playbooks.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/references/state-machine.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/scripts/autopilot.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/scripts/badbool.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/scripts/brokers.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/scripts/cdp.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/scripts/config.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/scripts/crypto.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/scripts/dossier.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/scripts/email_modes.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/scripts/emailer.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/scripts/ledger.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/scripts/legal.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/scripts/paths.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/scripts/pdd.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/scripts/registry.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/scripts/report.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/scripts/scan.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/scripts/storage.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/scripts/tiers.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/scripts/vectors.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/templates/consent/authorization.md +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/templates/emails/ccpa-authorized-agent.txt +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/templates/emails/ccpa-deletion.txt +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/templates/emails/ccpa-indirect-deletion.txt +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/templates/emails/gdpr-erasure.txt +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/bundled/unbroker/templates/emails/generic-optout.txt +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/clawhub.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/executor.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/loader.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skill/tools.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/skills.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/soul.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/spinner.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/string_utils.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/subagent.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/task/__init__.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/task/store.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/task/tools.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/task/types.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/tests/test_afk_yolo.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/tests/test_approval_runtime.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/tests/test_background_task_tools.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/tests/test_background_tasks.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/tests/test_checkpoint.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/tests/test_clipboard_utils.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/tests/test_compaction.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/tests/test_diff_view.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/tests/test_diff_visualization.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/tests/test_display_blocks.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/tests/test_export_import.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/tests/test_hook_engine.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/tests/test_injection_fix.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/tests/test_license.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/tests/test_mcp.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/tests/test_memory.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/tests/test_notification_manager.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/tests/test_plugin.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/tests/test_session_fork.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/tests/test_shell_mode.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/tests/test_skills.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/tests/test_steer_input.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/tests/test_subagent.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/tests/test_task.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/tests/test_telegram_buffer.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/tests/test_think_tool.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/tests/test_todo_tool.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/tests/test_todo_visualization.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/tests/test_tool_registry.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/tests/test_voice.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/tests/test_wire_events.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/tmux_offloader.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/tmux_tools.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/tool_registry.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/ui/__init__.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/ui/input.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/ui/render.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/voice/__init__.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/voice/audio_utils.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/voice/keyterms.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/voice/recorder.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/voice/wake_word.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/webbridge/__init__.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/webbridge/core.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/webbridge/tools.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/webchat.py +0 -0
- {dulus-3.7.0 → dulus-3.8.0}/webchat_server.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dulus
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.8.0
|
|
4
4
|
Summary: Use IA as an agent without an API key. $0. Harvest your browser session for Gemini (guest, no login), Claude.ai, Claude Code, Kimi.com, Qwen, DeepSeek — drive them like an API, no per-token billing. Plus LiteLLM (100+ paid providers), local models via Ollama, /lang in 34 languages, Mesa Redonda, voice, OCR, MemPalace, embedded sandbox OS.
|
|
5
5
|
Author: KevRojo
|
|
6
6
|
License: GPL-3.0
|
|
@@ -305,7 +305,8 @@ Dulus is proudly supported by these startup programs — powering our infrastruc
|
|
|
305
305
|
<a href="https://amplitude.com/startups"><img src="https://www.google.com/s2/favicons?domain=amplitude.com&sz=128" height="38" alt="Amplitude" title="Amplitude Startup Scholarship"></a>
|
|
306
306
|
<a href="https://www.digitalocean.com/hatch"><img src="https://cdn.simpleicons.org/digitalocean" height="38" alt="DigitalOcean" title="DigitalOcean Hatch"></a>
|
|
307
307
|
<a href="https://www.notion.com/startups"><img src="https://cdn.simpleicons.org/notion" height="38" alt="Notion" title="Notion for Startups"></a>
|
|
308
|
-
<a href="https://www.zendesk.com/campaign/startups/"><img src="https://cdn.simpleicons.org/zendesk" height="38" alt="Zendesk" title="Zendesk for Startups"></a
|
|
308
|
+
<a href="https://www.zendesk.com/campaign/startups/"><img src="https://cdn.simpleicons.org/zendesk" height="38" alt="Zendesk" title="Zendesk for Startups"></a>
|
|
309
|
+
<a href="https://deepgram.com/startups"><img src="https://www.google.com/s2/favicons?domain=deepgram.com&sz=128" height="38" alt="Deepgram" title="Deepgram Startup Program — powering Dulus's voice"></a>
|
|
309
310
|
</p>
|
|
310
311
|
|
|
311
312
|
<p align="center"><i>Want to support Dulus? → <a href="https://github.com/sponsors/KevRojo">GitHub Sponsors</a> 🦅</i></p>
|
|
@@ -239,7 +239,8 @@ Dulus is proudly supported by these startup programs — powering our infrastruc
|
|
|
239
239
|
<a href="https://amplitude.com/startups"><img src="https://www.google.com/s2/favicons?domain=amplitude.com&sz=128" height="38" alt="Amplitude" title="Amplitude Startup Scholarship"></a>
|
|
240
240
|
<a href="https://www.digitalocean.com/hatch"><img src="https://cdn.simpleicons.org/digitalocean" height="38" alt="DigitalOcean" title="DigitalOcean Hatch"></a>
|
|
241
241
|
<a href="https://www.notion.com/startups"><img src="https://cdn.simpleicons.org/notion" height="38" alt="Notion" title="Notion for Startups"></a>
|
|
242
|
-
<a href="https://www.zendesk.com/campaign/startups/"><img src="https://cdn.simpleicons.org/zendesk" height="38" alt="Zendesk" title="Zendesk for Startups"></a
|
|
242
|
+
<a href="https://www.zendesk.com/campaign/startups/"><img src="https://cdn.simpleicons.org/zendesk" height="38" alt="Zendesk" title="Zendesk for Startups"></a>
|
|
243
|
+
<a href="https://deepgram.com/startups"><img src="https://www.google.com/s2/favicons?domain=deepgram.com&sz=128" height="38" alt="Deepgram" title="Deepgram Startup Program — powering Dulus's voice"></a>
|
|
243
244
|
</p>
|
|
244
245
|
|
|
245
246
|
<p align="center"><i>Want to support Dulus? → <a href="https://github.com/sponsors/KevRojo">GitHub Sponsors</a> 🦅</i></p>
|
|
@@ -1,164 +1,164 @@
|
|
|
1
|
-
"""Dulus anonymous usage telemetry (opt-in, privacy-first).
|
|
2
|
-
|
|
3
|
-
Dulus asks ONCE on startup whether you want to share anonymous usage
|
|
4
|
-
statistics. Nothing is sent until you explicitly say yes.
|
|
5
|
-
|
|
6
|
-
What IS collected (when enabled):
|
|
7
|
-
- Event names (session_start, message_sent, tool_used, model_selected)
|
|
8
|
-
- Dulus version, OS name, Python version
|
|
9
|
-
- Provider/model *names* (e.g. "gemini", "claude-sonnet")
|
|
10
|
-
- A random anonymous ID (UUID generated locally — not tied to you)
|
|
11
|
-
|
|
12
|
-
What is NEVER collected:
|
|
13
|
-
- Prompts, responses, or any conversation content
|
|
14
|
-
- File paths, file contents, or code
|
|
15
|
-
- Usernames, emails, hostnames, IPs (Mixpanel geo is disabled via $ip=0)
|
|
16
|
-
- API keys or tokens
|
|
17
|
-
|
|
18
|
-
Where it goes: Mixpanel (https://mixpanel.com) — event analytics only.
|
|
19
|
-
|
|
20
|
-
Opt out at any time (any of these):
|
|
21
|
-
- Answer "n" at the first-run prompt
|
|
22
|
-
- /config telemetry=off (inside Dulus)
|
|
23
|
-
- DULUS_TELEMETRY=0 (environment variable)
|
|
24
|
-
|
|
25
|
-
Implementation notes: zero third-party dependencies — plain urllib POST to
|
|
26
|
-
the Mixpanel ingestion API in a fire-and-forget daemon thread. Failures are
|
|
27
|
-
silently ignored; telemetry must never slow down or break Dulus.
|
|
28
|
-
"""
|
|
29
|
-
from __future__ import annotations
|
|
30
|
-
|
|
31
|
-
import base64
|
|
32
|
-
import json
|
|
33
|
-
import os
|
|
34
|
-
import platform
|
|
35
|
-
import sys
|
|
36
|
-
import threading
|
|
37
|
-
import time
|
|
38
|
-
import uuid
|
|
39
|
-
|
|
40
|
-
# Mixpanel PROJECT TOKEN for the public Dulus project.
|
|
41
|
-
# NOTE: Mixpanel ingestion tokens are write-only and designed to ship in
|
|
42
|
-
# client code (every website using Mixpanel exposes theirs). It cannot be
|
|
43
|
-
# used to read any data. Override with DULUS_MP_TOKEN.
|
|
44
|
-
MP_TOKEN = os.environ.get("DULUS_MP_TOKEN", "966ce8f5ccb32f06788f51be9f8bf8f5")
|
|
45
|
-
|
|
46
|
-
_MP_ENDPOINT = "https://api.mixpanel.com/track"
|
|
47
|
-
|
|
48
|
-
# Populated by init_telemetry(); None = not initialised / disabled.
|
|
49
|
-
_distinct_id: str | None = None
|
|
50
|
-
_enabled: bool = False
|
|
51
|
-
_dulus_version: str = ""
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def _env_disabled() -> bool:
|
|
55
|
-
return os.environ.get("DULUS_TELEMETRY", "").strip().lower() in ("0", "off", "false", "no")
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
def is_enabled() -> bool:
|
|
59
|
-
return _enabled and not _env_disabled() and bool(MP_TOKEN)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
CONSENT_NOTICE = """
|
|
63
|
-
── Ayuda a mejorar Dulus / Help improve Dulus ─────────────────────────
|
|
64
|
-
Dulus can share ANONYMOUS usage statistics to help us understand
|
|
65
|
-
which features matter (event counts only — sent to Mixpanel).
|
|
66
|
-
|
|
67
|
-
Collected: event names, Dulus version, OS, model/provider names,
|
|
68
|
-
a random anonymous ID generated on this machine.
|
|
69
|
-
NEVER: prompts, responses, files, paths, keys, emails, IPs.
|
|
70
|
-
|
|
71
|
-
Change your mind anytime: /config telemetry=off or DULUS_TELEMETRY=0
|
|
72
|
-
────────────────────────────────────────────────────────────────────────
|
|
73
|
-
"""
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
def ask_consent(config: dict) -> dict:
|
|
77
|
-
"""One-time interactive consent prompt. Mutates + returns config.
|
|
78
|
-
|
|
79
|
-
Only call when config['telemetry'] is unset and stdin is a TTY.
|
|
80
|
-
"""
|
|
81
|
-
print(CONSENT_NOTICE)
|
|
82
|
-
try:
|
|
83
|
-
answer = input(" Share anonymous usage stats? [y/N] ").strip().lower()
|
|
84
|
-
except (EOFError, KeyboardInterrupt):
|
|
85
|
-
answer = ""
|
|
86
|
-
config["telemetry"] = answer in ("y", "yes", "s", "si", "sí")
|
|
87
|
-
if config["telemetry"] and not config.get("telemetry_id"):
|
|
88
|
-
config["telemetry_id"] = uuid.uuid4().hex
|
|
89
|
-
state = "enabled — thank you! 🦅" if config["telemetry"] else "disabled."
|
|
90
|
-
print(f" Telemetry {state}\n")
|
|
91
|
-
return config
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
def init_telemetry(config: dict, version: str = "") -> None:
|
|
95
|
-
"""Initialise the module from config. Safe to call multiple times."""
|
|
96
|
-
global _distinct_id, _enabled, _dulus_version
|
|
97
|
-
_dulus_version = version or _dulus_version
|
|
98
|
-
_enabled = bool(config.get("telemetry")) and not _env_disabled()
|
|
99
|
-
if _enabled:
|
|
100
|
-
_distinct_id = config.get("telemetry_id") or uuid.uuid4().hex
|
|
101
|
-
config.setdefault("telemetry_id", _distinct_id)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
def track(event: str, properties: dict | None = None) -> None:
|
|
105
|
-
"""Fire-and-forget anonymous event. No-op unless telemetry is enabled."""
|
|
106
|
-
if not is_enabled() or not _distinct_id:
|
|
107
|
-
return
|
|
108
|
-
|
|
109
|
-
payload = {
|
|
110
|
-
"event": event,
|
|
111
|
-
"properties": {
|
|
112
|
-
"token": MP_TOKEN,
|
|
113
|
-
"distinct_id": _distinct_id,
|
|
114
|
-
"time": int(time.time()),
|
|
115
|
-
"$ip": 0, # disable Mixpanel geolocation
|
|
116
|
-
"dulus_version": _dulus_version,
|
|
117
|
-
"os": platform.system(),
|
|
118
|
-
"python": f"{sys.version_info.major}.{sys.version_info.minor}",
|
|
119
|
-
**(properties or {}),
|
|
120
|
-
},
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
def _send() -> None:
|
|
124
|
-
try:
|
|
125
|
-
from urllib.request import Request, urlopen
|
|
126
|
-
from urllib.parse import urlencode
|
|
127
|
-
|
|
128
|
-
data = urlencode(
|
|
129
|
-
{"data": base64.b64encode(json.dumps([payload]).encode()).decode()}
|
|
130
|
-
).encode()
|
|
131
|
-
req = Request(_MP_ENDPOINT, data=data, method="POST")
|
|
132
|
-
urlopen(req, timeout=4).read()
|
|
133
|
-
except Exception:
|
|
134
|
-
pass # telemetry must never break Dulus
|
|
135
|
-
|
|
136
|
-
threading.Thread(target=_send, daemon=True, name="telemetry").start()
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
def track_session_start(config: dict) -> None:
|
|
140
|
-
"""Convenience: one event per REPL boot (enough for DAU/MAU counts)."""
|
|
141
|
-
track("session_start", {
|
|
142
|
-
"provider": str(config.get("provider", "")),
|
|
143
|
-
"model": str(config.get("model", "")),
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
def track_message_sent(model: str = "") -> None:
|
|
148
|
-
"""One event per user prompt sent to the LLM (no content — count only)."""
|
|
149
|
-
track("message_sent", {"model": model})
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
def track_tool_used(tool_name: str) -> None:
|
|
153
|
-
"""One event per tool invocation (tool NAME only — never inputs)."""
|
|
154
|
-
track("tool_used", {"tool": tool_name})
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
def track_command_used(command: str) -> None:
|
|
158
|
-
"""One event per slash command (command NAME only — never args)."""
|
|
159
|
-
track("command_used", {"command": command})
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
def track_model_selected(model: str, provider: str = "") -> None:
|
|
163
|
-
"""Fired when the user switches models via /model."""
|
|
164
|
-
track("model_selected", {"model": model, "provider": provider})
|
|
1
|
+
"""Dulus anonymous usage telemetry (opt-in, privacy-first).
|
|
2
|
+
|
|
3
|
+
Dulus asks ONCE on startup whether you want to share anonymous usage
|
|
4
|
+
statistics. Nothing is sent until you explicitly say yes.
|
|
5
|
+
|
|
6
|
+
What IS collected (when enabled):
|
|
7
|
+
- Event names (session_start, message_sent, tool_used, model_selected)
|
|
8
|
+
- Dulus version, OS name, Python version
|
|
9
|
+
- Provider/model *names* (e.g. "gemini", "claude-sonnet")
|
|
10
|
+
- A random anonymous ID (UUID generated locally — not tied to you)
|
|
11
|
+
|
|
12
|
+
What is NEVER collected:
|
|
13
|
+
- Prompts, responses, or any conversation content
|
|
14
|
+
- File paths, file contents, or code
|
|
15
|
+
- Usernames, emails, hostnames, IPs (Mixpanel geo is disabled via $ip=0)
|
|
16
|
+
- API keys or tokens
|
|
17
|
+
|
|
18
|
+
Where it goes: Mixpanel (https://mixpanel.com) — event analytics only.
|
|
19
|
+
|
|
20
|
+
Opt out at any time (any of these):
|
|
21
|
+
- Answer "n" at the first-run prompt
|
|
22
|
+
- /config telemetry=off (inside Dulus)
|
|
23
|
+
- DULUS_TELEMETRY=0 (environment variable)
|
|
24
|
+
|
|
25
|
+
Implementation notes: zero third-party dependencies — plain urllib POST to
|
|
26
|
+
the Mixpanel ingestion API in a fire-and-forget daemon thread. Failures are
|
|
27
|
+
silently ignored; telemetry must never slow down or break Dulus.
|
|
28
|
+
"""
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
import base64
|
|
32
|
+
import json
|
|
33
|
+
import os
|
|
34
|
+
import platform
|
|
35
|
+
import sys
|
|
36
|
+
import threading
|
|
37
|
+
import time
|
|
38
|
+
import uuid
|
|
39
|
+
|
|
40
|
+
# Mixpanel PROJECT TOKEN for the public Dulus project.
|
|
41
|
+
# NOTE: Mixpanel ingestion tokens are write-only and designed to ship in
|
|
42
|
+
# client code (every website using Mixpanel exposes theirs). It cannot be
|
|
43
|
+
# used to read any data. Override with DULUS_MP_TOKEN.
|
|
44
|
+
MP_TOKEN = os.environ.get("DULUS_MP_TOKEN", "966ce8f5ccb32f06788f51be9f8bf8f5")
|
|
45
|
+
|
|
46
|
+
_MP_ENDPOINT = "https://api.mixpanel.com/track"
|
|
47
|
+
|
|
48
|
+
# Populated by init_telemetry(); None = not initialised / disabled.
|
|
49
|
+
_distinct_id: str | None = None
|
|
50
|
+
_enabled: bool = False
|
|
51
|
+
_dulus_version: str = ""
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _env_disabled() -> bool:
|
|
55
|
+
return os.environ.get("DULUS_TELEMETRY", "").strip().lower() in ("0", "off", "false", "no")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def is_enabled() -> bool:
|
|
59
|
+
return _enabled and not _env_disabled() and bool(MP_TOKEN)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
CONSENT_NOTICE = """
|
|
63
|
+
── Ayuda a mejorar Dulus / Help improve Dulus ─────────────────────────
|
|
64
|
+
Dulus can share ANONYMOUS usage statistics to help us understand
|
|
65
|
+
which features matter (event counts only — sent to Mixpanel).
|
|
66
|
+
|
|
67
|
+
Collected: event names, Dulus version, OS, model/provider names,
|
|
68
|
+
a random anonymous ID generated on this machine.
|
|
69
|
+
NEVER: prompts, responses, files, paths, keys, emails, IPs.
|
|
70
|
+
|
|
71
|
+
Change your mind anytime: /config telemetry=off or DULUS_TELEMETRY=0
|
|
72
|
+
────────────────────────────────────────────────────────────────────────
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def ask_consent(config: dict) -> dict:
|
|
77
|
+
"""One-time interactive consent prompt. Mutates + returns config.
|
|
78
|
+
|
|
79
|
+
Only call when config['telemetry'] is unset and stdin is a TTY.
|
|
80
|
+
"""
|
|
81
|
+
print(CONSENT_NOTICE)
|
|
82
|
+
try:
|
|
83
|
+
answer = input(" Share anonymous usage stats? [y/N] ").strip().lower()
|
|
84
|
+
except (EOFError, KeyboardInterrupt):
|
|
85
|
+
answer = ""
|
|
86
|
+
config["telemetry"] = answer in ("y", "yes", "s", "si", "sí")
|
|
87
|
+
if config["telemetry"] and not config.get("telemetry_id"):
|
|
88
|
+
config["telemetry_id"] = uuid.uuid4().hex
|
|
89
|
+
state = "enabled — thank you! 🦅" if config["telemetry"] else "disabled."
|
|
90
|
+
print(f" Telemetry {state}\n")
|
|
91
|
+
return config
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def init_telemetry(config: dict, version: str = "") -> None:
|
|
95
|
+
"""Initialise the module from config. Safe to call multiple times."""
|
|
96
|
+
global _distinct_id, _enabled, _dulus_version
|
|
97
|
+
_dulus_version = version or _dulus_version
|
|
98
|
+
_enabled = bool(config.get("telemetry")) and not _env_disabled()
|
|
99
|
+
if _enabled:
|
|
100
|
+
_distinct_id = config.get("telemetry_id") or uuid.uuid4().hex
|
|
101
|
+
config.setdefault("telemetry_id", _distinct_id)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def track(event: str, properties: dict | None = None) -> None:
|
|
105
|
+
"""Fire-and-forget anonymous event. No-op unless telemetry is enabled."""
|
|
106
|
+
if not is_enabled() or not _distinct_id:
|
|
107
|
+
return
|
|
108
|
+
|
|
109
|
+
payload = {
|
|
110
|
+
"event": event,
|
|
111
|
+
"properties": {
|
|
112
|
+
"token": MP_TOKEN,
|
|
113
|
+
"distinct_id": _distinct_id,
|
|
114
|
+
"time": int(time.time()),
|
|
115
|
+
"$ip": 0, # disable Mixpanel geolocation
|
|
116
|
+
"dulus_version": _dulus_version,
|
|
117
|
+
"os": platform.system(),
|
|
118
|
+
"python": f"{sys.version_info.major}.{sys.version_info.minor}",
|
|
119
|
+
**(properties or {}),
|
|
120
|
+
},
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
def _send() -> None:
|
|
124
|
+
try:
|
|
125
|
+
from urllib.request import Request, urlopen
|
|
126
|
+
from urllib.parse import urlencode
|
|
127
|
+
|
|
128
|
+
data = urlencode(
|
|
129
|
+
{"data": base64.b64encode(json.dumps([payload]).encode()).decode()}
|
|
130
|
+
).encode()
|
|
131
|
+
req = Request(_MP_ENDPOINT, data=data, method="POST")
|
|
132
|
+
urlopen(req, timeout=4).read()
|
|
133
|
+
except Exception:
|
|
134
|
+
pass # telemetry must never break Dulus
|
|
135
|
+
|
|
136
|
+
threading.Thread(target=_send, daemon=True, name="telemetry").start()
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def track_session_start(config: dict) -> None:
|
|
140
|
+
"""Convenience: one event per REPL boot (enough for DAU/MAU counts)."""
|
|
141
|
+
track("session_start", {
|
|
142
|
+
"provider": str(config.get("provider", "")),
|
|
143
|
+
"model": str(config.get("model", "")),
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def track_message_sent(model: str = "") -> None:
|
|
148
|
+
"""One event per user prompt sent to the LLM (no content — count only)."""
|
|
149
|
+
track("message_sent", {"model": model})
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def track_tool_used(tool_name: str) -> None:
|
|
153
|
+
"""One event per tool invocation (tool NAME only — never inputs)."""
|
|
154
|
+
track("tool_used", {"tool": tool_name})
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def track_command_used(command: str) -> None:
|
|
158
|
+
"""One event per slash command (command NAME only — never args)."""
|
|
159
|
+
track("command_used", {"command": command})
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def track_model_selected(model: str, provider: str = "") -> None:
|
|
163
|
+
"""Fired when the user switches models via /model."""
|
|
164
|
+
track("model_selected", {"model": model, "provider": provider})
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import sys
|
|
2
2
|
import json
|
|
3
|
+
from pathlib import Path
|
|
3
4
|
|
|
4
5
|
# ── Import slash completer helpers ──
|
|
5
6
|
try:
|
|
@@ -131,6 +132,26 @@ def ok(msg: str): print(clr(msg, "green"))
|
|
|
131
132
|
def warn(msg: str): print(clr(f"Warning: {msg}", "yellow"))
|
|
132
133
|
def err(msg: str): print(clr(f"Error: {msg}", "red"), file=sys.stderr)
|
|
133
134
|
|
|
135
|
+
def pip_install_cmd(*packages: str) -> list[str]:
|
|
136
|
+
"""Build a pip-install command that works on externally-managed Pythons.
|
|
137
|
+
|
|
138
|
+
Debian 12+ / Ubuntu 23.04+ mark the system Python as externally managed
|
|
139
|
+
(PEP 668): any bare `pip install` dies with `externally-managed-environment`
|
|
140
|
+
unless `--break-system-packages` is passed. That flag only exists on
|
|
141
|
+
pip >= 23, so we add it exactly when the marker file is present (which
|
|
142
|
+
implies a modern pip) instead of always.
|
|
143
|
+
"""
|
|
144
|
+
import sysconfig
|
|
145
|
+
cmd = [sys.executable, "-m", "pip", "install"]
|
|
146
|
+
try:
|
|
147
|
+
marker = Path(sysconfig.get_path("stdlib")) / "EXTERNALLY-MANAGED"
|
|
148
|
+
if marker.exists():
|
|
149
|
+
cmd.append("--break-system-packages")
|
|
150
|
+
except Exception:
|
|
151
|
+
pass
|
|
152
|
+
return cmd + list(packages)
|
|
153
|
+
|
|
154
|
+
|
|
134
155
|
def stream_thinking(chunk: str, verbose: bool):
|
|
135
156
|
if verbose:
|
|
136
157
|
clean_chunk = chunk.replace("\n", " ")
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
## 🔥🔥🔥 News (Pacific Time)
|
|
4
4
|
|
|
5
|
+
- July 10, 2026: **🎙️ Dulus has his own voice — Deepgram lands as a first-class voice provider.** Voice on Dulus just leveled up: **Deepgram nova-3** is now the default STT backend the moment a key exists — ~300ms cloud transcription with native keyterm boosting, so your coding vocabulary (`grep`, `MemPalace`, `TypeScript`) comes out spelled right instead of whatever Whisper hallucinates on a noisy mic. And on the speaking side, **Aura-2** joins the TTS chain as the `deepgram` provider with natural per-language voices (`aura-2-celeste-es` for Spanish, `aura-2-thalia-en` for English). Zero SDK, zero heavy model downloads — both talk plain HTTPS, which means voice finally works great on laptops that can't afford a 1GB local Whisper model warming up for 30 seconds. Getting in is stupid simple: create a free account at [deepgram.com](https://deepgram.com) (**$200 in credit, no credit card** — that's roughly 775 hours of transcription), drop the key in `DEEPGRAM_API_KEY` or `deepgram_api_key` in your config, and `/voice`, `/wake` and `/say` are instantly running on it. No key? Nothing changes — local Whisper keeps working as always, and `DULUS_WAKE_FORCE_LOCAL=1` brings it back anytime. The eagle speaks now. 🦅🇩🇴
|
|
6
|
+
|
|
5
7
|
- July 3, 2026: **🌍 dulus.work — THE HUB, revealed.** The secret is out: dulus.work is a **global hub where every Dulus instance on the planet shares what it learns**. Your Dulus finds a fix, refines a skill, adapts a plugin locally → publishes it to the hub → every other Dulus in the world pulls it down. Not an update server — an **organism**: every installation is a node, every user effectively becomes a contributor. *The fix a Dulus in Tokyo finds at 3AM, the one in Santo Domingo has by sunrise.* Traditional agents scale linearly (N devs improving 1 product); Dulus scales with its network (every user is a dev). The building blocks have been quietly shipping for months — the SelfImprove engine, the Auto-Adapter, the skills format, cloudsave — and dulus.work is where they converge. Domain is live, execution underway. 🦅🇩🇴 → [dulus.work](https://dulus.work)
|
|
6
8
|
|
|
7
9
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dulus
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.8.0
|
|
4
4
|
Summary: Use IA as an agent without an API key. $0. Harvest your browser session for Gemini (guest, no login), Claude.ai, Claude Code, Kimi.com, Qwen, DeepSeek — drive them like an API, no per-token billing. Plus LiteLLM (100+ paid providers), local models via Ollama, /lang in 34 languages, Mesa Redonda, voice, OCR, MemPalace, embedded sandbox OS.
|
|
5
5
|
Author: KevRojo
|
|
6
6
|
License: GPL-3.0
|
|
@@ -305,7 +305,8 @@ Dulus is proudly supported by these startup programs — powering our infrastruc
|
|
|
305
305
|
<a href="https://amplitude.com/startups"><img src="https://www.google.com/s2/favicons?domain=amplitude.com&sz=128" height="38" alt="Amplitude" title="Amplitude Startup Scholarship"></a>
|
|
306
306
|
<a href="https://www.digitalocean.com/hatch"><img src="https://cdn.simpleicons.org/digitalocean" height="38" alt="DigitalOcean" title="DigitalOcean Hatch"></a>
|
|
307
307
|
<a href="https://www.notion.com/startups"><img src="https://cdn.simpleicons.org/notion" height="38" alt="Notion" title="Notion for Startups"></a>
|
|
308
|
-
<a href="https://www.zendesk.com/campaign/startups/"><img src="https://cdn.simpleicons.org/zendesk" height="38" alt="Zendesk" title="Zendesk for Startups"></a
|
|
308
|
+
<a href="https://www.zendesk.com/campaign/startups/"><img src="https://cdn.simpleicons.org/zendesk" height="38" alt="Zendesk" title="Zendesk for Startups"></a>
|
|
309
|
+
<a href="https://deepgram.com/startups"><img src="https://www.google.com/s2/favicons?domain=deepgram.com&sz=128" height="38" alt="Deepgram" title="Deepgram Startup Program — powering Dulus's voice"></a>
|
|
309
310
|
</p>
|
|
310
311
|
|
|
311
312
|
<p align="center"><i>Want to support Dulus? → <a href="https://github.com/sponsors/KevRojo">GitHub Sponsors</a> 🦅</i></p>
|
|
@@ -130,12 +130,12 @@ dulus_tools/background_tasks.py
|
|
|
130
130
|
dulus_tools/clipboard_utils.py
|
|
131
131
|
dulus_tools/diff_visualizer.py
|
|
132
132
|
dulus_tools/export_import.py
|
|
133
|
-
dulus_tools/fs_compass.py
|
|
134
133
|
dulus_tools/hook_engine.py
|
|
135
134
|
dulus_tools/notification_manager.py
|
|
136
135
|
dulus_tools/session_fork.py
|
|
137
136
|
dulus_tools/shell_mode.py
|
|
138
137
|
dulus_tools/todo_visualizer.py
|
|
138
|
+
dulus_tools/tree_ls.py
|
|
139
139
|
dulus_tools/wire_events.py
|
|
140
140
|
dulus_tools/yolo_mode.py
|
|
141
141
|
gui/__init__.py
|
|
@@ -294,7 +294,6 @@ tests/test_diff_view.py
|
|
|
294
294
|
tests/test_diff_visualization.py
|
|
295
295
|
tests/test_display_blocks.py
|
|
296
296
|
tests/test_export_import.py
|
|
297
|
-
tests/test_fs_compass.py
|
|
298
297
|
tests/test_hook_engine.py
|
|
299
298
|
tests/test_injection_fix.py
|
|
300
299
|
tests/test_license.py
|
|
@@ -313,6 +312,7 @@ tests/test_think_tool.py
|
|
|
313
312
|
tests/test_todo_tool.py
|
|
314
313
|
tests/test_todo_visualization.py
|
|
315
314
|
tests/test_tool_registry.py
|
|
315
|
+
tests/test_tree_ls.py
|
|
316
316
|
tests/test_voice.py
|
|
317
317
|
tests/test_wire_events.py
|
|
318
318
|
ui/__init__.py
|
|
@@ -2743,7 +2743,7 @@ def cmd_harvest(_args: str, _state, config) -> bool:
|
|
|
2743
2743
|
except ImportError:
|
|
2744
2744
|
import os
|
|
2745
2745
|
info("Installing playwright...")
|
|
2746
|
-
|
|
2746
|
+
__import__("subprocess").run(__import__("common").pip_install_cmd("playwright"))
|
|
2747
2747
|
os.system("playwright install chromium")
|
|
2748
2748
|
from playwright.sync_api import sync_playwright
|
|
2749
2749
|
|
|
@@ -2888,7 +2888,7 @@ def cmd_harvest_kimi(_args: str, _state, config) -> bool:
|
|
|
2888
2888
|
from playwright.sync_api import sync_playwright
|
|
2889
2889
|
except ImportError:
|
|
2890
2890
|
info("Installing playwright...")
|
|
2891
|
-
|
|
2891
|
+
__import__("subprocess").run(__import__("common").pip_install_cmd("playwright"))
|
|
2892
2892
|
os.system("playwright install chromium")
|
|
2893
2893
|
from playwright.sync_api import sync_playwright
|
|
2894
2894
|
|
|
@@ -2999,7 +2999,7 @@ def cmd_harvest_gemini(_args: str, _state, config) -> bool:
|
|
|
2999
2999
|
from playwright.sync_api import sync_playwright
|
|
3000
3000
|
except ImportError:
|
|
3001
3001
|
info("Installing playwright...")
|
|
3002
|
-
|
|
3002
|
+
__import__("subprocess").run(__import__("common").pip_install_cmd("playwright"))
|
|
3003
3003
|
os.system("playwright install chromium")
|
|
3004
3004
|
from playwright.sync_api import sync_playwright
|
|
3005
3005
|
|
|
@@ -3169,7 +3169,7 @@ def cmd_harvest_deepseek(_args: str, _state, config) -> bool:
|
|
|
3169
3169
|
from playwright.sync_api import sync_playwright
|
|
3170
3170
|
except ImportError:
|
|
3171
3171
|
info("Installing playwright...")
|
|
3172
|
-
|
|
3172
|
+
__import__("subprocess").run(__import__("common").pip_install_cmd("playwright"))
|
|
3173
3173
|
os.system("playwright install chromium")
|
|
3174
3174
|
from playwright.sync_api import sync_playwright
|
|
3175
3175
|
|
|
@@ -3317,7 +3317,7 @@ def cmd_harvest_qwen(_args: str, _state, config) -> bool:
|
|
|
3317
3317
|
from playwright.sync_api import sync_playwright
|
|
3318
3318
|
except ImportError:
|
|
3319
3319
|
info("Installing playwright...")
|
|
3320
|
-
|
|
3320
|
+
__import__("subprocess").run(__import__("common").pip_install_cmd("playwright"))
|
|
3321
3321
|
os.system("playwright install chromium")
|
|
3322
3322
|
from playwright.sync_api import sync_playwright
|
|
3323
3323
|
|
|
@@ -6991,7 +6991,7 @@ def cmd_tts(args: str, state, config) -> bool:
|
|
|
6991
6991
|
|
|
6992
6992
|
if parts and parts[0].lower() == "provider":
|
|
6993
6993
|
name = parts[1].strip().lower() if len(parts) > 1 else ""
|
|
6994
|
-
valid = ("auto", "azure", "riva", "openai", "gtts", "pyttsx3", "edge", "elevenlabs")
|
|
6994
|
+
valid = ("auto", "azure", "riva", "openai", "gtts", "pyttsx3", "edge", "elevenlabs", "deepgram")
|
|
6995
6995
|
if not name:
|
|
6996
6996
|
current = config.get("tts_provider", "auto")
|
|
6997
6997
|
info(f"TTS provider: {current}")
|