comodor 0.20.2__tar.gz → 0.20.3__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.
- {comodor-0.20.2 → comodor-0.20.3}/PKG-INFO +7 -3
- {comodor-0.20.2 → comodor-0.20.3}/README.md +6 -2
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/_version.py +2 -2
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/catalogue.py +15 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_docker.py +50 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_providers.py +99 -0
- {comodor-0.20.2 → comodor-0.20.3}/.gitignore +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/LICENSE +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/bench/README.md +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/bench/tasks/careful-unknowable/repo/README.md +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/docs/AR/README.md +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/docs/DE/README.md +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/docs/ES/README.md +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/docs/FA/README.md +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/docs/FR/README.md +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/docs/README.md +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/docs/RU/README.md +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/docs/TR/README.md +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/docs/ZH/README.md +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/pyproject.toml +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/__init__.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/__main__.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/acp/__init__.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/acp/agent.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/acp/commands.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/acp/jsonrpc.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/agent/__init__.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/agent/claims.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/agent/context.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/agent/loop.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/agent/prompts.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/agent/spawn.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/agent/tokens.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/browser/__init__.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/browser/cdp.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/browser/launch.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/browser/page.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/channels/__init__.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/channels/daemon.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/channels/markdown.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/channels/unit.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/cli.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/config.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/desktop/__init__.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/desktop/guard.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/desktop/keys.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/desktop/overlay.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/desktop/png.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/desktop/screen.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/desktop/win32.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/doctor.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/events.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/help.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/learning/__init__.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/learning/associations.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/learning/bm25.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/learning/hotindex.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/learning/memory.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/learning/progress.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/learning/reflect.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/learning/rules.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/learning/signals.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/learning/store.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/learning/writer.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/local/__init__.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/local/catalogue.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/local/commands.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/local/download.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/local/models.json +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/local/provider.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/local/runtime.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/local/store.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/mcp/__init__.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/mcp/catalogue.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/mcp/commands.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/mcp/http.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/mcp/manager.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/mcp/protocol.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/migrate.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/net/__init__.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/net/http.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/net/sse.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/net/ws.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/paths.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/providers/__init__.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/providers/anthropic.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/providers/base.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/providers/caching.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/providers/discover.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/providers/fake.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/providers/gateway.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/providers/models.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/providers/oauth.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/providers/openai_compat.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/providers/profile.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/providers/registry.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/questions.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/safety/__init__.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/safety/checkpoints.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/safety/permissions.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/safety/redact.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/session/__init__.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/session/search.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/session/store.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/setup.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/skills/__init__.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/skills/catalogue.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/skills/commands.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/skills/examples.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/skills/loader.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/skills/propose.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/skills/registry.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/slack/__init__.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/slack/api.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/slack/blocks.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/slack/bot.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/slack/commands.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/slack/service.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/slack/socket.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/slack/unit.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/telegram/__init__.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/telegram/api.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/telegram/bot.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/telegram/commands.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/telegram/keyboard.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/telegram/service.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/telegram/unit.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/__init__.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/ask.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/base.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/browse.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/browser.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/computer.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/delegate.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/fs.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/history.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/matching.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/mcp.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/overflow.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/registry.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/search.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/shell.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/skills.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/todo.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/verify.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/web.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/__init__.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/app.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/banner.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/bidi.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/chooser.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/clipboard.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/console.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/input/__init__.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/input/keys.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/input/reader.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/layout.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/markdown.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/screen.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/theme.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/widgets/__init__.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/widgets/buttons.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/widgets/chat.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/widgets/history.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/widgets/overlay.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/widgets/panel.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/widgets/progress.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/widgets/prompt.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/widgets/questions.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/widgets/statusbar.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/widgets/toast.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/widgets/welcome.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/uninstall.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/update.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/web/__init__.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/web/commands.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/web/index.html +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/web/server.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/web/session.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/web/ui.css +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/web/ui.js +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/web/vazirmatn-OFL.txt +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/web/vazirmatn.woff2 +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/whatsapp/__init__.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/whatsapp/api.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/whatsapp/bot.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/whatsapp/commands.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/whatsapp/guide.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/whatsapp/menu.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/whatsapp/service.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/whatsapp/tunnel.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/whatsapp/unit.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/whatsapp/webhook.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/src/comodor/workspace.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/conftest.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/support/fake_mcp_server.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_acp.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_agent_loop.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_app.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_associations.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_banner.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_bench.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_bidi.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_browser.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_caching.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_channel_service.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_chooser.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_claims.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_computer_tool.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_config_save.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_copy_and_bands.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_delegate.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_desktop.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_discover.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_doctor.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_headless.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_help.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_history.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_import_command.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_input.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_layout.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_learning.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_local.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_markdown.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_matching.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_mcp.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_mcp_http.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_migrate.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_oauth.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_overflow.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_overlay.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_performance.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_prefix_stability.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_profile.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_progress.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_propose.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_questions.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_real_browser.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_real_desktop.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_real_web_ui.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_reflex.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_run_loop.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_setup.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_setup_import.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_skill_budget.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_skill_catalogue.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_skills.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_slack.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_source_hygiene.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_switching_model.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_telegram.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_tools.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_uninstall.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_update.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_verify.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_web.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_welcome.py +0 -0
- {comodor-0.20.2 → comodor-0.20.3}/tests/test_whatsapp.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: comodor
|
|
3
|
-
Version: 0.20.
|
|
3
|
+
Version: 0.20.3
|
|
4
4
|
Summary: Comodor — a self-improving terminal coding agent with a Rich TUI
|
|
5
5
|
Project-URL: Homepage, https://comodor.ai
|
|
6
6
|
Project-URL: Repository, https://github.com/ifekri/Comodor
|
|
@@ -136,7 +136,7 @@ The whole table, and how to run it against your own model, is in
|
|
|
136
136
|
| **Runs a model locally** | Pick one, watch it download, use it with the network unplugged. No key, no account. [→](docs/local-models.md) |
|
|
137
137
|
| **Lives in your editor** | Speaks ACP, so VS Code, JetBrains and Zed drive it from their own panels. [→](docs/acp.md) |
|
|
138
138
|
| **Follows your procedures** | Write a skill once; it loads when the work matches. 147 ready to install. [→](docs/skills.md) |
|
|
139
|
-
| **Works with any model** |
|
|
139
|
+
| **Works with any model** | Sixteen hosted providers and three local runtimes, or anything with an OpenAI-compatible URL. [→](docs/models.md) |
|
|
140
140
|
| **Costs less** | 86% of input tokens served from cache, measured — not estimated. [→](docs/cost.md) |
|
|
141
141
|
|
|
142
142
|
**One dependency.** The HTTP client, the SSE reader, the WebSocket that drives
|
|
@@ -215,9 +215,13 @@ The agent works on `./work`; point it somewhere else with
|
|
|
215
215
|
anything:
|
|
216
216
|
|
|
217
217
|
```bash
|
|
218
|
-
docker run --rm -it -p 127.0.0.1:8765:8765
|
|
218
|
+
docker run --rm -it -p 127.0.0.1:8765:8765 \
|
|
219
|
+
-e ANTHROPIC_API_KEY -v "$PWD:/work" ifekri/comodor:latest
|
|
219
220
|
```
|
|
220
221
|
|
|
222
|
+
Published to both [Docker Hub](https://hub.docker.com/r/ifekri/comodor)
|
|
223
|
+
(`ifekri/comodor`) and GitHub (`ghcr.io/ifekri/comodor`) on every release.
|
|
224
|
+
|
|
221
225
|
Full details, including how to reach it from another machine safely:
|
|
222
226
|
[Docker](docs/docker.md).
|
|
223
227
|
|
|
@@ -105,7 +105,7 @@ The whole table, and how to run it against your own model, is in
|
|
|
105
105
|
| **Runs a model locally** | Pick one, watch it download, use it with the network unplugged. No key, no account. [→](docs/local-models.md) |
|
|
106
106
|
| **Lives in your editor** | Speaks ACP, so VS Code, JetBrains and Zed drive it from their own panels. [→](docs/acp.md) |
|
|
107
107
|
| **Follows your procedures** | Write a skill once; it loads when the work matches. 147 ready to install. [→](docs/skills.md) |
|
|
108
|
-
| **Works with any model** |
|
|
108
|
+
| **Works with any model** | Sixteen hosted providers and three local runtimes, or anything with an OpenAI-compatible URL. [→](docs/models.md) |
|
|
109
109
|
| **Costs less** | 86% of input tokens served from cache, measured — not estimated. [→](docs/cost.md) |
|
|
110
110
|
|
|
111
111
|
**One dependency.** The HTTP client, the SSE reader, the WebSocket that drives
|
|
@@ -184,9 +184,13 @@ The agent works on `./work`; point it somewhere else with
|
|
|
184
184
|
anything:
|
|
185
185
|
|
|
186
186
|
```bash
|
|
187
|
-
docker run --rm -it -p 127.0.0.1:8765:8765
|
|
187
|
+
docker run --rm -it -p 127.0.0.1:8765:8765 \
|
|
188
|
+
-e ANTHROPIC_API_KEY -v "$PWD:/work" ifekri/comodor:latest
|
|
188
189
|
```
|
|
189
190
|
|
|
191
|
+
Published to both [Docker Hub](https://hub.docker.com/r/ifekri/comodor)
|
|
192
|
+
(`ifekri/comodor`) and GitHub (`ghcr.io/ifekri/comodor`) on every release.
|
|
193
|
+
|
|
190
194
|
Full details, including how to reach it from another machine safely:
|
|
191
195
|
[Docker](docs/docker.md).
|
|
192
196
|
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.20.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 20,
|
|
21
|
+
__version__ = version = '0.20.3'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 20, 3)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -231,6 +231,21 @@ CATALOGUE: tuple[ProviderSpec, ...] = (
|
|
|
231
231
|
env_key="XIAOMI_API_KEY",
|
|
232
232
|
rank=15,
|
|
233
233
|
),
|
|
234
|
+
ProviderSpec(
|
|
235
|
+
id="bai",
|
|
236
|
+
label="B.AI",
|
|
237
|
+
base_url="https://api.b.ai/v1",
|
|
238
|
+
keys_url="https://b.ai",
|
|
239
|
+
blurb="One key, many houses' models — GLM, DeepSeek, MiniMax, GPT.",
|
|
240
|
+
# Chosen because it is the one verified against the live endpoint: it
|
|
241
|
+
# returns a well-formed `tool_calls` and a `finish_reason` of
|
|
242
|
+
# `tool_calls`, which is what makes a provider usable for coding at all.
|
|
243
|
+
default_model="glm-5.3-flash",
|
|
244
|
+
models=("glm-5.3-flash", "deepseek-v4-flash", "glm-5.2",
|
|
245
|
+
"minimax-m3", "gpt-5.6-sol"),
|
|
246
|
+
env_key="BAI_API_KEY",
|
|
247
|
+
rank=16,
|
|
248
|
+
),
|
|
234
249
|
ProviderSpec(
|
|
235
250
|
id="ollama",
|
|
236
251
|
label="Ollama (local)",
|
|
@@ -304,3 +304,53 @@ def test_the_code_really_does_start_without_a_provider():
|
|
|
304
304
|
assert "return config" in source
|
|
305
305
|
assert "No provider is configured yet" in source, (
|
|
306
306
|
"the message the documentation quotes is not the one the code prints")
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
# --------------------------------------------------------------------------- #
|
|
310
|
+
# where the image is published
|
|
311
|
+
# --------------------------------------------------------------------------- #
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def test_it_publishes_to_both_registries():
|
|
315
|
+
"""Docker Hub is where somebody looks first — `docker run comodor` with no
|
|
316
|
+
prefix means Docker Hub — and GHCR is where a fork can publish without any
|
|
317
|
+
secret at all."""
|
|
318
|
+
steps = yaml.safe_load(text(WORKFLOW))["jobs"]["build"]["steps"]
|
|
319
|
+
names = [step.get("name", "") for step in steps]
|
|
320
|
+
|
|
321
|
+
assert any("GitHub's registry" in name for name in names)
|
|
322
|
+
assert any("Docker Hub" in name for name in names)
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def test_both_registries_come_from_one_build():
|
|
326
|
+
"""Two builds would publish two images from two runs of the same
|
|
327
|
+
Dockerfile, and only one of them would have been tested."""
|
|
328
|
+
steps = yaml.safe_load(text(WORKFLOW))["jobs"]["build"]["steps"]
|
|
329
|
+
builds = [step for step in steps
|
|
330
|
+
if "build-push-action" in str(step.get("uses", ""))]
|
|
331
|
+
|
|
332
|
+
assert len(builds) == 1, f"{len(builds)} builds; the tested one is ambiguous"
|
|
333
|
+
assert "steps.tags.outputs.list" in str(builds[0]["with"]["tags"])
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def test_a_missing_docker_hub_secret_skips_rather_than_fails():
|
|
337
|
+
"""A fork has neither secret, and an image that reaches GHCR is still a
|
|
338
|
+
published image."""
|
|
339
|
+
body = text(WORKFLOW)
|
|
340
|
+
|
|
341
|
+
assert "steps.hub.outputs.ready == 'yes'" in body
|
|
342
|
+
assert "No Docker Hub credentials" in body
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def test_the_secrets_are_read_by_the_names_that_were_set():
|
|
346
|
+
body = text(WORKFLOW)
|
|
347
|
+
|
|
348
|
+
assert "secrets.DOCKERHUB_USERNAME" in body
|
|
349
|
+
assert "secrets.DOCKERHUB_TOKEN" in body
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def test_the_documentation_names_both():
|
|
353
|
+
for page in (ROOT / "README.md", ROOT / "docs" / "docker.md"):
|
|
354
|
+
body = text(page)
|
|
355
|
+
assert "ifekri/comodor" in body, f"{page.name}: no Docker Hub image"
|
|
356
|
+
assert "ghcr.io/ifekri/comodor" in body, f"{page.name}: no GHCR image"
|
|
@@ -529,3 +529,102 @@ def test_the_user_agent_says_comodor():
|
|
|
529
529
|
assert DEFAULT_USER_AGENT.startswith("Comodor/")
|
|
530
530
|
assert "comodor.ai" in DEFAULT_USER_AGENT
|
|
531
531
|
assert "requests/" not in DEFAULT_USER_AGENT
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
# --------------------------------------------------------------------------- #
|
|
535
|
+
# the count in the documentation, against the catalogue
|
|
536
|
+
#
|
|
537
|
+
# "Nineteen providers" was in the README, and adding one made it wrong without
|
|
538
|
+
# anything saying so. A number written in prose is a number that goes stale.
|
|
539
|
+
# --------------------------------------------------------------------------- #
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
def test_the_readme_counts_the_providers_that_exist():
|
|
543
|
+
import re
|
|
544
|
+
from pathlib import Path
|
|
545
|
+
|
|
546
|
+
from comodor import catalogue
|
|
547
|
+
|
|
548
|
+
readme = (Path(__file__).resolve().parents[1] / "README.md").read_text(
|
|
549
|
+
encoding="utf-8")
|
|
550
|
+
|
|
551
|
+
words = {2: "Two", 3: "Three", 4: "Four", 5: "Five", 6: "Six", 7: "Seven",
|
|
552
|
+
8: "Eight", 9: "Nine", 10: "Ten", 11: "Eleven", 12: "Twelve",
|
|
553
|
+
13: "Thirteen", 14: "Fourteen", 15: "Fifteen", 16: "Sixteen",
|
|
554
|
+
17: "Seventeen", 18: "Eighteen", 19: "Nineteen", 20: "Twenty"}
|
|
555
|
+
hosted = words[len(catalogue.hosted())]
|
|
556
|
+
local = words[len(catalogue.local())].lower()
|
|
557
|
+
|
|
558
|
+
claim = re.search(r"\|\s*\*\*Works with any model\*\*\s*\|([^|]+)\|", readme)
|
|
559
|
+
assert claim, "the README no longer says what it works with"
|
|
560
|
+
|
|
561
|
+
line = claim.group(1)
|
|
562
|
+
assert f"{hosted} hosted" in line, \
|
|
563
|
+
f"the README says {line.strip()[:60]!r}; there are {hosted.lower()} hosted"
|
|
564
|
+
assert f"{local} local" in line, \
|
|
565
|
+
f"the README does not say there are {local} local runtimes"
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
def test_every_hosted_provider_is_listed_in_the_models_page():
|
|
569
|
+
from pathlib import Path
|
|
570
|
+
|
|
571
|
+
from comodor import catalogue
|
|
572
|
+
|
|
573
|
+
page = (Path(__file__).resolve().parents[1] / "docs" / "models.md").read_text(
|
|
574
|
+
encoding="utf-8")
|
|
575
|
+
|
|
576
|
+
missing = [spec.label for spec in catalogue.hosted()
|
|
577
|
+
if spec.label.split(" (")[0] not in page]
|
|
578
|
+
assert not missing, f"not documented: {missing}"
|
|
579
|
+
|
|
580
|
+
|
|
581
|
+
def test_bai_is_reachable_and_makes_tool_calls():
|
|
582
|
+
"""Live, against the real endpoint, because the only thing that matters
|
|
583
|
+
about a new provider is whether it can drive tools — a provider that
|
|
584
|
+
cannot is unusable for coding, and finding that out on the sixth turn of a
|
|
585
|
+
real task is the failure this prevents.
|
|
586
|
+
|
|
587
|
+
Skipped without a key, so the suite stays offline for everyone else.
|
|
588
|
+
"""
|
|
589
|
+
import json
|
|
590
|
+
import os
|
|
591
|
+
import urllib.request
|
|
592
|
+
from pathlib import Path
|
|
593
|
+
|
|
594
|
+
env = Path(__file__).resolve().parents[1] / "src" / ".env"
|
|
595
|
+
key = os.environ.get("BAI_API_KEY", "")
|
|
596
|
+
if not key and env.is_file():
|
|
597
|
+
for line in env.read_text(encoding="utf-8").splitlines():
|
|
598
|
+
if line.startswith("BAI_API_KEY="):
|
|
599
|
+
key = line.partition("=")[2].strip()
|
|
600
|
+
if not key:
|
|
601
|
+
pytest.skip("no BAI_API_KEY")
|
|
602
|
+
|
|
603
|
+
from comodor import catalogue
|
|
604
|
+
|
|
605
|
+
spec = catalogue.get("bai")
|
|
606
|
+
request = urllib.request.Request(
|
|
607
|
+
f"{spec.base_url}/chat/completions",
|
|
608
|
+
data=json.dumps({
|
|
609
|
+
"model": spec.default_model,
|
|
610
|
+
"messages": [{"role": "user", "content": "Weather in Tehran? Use the tool."}],
|
|
611
|
+
"tools": [{"type": "function", "function": {
|
|
612
|
+
"name": "get_weather",
|
|
613
|
+
"description": "Weather for a city.",
|
|
614
|
+
"parameters": {"type": "object",
|
|
615
|
+
"properties": {"city": {"type": "string"}},
|
|
616
|
+
"required": ["city"]}}}],
|
|
617
|
+
"max_tokens": 300,
|
|
618
|
+
}).encode(),
|
|
619
|
+
headers={"Authorization": f"Bearer {key}",
|
|
620
|
+
"Content-Type": "application/json"})
|
|
621
|
+
|
|
622
|
+
try:
|
|
623
|
+
with urllib.request.urlopen(request, timeout=90) as reply:
|
|
624
|
+
body = json.loads(reply.read().decode())
|
|
625
|
+
except Exception as problem: # rate limit, or offline
|
|
626
|
+
pytest.skip(f"B.AI unreachable: {type(problem).__name__}")
|
|
627
|
+
|
|
628
|
+
choice = body["choices"][0]
|
|
629
|
+
assert choice.get("finish_reason") == "tool_calls"
|
|
630
|
+
assert choice["message"].get("tool_calls"), "no tool call came back"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|