dulus 3.9.0__tar.gz → 3.9.2__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.9.0/dulus.egg-info → dulus-3.9.2}/PKG-INFO +3 -1
- {dulus-3.9.0 → dulus-3.9.2}/README.md +2 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/news.md +2 -0
- {dulus-3.9.0 → dulus-3.9.2/dulus.egg-info}/PKG-INFO +3 -1
- {dulus-3.9.0 → dulus-3.9.2}/dulus.egg-info/SOURCES.txt +1 -0
- {dulus-3.9.0 → dulus-3.9.2}/dulus.egg-info/top_level.txt +1 -0
- {dulus-3.9.0 → dulus-3.9.2}/dulus.py +132 -2
- {dulus-3.9.0 → dulus-3.9.2}/pyproject.toml +2 -1
- dulus-3.9.2/updater.py +178 -0
- {dulus-3.9.0 → dulus-3.9.2}/LICENSE +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/MANIFEST.in +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/agent.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/analytics.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/backend/__init__.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/backend/agents_bridge.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/backend/compressor.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/backend/context.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/backend/githook.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/backend/marketplace.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/backend/mempalace_bridge.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/backend/personas.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/backend/plugins.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/backend/server.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/backend/tasks.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/batch_api.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/checkpoint/__init__.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/checkpoint/hooks.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/checkpoint/store.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/checkpoint/types.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/claude_code_watcher.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/clipboard_utils.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/cloudsave.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/common.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/compaction.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/config.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/context.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/data/__init__.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/data/active_persona.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/data/marketplace.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/data/personas.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/data/plugins/__init__.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/data/plugins/composio/__init__.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/data/plugins/composio/composio_plugin/__init__.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/data/plugins/composio/composio_plugin/session_manager.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/data/plugins/composio/composio_plugin/tool_generator.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/data/plugins/composio/plugin.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/data/plugins/composio/plugin_tool.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/data/tasks.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/API.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/BRAND_GUIDE.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/CHANGELOG.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/CONTRIBUTING.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/DEPLOYMENT.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/Dulus_AI_Whitepaper_(v2.0).pdf +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/FAQ.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/GETTING_STARTED.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/INVESTOR_BRIEF.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/README.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/README_AR.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/README_EN.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/README_ES.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/README_FR.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/README_JA.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/README_KO.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/README_PT.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/README_RU.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/README_ZH.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/SECURITY.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/__init__.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/api.html +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/architecture.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/azure-speech-template.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/dashboard/index.html +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/divider.svg +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/dulus-bird.png +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/generate.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/hero.svg +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/index.html +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/nvidia-models.svg +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/particle-playground.html +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/personas/index.html +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/poetry-banner.png +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/preview.html +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/sec-agents.svg +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/sec-brainstorm.svg +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/sec-bridges.svg +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/sec-features.svg +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/sec-freetier.svg +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/sec-memory.svg +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/sec-models.svg +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/sec-perms.svg +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/sec-plugins.svg +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/sec-quickstart.svg +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/sec-ssj.svg +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/spinners.svg +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/split-pane.svg +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/docs/terminal-boot.svg +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/dulus.egg-info/dependency_links.txt +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/dulus.egg-info/entry_points.txt +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/dulus.egg-info/requires.txt +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/dulus_gui.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/dulus_gui_web.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/dulus_mcp/__init__.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/dulus_mcp/client.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/dulus_mcp/commands.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/dulus_mcp/config.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/dulus_mcp/hub.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/dulus_mcp/tools.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/dulus_mcp/types.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/dulus_tools/__init__.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/dulus_tools/add_dir_manager.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/dulus_tools/afk_mode.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/dulus_tools/approval_runtime.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/dulus_tools/background_tasks.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/dulus_tools/clipboard_utils.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/dulus_tools/diff_visualizer.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/dulus_tools/export_import.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/dulus_tools/hook_engine.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/dulus_tools/notification_manager.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/dulus_tools/session_fork.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/dulus_tools/shell_mode.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/dulus_tools/todo_visualizer.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/dulus_tools/tree_ls.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/dulus_tools/wire_events.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/dulus_tools/yolo_mode.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/governance.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/gui/__init__.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/gui/agent_bridge.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/gui/chat_widget.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/gui/dulus_orchestrator/App.tsx +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/gui/dulus_orchestrator/THIRD_PARTY_NOTICES.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/gui/dulus_orchestrator/dist/index.html +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/gui/dulus_orchestrator/dist/standalone.html +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/gui/dulus_orchestrator/index.html +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/gui/dulus_orchestrator/index.tsx +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/gui/dulus_orchestrator/metadata.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/gui/dulus_orchestrator/package-lock.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/gui/dulus_orchestrator/package.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/gui/dulus_orchestrator/types.ts +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/gui/dulus_orchestrator/utils.ts +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/gui/dulus_orchestrator/vertex-ai-proxy-interceptor.js +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/gui/dulus_orchestrator/vite.config.ts +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/gui/main_window.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/gui/personas.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/gui/sandbox_server.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/gui/session_utils.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/gui/settings_dialog.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/gui/sidebar.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/gui/tasks_view.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/gui/themes.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/gui/tool_panel.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/input.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/license_manager.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/memory/__init__.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/memory/audit.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/memory/consolidator.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/memory/context.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/memory/offload.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/memory/palace.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/memory/scan.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/memory/sessions.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/memory/store.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/memory/tools.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/memory/types.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/memory/vector_search.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/multi_agent/__init__.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/multi_agent/subagent.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/multi_agent/tools.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/offload_helper.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/personality_config.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/plugin/__init__.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/plugin/autoadapter.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/plugin/loader.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/plugin/recommend.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/plugin/store.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/plugin/types.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/providers.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/sandbox/README.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/sandbox/__init__.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/sandbox/components.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/sandbox/dist/assets/index-DE51D6wI.css +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/sandbox/dist/assets/index-DMCCNE9Y.js +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/sandbox/dist/index.html +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/sandbox/dist/wallpaper-default.jpg +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/sandbox/dist/wallpapers/default.jpeg +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/sandbox/dist/wallpapers/light.jpeg +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/sandbox/dist/wallpapers/nature.jpeg +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/sandbox/dist/wallpapers/tech.jpeg +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/sandbox/eslint.config.js +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/sandbox/index.html +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/sandbox/info.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/sandbox/package-lock.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/sandbox/package.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/sandbox/postcss.config.js +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/sandbox/tailwind.config.js +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/sandbox/tsconfig.app.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/sandbox/tsconfig.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/sandbox/tsconfig.node.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/sandbox/vite.config.ts +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/sandbox_bootstrap.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/setup.cfg +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/__init__.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/builtin.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/LICENSE-obsidian-skills +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/README.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/defuddle/SKILL.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/json-canvas/SKILL.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/json-canvas/references/EXAMPLES.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/obsidian-bases/SKILL.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/obsidian-bases/references/FUNCTIONS_REFERENCE.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/obsidian-cli/SKILL.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/obsidian-markdown/SKILL.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/obsidian-markdown/references/CALLOUTS.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/obsidian-markdown/references/EMBEDS.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/obsidian-markdown/references/PROPERTIES.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/README.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/SKILL.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/references/brokers/addresses.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/references/brokers/advancedbackgroundchecks.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/references/brokers/beenverified.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/references/brokers/clustal.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/references/brokers/clustrmaps.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/references/brokers/cyberbackgroundchecks.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/references/brokers/familytreenow.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/references/brokers/fastpeoplesearch.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/references/brokers/intelius.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/references/brokers/mylife.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/references/brokers/nuwber.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/references/brokers/peekyou.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/references/brokers/peoplefinders.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/references/brokers/radaris.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/references/brokers/rehold.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/references/brokers/searchpeoplefree.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/references/brokers/socialcatfish.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/references/brokers/spokeo.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/references/brokers/thatsthem.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/references/brokers/truepeoplesearch.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/references/brokers/usphonebook.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/references/brokers/whitepages.json +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/references/legal/ccpa.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/references/legal/drop.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/references/legal/gdpr.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/references/methods.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/references/site-playbooks.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/references/state-machine.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/scripts/autopilot.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/scripts/badbool.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/scripts/brokers.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/scripts/cdp.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/scripts/config.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/scripts/crypto.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/scripts/dossier.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/scripts/email_modes.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/scripts/emailer.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/scripts/ledger.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/scripts/legal.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/scripts/paths.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/scripts/pdd.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/scripts/registry.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/scripts/report.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/scripts/scan.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/scripts/storage.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/scripts/tiers.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/scripts/vectors.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/templates/consent/authorization.md +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/templates/emails/ccpa-authorized-agent.txt +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/templates/emails/ccpa-deletion.txt +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/templates/emails/ccpa-indirect-deletion.txt +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/templates/emails/gdpr-erasure.txt +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/bundled/unbroker/templates/emails/generic-optout.txt +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/clawhub.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/executor.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/loader.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skill/tools.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/skills.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/soul.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/spinner.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/string_utils.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/subagent.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/task/__init__.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/task/store.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/task/tools.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/task/types.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tests/test_afk_yolo.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tests/test_approval_runtime.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tests/test_background_task_tools.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tests/test_background_tasks.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tests/test_checkpoint.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tests/test_clipboard_utils.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tests/test_compaction.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tests/test_diff_view.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tests/test_diff_visualization.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tests/test_display_blocks.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tests/test_export_import.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tests/test_hook_engine.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tests/test_injection_fix.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tests/test_license.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tests/test_mcp.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tests/test_memory.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tests/test_notification_manager.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tests/test_plugin.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tests/test_session_fork.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tests/test_shell_mode.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tests/test_skills.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tests/test_steer_input.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tests/test_subagent.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tests/test_task.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tests/test_telegram_buffer.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tests/test_think_tool.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tests/test_todo_tool.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tests/test_todo_visualization.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tests/test_tool_registry.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tests/test_tree_ls.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tests/test_voice.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tests/test_wire_events.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tmux_offloader.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tmux_tools.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tool_registry.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/tools.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/ui/__init__.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/ui/input.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/ui/render.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/voice/__init__.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/voice/audio_utils.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/voice/keyterms.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/voice/recorder.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/voice/stt.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/voice/tts.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/voice/wake_word.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/webbridge/__init__.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/webbridge/core.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/webbridge/tools.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/webchat.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/webchat_server.py +0 -0
- {dulus-3.9.0 → dulus-3.9.2}/welcome.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dulus
|
|
3
|
-
Version: 3.9.
|
|
3
|
+
Version: 3.9.2
|
|
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
|
|
@@ -134,6 +134,8 @@ SET /sticky_input ON since the first run for the best experience!
|
|
|
134
134
|
|
|
135
135
|
> Full changelog: [`docs/news.md`](docs/news.md) · Inside the REPL: `/news`
|
|
136
136
|
|
|
137
|
+
- **🔄 Self-update — the fleet moves as one.** Dulus now keeps itself current: a quiet, cached, non-blocking check against PyPI at startup, and if there's a newer release it upgrades in place. `/update`, `/update now`, `/update status`, `/update on|off`. On by default (the organism heals fastest when every node runs the latest), handles PEP 668, and never blocks your boot. 🦅
|
|
138
|
+
|
|
137
139
|
- **🔌 MCP Marketplace — 2000+ servers, zero friction.** MCP was always powerful but nobody used it — too much friction: find a server, dig up its launch command, hand-edit JSON, hope it connects. Now: `/mcp search <anything>` browses **2000+ servers** (official [modelcontextprotocol.io](https://registry.modelcontextprotocol.io) registry + the awesome-mcp list, cached & offline-safe), and `/mcp install <name>` installs *and auto-connects* in one shot — tools go live in the same session. Plus a Windows fix so node servers (`npx`) actually launch. From "never touched MCP" to "connected in 5 seconds." 🦅
|
|
138
140
|
|
|
139
141
|
- **🌍 dulus.work — THE HUB. The secret is out.** This is what Kevin had been hiding (even from Dulus itself 😂): **a global hub where every Dulus instance on the planet shares what it learns.** Your Dulus finds a fix, polishes a skill, adapts a plugin → it can publish it to the hub → every other Dulus in the world can pull it down. Not an update server — an **organism**. Every installation is a node. *The fix a Dulus in Tokyo finds at 3AM, the one in Santo Domingo has by sunrise.* Other agents have N devs improving one product (linear). Dulus makes every user a dev (exponential). The pieces have been quietly shipping for months — SelfImprove engine, Auto-Adapter, the skills format, cloudsave — and now they converge. Domain is live, execution underway. 🦅🇩🇴 → [dulus.work](https://dulus.work)
|
|
@@ -68,6 +68,8 @@ SET /sticky_input ON since the first run for the best experience!
|
|
|
68
68
|
|
|
69
69
|
> Full changelog: [`docs/news.md`](docs/news.md) · Inside the REPL: `/news`
|
|
70
70
|
|
|
71
|
+
- **🔄 Self-update — the fleet moves as one.** Dulus now keeps itself current: a quiet, cached, non-blocking check against PyPI at startup, and if there's a newer release it upgrades in place. `/update`, `/update now`, `/update status`, `/update on|off`. On by default (the organism heals fastest when every node runs the latest), handles PEP 668, and never blocks your boot. 🦅
|
|
72
|
+
|
|
71
73
|
- **🔌 MCP Marketplace — 2000+ servers, zero friction.** MCP was always powerful but nobody used it — too much friction: find a server, dig up its launch command, hand-edit JSON, hope it connects. Now: `/mcp search <anything>` browses **2000+ servers** (official [modelcontextprotocol.io](https://registry.modelcontextprotocol.io) registry + the awesome-mcp list, cached & offline-safe), and `/mcp install <name>` installs *and auto-connects* in one shot — tools go live in the same session. Plus a Windows fix so node servers (`npx`) actually launch. From "never touched MCP" to "connected in 5 seconds." 🦅
|
|
72
74
|
|
|
73
75
|
- **🌍 dulus.work — THE HUB. The secret is out.** This is what Kevin had been hiding (even from Dulus itself 😂): **a global hub where every Dulus instance on the planet shares what it learns.** Your Dulus finds a fix, polishes a skill, adapts a plugin → it can publish it to the hub → every other Dulus in the world can pull it down. Not an update server — an **organism**. Every installation is a node. *The fix a Dulus in Tokyo finds at 3AM, the one in Santo Domingo has by sunrise.* Other agents have N devs improving one product (linear). Dulus makes every user a dev (exponential). The pieces have been quietly shipping for months — SelfImprove engine, Auto-Adapter, the skills format, cloudsave — and now they converge. Domain is live, execution underway. 🦅🇩🇴 → [dulus.work](https://dulus.work)
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
## 🔥🔥🔥 News (Pacific Time)
|
|
4
4
|
|
|
5
|
+
- July 11, 2026: **🔄 Self-update — every Dulus stays on the latest, automatically.** The whole point of the organism is that a fix found by one Dulus reaches every other Dulus fast. So now Dulus keeps *itself* current: on startup it does a quiet, cached (6h TTL), non-blocking check against PyPI, and if there's a newer release it upgrades in place — no `pip install -U` dance, no stale nodes drifting behind. New `/update` command with everything you'd expect: `/update` (check + upgrade if newer), `/update now` (force), `/update check` (look but don't touch), `/update status` (see installed vs latest), and `/update on|off` to toggle the automatic startup check (on by default). Handles PEP 668 externally-managed Pythons automatically, and the check *never* blocks or crashes your boot — if PyPI is down or you're offline, Dulus just carries on. The fleet moves as one. 🦅🇩🇴
|
|
6
|
+
|
|
5
7
|
- July 11, 2026: **🔌 MCP Marketplace — 2000+ servers, zero friction, install by name.** MCP (Model Context Protocol) was always one of Dulus's most powerful features and almost nobody used it — the friction killed it. You had to *find* a server somewhere, dig up its exact launch command, hand-edit a `.mcp.json`, and pray it connected. Dead on arrival. So we built a real marketplace on top of it. `/mcp list` and `/mcp search <anything>` now browse a live catalog of **2000+ servers** pulled from the official [registry.modelcontextprotocol.io](https://registry.modelcontextprotocol.io) (the metaregistry backed by Anthropic, GitHub, Microsoft & PulseMCP) plus the community awesome-mcp list — deduped, cached with a 6h TTL, and **offline-safe** (falls back to a curated set if you're on a plane). Then `/mcp install <name>` does the whole thing in one shot: resolves the launch command, writes the config, **connects, verifies the tools, and hot-reloads them live into your current session** — no restart, no JSON, no manual anything. `/mcp installed` shows what's live with a green/red status dot, `/mcp runtimes` tells you which runtimes (node/python/docker) you've got. Also shipped a Windows launcher fix so node-based servers (`npx`, which ships as `npx.cmd` on Windows) actually start instead of silently failing — probably the real reason MCP "never worked" for a lot of Windows users. From *"I've never touched MCP"* to *"connected in 5 seconds."* 🦅🇩🇴
|
|
6
8
|
|
|
7
9
|
- 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. 🦅🇩🇴
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dulus
|
|
3
|
-
Version: 3.9.
|
|
3
|
+
Version: 3.9.2
|
|
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
|
|
@@ -134,6 +134,8 @@ SET /sticky_input ON since the first run for the best experience!
|
|
|
134
134
|
|
|
135
135
|
> Full changelog: [`docs/news.md`](docs/news.md) · Inside the REPL: `/news`
|
|
136
136
|
|
|
137
|
+
- **🔄 Self-update — the fleet moves as one.** Dulus now keeps itself current: a quiet, cached, non-blocking check against PyPI at startup, and if there's a newer release it upgrades in place. `/update`, `/update now`, `/update status`, `/update on|off`. On by default (the organism heals fastest when every node runs the latest), handles PEP 668, and never blocks your boot. 🦅
|
|
138
|
+
|
|
137
139
|
- **🔌 MCP Marketplace — 2000+ servers, zero friction.** MCP was always powerful but nobody used it — too much friction: find a server, dig up its launch command, hand-edit JSON, hope it connects. Now: `/mcp search <anything>` browses **2000+ servers** (official [modelcontextprotocol.io](https://registry.modelcontextprotocol.io) registry + the awesome-mcp list, cached & offline-safe), and `/mcp install <name>` installs *and auto-connects* in one shot — tools go live in the same session. Plus a Windows fix so node servers (`npx`) actually launch. From "never touched MCP" to "connected in 5 seconds." 🦅
|
|
138
140
|
|
|
139
141
|
- **🌍 dulus.work — THE HUB. The secret is out.** This is what Kevin had been hiding (even from Dulus itself 😂): **a global hub where every Dulus instance on the planet shares what it learns.** Your Dulus finds a fix, polishes a skill, adapts a plugin → it can publish it to the hub → every other Dulus in the world can pull it down. Not an update server — an **organism**. Every installation is a node. *The fix a Dulus in Tokyo finds at 3AM, the one in Santo Domingo has by sunrise.* Other agents have N devs improving one product (linear). Dulus makes every user a dev (exponential). The pieces have been quietly shipping for months — SelfImprove engine, Auto-Adapter, the skills format, cloudsave — and now they converge. Domain is live, execution underway. 🦅🇩🇴 → [dulus.work](https://dulus.work)
|
|
@@ -71,6 +71,11 @@ Slash commands in REPL:
|
|
|
71
71
|
/skill use <name> Inject skill into next message /skill remove <name> Uninstall
|
|
72
72
|
/agents Show sub-agent tasks
|
|
73
73
|
/mcp List MCP servers and their tools
|
|
74
|
+
/mcp list [query] Browse the catalog of 2000+ MCP servers
|
|
75
|
+
/mcp search <q> Search every source for matching servers
|
|
76
|
+
/mcp install <n> Install a server by name (auto-connects)
|
|
77
|
+
/mcp installed Show installed servers + live status
|
|
78
|
+
/mcp runtimes Show available runtimes (node/python/docker)
|
|
74
79
|
/mcp reload Reconnect all MCP servers
|
|
75
80
|
/mcp add <n> <cmd> [args] Add a stdio MCP server
|
|
76
81
|
/mcp remove <n> Remove an MCP server from config
|
|
@@ -82,6 +87,10 @@ Slash commands in REPL:
|
|
|
82
87
|
/plugin enable/disable name Toggle plugin
|
|
83
88
|
/plugin update name Update a plugin
|
|
84
89
|
/plugin recommend [ctx] Recommend plugins for context
|
|
90
|
+
/update Check PyPI and update Dulus if a newer version exists
|
|
91
|
+
/update now Force update to the latest release
|
|
92
|
+
/update on|off Toggle the automatic update check at startup (default: on)
|
|
93
|
+
/update status Show installed version, latest, and auto-check setting
|
|
85
94
|
/tasks List all tasks
|
|
86
95
|
/tasks create <subject> Quick-create a task
|
|
87
96
|
/tasks start/done/cancel <id> Update task status
|
|
@@ -834,6 +843,7 @@ _HELP_PAGES = [
|
|
|
834
843
|
("/cwd [path]", "Show or change working directory"),
|
|
835
844
|
("/copy [file]", "Copy last response (or file) to clipboard"),
|
|
836
845
|
("/shell [cmd|on|off]", "Shell mode toggle or one-shot command"),
|
|
846
|
+
("/update [now|on|off]", "Self-update Dulus from PyPI (auto-check at startup)"),
|
|
837
847
|
("/exit /quit", "Exit Dulus"),
|
|
838
848
|
]),
|
|
839
849
|
("Session", [
|
|
@@ -873,7 +883,10 @@ _HELP_PAGES = [
|
|
|
873
883
|
("/plugin recommend [ctx]", "Recommend plugins for a context"),
|
|
874
884
|
("/agents", "Show sub-agent tasks"),
|
|
875
885
|
("/mcp", "List MCP servers and tools"),
|
|
876
|
-
("/mcp
|
|
886
|
+
("/mcp list | search <q>", "Browse/search 2000+ MCP servers"),
|
|
887
|
+
("/mcp install <name>", "Install a server by name (auto-connects)"),
|
|
888
|
+
("/mcp installed | runtimes","Show installed servers / available runtimes"),
|
|
889
|
+
("/mcp reload | add | remove","Manage MCP servers manually"),
|
|
877
890
|
("/tasks", "List/create/update tasks"),
|
|
878
891
|
]),
|
|
879
892
|
("Voice · Wake", [
|
|
@@ -8486,6 +8499,89 @@ def cmd_profile(args: str, state, config) -> bool:
|
|
|
8486
8499
|
return True
|
|
8487
8500
|
|
|
8488
8501
|
|
|
8502
|
+
def cmd_update(args: str, state, config) -> bool:
|
|
8503
|
+
"""Self-update Dulus from PyPI — keep every node on the latest release.
|
|
8504
|
+
|
|
8505
|
+
/update — check PyPI and update now if a newer version exists
|
|
8506
|
+
/update now — force an update to the latest release
|
|
8507
|
+
/update check — just check, don't install
|
|
8508
|
+
/update on — enable the automatic startup check (default)
|
|
8509
|
+
/update off — disable the automatic startup check
|
|
8510
|
+
/update status — show current version, latest, and the auto-check setting
|
|
8511
|
+
"""
|
|
8512
|
+
try:
|
|
8513
|
+
import updater
|
|
8514
|
+
except Exception as e:
|
|
8515
|
+
err(f"Updater unavailable: {e}")
|
|
8516
|
+
return True
|
|
8517
|
+
|
|
8518
|
+
sub = (args or "").strip().lower()
|
|
8519
|
+
|
|
8520
|
+
# Toggle the automatic startup check
|
|
8521
|
+
if sub in ("on", "enable"):
|
|
8522
|
+
config["auto_update"] = True
|
|
8523
|
+
try:
|
|
8524
|
+
from config import save_config
|
|
8525
|
+
save_config(config)
|
|
8526
|
+
except Exception:
|
|
8527
|
+
pass
|
|
8528
|
+
ok("Automatic update check enabled — Dulus will check PyPI at startup.")
|
|
8529
|
+
return True
|
|
8530
|
+
if sub in ("off", "disable"):
|
|
8531
|
+
config["auto_update"] = False
|
|
8532
|
+
try:
|
|
8533
|
+
from config import save_config
|
|
8534
|
+
save_config(config)
|
|
8535
|
+
except Exception:
|
|
8536
|
+
pass
|
|
8537
|
+
info("Automatic update check disabled. Run /update anytime to check manually.")
|
|
8538
|
+
return True
|
|
8539
|
+
|
|
8540
|
+
if sub == "status":
|
|
8541
|
+
current = updater.get_installed_version()
|
|
8542
|
+
latest = updater.get_latest_version() or "(unreachable)"
|
|
8543
|
+
auto = config.get("auto_update", True)
|
|
8544
|
+
info(f"Installed: {current}")
|
|
8545
|
+
info(f"Latest on PyPI: {latest}")
|
|
8546
|
+
info(f"Auto-check at startup: {'ON' if auto else 'OFF'}")
|
|
8547
|
+
return True
|
|
8548
|
+
|
|
8549
|
+
if sub == "check":
|
|
8550
|
+
available, current, latest = updater.is_update_available()
|
|
8551
|
+
if not latest:
|
|
8552
|
+
info(f"Couldn't reach PyPI. Installed: {current}")
|
|
8553
|
+
elif available:
|
|
8554
|
+
ok(f"Update available: {current} -> {latest}. Run /update now")
|
|
8555
|
+
else:
|
|
8556
|
+
ok(f"You're on the latest Dulus ({current}). 🦅")
|
|
8557
|
+
return True
|
|
8558
|
+
|
|
8559
|
+
# Default (no arg) and "now" → check + install if newer
|
|
8560
|
+
available, current, latest = updater.is_update_available()
|
|
8561
|
+
if sub != "now":
|
|
8562
|
+
if not latest:
|
|
8563
|
+
info(f"Couldn't reach PyPI. Installed: {current}")
|
|
8564
|
+
return True
|
|
8565
|
+
if not available:
|
|
8566
|
+
ok(f"You're already on the latest Dulus ({current}). 🦅")
|
|
8567
|
+
return True
|
|
8568
|
+
info(f"Updating {current} -> {latest} ...")
|
|
8569
|
+
|
|
8570
|
+
# Perform the upgrade (force for "now", or when an update is available)
|
|
8571
|
+
target = latest if latest else None
|
|
8572
|
+
if sub == "now" and not available and latest and current == latest:
|
|
8573
|
+
ok(f"Already on {current}. Nothing to do.")
|
|
8574
|
+
return True
|
|
8575
|
+
print(" ⏳ Running pip install --upgrade dulus ...")
|
|
8576
|
+
success, message = updater.perform_update(target if available else None)
|
|
8577
|
+
if success:
|
|
8578
|
+
ok(message)
|
|
8579
|
+
info("Restart Dulus to run the new version.")
|
|
8580
|
+
else:
|
|
8581
|
+
err(message)
|
|
8582
|
+
return True
|
|
8583
|
+
|
|
8584
|
+
|
|
8489
8585
|
def cmd_news(args: str, state, config) -> bool:
|
|
8490
8586
|
"""Show the latest news from docs/news.md."""
|
|
8491
8587
|
news_file = Path(__file__).parent / "docs" / "news.md"
|
|
@@ -9784,6 +9880,9 @@ COMMANDS = {
|
|
|
9784
9880
|
"exit": cmd_exit,
|
|
9785
9881
|
"quit": cmd_exit,
|
|
9786
9882
|
"resume": cmd_resume,
|
|
9883
|
+
"update": cmd_update,
|
|
9884
|
+
"upgrade": cmd_update,
|
|
9885
|
+
"selfupdate": cmd_update,
|
|
9787
9886
|
"news": cmd_news,
|
|
9788
9887
|
"batch": cmd_batch,
|
|
9789
9888
|
"claude_batch": cmd_claude_batch,
|
|
@@ -9862,7 +9961,9 @@ _CMD_META: dict[str, tuple[str, list[str]]] = {
|
|
|
9862
9961
|
"profiles": ("Manage agent Profiles (alias)", ["list", "show", "create", "switch", "delete", "inherit"]),
|
|
9863
9962
|
"memory": ("Manage persistent memories", ["list", "load", "permanent", "unbind", "consolidate", "delete", "purge", "purge-soul"]),
|
|
9864
9963
|
"agents": ("Show background agents", []),
|
|
9865
|
-
"mcp": ("
|
|
9964
|
+
"mcp": ("Browse & install MCP servers", ["list", "search", "install",
|
|
9965
|
+
"installed", "runtimes", "reload",
|
|
9966
|
+
"add", "remove"]),
|
|
9866
9967
|
"plugin": ("Manage plugins", ["install", "uninstall", "enable",
|
|
9867
9968
|
"disable", "disable-all", "update",
|
|
9868
9969
|
"recommend", "info"]),
|
|
@@ -9912,6 +10013,7 @@ _CMD_META: dict[str, tuple[str, list[str]]] = {
|
|
|
9912
10013
|
"exit": ("Exit dulus", []),
|
|
9913
10014
|
"quit": ("Exit (alias for /exit)", []),
|
|
9914
10015
|
"resume": ("Resume last session", []),
|
|
10016
|
+
"update": ("Self-update Dulus from PyPI", ["now", "check", "status", "on", "off"]),
|
|
9915
10017
|
"news": ("Show latest project news", []),
|
|
9916
10018
|
"claude_chats": ("List Claude.ai conversations", ["all"]),
|
|
9917
10019
|
"gemini_chats": ("Manage Gemini Web conversations", ["new"]),
|
|
@@ -10352,6 +10454,34 @@ def repl(config: dict, initial_prompt: str = None):
|
|
|
10352
10454
|
except Exception:
|
|
10353
10455
|
pass
|
|
10354
10456
|
|
|
10457
|
+
# ── Auto-update check (on by default; /update off to disable) ─────────
|
|
10458
|
+
# Keep every Dulus in the world on the latest release: the organism
|
|
10459
|
+
# heals fastest when fixes propagate instantly. This is intentionally
|
|
10460
|
+
# quiet & fast — cached PyPI check (6h TTL), short timeout, never blocks
|
|
10461
|
+
# or crashes the boot. Set auto_update=False (or /update off) to skip.
|
|
10462
|
+
if config.get("auto_update", True):
|
|
10463
|
+
try:
|
|
10464
|
+
import updater as _updater
|
|
10465
|
+
_avail, _cur, _latest = _updater.is_update_available()
|
|
10466
|
+
if _avail:
|
|
10467
|
+
print()
|
|
10468
|
+
info(f" 🆕 A new Dulus is available: {_cur} → {_latest}")
|
|
10469
|
+
if config.get("auto_update_install", True):
|
|
10470
|
+
# Kevin's call: force the update at startup so we keep
|
|
10471
|
+
# the whole fleet in sync. Set auto_update_install=False
|
|
10472
|
+
# to only notify instead of installing.
|
|
10473
|
+
print(" ⏳ Updating automatically...")
|
|
10474
|
+
_ok, _msg = _updater.perform_update(_latest)
|
|
10475
|
+
if _ok:
|
|
10476
|
+
ok(f" {_msg} Restart to run {_latest}.")
|
|
10477
|
+
else:
|
|
10478
|
+
info(f" Auto-update skipped: {_msg}")
|
|
10479
|
+
info(" Run /update now to try manually.")
|
|
10480
|
+
else:
|
|
10481
|
+
info(" Run /update now to upgrade.")
|
|
10482
|
+
except Exception:
|
|
10483
|
+
pass # never let the update check break the boot
|
|
10484
|
+
|
|
10355
10485
|
# First-run /harvest — wizard sets `pending_first_run_harvest` to
|
|
10356
10486
|
# the provider name the user picked (claude / kimi / gemini / qwen /
|
|
10357
10487
|
# deepseek). We auto-fire the matching command so they actually
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "dulus"
|
|
7
|
-
version = "3.9.
|
|
7
|
+
version = "3.9.2"
|
|
8
8
|
description = "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."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -127,6 +127,7 @@ py-modules = [
|
|
|
127
127
|
"tmux_tools",
|
|
128
128
|
"tool_registry",
|
|
129
129
|
"tools",
|
|
130
|
+
"updater",
|
|
130
131
|
"webchat",
|
|
131
132
|
"webchat_server",
|
|
132
133
|
"welcome",
|
dulus-3.9.2/updater.py
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"""Dulus self-update — keep every Dulus in the world on the latest release.
|
|
2
|
+
|
|
3
|
+
The organism heals fastest when every node runs the newest code. This module
|
|
4
|
+
checks PyPI for a newer `dulus` release and (optionally) upgrades in place.
|
|
5
|
+
|
|
6
|
+
Design goals:
|
|
7
|
+
- Zero friction: on by default, checks quietly at startup.
|
|
8
|
+
- Fast & non-blocking: PyPI JSON API with a short timeout + local cache so we
|
|
9
|
+
never hammer the network or slow the boot.
|
|
10
|
+
- Safe: never crashes the CLI if offline / PyPI is down / pip is weird.
|
|
11
|
+
- Respectful: a single config flag (`auto_update`) turns it off; a cache TTL
|
|
12
|
+
avoids checking on every single launch.
|
|
13
|
+
|
|
14
|
+
Public API:
|
|
15
|
+
get_installed_version() -> str
|
|
16
|
+
get_latest_version(timeout=4, use_cache=True) -> str | None
|
|
17
|
+
is_update_available() -> tuple[bool, str, str] # (available, current, latest)
|
|
18
|
+
perform_update(target=None) -> tuple[bool, str] # (ok, message)
|
|
19
|
+
"""
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import json
|
|
23
|
+
import os
|
|
24
|
+
import subprocess
|
|
25
|
+
import sys
|
|
26
|
+
import time
|
|
27
|
+
import urllib.request
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
|
|
30
|
+
PYPI_JSON_URL = "https://pypi.org/pypi/dulus/json"
|
|
31
|
+
_CACHE_FILE = Path.home() / ".dulus" / "cache" / "update-check.json"
|
|
32
|
+
_CACHE_TTL_SEC = 6 * 3600 # only hit PyPI at most every 6h for the auto-check
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# ── Version helpers ─────────────────────────────────────────────────────────
|
|
36
|
+
|
|
37
|
+
def get_installed_version() -> str:
|
|
38
|
+
"""Return the installed dulus version, or '0.0.0' if undeterminable."""
|
|
39
|
+
try:
|
|
40
|
+
from importlib.metadata import version
|
|
41
|
+
return version("dulus")
|
|
42
|
+
except Exception:
|
|
43
|
+
# Running from source without an install — try pyproject as a fallback.
|
|
44
|
+
try:
|
|
45
|
+
here = Path(__file__).parent / "pyproject.toml"
|
|
46
|
+
for line in here.read_text(encoding="utf-8").splitlines():
|
|
47
|
+
if line.strip().startswith("version"):
|
|
48
|
+
return line.split("=")[1].strip().strip('"').strip("'")
|
|
49
|
+
except Exception:
|
|
50
|
+
pass
|
|
51
|
+
return "0.0.0"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _parse(v: str) -> tuple:
|
|
55
|
+
"""Turn '3.9.0' into a comparable tuple (3, 9, 0). Non-numeric parts -> 0."""
|
|
56
|
+
out = []
|
|
57
|
+
for part in str(v).split("."):
|
|
58
|
+
num = "".join(ch for ch in part if ch.isdigit())
|
|
59
|
+
out.append(int(num) if num else 0)
|
|
60
|
+
return tuple(out)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _is_newer(latest: str, current: str) -> bool:
|
|
64
|
+
return _parse(latest) > _parse(current)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# ── PyPI query (cached) ─────────────────────────────────────────────────────
|
|
68
|
+
|
|
69
|
+
def _read_cache() -> dict | None:
|
|
70
|
+
try:
|
|
71
|
+
data = json.loads(_CACHE_FILE.read_text(encoding="utf-8"))
|
|
72
|
+
if time.time() - float(data.get("fetched_at", 0)) < _CACHE_TTL_SEC:
|
|
73
|
+
return data
|
|
74
|
+
except Exception:
|
|
75
|
+
pass
|
|
76
|
+
return None
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _write_cache(latest: str) -> None:
|
|
80
|
+
try:
|
|
81
|
+
_CACHE_FILE.parent.mkdir(parents=True, exist_ok=True)
|
|
82
|
+
_CACHE_FILE.write_text(
|
|
83
|
+
json.dumps({"fetched_at": time.time(), "latest": latest}),
|
|
84
|
+
encoding="utf-8",
|
|
85
|
+
)
|
|
86
|
+
except Exception:
|
|
87
|
+
pass
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def get_latest_version(timeout: int = 4, use_cache: bool = True) -> str | None:
|
|
91
|
+
"""Return the newest dulus version on PyPI, or None if unreachable."""
|
|
92
|
+
if use_cache:
|
|
93
|
+
cached = _read_cache()
|
|
94
|
+
if cached and cached.get("latest"):
|
|
95
|
+
return cached["latest"]
|
|
96
|
+
|
|
97
|
+
# truststore first so we work behind corporate TLS-intercepting proxies
|
|
98
|
+
try:
|
|
99
|
+
import truststore
|
|
100
|
+
truststore.inject_into_ssl()
|
|
101
|
+
except Exception:
|
|
102
|
+
pass
|
|
103
|
+
|
|
104
|
+
try:
|
|
105
|
+
req = urllib.request.Request(
|
|
106
|
+
PYPI_JSON_URL, headers={"User-Agent": "Dulus-Updater/1.0"}
|
|
107
|
+
)
|
|
108
|
+
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
|
109
|
+
payload = json.loads(resp.read())
|
|
110
|
+
latest = payload.get("info", {}).get("version")
|
|
111
|
+
if latest:
|
|
112
|
+
_write_cache(latest)
|
|
113
|
+
return latest
|
|
114
|
+
except Exception:
|
|
115
|
+
return None
|
|
116
|
+
return None
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def is_update_available() -> tuple[bool, str, str]:
|
|
120
|
+
"""Return (update_available, current_version, latest_version_or_empty)."""
|
|
121
|
+
current = get_installed_version()
|
|
122
|
+
latest = get_latest_version()
|
|
123
|
+
if not latest:
|
|
124
|
+
return (False, current, "")
|
|
125
|
+
return (_is_newer(latest, current), current, latest)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
# ── Perform the upgrade ─────────────────────────────────────────────────────
|
|
129
|
+
|
|
130
|
+
def perform_update(target: str | None = None) -> tuple[bool, str]:
|
|
131
|
+
"""pip-install --upgrade dulus (optionally pinning a specific version).
|
|
132
|
+
|
|
133
|
+
Returns (ok, message). Never raises.
|
|
134
|
+
"""
|
|
135
|
+
pkg = f"dulus=={target}" if target else "dulus"
|
|
136
|
+
# --upgrade to move forward; PEP 668 environments need --break-system-packages
|
|
137
|
+
# but only add it as a fallback so we don't clobber managed installs on the
|
|
138
|
+
# first, cleaner attempt.
|
|
139
|
+
base = [sys.executable, "-m", "pip", "install", "--upgrade", "--quiet", pkg]
|
|
140
|
+
try:
|
|
141
|
+
proc = subprocess.run(base, capture_output=True, text=True, timeout=300)
|
|
142
|
+
if proc.returncode == 0:
|
|
143
|
+
# bust the cache so the next check reflects reality
|
|
144
|
+
try:
|
|
145
|
+
_CACHE_FILE.unlink(missing_ok=True)
|
|
146
|
+
except Exception:
|
|
147
|
+
pass
|
|
148
|
+
return (True, f"Updated to the latest dulus ({target or 'latest'}).")
|
|
149
|
+
# Retry once for externally-managed (PEP 668) Pythons.
|
|
150
|
+
if "externally-managed" in (proc.stderr or "") or "break-system-packages" in (proc.stderr or ""):
|
|
151
|
+
proc2 = subprocess.run(
|
|
152
|
+
base + ["--break-system-packages"],
|
|
153
|
+
capture_output=True, text=True, timeout=300,
|
|
154
|
+
)
|
|
155
|
+
if proc2.returncode == 0:
|
|
156
|
+
try:
|
|
157
|
+
_CACHE_FILE.unlink(missing_ok=True)
|
|
158
|
+
except Exception:
|
|
159
|
+
pass
|
|
160
|
+
return (True, "Updated (PEP 668 override).")
|
|
161
|
+
return (False, f"pip failed: {(proc2.stderr or proc2.stdout or '').strip()[:300]}")
|
|
162
|
+
return (False, f"pip failed: {(proc.stderr or proc.stdout or '').strip()[:300]}")
|
|
163
|
+
except subprocess.TimeoutExpired:
|
|
164
|
+
return (False, "Update timed out after 5 minutes.")
|
|
165
|
+
except Exception as e:
|
|
166
|
+
return (False, f"Update error: {e}")
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def startup_check_line() -> str | None:
|
|
170
|
+
"""Cheap check used at REPL boot. Returns a one-line notice or None.
|
|
171
|
+
|
|
172
|
+
Uses the cache aggressively so startup stays fast. Returns None when
|
|
173
|
+
up-to-date, offline, or the check is skipped.
|
|
174
|
+
"""
|
|
175
|
+
available, current, latest = is_update_available()
|
|
176
|
+
if available:
|
|
177
|
+
return f"A new Dulus is available: {current} -> {latest}. Run /update now (auto: /update on)"
|
|
178
|
+
return None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|