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.
Files changed (258) hide show
  1. {comodor-0.20.2 → comodor-0.20.3}/PKG-INFO +7 -3
  2. {comodor-0.20.2 → comodor-0.20.3}/README.md +6 -2
  3. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/_version.py +2 -2
  4. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/catalogue.py +15 -0
  5. {comodor-0.20.2 → comodor-0.20.3}/tests/test_docker.py +50 -0
  6. {comodor-0.20.2 → comodor-0.20.3}/tests/test_providers.py +99 -0
  7. {comodor-0.20.2 → comodor-0.20.3}/.gitignore +0 -0
  8. {comodor-0.20.2 → comodor-0.20.3}/LICENSE +0 -0
  9. {comodor-0.20.2 → comodor-0.20.3}/bench/README.md +0 -0
  10. {comodor-0.20.2 → comodor-0.20.3}/bench/tasks/careful-unknowable/repo/README.md +0 -0
  11. {comodor-0.20.2 → comodor-0.20.3}/docs/AR/README.md +0 -0
  12. {comodor-0.20.2 → comodor-0.20.3}/docs/DE/README.md +0 -0
  13. {comodor-0.20.2 → comodor-0.20.3}/docs/ES/README.md +0 -0
  14. {comodor-0.20.2 → comodor-0.20.3}/docs/FA/README.md +0 -0
  15. {comodor-0.20.2 → comodor-0.20.3}/docs/FR/README.md +0 -0
  16. {comodor-0.20.2 → comodor-0.20.3}/docs/README.md +0 -0
  17. {comodor-0.20.2 → comodor-0.20.3}/docs/RU/README.md +0 -0
  18. {comodor-0.20.2 → comodor-0.20.3}/docs/TR/README.md +0 -0
  19. {comodor-0.20.2 → comodor-0.20.3}/docs/ZH/README.md +0 -0
  20. {comodor-0.20.2 → comodor-0.20.3}/pyproject.toml +0 -0
  21. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/__init__.py +0 -0
  22. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/__main__.py +0 -0
  23. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/acp/__init__.py +0 -0
  24. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/acp/agent.py +0 -0
  25. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/acp/commands.py +0 -0
  26. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/acp/jsonrpc.py +0 -0
  27. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/agent/__init__.py +0 -0
  28. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/agent/claims.py +0 -0
  29. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/agent/context.py +0 -0
  30. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/agent/loop.py +0 -0
  31. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/agent/prompts.py +0 -0
  32. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/agent/spawn.py +0 -0
  33. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/agent/tokens.py +0 -0
  34. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/browser/__init__.py +0 -0
  35. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/browser/cdp.py +0 -0
  36. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/browser/launch.py +0 -0
  37. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/browser/page.py +0 -0
  38. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/channels/__init__.py +0 -0
  39. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/channels/daemon.py +0 -0
  40. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/channels/markdown.py +0 -0
  41. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/channels/unit.py +0 -0
  42. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/cli.py +0 -0
  43. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/config.py +0 -0
  44. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/desktop/__init__.py +0 -0
  45. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/desktop/guard.py +0 -0
  46. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/desktop/keys.py +0 -0
  47. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/desktop/overlay.py +0 -0
  48. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/desktop/png.py +0 -0
  49. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/desktop/screen.py +0 -0
  50. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/desktop/win32.py +0 -0
  51. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/doctor.py +0 -0
  52. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/events.py +0 -0
  53. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/help.py +0 -0
  54. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/learning/__init__.py +0 -0
  55. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/learning/associations.py +0 -0
  56. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/learning/bm25.py +0 -0
  57. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/learning/hotindex.py +0 -0
  58. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/learning/memory.py +0 -0
  59. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/learning/progress.py +0 -0
  60. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/learning/reflect.py +0 -0
  61. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/learning/rules.py +0 -0
  62. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/learning/signals.py +0 -0
  63. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/learning/store.py +0 -0
  64. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/learning/writer.py +0 -0
  65. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/local/__init__.py +0 -0
  66. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/local/catalogue.py +0 -0
  67. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/local/commands.py +0 -0
  68. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/local/download.py +0 -0
  69. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/local/models.json +0 -0
  70. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/local/provider.py +0 -0
  71. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/local/runtime.py +0 -0
  72. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/local/store.py +0 -0
  73. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/mcp/__init__.py +0 -0
  74. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/mcp/catalogue.py +0 -0
  75. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/mcp/commands.py +0 -0
  76. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/mcp/http.py +0 -0
  77. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/mcp/manager.py +0 -0
  78. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/mcp/protocol.py +0 -0
  79. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/migrate.py +0 -0
  80. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/net/__init__.py +0 -0
  81. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/net/http.py +0 -0
  82. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/net/sse.py +0 -0
  83. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/net/ws.py +0 -0
  84. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/paths.py +0 -0
  85. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/providers/__init__.py +0 -0
  86. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/providers/anthropic.py +0 -0
  87. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/providers/base.py +0 -0
  88. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/providers/caching.py +0 -0
  89. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/providers/discover.py +0 -0
  90. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/providers/fake.py +0 -0
  91. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/providers/gateway.py +0 -0
  92. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/providers/models.py +0 -0
  93. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/providers/oauth.py +0 -0
  94. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/providers/openai_compat.py +0 -0
  95. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/providers/profile.py +0 -0
  96. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/providers/registry.py +0 -0
  97. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/questions.py +0 -0
  98. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/safety/__init__.py +0 -0
  99. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/safety/checkpoints.py +0 -0
  100. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/safety/permissions.py +0 -0
  101. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/safety/redact.py +0 -0
  102. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/session/__init__.py +0 -0
  103. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/session/search.py +0 -0
  104. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/session/store.py +0 -0
  105. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/setup.py +0 -0
  106. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/skills/__init__.py +0 -0
  107. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/skills/catalogue.py +0 -0
  108. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/skills/commands.py +0 -0
  109. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/skills/examples.py +0 -0
  110. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/skills/loader.py +0 -0
  111. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/skills/propose.py +0 -0
  112. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/skills/registry.py +0 -0
  113. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/slack/__init__.py +0 -0
  114. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/slack/api.py +0 -0
  115. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/slack/blocks.py +0 -0
  116. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/slack/bot.py +0 -0
  117. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/slack/commands.py +0 -0
  118. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/slack/service.py +0 -0
  119. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/slack/socket.py +0 -0
  120. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/slack/unit.py +0 -0
  121. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/telegram/__init__.py +0 -0
  122. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/telegram/api.py +0 -0
  123. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/telegram/bot.py +0 -0
  124. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/telegram/commands.py +0 -0
  125. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/telegram/keyboard.py +0 -0
  126. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/telegram/service.py +0 -0
  127. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/telegram/unit.py +0 -0
  128. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/__init__.py +0 -0
  129. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/ask.py +0 -0
  130. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/base.py +0 -0
  131. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/browse.py +0 -0
  132. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/browser.py +0 -0
  133. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/computer.py +0 -0
  134. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/delegate.py +0 -0
  135. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/fs.py +0 -0
  136. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/history.py +0 -0
  137. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/matching.py +0 -0
  138. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/mcp.py +0 -0
  139. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/overflow.py +0 -0
  140. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/registry.py +0 -0
  141. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/search.py +0 -0
  142. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/shell.py +0 -0
  143. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/skills.py +0 -0
  144. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/todo.py +0 -0
  145. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/verify.py +0 -0
  146. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/tools/web.py +0 -0
  147. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/__init__.py +0 -0
  148. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/app.py +0 -0
  149. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/banner.py +0 -0
  150. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/bidi.py +0 -0
  151. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/chooser.py +0 -0
  152. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/clipboard.py +0 -0
  153. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/console.py +0 -0
  154. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/input/__init__.py +0 -0
  155. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/input/keys.py +0 -0
  156. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/input/reader.py +0 -0
  157. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/layout.py +0 -0
  158. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/markdown.py +0 -0
  159. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/screen.py +0 -0
  160. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/theme.py +0 -0
  161. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/widgets/__init__.py +0 -0
  162. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/widgets/buttons.py +0 -0
  163. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/widgets/chat.py +0 -0
  164. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/widgets/history.py +0 -0
  165. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/widgets/overlay.py +0 -0
  166. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/widgets/panel.py +0 -0
  167. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/widgets/progress.py +0 -0
  168. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/widgets/prompt.py +0 -0
  169. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/widgets/questions.py +0 -0
  170. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/widgets/statusbar.py +0 -0
  171. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/widgets/toast.py +0 -0
  172. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/ui/widgets/welcome.py +0 -0
  173. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/uninstall.py +0 -0
  174. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/update.py +0 -0
  175. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/web/__init__.py +0 -0
  176. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/web/commands.py +0 -0
  177. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/web/index.html +0 -0
  178. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/web/server.py +0 -0
  179. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/web/session.py +0 -0
  180. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/web/ui.css +0 -0
  181. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/web/ui.js +0 -0
  182. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/web/vazirmatn-OFL.txt +0 -0
  183. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/web/vazirmatn.woff2 +0 -0
  184. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/whatsapp/__init__.py +0 -0
  185. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/whatsapp/api.py +0 -0
  186. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/whatsapp/bot.py +0 -0
  187. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/whatsapp/commands.py +0 -0
  188. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/whatsapp/guide.py +0 -0
  189. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/whatsapp/menu.py +0 -0
  190. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/whatsapp/service.py +0 -0
  191. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/whatsapp/tunnel.py +0 -0
  192. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/whatsapp/unit.py +0 -0
  193. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/whatsapp/webhook.py +0 -0
  194. {comodor-0.20.2 → comodor-0.20.3}/src/comodor/workspace.py +0 -0
  195. {comodor-0.20.2 → comodor-0.20.3}/tests/conftest.py +0 -0
  196. {comodor-0.20.2 → comodor-0.20.3}/tests/support/fake_mcp_server.py +0 -0
  197. {comodor-0.20.2 → comodor-0.20.3}/tests/test_acp.py +0 -0
  198. {comodor-0.20.2 → comodor-0.20.3}/tests/test_agent_loop.py +0 -0
  199. {comodor-0.20.2 → comodor-0.20.3}/tests/test_app.py +0 -0
  200. {comodor-0.20.2 → comodor-0.20.3}/tests/test_associations.py +0 -0
  201. {comodor-0.20.2 → comodor-0.20.3}/tests/test_banner.py +0 -0
  202. {comodor-0.20.2 → comodor-0.20.3}/tests/test_bench.py +0 -0
  203. {comodor-0.20.2 → comodor-0.20.3}/tests/test_bidi.py +0 -0
  204. {comodor-0.20.2 → comodor-0.20.3}/tests/test_browser.py +0 -0
  205. {comodor-0.20.2 → comodor-0.20.3}/tests/test_caching.py +0 -0
  206. {comodor-0.20.2 → comodor-0.20.3}/tests/test_channel_service.py +0 -0
  207. {comodor-0.20.2 → comodor-0.20.3}/tests/test_chooser.py +0 -0
  208. {comodor-0.20.2 → comodor-0.20.3}/tests/test_claims.py +0 -0
  209. {comodor-0.20.2 → comodor-0.20.3}/tests/test_computer_tool.py +0 -0
  210. {comodor-0.20.2 → comodor-0.20.3}/tests/test_config_save.py +0 -0
  211. {comodor-0.20.2 → comodor-0.20.3}/tests/test_copy_and_bands.py +0 -0
  212. {comodor-0.20.2 → comodor-0.20.3}/tests/test_delegate.py +0 -0
  213. {comodor-0.20.2 → comodor-0.20.3}/tests/test_desktop.py +0 -0
  214. {comodor-0.20.2 → comodor-0.20.3}/tests/test_discover.py +0 -0
  215. {comodor-0.20.2 → comodor-0.20.3}/tests/test_doctor.py +0 -0
  216. {comodor-0.20.2 → comodor-0.20.3}/tests/test_headless.py +0 -0
  217. {comodor-0.20.2 → comodor-0.20.3}/tests/test_help.py +0 -0
  218. {comodor-0.20.2 → comodor-0.20.3}/tests/test_history.py +0 -0
  219. {comodor-0.20.2 → comodor-0.20.3}/tests/test_import_command.py +0 -0
  220. {comodor-0.20.2 → comodor-0.20.3}/tests/test_input.py +0 -0
  221. {comodor-0.20.2 → comodor-0.20.3}/tests/test_layout.py +0 -0
  222. {comodor-0.20.2 → comodor-0.20.3}/tests/test_learning.py +0 -0
  223. {comodor-0.20.2 → comodor-0.20.3}/tests/test_local.py +0 -0
  224. {comodor-0.20.2 → comodor-0.20.3}/tests/test_markdown.py +0 -0
  225. {comodor-0.20.2 → comodor-0.20.3}/tests/test_matching.py +0 -0
  226. {comodor-0.20.2 → comodor-0.20.3}/tests/test_mcp.py +0 -0
  227. {comodor-0.20.2 → comodor-0.20.3}/tests/test_mcp_http.py +0 -0
  228. {comodor-0.20.2 → comodor-0.20.3}/tests/test_migrate.py +0 -0
  229. {comodor-0.20.2 → comodor-0.20.3}/tests/test_oauth.py +0 -0
  230. {comodor-0.20.2 → comodor-0.20.3}/tests/test_overflow.py +0 -0
  231. {comodor-0.20.2 → comodor-0.20.3}/tests/test_overlay.py +0 -0
  232. {comodor-0.20.2 → comodor-0.20.3}/tests/test_performance.py +0 -0
  233. {comodor-0.20.2 → comodor-0.20.3}/tests/test_prefix_stability.py +0 -0
  234. {comodor-0.20.2 → comodor-0.20.3}/tests/test_profile.py +0 -0
  235. {comodor-0.20.2 → comodor-0.20.3}/tests/test_progress.py +0 -0
  236. {comodor-0.20.2 → comodor-0.20.3}/tests/test_propose.py +0 -0
  237. {comodor-0.20.2 → comodor-0.20.3}/tests/test_questions.py +0 -0
  238. {comodor-0.20.2 → comodor-0.20.3}/tests/test_real_browser.py +0 -0
  239. {comodor-0.20.2 → comodor-0.20.3}/tests/test_real_desktop.py +0 -0
  240. {comodor-0.20.2 → comodor-0.20.3}/tests/test_real_web_ui.py +0 -0
  241. {comodor-0.20.2 → comodor-0.20.3}/tests/test_reflex.py +0 -0
  242. {comodor-0.20.2 → comodor-0.20.3}/tests/test_run_loop.py +0 -0
  243. {comodor-0.20.2 → comodor-0.20.3}/tests/test_setup.py +0 -0
  244. {comodor-0.20.2 → comodor-0.20.3}/tests/test_setup_import.py +0 -0
  245. {comodor-0.20.2 → comodor-0.20.3}/tests/test_skill_budget.py +0 -0
  246. {comodor-0.20.2 → comodor-0.20.3}/tests/test_skill_catalogue.py +0 -0
  247. {comodor-0.20.2 → comodor-0.20.3}/tests/test_skills.py +0 -0
  248. {comodor-0.20.2 → comodor-0.20.3}/tests/test_slack.py +0 -0
  249. {comodor-0.20.2 → comodor-0.20.3}/tests/test_source_hygiene.py +0 -0
  250. {comodor-0.20.2 → comodor-0.20.3}/tests/test_switching_model.py +0 -0
  251. {comodor-0.20.2 → comodor-0.20.3}/tests/test_telegram.py +0 -0
  252. {comodor-0.20.2 → comodor-0.20.3}/tests/test_tools.py +0 -0
  253. {comodor-0.20.2 → comodor-0.20.3}/tests/test_uninstall.py +0 -0
  254. {comodor-0.20.2 → comodor-0.20.3}/tests/test_update.py +0 -0
  255. {comodor-0.20.2 → comodor-0.20.3}/tests/test_verify.py +0 -0
  256. {comodor-0.20.2 → comodor-0.20.3}/tests/test_web.py +0 -0
  257. {comodor-0.20.2 → comodor-0.20.3}/tests/test_welcome.py +0 -0
  258. {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.2
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** | Nineteen providers, or anything with an OpenAI-compatible URL. [→](docs/models.md) |
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 -e ANTHROPIC_API_KEY -v "$PWD:/work" ghcr.io/ifekri/comodor:latest
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** | Nineteen providers, or anything with an OpenAI-compatible URL. [→](docs/models.md) |
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 -e ANTHROPIC_API_KEY -v "$PWD:/work" ghcr.io/ifekri/comodor:latest
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.2'
22
- __version_tuple__ = version_tuple = (0, 20, 2)
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