cade-cli 0.16.0__tar.gz → 0.16.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.
Files changed (200) hide show
  1. {cade_cli-0.16.0 → cade_cli-0.16.2}/PKG-INFO +32 -16
  2. {cade_cli-0.16.0 → cade_cli-0.16.2}/README.md +28 -13
  3. {cade_cli-0.16.0 → cade_cli-0.16.2}/pyproject.toml +4 -3
  4. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/cli/app.py +18 -12
  5. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/cli/commands/auth.py +6 -6
  6. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/cli/commands/mcp.py +51 -19
  7. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/cli/onboarding.py +173 -15
  8. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/gateway/app.py +6 -0
  9. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/server.py +6 -0
  10. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/tools/manager/__init__.py +4 -0
  11. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/tools/manager/config.py +32 -12
  12. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/ui/session.py +3 -1
  13. {cade_cli-0.16.0 → cade_cli-0.16.2}/.gitignore +0 -0
  14. {cade_cli-0.16.0 → cade_cli-0.16.2}/LICENSE +0 -0
  15. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/__init__.py +0 -0
  16. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/__main__.py +0 -0
  17. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/_metadata.py +0 -0
  18. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/config.py +0 -0
  19. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/errors.py +0 -0
  20. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/grants.py +0 -0
  21. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/resources.py +0 -0
  22. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/sandbox.py +0 -0
  23. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/server.py +0 -0
  24. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/tools/__init__.py +0 -0
  25. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/tools/_read_cache.py +0 -0
  26. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/tools/access.py +0 -0
  27. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/tools/agents.py +0 -0
  28. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/tools/context.py +0 -0
  29. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/tools/filesystem.py +0 -0
  30. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/tools/git.py +0 -0
  31. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/tools/notifications.py +0 -0
  32. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/tools/questions.py +0 -0
  33. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/tools/results.py +0 -0
  34. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/tools/schemas.py +0 -0
  35. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/tools/search.py +0 -0
  36. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/tools/shell.py +0 -0
  37. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/tools/snippets.py +0 -0
  38. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/tools/tasks.py +0 -0
  39. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/tools/web_fetch.py +0 -0
  40. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/tools/web_search.py +0 -0
  41. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/utils.py +0 -0
  42. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/web/__init__.py +0 -0
  43. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/web/arcade_client.py +0 -0
  44. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/web/cache.py +0 -0
  45. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/web/extract.py +0 -0
  46. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/web/markdown.py +0 -0
  47. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/web/preapproved.py +0 -0
  48. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/web/prompts.py +0 -0
  49. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/web/scrape.py +0 -0
  50. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/web/search/__init__.py +0 -0
  51. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/web/search/arcade.py +0 -0
  52. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/web/search/base.py +0 -0
  53. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cade_mcp_local/web/search/http.py +0 -0
  54. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/__init__.py +0 -0
  55. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/ai/__init__.py +0 -0
  56. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/ai/prompts.py +0 -0
  57. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/cli/__init__.py +0 -0
  58. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/cli/auth.py +0 -0
  59. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/cli/commands/__init__.py +0 -0
  60. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/cli/commands/account.py +0 -0
  61. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/cli/commands/agents.py +0 -0
  62. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/cli/commands/chat.py +0 -0
  63. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/cli/commands/cron.py +0 -0
  64. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/cli/commands/daemon.py +0 -0
  65. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/cli/commands/grant.py +0 -0
  66. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/cli/commands/hooks.py +0 -0
  67. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/cli/commands/mem.py +0 -0
  68. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/cli/commands/tasks.py +0 -0
  69. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/cli/commands/thread.py +0 -0
  70. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/cli/commands/tools.py +0 -0
  71. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/core/__init__.py +0 -0
  72. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/core/agent.py +0 -0
  73. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/core/comms.py +0 -0
  74. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/core/config.py +0 -0
  75. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/core/constants.py +0 -0
  76. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/core/errors.py +0 -0
  77. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/core/frozen.py +0 -0
  78. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/core/git.py +0 -0
  79. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/core/inbox.py +0 -0
  80. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/core/logging.py +0 -0
  81. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/core/memory.py +0 -0
  82. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/core/model_specs.py +0 -0
  83. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/core/names.py +0 -0
  84. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/core/paths.py +0 -0
  85. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/core/runtime.py +0 -0
  86. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/core/secrets.py +0 -0
  87. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/core/spawn.py +0 -0
  88. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/core/types.py +0 -0
  89. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/__init__.py +0 -0
  90. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/adapters/__init__.py +0 -0
  91. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/adapters/base.py +0 -0
  92. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/adapters/factory.py +0 -0
  93. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/adapters/imessage.py +0 -0
  94. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/adapters/registry.py +0 -0
  95. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/adapters/stub.py +0 -0
  96. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/adapters/telegram.py +0 -0
  97. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/allowlist.py +0 -0
  98. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/attach.py +0 -0
  99. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/chunker.py +0 -0
  100. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/client.py +0 -0
  101. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/commands.py +0 -0
  102. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/config.py +0 -0
  103. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/control.py +0 -0
  104. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/cron.py +0 -0
  105. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/elicitation.py +0 -0
  106. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/ensure.py +0 -0
  107. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/format.py +0 -0
  108. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/gateway/__init__.py +0 -0
  109. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/gateway/arcade_export.py +0 -0
  110. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/gateway/auth.py +0 -0
  111. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/gateway/constants.py +0 -0
  112. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/gateway/elicitation.py +0 -0
  113. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/gateway/exposure.py +0 -0
  114. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/gateway/limits.py +0 -0
  115. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/gateway/middleware.py +0 -0
  116. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/gateway/resources.py +0 -0
  117. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/gateway/subscriptions.py +0 -0
  118. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/gateway/turn_bridge.py +0 -0
  119. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/grants.py +0 -0
  120. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/inbox_cron.py +0 -0
  121. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/install.py +0 -0
  122. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/live.py +0 -0
  123. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/lock.py +0 -0
  124. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/mesh.py +0 -0
  125. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/message_pump.py +0 -0
  126. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/observability.py +0 -0
  127. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/owner.py +0 -0
  128. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/progress_sink.py +0 -0
  129. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/registry.py +0 -0
  130. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/router.py +0 -0
  131. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/runner.py +0 -0
  132. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/runner_main.py +0 -0
  133. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/secrets.py +0 -0
  134. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/secrets_broker.py +0 -0
  135. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/services.py +0 -0
  136. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/shim.py +0 -0
  137. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/sinks.py +0 -0
  138. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/supervisor.py +0 -0
  139. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/tokens.py +0 -0
  140. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/traffic.py +0 -0
  141. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/turns.py +0 -0
  142. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/daemon/wake.py +0 -0
  143. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/execution/__init__.py +0 -0
  144. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/execution/context_window.py +0 -0
  145. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/execution/mcp_schema_index.py +0 -0
  146. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/execution/orchestrator.py +0 -0
  147. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/execution/parallel.py +0 -0
  148. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/execution/tool_result_store.py +0 -0
  149. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/execution/tool_schema_cache.py +0 -0
  150. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/hooks/__init__.py +0 -0
  151. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/hooks/config.py +0 -0
  152. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/hooks/engine.py +0 -0
  153. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/hooks/executors/__init__.py +0 -0
  154. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/hooks/executors/callback.py +0 -0
  155. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/hooks/executors/command.py +0 -0
  156. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/hooks/executors/http.py +0 -0
  157. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/hooks/executors/prompt.py +0 -0
  158. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/hooks/executors/ssrf_guard.py +0 -0
  159. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/hooks/matchers.py +0 -0
  160. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/hooks/registry.py +0 -0
  161. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/hooks/types.py +0 -0
  162. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/providers/__init__.py +0 -0
  163. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/providers/anthropic.py +0 -0
  164. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/providers/base.py +0 -0
  165. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/providers/ollama.py +0 -0
  166. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/providers/openai.py +0 -0
  167. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/serve/__init__.py +0 -0
  168. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/storage/__init__.py +0 -0
  169. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/storage/threads.py +0 -0
  170. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/tasks/__init__.py +0 -0
  171. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/tasks/scheduler.py +0 -0
  172. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/tasks/store.py +0 -0
  173. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/tasks/types.py +0 -0
  174. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/templates/login_failed.html +0 -0
  175. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/templates/login_success.html +0 -0
  176. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/templates/serve/launchd.plist.tmpl +0 -0
  177. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/templates/serve/serve.toml.example +0 -0
  178. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/templates/serve/systemd.service.tmpl +0 -0
  179. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/templates/styles.css +0 -0
  180. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/tools/__init__.py +0 -0
  181. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/tools/manager/_request_ctx.py +0 -0
  182. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/tools/manager/base.py +0 -0
  183. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/tools/manager/composite.py +0 -0
  184. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/tools/manager/mcp.py +0 -0
  185. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/tools/search/__init__.py +0 -0
  186. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/tools/search/discovered.py +0 -0
  187. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/tools/search/scoring.py +0 -0
  188. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/tools/search/service.py +0 -0
  189. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/ui/__init__.py +0 -0
  190. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/ui/completion.py +0 -0
  191. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/ui/display.py +0 -0
  192. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/ui/elicitation.py +0 -0
  193. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/ui/input.py +0 -0
  194. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/ui/slash.py +0 -0
  195. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/voice/__init__.py +0 -0
  196. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/voice/audio.py +0 -0
  197. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/voice/cleanup.py +0 -0
  198. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/voice/session.py +0 -0
  199. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/voice/stt.py +0 -0
  200. {cade_cli-0.16.0 → cade_cli-0.16.2}/src/cadecoder/voice/tts.py +0 -0
@@ -1,9 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cade-cli
3
- Version: 0.16.0
4
- Summary: Cade - The CLI Agent from Arcade.dev
3
+ Version: 0.16.2
4
+ Summary: Cade - The CLI Agent from Arcade.dev (install: curl -fsSL https://arcadeagent.dev/install | sh)
5
5
  Project-URL: Homepage, https://arcade.dev
6
- Project-URL: Documentation, https://docs.arcade.dev
6
+ Project-URL: Documentation, https://arcadeagent.dev/docs
7
+ Project-URL: Install, https://arcadeagent.dev/install
7
8
  Project-URL: Repository, https://github.com/arcadeai-labs/cade
8
9
  Project-URL: Issues, https://github.com/arcadeai-labs/cade/issues
9
10
  Project-URL: Changelog, https://github.com/arcadeai-labs/cade/releases
@@ -93,11 +94,15 @@ Description-Content-Type: text/markdown
93
94
  The CLI agent from [Arcade.dev](https://arcade.dev). Coding, research, and everyday automation — driven from a terminal, a single shell pipe, or a long-lived daemon that replies over Telegram.
94
95
 
95
96
  ```bash
96
- curl -fsSL https://arcadeagent.dev/install | sh # or: uv tool install cade-cli | pip install cade-cli
97
- cade login
97
+ curl -fsSL https://arcadeagent.dev/install | sh
98
98
  cade
99
99
  ```
100
100
 
101
+ That is the **recommended install** — a prebuilt binary from
102
+ [arcadeagent.dev](https://arcadeagent.dev/install). No GitHub token, no Python
103
+ toolchain. First `cade` walks you through model setup, Arcade login, and the
104
+ [Arcade Agent Hub](https://hub.arcade.dev). Re-run the same curl to upgrade.
105
+
101
106
  ---
102
107
 
103
108
  ## Highlights
@@ -113,19 +118,18 @@ cade
113
118
 
114
119
  ## Install
115
120
 
116
- | Source | Command |
117
- |---|---|
118
- | **Install script** (recommended) | `curl -fsSL https://arcadeagent.dev/install \| sh` |
119
- | uv | `uv tool install cade-cli` |
120
- | pip | `pip install cade-cli` |
121
- | From source | `git clone https://github.com/arcadeai-labs/cade.git && cd cade && uv sync` |
121
+ ### Recommended (macOS + Linux)
122
+
123
+ ```bash
124
+ curl -fsSL https://arcadeagent.dev/install | sh
125
+ ```
122
126
 
123
- The install script detects your OS/arch, downloads the matching prebuilt binary
127
+ The script detects your OS/arch, downloads the matching prebuilt binary
124
128
  (macOS arm64, Linux x64), verifies its checksum, and drops `cade` in
125
- `~/.local/bin` — no GitHub token required. Pin a version or install location:
129
+ `~/.local/bin`. Pin a version or install location:
126
130
 
127
131
  ```bash
128
- CADE_VERSION=0.15.5 CADE_INSTALL_DIR=/usr/local/bin \
132
+ CADE_VERSION=0.16.2 CADE_INSTALL_DIR=/usr/local/bin \
129
133
  sh -c "$(curl -fsSL https://arcadeagent.dev/install)"
130
134
  ```
131
135
 
@@ -133,9 +137,21 @@ On Linux the installer auto-picks a **cpu** or **gpu** build (GPU when
133
137
  `nvidia-smi` detects a card; the GPU build bundles the CUDA stack and is a much
134
138
  larger download). Force it with `CADE_VARIANT=cpu` or `CADE_VARIANT=gpu`.
135
139
 
136
- (Windows: grab the `.zip` from the releases page, or `pip install cade-cli`.)
140
+ ### Alternatives
141
+
142
+ | Source | Command | When |
143
+ |---|---|---|
144
+ | uv | `uv tool install cade-cli` | Prefer a PyPI-managed tool venv |
145
+ | pip | `pip install cade-cli` | Windows, or no curl install |
146
+ | From source | `git clone … && uv sync` | Contributing / hacking on Cade |
147
+
148
+ (Windows: grab the `.zip` from the [releases](https://github.com/arcadeai-labs/cade/releases) page, or `pip install cade-cli`.)
137
149
 
138
- **Prerequisites:** Python 3.11+, an Arcade account ([arcade.dev](https://arcade.dev)) for cloud tools, and an LLM provider key (`OPENAI_API_KEY` or `ANTHROPIC_API_KEY`). Skip the Arcade account with `--local-only` or `CADE_LOCAL_ONLY=1`.
150
+ **Prerequisites to run Cade:** an LLM provider key (`OPENAI_API_KEY` or
151
+ `ANTHROPIC_API_KEY`), and an Arcade account ([arcade.dev](https://arcade.dev))
152
+ for cloud tools. Skip Arcade with `--local-only` or `CADE_LOCAL_ONLY=1`. The
153
+ curl installer does **not** require a local Python — Python 3.11+ is only needed
154
+ for the uv / pip / from-source paths.
139
155
 
140
156
  The first run walks you through picking a model provider and key (stored in your
141
157
  OS keyring); re-run it any time with `cade init`.
@@ -3,11 +3,15 @@
3
3
  The CLI agent from [Arcade.dev](https://arcade.dev). Coding, research, and everyday automation — driven from a terminal, a single shell pipe, or a long-lived daemon that replies over Telegram.
4
4
 
5
5
  ```bash
6
- curl -fsSL https://arcadeagent.dev/install | sh # or: uv tool install cade-cli | pip install cade-cli
7
- cade login
6
+ curl -fsSL https://arcadeagent.dev/install | sh
8
7
  cade
9
8
  ```
10
9
 
10
+ That is the **recommended install** — a prebuilt binary from
11
+ [arcadeagent.dev](https://arcadeagent.dev/install). No GitHub token, no Python
12
+ toolchain. First `cade` walks you through model setup, Arcade login, and the
13
+ [Arcade Agent Hub](https://hub.arcade.dev). Re-run the same curl to upgrade.
14
+
11
15
  ---
12
16
 
13
17
  ## Highlights
@@ -23,19 +27,18 @@ cade
23
27
 
24
28
  ## Install
25
29
 
26
- | Source | Command |
27
- |---|---|
28
- | **Install script** (recommended) | `curl -fsSL https://arcadeagent.dev/install \| sh` |
29
- | uv | `uv tool install cade-cli` |
30
- | pip | `pip install cade-cli` |
31
- | From source | `git clone https://github.com/arcadeai-labs/cade.git && cd cade && uv sync` |
30
+ ### Recommended (macOS + Linux)
31
+
32
+ ```bash
33
+ curl -fsSL https://arcadeagent.dev/install | sh
34
+ ```
32
35
 
33
- The install script detects your OS/arch, downloads the matching prebuilt binary
36
+ The script detects your OS/arch, downloads the matching prebuilt binary
34
37
  (macOS arm64, Linux x64), verifies its checksum, and drops `cade` in
35
- `~/.local/bin` — no GitHub token required. Pin a version or install location:
38
+ `~/.local/bin`. Pin a version or install location:
36
39
 
37
40
  ```bash
38
- CADE_VERSION=0.15.5 CADE_INSTALL_DIR=/usr/local/bin \
41
+ CADE_VERSION=0.16.2 CADE_INSTALL_DIR=/usr/local/bin \
39
42
  sh -c "$(curl -fsSL https://arcadeagent.dev/install)"
40
43
  ```
41
44
 
@@ -43,9 +46,21 @@ On Linux the installer auto-picks a **cpu** or **gpu** build (GPU when
43
46
  `nvidia-smi` detects a card; the GPU build bundles the CUDA stack and is a much
44
47
  larger download). Force it with `CADE_VARIANT=cpu` or `CADE_VARIANT=gpu`.
45
48
 
46
- (Windows: grab the `.zip` from the releases page, or `pip install cade-cli`.)
49
+ ### Alternatives
50
+
51
+ | Source | Command | When |
52
+ |---|---|---|
53
+ | uv | `uv tool install cade-cli` | Prefer a PyPI-managed tool venv |
54
+ | pip | `pip install cade-cli` | Windows, or no curl install |
55
+ | From source | `git clone … && uv sync` | Contributing / hacking on Cade |
56
+
57
+ (Windows: grab the `.zip` from the [releases](https://github.com/arcadeai-labs/cade/releases) page, or `pip install cade-cli`.)
47
58
 
48
- **Prerequisites:** Python 3.11+, an Arcade account ([arcade.dev](https://arcade.dev)) for cloud tools, and an LLM provider key (`OPENAI_API_KEY` or `ANTHROPIC_API_KEY`). Skip the Arcade account with `--local-only` or `CADE_LOCAL_ONLY=1`.
59
+ **Prerequisites to run Cade:** an LLM provider key (`OPENAI_API_KEY` or
60
+ `ANTHROPIC_API_KEY`), and an Arcade account ([arcade.dev](https://arcade.dev))
61
+ for cloud tools. Skip Arcade with `--local-only` or `CADE_LOCAL_ONLY=1`. The
62
+ curl installer does **not** require a local Python — Python 3.11+ is only needed
63
+ for the uv / pip / from-source paths.
49
64
 
50
65
  The first run walks you through picking a model provider and key (stored in your
51
66
  OS keyring); re-run it any time with `cade init`.
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "cade-cli"
3
- version = "0.16.0"
4
- description = "Cade - The CLI Agent from Arcade.dev"
3
+ version = "0.16.2"
4
+ description = "Cade - The CLI Agent from Arcade.dev (install: curl -fsSL https://arcadeagent.dev/install | sh)"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
7
7
  license = {file = "LICENSE"}
@@ -47,7 +47,8 @@ dependencies = [
47
47
 
48
48
  [project.urls]
49
49
  Homepage = "https://arcade.dev"
50
- Documentation = "https://docs.arcade.dev"
50
+ Documentation = "https://arcadeagent.dev/docs"
51
+ Install = "https://arcadeagent.dev/install"
51
52
  Repository = "https://github.com/arcadeai-labs/cade"
52
53
  Issues = "https://github.com/arcadeai-labs/cade/issues"
53
54
  Changelog = "https://github.com/arcadeai-labs/cade/releases"
@@ -304,7 +304,7 @@ def _doctor_paths(runtime: Any) -> dict[str, Any]:
304
304
  rich_help_panel=PANEL_SETUP,
305
305
  )
306
306
  def init_cmd() -> None:
307
- from cadecoder.cli.onboarding import can_prompt, run_onboarding
307
+ from cadecoder.cli.onboarding import can_prompt, finish_first_run_cloud, run_onboarding
308
308
 
309
309
  if not can_prompt():
310
310
  console.print(
@@ -312,7 +312,11 @@ def init_cmd() -> None:
312
312
  "Set OPENAI_API_KEY or ANTHROPIC_API_KEY instead."
313
313
  )
314
314
  raise typer.Exit(1)
315
- run_onboarding(console, force=True)
315
+ result = run_onboarding(console, force=True)
316
+ if result:
317
+ runtime = build_runtime()
318
+ set_runtime(runtime)
319
+ finish_first_run_cloud(console, prefer_skip_login=result.chose_local)
316
320
 
317
321
 
318
322
  @app.command(
@@ -484,11 +488,16 @@ def main_callback(
484
488
  # provider + model before we build the runtime, so the first session uses
485
489
  # what they chose (not the hardcoded default). No-ops when already set up or
486
490
  # when not attached to an interactive terminal (piped/CI).
487
- from cadecoder.cli.onboarding import needs_onboarding, run_onboarding
488
-
489
- did_onboard = False
491
+ from cadecoder.cli.onboarding import (
492
+ finish_first_run_cloud,
493
+ needs_onboarding,
494
+ run_onboarding,
495
+ )
496
+ from cadecoder.cli.onboarding import OnboardingResult
497
+
498
+ onboard_result = OnboardingResult()
490
499
  if needs_onboarding():
491
- did_onboard = run_onboarding(console)
500
+ onboard_result = run_onboarding(console)
492
501
 
493
502
  # Interaction + runtime-backed commands run as the current agent, auto-creating
494
503
  # a `default` agent on first run via build_runtime → ensure_current_agent.
@@ -496,13 +505,10 @@ def main_callback(
496
505
  set_runtime(runtime)
497
506
  ctx.obj["runtime"] = runtime
498
507
 
499
- # Right after first-run setup (runtime now available), offer to connect
500
- # Arcade Omni so external tools work out of the box. No-op otherwise.
501
- if did_onboard:
508
+ # After runtime is up: Arcade login (if needed) + Arcade Agent Hub connect.
509
+ if onboard_result:
502
510
  try:
503
- from cadecoder.cli.commands.mcp import offer_connect_omni
504
-
505
- offer_connect_omni(console)
511
+ finish_first_run_cloud(console, prefer_skip_login=onboard_result.chose_local)
506
512
  except Exception: # noqa: BLE001
507
513
  pass
508
514
 
@@ -86,15 +86,15 @@ def login(
86
86
 
87
87
  log.info(f"User {whoami.email} logged in via OAuth.")
88
88
 
89
- # Offer to connect Arcade Omni so the agent can use external tools
90
- # (Gmail, Slack, web search, …) without a separate API key. No-op when
91
- # non-interactive or already authorized.
89
+ # Offer to connect the Arcade Agent Hub so the agent can use external
90
+ # tools (Gmail, Slack, web search, …) without a separate API key.
91
+ # No-op when non-interactive or already authorized.
92
92
  try:
93
- from cadecoder.cli.commands.mcp import offer_connect_omni
93
+ from cadecoder.cli.commands.mcp import offer_connect_hub
94
94
 
95
- offer_connect_omni(console)
95
+ offer_connect_hub(console)
96
96
  except Exception as e: # noqa: BLE001
97
- log.debug(f"omni connect offer skipped: {e}")
97
+ log.debug(f"hub connect offer skipped: {e}")
98
98
 
99
99
  except OAuthLoginError as e:
100
100
  console.print(f"\n[red]✗ Login failed:[/red] {escape(str(e))}")
@@ -372,22 +372,26 @@ def run_mcp_authorization(
372
372
  """Run the MCP OAuth authorization flow for a server. Returns success.
373
373
 
374
374
  If ``name`` isn't configured yet but is a built-in known server (e.g.
375
- ``omni``), it's added in a disabled state first and only enabled once
375
+ ``hub``), it's added in a disabled state first and only enabled once
376
376
  authorization succeeds — so a cancelled/failed flow never leaves a broken
377
- server that would error during chat.
377
+ server that would error during chat. The legacy name ``omni`` resolves to
378
+ the Arcade Agent Hub.
378
379
  """
379
380
  global _oauth_result
380
381
  _oauth_result = {"code": None, "error": None}
381
382
 
382
383
  store = MCPServerStore()
383
- server = store.get(name)
384
+ # Resolve aliases (omni → hub) before looking up a stored config.
385
+ known = known_server_config(name)
386
+ lookup_name = known.name if known is not None else name
387
+ server = store.get(lookup_name) or store.get(name)
384
388
  if server is None:
385
- server = known_server_config(name)
389
+ server = known
386
390
  if server is None:
387
391
  console.print(f"[red]Server '{name}' not found.[/red]")
388
392
  console.print(
389
393
  "[dim]Add it with 'cade mcp add <name> <url>', "
390
- "or authorize a known server like 'omni'.[/dim]"
394
+ "or authorize a known server like 'hub'.[/dim]"
391
395
  )
392
396
  return False
393
397
  store.add(server) # persisted disabled; enabled on success
@@ -515,16 +519,17 @@ def run_mcp_authorization(
515
519
  return asyncio.run(run_oauth_flow())
516
520
 
517
521
 
518
- def offer_connect_omni(target_console: Console | None = None) -> None:
519
- """Offer to connect Arcade Omni (external tools) if not already authorized.
522
+ def offer_connect_hub(target_console: Console | None = None) -> None:
523
+ """Offer to connect the Arcade Agent Hub if not already authorized.
520
524
 
521
525
  Called after ``cade login`` and after first-run onboarding. No-op when the
522
- terminal isn't interactive or when Omni is already authorized, so it never
523
- blocks scripted/CI use.
526
+ terminal isn't interactive or when the Hub is already authorized, so it
527
+ never blocks scripted/CI use.
524
528
  """
525
529
  con = target_console or console
526
530
 
527
- from cadecoder.cli.onboarding import can_prompt
531
+ from cadecoder.cli.onboarding import can_prompt, is_arcade_logged_in, offer_arcade_login
532
+ from cadecoder.tools.manager.config import HUB_SERVER_NAME
528
533
 
529
534
  if not can_prompt():
530
535
  return
@@ -534,30 +539,57 @@ def offer_connect_omni(target_console: Console | None = None) -> None:
534
539
  except Exception: # noqa: BLE001
535
540
  return
536
541
 
537
- existing = store.get("omni")
542
+ existing = store.get(HUB_SERVER_NAME) or store.get("omni")
538
543
  if existing is not None and existing.oauth_tokens is not None:
539
544
  return # already connected
540
545
 
546
+ if not is_arcade_logged_in():
547
+ con.print()
548
+ con.print(
549
+ "[bold]Arcade login required[/bold] before connecting the "
550
+ "[cyan]Arcade Agent Hub[/cyan]."
551
+ )
552
+ if not offer_arcade_login(con, prefer_skip=False):
553
+ con.print(
554
+ "[dim]Skipped. Log in later with [bold]cade login[/bold], then "
555
+ "[bold]cade mcp authorize hub[/bold].[/dim]"
556
+ )
557
+ return
558
+
541
559
  from rich.prompt import Confirm
542
560
 
543
561
  con.print()
544
562
  con.print(
545
- "[bold]Connect external tools?[/bold] Arcade [cyan]Omni[/cyan] lets Cade use "
546
- "Gmail, Slack, GitHub, web search and more — authorized with your Arcade login "
547
- "(no API key needed)."
563
+ "[bold]Connect external tools?[/bold] The [cyan]Arcade Agent Hub[/cyan] "
564
+ "([link=https://hub.arcade.dev]hub.arcade.dev[/link]) lets Cade use "
565
+ "Gmail, Slack, GitHub, web search and more — authorized with your Arcade "
566
+ "login (no API key needed)."
548
567
  )
549
568
  try:
550
- proceed = Confirm.ask("Authorize Omni now? (opens your browser)", default=True, console=con)
569
+ proceed = Confirm.ask(
570
+ "Authorize Arcade Agent Hub now? (opens your browser)",
571
+ default=True,
572
+ console=con,
573
+ )
551
574
  except (KeyboardInterrupt, EOFError):
552
575
  con.print()
553
576
  return
554
577
 
555
578
  if not proceed:
556
- con.print("[dim]Skipped. Connect anytime with [bold]cade mcp authorize omni[/bold].[/dim]")
579
+ con.print(
580
+ "[dim]Skipped. Connect anytime with [bold]cade mcp authorize hub[/bold].[/dim]"
581
+ )
557
582
  return
558
583
 
559
- if not run_mcp_authorization("omni"):
560
- con.print("[dim]You can retry later with [bold]cade mcp authorize omni[/bold].[/dim]")
584
+ if not run_mcp_authorization(HUB_SERVER_NAME):
585
+ con.print(
586
+ "[dim]You can retry later with [bold]cade mcp authorize hub[/bold].[/dim]"
587
+ )
588
+
589
+
590
+ def offer_connect_omni(target_console: Console | None = None) -> None:
591
+ """Deprecated alias for :func:`offer_connect_hub`."""
592
+ offer_connect_hub(target_console)
561
593
 
562
594
 
563
595
  @mcp_app.command("authorize")
@@ -579,7 +611,7 @@ def authorize_server(
579
611
  """Authorize an MCP server over OAuth (browser flow).
580
612
 
581
613
  Works with any configured server, or a built-in known server by name
582
- (e.g. ``cade mcp authorize omni`` to connect Arcade's external tools).
614
+ (e.g. ``cade mcp authorize hub`` to connect the Arcade Agent Hub).
583
615
  """
584
616
  if not run_mcp_authorization(name, client_id=client_id, scope=scope, skip_dcr=skip_dcr):
585
617
  raise typer.Exit(1)
@@ -3,14 +3,18 @@
3
3
  A brand-new install has no obvious way to pick a model provider, and the silent
4
4
  provider fallback used to log a GPT model even when only an Anthropic key was
5
5
  set. This wizard runs once on first launch (and on demand via ``cade init``):
6
- it asks which provider to use, captures/stores an API key in the OS keyring,
7
- records a recommended default model, and writes the global config + default
8
- agent so the very first session uses exactly what the user chose.
6
+
7
+ 1. Model provider + API key + default model
8
+ 2. Arcade Cloud login (browser OAuth) skipped only if the user opts out
9
+ 3. Arcade Agent Hub connect (after the runtime is up)
10
+
11
+ Step 3 is :func:`finish_first_run_cloud` and must run after ``build_runtime``.
9
12
  """
10
13
 
11
14
  from __future__ import annotations
12
15
 
13
16
  import os
17
+ from dataclasses import dataclass
14
18
 
15
19
  from rich.console import Console
16
20
  from rich.panel import Panel
@@ -28,6 +32,17 @@ _PROVIDER_LOCAL = "local"
28
32
  _KEYRING_SERVICE = "cade/model"
29
33
 
30
34
 
35
+ @dataclass(frozen=True)
36
+ class OnboardingResult:
37
+ """Outcome of the model-setup half of first-run onboarding."""
38
+
39
+ configured: bool = False
40
+ chose_local: bool = False
41
+
42
+ def __bool__(self) -> bool:
43
+ return self.configured
44
+
45
+
31
46
  def _marker_path():
32
47
  """Path to the sentinel that records onboarding has completed."""
33
48
  return get_paths().state_dir / "onboarded"
@@ -168,28 +183,170 @@ class OnboardingAborted(Exception):
168
183
  """Raised when the user cancels onboarding (Ctrl-C / empty required input)."""
169
184
 
170
185
 
171
- def run_onboarding(console: Console, *, force: bool = False) -> bool:
172
- """Run the interactive setup wizard.
186
+ def is_arcade_logged_in() -> bool:
187
+ """Return True when Arcade Cloud credentials are present and usable."""
188
+ try:
189
+ from cadecoder.cli.auth import check_existing_login
190
+
191
+ return check_existing_login(suppress_message=True)
192
+ except Exception: # noqa: BLE001
193
+ return False
194
+
195
+
196
+ def offer_arcade_login(console: Console, *, prefer_skip: bool = False) -> bool:
197
+ """Offer Arcade Cloud OAuth during first-run / init.
198
+
199
+ Args:
200
+ console: Rich console for prompts.
201
+ prefer_skip: When True (local model path), default the confirm to No.
202
+
203
+ Returns:
204
+ True if the user is logged in when this returns (already was, or just did).
205
+ """
206
+ if not can_prompt():
207
+ return is_arcade_logged_in()
208
+
209
+ if is_arcade_logged_in():
210
+ console.print()
211
+ console.print("[green]✓[/green] Already logged in to Arcade Cloud.")
212
+ return True
213
+
214
+ console.print()
215
+ console.print(
216
+ Panel.fit(
217
+ "[bold]Step 2 of 3 — Arcade Cloud[/bold]\n\n"
218
+ "Log in so Cade can use Gmail, Slack, GitHub, calendars, and the\n"
219
+ "rest of the Arcade catalogue. Opens your browser (free account).\n\n"
220
+ "[dim]Skip only if you want local tools + your model — nothing else.[/dim]",
221
+ border_style="cyan",
222
+ )
223
+ )
224
+ try:
225
+ proceed = Confirm.ask(
226
+ "Log in to Arcade Cloud now?",
227
+ default=not prefer_skip,
228
+ console=console,
229
+ )
230
+ except (KeyboardInterrupt, EOFError):
231
+ console.print()
232
+ proceed = False
233
+
234
+ if not proceed:
235
+ console.print(
236
+ "[dim]Skipped. Chat still works with your model and local tools.\n"
237
+ "Run [bold]cade login[/bold] anytime for cloud apps "
238
+ "(or [bold]cade --local-only[/bold] to stay offline).[/dim]"
239
+ )
240
+ return False
241
+
242
+ return try_interactive_login(console)
243
+
244
+
245
+ def try_interactive_login(console: Console) -> bool:
246
+ """Run browser OAuth login; return True on success. Never raises Exit."""
247
+ from arcade_core.constants import PROD_COORDINATOR_HOST
248
+ from rich.markup import escape
249
+
250
+ from cadecoder.cli.auth import (
251
+ OAuthLoginError,
252
+ build_coordinator_url,
253
+ perform_oauth_login,
254
+ save_credentials_from_whoami,
255
+ )
256
+
257
+ try:
258
+ coordinator_url = build_coordinator_url(PROD_COORDINATOR_HOST, None)
259
+
260
+ def on_status(msg: str) -> None:
261
+ console.print(f"[dim]{msg}[/dim]")
262
+
263
+ tokens, whoami, client_id = perform_oauth_login(coordinator_url, on_status=on_status)
264
+ save_credentials_from_whoami(tokens, whoami, coordinator_url, client_id)
265
+ console.print("\n[green]✓ Login successful![/green]")
266
+ console.print(f" Email: {whoami.email}")
267
+ org = whoami.get_selected_org()
268
+ project = whoami.get_selected_project()
269
+ if org and project:
270
+ console.print(
271
+ f" Active: {org.get('name', 'unknown')} / {project.get('name', 'unknown')}"
272
+ )
273
+ return True
274
+ except OAuthLoginError as e:
275
+ console.print(f"\n[red]✗ Login failed:[/red] {escape(str(e))}")
276
+ console.print(
277
+ "[dim]Continue without cloud tools for now — "
278
+ "retry with [bold]cade login[/bold].[/dim]"
279
+ )
280
+ return False
281
+ except (KeyboardInterrupt, EOFError):
282
+ console.print("\n[yellow]Login cancelled.[/yellow]")
283
+ return False
284
+ except Exception as e: # noqa: BLE001
285
+ console.print(f"\n[red]✗ Login failed:[/red] {escape(str(e))}")
286
+ console.print("[dim]Retry anytime with [bold]cade login[/bold].[/dim]")
287
+ return False
288
+
289
+
290
+ def finish_first_run_cloud(console: Console, *, prefer_skip_login: bool = False) -> None:
291
+ """After runtime is ready: Arcade login (if needed), then Agent Hub connect.
292
+
293
+ Call this only after ``build_runtime()`` / ``set_runtime()`` so the Hub
294
+ MCP store can resolve the current agent.
295
+ """
296
+ if not can_prompt():
297
+ return
298
+
299
+ logged_in = offer_arcade_login(console, prefer_skip=prefer_skip_login)
300
+ if not logged_in:
301
+ return
302
+
303
+ try:
304
+ from cadecoder.cli.commands.mcp import offer_connect_hub
305
+
306
+ console.print()
307
+ console.print("[bold]Step 3 of 3 — Arcade Agent Hub[/bold] ([cyan]hub.arcade.dev[/cyan])")
308
+ offer_connect_hub(console)
309
+ except Exception: # noqa: BLE001
310
+ console.print(
311
+ "[dim]Skipped Arcade Agent Hub. Connect later with "
312
+ "[bold]cade mcp authorize hub[/bold].[/dim]"
313
+ )
314
+
315
+
316
+ def run_onboarding(console: Console, *, force: bool = False) -> OnboardingResult:
317
+ """Run the interactive model-setup wizard (step 1 of first-run).
173
318
 
174
319
  Args:
175
320
  console: Rich console (stderr) to render prompts on.
176
321
  force: Re-run even if already onboarded (``cade init``).
177
322
 
178
323
  Returns:
179
- True if a provider was configured; False if skipped/aborted.
324
+ :class:`OnboardingResult` (truthy when a provider was configured).
325
+ Call :func:`finish_first_run_cloud` after the runtime is built for
326
+ steps 2–3 (login + Arcade Agent Hub).
180
327
  """
181
328
  if not force and not needs_onboarding():
182
- return False
329
+ return OnboardingResult()
183
330
 
184
331
  if not can_prompt():
185
332
  # Non-interactive (piped/CI). Don't block; the provider error path will
186
333
  # guide the user if nothing is configured.
187
- return False
334
+ return OnboardingResult()
188
335
 
189
336
  console.print(
190
337
  Panel.fit(
191
- "Welcome to [bold]Cade[/bold] — let's pick a model provider.\n"
192
- "You can change this anytime with [bold]cade init[/bold].",
338
+ "Welcome to [bold]Cade[/bold] — three quick steps:\n"
339
+ " [bold]1.[/bold] Pick a model provider\n"
340
+ " [bold]2.[/bold] Log in to Arcade Cloud (cloud apps)\n"
341
+ " [bold]3.[/bold] Connect the Arcade Agent Hub\n\n"
342
+ "You can re-run this anytime with [bold]cade init[/bold].",
343
+ border_style="cyan",
344
+ )
345
+ )
346
+ console.print()
347
+ console.print(
348
+ Panel.fit(
349
+ "[bold]Step 1 of 3 — Model provider[/bold]",
193
350
  border_style="cyan",
194
351
  )
195
352
  )
@@ -238,10 +395,10 @@ def run_onboarding(console: Console, *, force: bool = False) -> bool:
238
395
  model = Prompt.ask("Model", default=default_model, console=console).strip() or default_model
239
396
  except (KeyboardInterrupt, EOFError):
240
397
  console.print("\n[yellow]Setup cancelled.[/yellow]")
241
- return False
398
+ return OnboardingResult()
242
399
  except OnboardingAborted as e:
243
400
  console.print(f"[yellow]Setup cancelled: {e}[/yellow]")
244
- return False
401
+ return OnboardingResult()
245
402
 
246
403
  _persist(
247
404
  provider=provider,
@@ -257,15 +414,16 @@ def run_onboarding(console: Console, *, force: bool = False) -> bool:
257
414
  console.print()
258
415
  console.print(
259
416
  Panel.fit(
260
- f"[bold]You're set up.[/bold]\n"
417
+ f"[bold]Step 1 done.[/bold]\n"
261
418
  f"Model: [cyan]{model}[/cyan]\n"
262
419
  f"Provider: [cyan]{label}[/cyan]\n\n"
263
- "Change it later with [bold]cade init[/bold] or edit "
420
+ "Next: Arcade login + Arcade Agent Hub (cloud apps).\n"
421
+ "Change the model later with [bold]cade init[/bold] or edit "
264
422
  f"[dim]{get_paths().global_config}[/dim].",
265
423
  border_style="green",
266
424
  )
267
425
  )
268
- return True
426
+ return OnboardingResult(configured=True, chose_local=(choice == "3"))
269
427
 
270
428
 
271
429
  def _persist(
@@ -151,6 +151,12 @@ def build_gateway_app(
151
151
  Returns:
152
152
  A configured MCPApp with ``Cade_Ask`` registered.
153
153
  """
154
+ # Idempotent; required for curl/PyInstaller installs where tool source
155
+ # text is unavailable to arcade-core's return-value introspection.
156
+ from cadecoder.core.frozen import patch_frozen_return_detection
157
+
158
+ patch_frozen_return_detection()
159
+
154
160
  validator = CadeTokenValidator(token_store) if require_auth else None
155
161
  # Exposure enforcement runs as MCP middleware (forwarded to MCPServer via
156
162
  # server_kwargs): it hides/blocks tools the principal's groups exclude.
@@ -92,6 +92,12 @@ async def run_daemon(
92
92
  seam: an async ``(app, host, port)`` callable used instead of the real
93
93
  uvicorn server so the wiring can be exercised without binding a socket.
94
94
  """
95
+ # Frozen/PyInstaller builds have no .py source for inspect.getsource();
96
+ # arcade-core needs this patch before any MCPApp.add_tool (Cade_Ask).
97
+ from cadecoder.core.frozen import patch_frozen_return_detection
98
+
99
+ patch_frozen_return_detection()
100
+
95
101
  host, port = _parse_bind(bind)
96
102
  _guard_auth_bind(host, require_auth)
97
103
  prewarm = prewarm or []