cade-cli 0.11.0__tar.gz → 0.13.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.
- {cade_cli-0.11.0 → cade_cli-0.13.2}/PKG-INFO +34 -28
- {cade_cli-0.11.0 → cade_cli-0.13.2}/README.md +27 -20
- {cade_cli-0.11.0 → cade_cli-0.13.2}/pyproject.toml +11 -8
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cade_mcp_local/server.py +7 -3
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/cli/app.py +48 -13
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/cli/commands/context.py +8 -1
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/core/config.py +12 -0
- cade_cli-0.13.2/src/cadecoder/core/frozen.py +37 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/execution/tool_result_store.py +4 -2
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/execution/tool_schema_cache.py +34 -2
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/providers/ollama.py +36 -6
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/serve/adapters/__init__.py +4 -1
- cade_cli-0.13.2/src/cadecoder/serve/adapters/desktop.py +408 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/serve/config.py +9 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/serve/daemon.py +56 -26
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/serve/observability.py +4 -1
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/serve/worker.py +91 -11
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/templates/serve/serve.toml.example +9 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/tools/manager/composite.py +41 -31
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/tools/search/service.py +61 -12
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/ui/session.py +75 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/ui/slash.py +1 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/voice/__init__.py +7 -1
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/voice/session.py +66 -7
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/voice/tts.py +15 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/.gitignore +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/LICENSE +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cade_mcp_local/__init__.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cade_mcp_local/__main__.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cade_mcp_local/_metadata.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cade_mcp_local/config.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cade_mcp_local/errors.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cade_mcp_local/resources.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cade_mcp_local/tools/__init__.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cade_mcp_local/tools/_read_cache.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cade_mcp_local/tools/context.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cade_mcp_local/tools/filesystem.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cade_mcp_local/tools/git.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cade_mcp_local/tools/notifications.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cade_mcp_local/tools/questions.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cade_mcp_local/tools/results.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cade_mcp_local/tools/schemas.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cade_mcp_local/tools/search.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cade_mcp_local/tools/shell.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cade_mcp_local/tools/snippets.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cade_mcp_local/tools/tasks.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cade_mcp_local/tools/web_fetch.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cade_mcp_local/tools/web_search.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cade_mcp_local/utils.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cade_mcp_local/web/__init__.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cade_mcp_local/web/arcade_client.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cade_mcp_local/web/cache.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cade_mcp_local/web/extract.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cade_mcp_local/web/markdown.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cade_mcp_local/web/preapproved.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cade_mcp_local/web/prompts.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cade_mcp_local/web/scrape.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cade_mcp_local/web/search/__init__.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cade_mcp_local/web/search/arcade.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cade_mcp_local/web/search/base.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cade_mcp_local/web/search/http.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/__init__.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/ai/__init__.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/ai/prompts.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/cli/__init__.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/cli/auth.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/cli/commands/__init__.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/cli/commands/account.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/cli/commands/auth.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/cli/commands/channels.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/cli/commands/chat.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/cli/commands/cron.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/cli/commands/hooks.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/cli/commands/mcp.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/cli/commands/mem.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/cli/commands/model.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/cli/commands/persona.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/cli/commands/serve.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/cli/commands/tasks.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/cli/commands/thread.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/cli/commands/tools.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/core/__init__.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/core/constants.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/core/errors.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/core/git.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/core/logging.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/core/names.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/core/paths.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/core/runtime.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/core/types.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/execution/__init__.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/execution/context_window.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/execution/mcp_schema_index.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/execution/orchestrator.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/execution/parallel.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/hooks/__init__.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/hooks/config.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/hooks/engine.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/hooks/executors/__init__.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/hooks/executors/callback.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/hooks/executors/command.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/hooks/executors/http.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/hooks/executors/prompt.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/hooks/executors/ssrf_guard.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/hooks/matchers.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/hooks/registry.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/hooks/types.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/providers/__init__.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/providers/anthropic.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/providers/base.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/providers/openai.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/serve/__init__.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/serve/adapters/base.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/serve/adapters/registry.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/serve/adapters/stub.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/serve/adapters/telegram.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/serve/allowlist.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/serve/chunker.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/serve/commands.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/serve/elicitation.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/serve/format.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/serve/install.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/serve/lock.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/serve/progress_sink.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/serve/router.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/serve/secrets.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/serve/sinks.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/storage/__init__.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/storage/personas.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/storage/threads.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/tasks/__init__.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/tasks/channels.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/tasks/lock.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/tasks/notifications.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/tasks/scheduler.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/tasks/store.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/tasks/types.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/templates/login_failed.html +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/templates/login_success.html +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/templates/serve/launchd.plist.tmpl +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/templates/serve/systemd.service.tmpl +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/templates/styles.css +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/tools/__init__.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/tools/manager/__init__.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/tools/manager/_request_ctx.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/tools/manager/base.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/tools/manager/config.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/tools/manager/mcp.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/tools/search/__init__.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/tools/search/discovered.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/tools/search/scoring.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/ui/__init__.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/ui/display.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/ui/elicitation.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/ui/input.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/voice/audio.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/voice/cleanup.py +0 -0
- {cade_cli-0.11.0 → cade_cli-0.13.2}/src/cadecoder/voice/stt.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cade-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.13.2
|
|
4
4
|
Summary: Cade - The CLI Agent from Arcade.dev
|
|
5
5
|
Project-URL: Homepage, https://arcade.dev
|
|
6
6
|
Project-URL: Documentation, https://docs.arcade.dev
|
|
@@ -43,10 +43,10 @@ Classifier: Topic :: Software Development
|
|
|
43
43
|
Classifier: Topic :: Software Development :: Code Generators
|
|
44
44
|
Classifier: Typing :: Typed
|
|
45
45
|
Requires-Python: >=3.11
|
|
46
|
-
Requires-Dist: agent-library<1.0.0,>=0.
|
|
46
|
+
Requires-Dist: agent-library<1.0.0,>=0.13.1
|
|
47
47
|
Requires-Dist: anthropic<1.0.0,>=0.34.0
|
|
48
48
|
Requires-Dist: arcade-core<5.0.0,>=4.7.0
|
|
49
|
-
Requires-Dist: arcade-mcp-server<2.0.0,>=1.
|
|
49
|
+
Requires-Dist: arcade-mcp-server<2.0.0,>=1.22.0
|
|
50
50
|
Requires-Dist: arcade-tdk<4.0.0,>=3.8.0
|
|
51
51
|
Requires-Dist: arcadepy<2.0.0,>=1.10.0
|
|
52
52
|
Requires-Dist: authlib<2.0.0,>=1.6.0
|
|
@@ -58,15 +58,14 @@ Requires-Dist: keyring<26.0,>=24.0
|
|
|
58
58
|
Requires-Dist: openai<2.0.0,>=1.0.0
|
|
59
59
|
Requires-Dist: prompt-toolkit<4.0.0,>=3.0.52
|
|
60
60
|
Requires-Dist: pydantic[email]<3.0.0,>=2.0.0
|
|
61
|
-
Requires-Dist: pyperclip<2.0.0,>=1.8.0
|
|
62
61
|
Requires-Dist: python-telegram-bot<22.0,>=21.0
|
|
63
|
-
Requires-Dist: pyyaml<7.0.0,>=6.0
|
|
64
62
|
Requires-Dist: rich<14.0.0,>=13.0.0
|
|
65
|
-
Requires-Dist: sounddevice>=0.5.5
|
|
66
|
-
Requires-Dist: tiktoken<1.0.0,>=0.11.0
|
|
67
63
|
Requires-Dist: toml<1.0.0,>=0.10.0
|
|
68
64
|
Requires-Dist: typer>0.10.0
|
|
69
65
|
Requires-Dist: ulid==1.1
|
|
66
|
+
Requires-Dist: websockets<16.0,>=14.0
|
|
67
|
+
Provides-Extra: build
|
|
68
|
+
Requires-Dist: pyinstaller<7.0.0,>=6.16.0; extra == 'build'
|
|
70
69
|
Provides-Extra: dev
|
|
71
70
|
Requires-Dist: mypy<2.0.0,>=1.10.0; extra == 'dev'
|
|
72
71
|
Requires-Dist: pytest-asyncio<1.0.0,>=0.24.0; extra == 'dev'
|
|
@@ -83,7 +82,7 @@ Provides-Extra: voice
|
|
|
83
82
|
Requires-Dist: mlx-audio<0.4.0,>=0.2.0; extra == 'voice'
|
|
84
83
|
Requires-Dist: mlx-whisper>=0.1.0; extra == 'voice'
|
|
85
84
|
Requires-Dist: numpy>=1.24.0; extra == 'voice'
|
|
86
|
-
Requires-Dist: sounddevice
|
|
85
|
+
Requires-Dist: sounddevice<1.0.0,>=0.5.5; extra == 'voice'
|
|
87
86
|
Requires-Dist: webrtcvad>=2.0.10; extra == 'voice'
|
|
88
87
|
Description-Content-Type: text/markdown
|
|
89
88
|
|
|
@@ -119,6 +118,8 @@ cade
|
|
|
119
118
|
| pip | `pip install cade-cli` |
|
|
120
119
|
| From source | `git clone https://github.com/arcadeai-labs/cade.git && cd cade && uv sync` |
|
|
121
120
|
|
|
121
|
+
Homebrew installs from a private release feed. Export `HOMEBREW_GITHUB_API_TOKEN` (a token with read access to `arcadeai-labs/cade`) before running `brew install`.
|
|
122
|
+
|
|
122
123
|
**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`.
|
|
123
124
|
|
|
124
125
|
```bash
|
|
@@ -126,6 +127,9 @@ cade login # Arcade Cloud OAuth (skip with --local-only)
|
|
|
126
127
|
cade --version
|
|
127
128
|
```
|
|
128
129
|
|
|
130
|
+
Full install and first-run docs: [`docs/install.md`](./docs/install.md) and
|
|
131
|
+
[`docs/quickstart.md`](./docs/quickstart.md).
|
|
132
|
+
|
|
129
133
|
---
|
|
130
134
|
|
|
131
135
|
## Quick start
|
|
@@ -133,11 +137,11 @@ cade --version
|
|
|
133
137
|
```bash
|
|
134
138
|
cade # interactive chat
|
|
135
139
|
cade -m "What changed in HEAD?" # single message, then exit
|
|
136
|
-
|
|
140
|
+
cade -m "What went wrong?" < error.log # prompt + file/stdin
|
|
137
141
|
cade -r # resume the most recent thread
|
|
138
142
|
cade resume "auth-rewrite" # resume a thread by name
|
|
139
143
|
cade --persona reviewer # use a saved persona's system prompt
|
|
140
|
-
cade --voice # speak / hear (voice
|
|
144
|
+
cade --voice # speak / hear (requires cade-cli[voice])
|
|
141
145
|
```
|
|
142
146
|
|
|
143
147
|
### Top-level options
|
|
@@ -145,9 +149,9 @@ cade --voice # speak / hear (voice mode)
|
|
|
145
149
|
| Flag | Description |
|
|
146
150
|
|---|---|
|
|
147
151
|
| `-r`, `--resume` | Resume most recent thread |
|
|
148
|
-
| `-m`, `--message` | Single-message mode
|
|
152
|
+
| `-m`, `--message` | Single-message mode; piped stdin is appended to the prompt |
|
|
149
153
|
| `-P`, `--persona` | Use a saved persona |
|
|
150
|
-
| `-V`, `--voice` | Voice mode |
|
|
154
|
+
| `-V`, `--voice` | Voice mode; install `cade-cli[voice]` first |
|
|
151
155
|
| `-v`, `--verbose` | Debug logging |
|
|
152
156
|
| `--version` | Print version |
|
|
153
157
|
|
|
@@ -172,7 +176,7 @@ cade --voice # speak / hear (voice mode)
|
|
|
172
176
|
|
|
173
177
|
Tools come from three sources and are addressed uniformly by their registered name:
|
|
174
178
|
|
|
175
|
-
- **`Local_*`** — built-in: filesystem (`ReadFile`, `WriteFile`, `Edit`, `ListFiles`), shell (`Bash`
|
|
179
|
+
- **`Local_*`** — built-in: filesystem (`ReadFile`, `WriteFile`, `Edit`, `ListFiles`), shell (`Bash`), `Search`, `Git`, `Task*`, `AskUserQuestion`, `RetrieveToolResult`, `ToolSchema`, `PinContext`, `PushNotification`, `WebFetch`, and `WebSearch`.
|
|
176
180
|
- **`Memory_*`** — agent-library backed, per-persona indexed knowledge store.
|
|
177
181
|
- **Arcade Cloud + user MCP servers** — anything you register.
|
|
178
182
|
|
|
@@ -215,7 +219,7 @@ Memory is persona-scoped — each persona has its own `~/.cade/data/contexts/<co
|
|
|
215
219
|
|
|
216
220
|
## Headless daemon: `cade serve`
|
|
217
221
|
|
|
218
|
-
Run Cade as a long-lived daemon that replies
|
|
222
|
+
Run Cade as a long-lived daemon that replies through adapters. Telegram is supported for personal bot workflows, and Desktop exposes a local WebSocket for native clients. Each conversation gets its own persistent thread — persona, memory, and tools carry across messages.
|
|
219
223
|
|
|
220
224
|
```bash
|
|
221
225
|
cade serve init # wizard: bot token, allowed senders
|
|
@@ -226,9 +230,9 @@ cade serve status / stop # health, uptime; SIGTERM running daemo
|
|
|
226
230
|
|
|
227
231
|
Config lives at `~/.cade/config/contexts/<context>.toml [serve]`. Edit and `kill -HUP $(cat ~/.cade/run/serve.pid)` to reload without restart.
|
|
228
232
|
|
|
229
|
-
**Security defaults are fail-closed:** `allowed_senders = []` blocks everyone; tools are default-deny against `[tools].allow` (globs / regex / pipe lists supported); `deny` always wins.
|
|
233
|
+
**Security defaults are fail-closed:** `allowed_senders = []` blocks everyone; tools are default-deny against `[serve.tools].allow` (globs / regex / pipe lists supported); `deny` always wins.
|
|
230
234
|
|
|
231
|
-
The MCP transport is fully bidirectional, so tools running inside the daemon can elicit structured input
|
|
235
|
+
The MCP transport is fully bidirectional, so tools running inside the daemon can elicit structured input, stream live progress, and emit logs through whichever adapter is active. See [`docs/adapters/`](./docs/adapters/) for adapter details, [`docs/mcp.md`](./docs/mcp.md) for protocol details, and [`docs/configuration.md`](./docs/configuration.md) for the full daemon config reference.
|
|
232
236
|
|
|
233
237
|
---
|
|
234
238
|
|
|
@@ -252,17 +256,18 @@ See [`docs/channels.md`](./docs/channels.md) for the channels protocol; hooks an
|
|
|
252
256
|
Cade works with any OpenAI-compatible endpoint. Three ways to wire one in (CLI flag → env → config, first match wins):
|
|
253
257
|
|
|
254
258
|
```bash
|
|
255
|
-
# CLI flag
|
|
256
|
-
cade chat --endpoint http://localhost:11434
|
|
259
|
+
# CLI flag for Ollama. Use a model from `ollama list`.
|
|
260
|
+
cade chat -L --endpoint http://localhost:11434 --model qwen3:4b
|
|
257
261
|
|
|
258
|
-
# Environment
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
+
# Environment for one-shot mode
|
|
263
|
+
cade context create local-qwen --model qwen3:4b
|
|
264
|
+
cade context use local-qwen
|
|
265
|
+
export OLLAMA_BASE_URL="http://localhost:11434"
|
|
266
|
+
CADE_LOCAL_ONLY=1 cade -m "hello from a local model"
|
|
262
267
|
|
|
263
268
|
# Config — ~/.cade/config/cade.toml
|
|
264
269
|
[model_settings]
|
|
265
|
-
host = "http://localhost:
|
|
270
|
+
host = "http://localhost:8000/v1"
|
|
266
271
|
api_key = "ollama"
|
|
267
272
|
```
|
|
268
273
|
|
|
@@ -303,14 +308,15 @@ Full layout, env vars, and TOML schema in [`docs/configuration.md`](./docs/confi
|
|
|
303
308
|
```bash
|
|
304
309
|
git clone https://github.com/arcadeai-labs/cade.git
|
|
305
310
|
cd cade
|
|
306
|
-
uv sync --
|
|
307
|
-
|
|
308
|
-
|
|
311
|
+
uv sync --extra dev
|
|
312
|
+
uv run pytest
|
|
313
|
+
uv run ruff check src/ tests/
|
|
309
314
|
```
|
|
310
315
|
|
|
311
316
|
Style: Python 3.11+ with modern type hints (`dict`, `list`, `| None`); ruff for lint + format; pytest with `asyncio_mode = "auto"`. Public functions and classes get docstrings.
|
|
312
317
|
|
|
313
|
-
|
|
318
|
+
Build/release docs live in [`docs/development.md`](./docs/development.md). PRs
|
|
319
|
+
welcome — open an issue first for anything substantial.
|
|
314
320
|
|
|
315
321
|
---
|
|
316
322
|
|
|
@@ -318,7 +324,7 @@ PRs welcome — open an issue first for anything substantial.
|
|
|
318
324
|
|
|
319
325
|
- [arcade.dev](https://arcade.dev) — product
|
|
320
326
|
- [docs.arcade.dev](https://docs.arcade.dev) — API and tool catalog
|
|
321
|
-
- [`docs/`](./docs/) —
|
|
327
|
+
- [`docs/`](./docs/) — quickstart, examples, install, config, MCP, channels, voice, development, and adapters
|
|
322
328
|
- [Issues](https://github.com/arcadeai-labs/cade/issues) · [Releases](https://github.com/arcadeai-labs/cade/releases)
|
|
323
329
|
|
|
324
330
|
## License
|
|
@@ -30,6 +30,8 @@ cade
|
|
|
30
30
|
| pip | `pip install cade-cli` |
|
|
31
31
|
| From source | `git clone https://github.com/arcadeai-labs/cade.git && cd cade && uv sync` |
|
|
32
32
|
|
|
33
|
+
Homebrew installs from a private release feed. Export `HOMEBREW_GITHUB_API_TOKEN` (a token with read access to `arcadeai-labs/cade`) before running `brew install`.
|
|
34
|
+
|
|
33
35
|
**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`.
|
|
34
36
|
|
|
35
37
|
```bash
|
|
@@ -37,6 +39,9 @@ cade login # Arcade Cloud OAuth (skip with --local-only)
|
|
|
37
39
|
cade --version
|
|
38
40
|
```
|
|
39
41
|
|
|
42
|
+
Full install and first-run docs: [`docs/install.md`](./docs/install.md) and
|
|
43
|
+
[`docs/quickstart.md`](./docs/quickstart.md).
|
|
44
|
+
|
|
40
45
|
---
|
|
41
46
|
|
|
42
47
|
## Quick start
|
|
@@ -44,11 +49,11 @@ cade --version
|
|
|
44
49
|
```bash
|
|
45
50
|
cade # interactive chat
|
|
46
51
|
cade -m "What changed in HEAD?" # single message, then exit
|
|
47
|
-
|
|
52
|
+
cade -m "What went wrong?" < error.log # prompt + file/stdin
|
|
48
53
|
cade -r # resume the most recent thread
|
|
49
54
|
cade resume "auth-rewrite" # resume a thread by name
|
|
50
55
|
cade --persona reviewer # use a saved persona's system prompt
|
|
51
|
-
cade --voice # speak / hear (voice
|
|
56
|
+
cade --voice # speak / hear (requires cade-cli[voice])
|
|
52
57
|
```
|
|
53
58
|
|
|
54
59
|
### Top-level options
|
|
@@ -56,9 +61,9 @@ cade --voice # speak / hear (voice mode)
|
|
|
56
61
|
| Flag | Description |
|
|
57
62
|
|---|---|
|
|
58
63
|
| `-r`, `--resume` | Resume most recent thread |
|
|
59
|
-
| `-m`, `--message` | Single-message mode
|
|
64
|
+
| `-m`, `--message` | Single-message mode; piped stdin is appended to the prompt |
|
|
60
65
|
| `-P`, `--persona` | Use a saved persona |
|
|
61
|
-
| `-V`, `--voice` | Voice mode |
|
|
66
|
+
| `-V`, `--voice` | Voice mode; install `cade-cli[voice]` first |
|
|
62
67
|
| `-v`, `--verbose` | Debug logging |
|
|
63
68
|
| `--version` | Print version |
|
|
64
69
|
|
|
@@ -83,7 +88,7 @@ cade --voice # speak / hear (voice mode)
|
|
|
83
88
|
|
|
84
89
|
Tools come from three sources and are addressed uniformly by their registered name:
|
|
85
90
|
|
|
86
|
-
- **`Local_*`** — built-in: filesystem (`ReadFile`, `WriteFile`, `Edit`, `ListFiles`), shell (`Bash`
|
|
91
|
+
- **`Local_*`** — built-in: filesystem (`ReadFile`, `WriteFile`, `Edit`, `ListFiles`), shell (`Bash`), `Search`, `Git`, `Task*`, `AskUserQuestion`, `RetrieveToolResult`, `ToolSchema`, `PinContext`, `PushNotification`, `WebFetch`, and `WebSearch`.
|
|
87
92
|
- **`Memory_*`** — agent-library backed, per-persona indexed knowledge store.
|
|
88
93
|
- **Arcade Cloud + user MCP servers** — anything you register.
|
|
89
94
|
|
|
@@ -126,7 +131,7 @@ Memory is persona-scoped — each persona has its own `~/.cade/data/contexts/<co
|
|
|
126
131
|
|
|
127
132
|
## Headless daemon: `cade serve`
|
|
128
133
|
|
|
129
|
-
Run Cade as a long-lived daemon that replies
|
|
134
|
+
Run Cade as a long-lived daemon that replies through adapters. Telegram is supported for personal bot workflows, and Desktop exposes a local WebSocket for native clients. Each conversation gets its own persistent thread — persona, memory, and tools carry across messages.
|
|
130
135
|
|
|
131
136
|
```bash
|
|
132
137
|
cade serve init # wizard: bot token, allowed senders
|
|
@@ -137,9 +142,9 @@ cade serve status / stop # health, uptime; SIGTERM running daemo
|
|
|
137
142
|
|
|
138
143
|
Config lives at `~/.cade/config/contexts/<context>.toml [serve]`. Edit and `kill -HUP $(cat ~/.cade/run/serve.pid)` to reload without restart.
|
|
139
144
|
|
|
140
|
-
**Security defaults are fail-closed:** `allowed_senders = []` blocks everyone; tools are default-deny against `[tools].allow` (globs / regex / pipe lists supported); `deny` always wins.
|
|
145
|
+
**Security defaults are fail-closed:** `allowed_senders = []` blocks everyone; tools are default-deny against `[serve.tools].allow` (globs / regex / pipe lists supported); `deny` always wins.
|
|
141
146
|
|
|
142
|
-
The MCP transport is fully bidirectional, so tools running inside the daemon can elicit structured input
|
|
147
|
+
The MCP transport is fully bidirectional, so tools running inside the daemon can elicit structured input, stream live progress, and emit logs through whichever adapter is active. See [`docs/adapters/`](./docs/adapters/) for adapter details, [`docs/mcp.md`](./docs/mcp.md) for protocol details, and [`docs/configuration.md`](./docs/configuration.md) for the full daemon config reference.
|
|
143
148
|
|
|
144
149
|
---
|
|
145
150
|
|
|
@@ -163,17 +168,18 @@ See [`docs/channels.md`](./docs/channels.md) for the channels protocol; hooks an
|
|
|
163
168
|
Cade works with any OpenAI-compatible endpoint. Three ways to wire one in (CLI flag → env → config, first match wins):
|
|
164
169
|
|
|
165
170
|
```bash
|
|
166
|
-
# CLI flag
|
|
167
|
-
cade chat --endpoint http://localhost:11434
|
|
171
|
+
# CLI flag for Ollama. Use a model from `ollama list`.
|
|
172
|
+
cade chat -L --endpoint http://localhost:11434 --model qwen3:4b
|
|
168
173
|
|
|
169
|
-
# Environment
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
174
|
+
# Environment for one-shot mode
|
|
175
|
+
cade context create local-qwen --model qwen3:4b
|
|
176
|
+
cade context use local-qwen
|
|
177
|
+
export OLLAMA_BASE_URL="http://localhost:11434"
|
|
178
|
+
CADE_LOCAL_ONLY=1 cade -m "hello from a local model"
|
|
173
179
|
|
|
174
180
|
# Config — ~/.cade/config/cade.toml
|
|
175
181
|
[model_settings]
|
|
176
|
-
host = "http://localhost:
|
|
182
|
+
host = "http://localhost:8000/v1"
|
|
177
183
|
api_key = "ollama"
|
|
178
184
|
```
|
|
179
185
|
|
|
@@ -214,14 +220,15 @@ Full layout, env vars, and TOML schema in [`docs/configuration.md`](./docs/confi
|
|
|
214
220
|
```bash
|
|
215
221
|
git clone https://github.com/arcadeai-labs/cade.git
|
|
216
222
|
cd cade
|
|
217
|
-
uv sync --
|
|
218
|
-
|
|
219
|
-
|
|
223
|
+
uv sync --extra dev
|
|
224
|
+
uv run pytest
|
|
225
|
+
uv run ruff check src/ tests/
|
|
220
226
|
```
|
|
221
227
|
|
|
222
228
|
Style: Python 3.11+ with modern type hints (`dict`, `list`, `| None`); ruff for lint + format; pytest with `asyncio_mode = "auto"`. Public functions and classes get docstrings.
|
|
223
229
|
|
|
224
|
-
|
|
230
|
+
Build/release docs live in [`docs/development.md`](./docs/development.md). PRs
|
|
231
|
+
welcome — open an issue first for anything substantial.
|
|
225
232
|
|
|
226
233
|
---
|
|
227
234
|
|
|
@@ -229,7 +236,7 @@ PRs welcome — open an issue first for anything substantial.
|
|
|
229
236
|
|
|
230
237
|
- [arcade.dev](https://arcade.dev) — product
|
|
231
238
|
- [docs.arcade.dev](https://docs.arcade.dev) — API and tool catalog
|
|
232
|
-
- [`docs/`](./docs/) —
|
|
239
|
+
- [`docs/`](./docs/) — quickstart, examples, install, config, MCP, channels, voice, development, and adapters
|
|
233
240
|
- [Issues](https://github.com/arcadeai-labs/cade/issues) · [Releases](https://github.com/arcadeai-labs/cade/releases)
|
|
234
241
|
|
|
235
242
|
## License
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "cade-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.13.2"
|
|
4
4
|
description = "Cade - The CLI Agent from Arcade.dev"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -27,26 +27,23 @@ dependencies = [
|
|
|
27
27
|
"typer>0.10.0",
|
|
28
28
|
"pydantic[email]>=2.0.0,<3.0.0",
|
|
29
29
|
"toml>=0.10.0,<1.0.0",
|
|
30
|
-
"pyyaml>=6.0,<7.0.0",
|
|
31
30
|
"openai>=1.0.0,<2.0.0",
|
|
32
31
|
"anthropic>=0.34.0,<1.0.0",
|
|
33
32
|
"ulid==1.1",
|
|
34
33
|
"arcade-tdk>=3.8.0,<4.0.0",
|
|
35
|
-
"arcade-mcp-server>=1.
|
|
34
|
+
"arcade-mcp-server>=1.22.0,<2.0.0",
|
|
36
35
|
"arcade-core>=4.7.0,<5.0.0",
|
|
37
36
|
"arcadepy>=1.10.0,<2.0.0",
|
|
38
37
|
"authlib>=1.6.0,<2.0.0",
|
|
39
|
-
"pyperclip>=1.8.0,<2.0.0",
|
|
40
38
|
"prompt-toolkit>=3.0.52,<4.0.0",
|
|
41
|
-
"tiktoken>=0.11.0,<1.0.0",
|
|
42
39
|
"httpx>=0.27.0,<1.0.0",
|
|
43
40
|
"filelock>=3.0.0,<4.0.0",
|
|
44
|
-
"agent-library>=0.
|
|
45
|
-
"sounddevice>=0.5.5",
|
|
41
|
+
"agent-library>=0.13.1,<1.0.0",
|
|
46
42
|
"croniter>=2.0.0,<4.0.0",
|
|
47
43
|
"python-telegram-bot>=21.0,<22.0",
|
|
48
44
|
"keyring>=24.0,<26.0",
|
|
49
45
|
"html2text>=2024.2.26",
|
|
46
|
+
"websockets>=14.0,<16.0",
|
|
50
47
|
]
|
|
51
48
|
|
|
52
49
|
[project.urls]
|
|
@@ -69,10 +66,13 @@ dev = [
|
|
|
69
66
|
"pytest-cov>=4.0.0,<5.0.0",
|
|
70
67
|
"mypy>=1.10.0,<2.0.0",
|
|
71
68
|
]
|
|
69
|
+
build = [
|
|
70
|
+
"pyinstaller>=6.16.0,<7.0.0",
|
|
71
|
+
]
|
|
72
72
|
voice = [
|
|
73
73
|
"mlx-audio>=0.2.0,<0.4.0",
|
|
74
74
|
"mlx-whisper>=0.1.0",
|
|
75
|
-
"sounddevice>=0.
|
|
75
|
+
"sounddevice>=0.5.5,<1.0.0",
|
|
76
76
|
"numpy>=1.24.0",
|
|
77
77
|
"webrtcvad>=2.0.10",
|
|
78
78
|
]
|
|
@@ -100,6 +100,9 @@ packages = ["src/cadecoder", "src/cade_mcp_local"]
|
|
|
100
100
|
testpaths = ["tests"]
|
|
101
101
|
asyncio_mode = "auto"
|
|
102
102
|
asyncio_default_fixture_loop_scope = "function"
|
|
103
|
+
markers = [
|
|
104
|
+
"integration: tests that cross process or network boundaries without external services",
|
|
105
|
+
]
|
|
103
106
|
|
|
104
107
|
[tool.ruff]
|
|
105
108
|
line-length = 100
|
|
@@ -13,8 +13,12 @@ os.environ.setdefault("LOGURU_LEVEL", "WARNING")
|
|
|
13
13
|
|
|
14
14
|
from arcade_mcp_server import MCPApp
|
|
15
15
|
|
|
16
|
-
from
|
|
17
|
-
|
|
16
|
+
from cadecoder.core.frozen import patch_frozen_return_detection
|
|
17
|
+
|
|
18
|
+
patch_frozen_return_detection()
|
|
19
|
+
|
|
20
|
+
from cade_mcp_local.resources import register_resources # noqa: E402
|
|
21
|
+
from cade_mcp_local.tools import ( # noqa: E402
|
|
18
22
|
ask_user_question_tool,
|
|
19
23
|
bash_tool,
|
|
20
24
|
edit_tool,
|
|
@@ -69,7 +73,7 @@ def create_app() -> MCPApp:
|
|
|
69
73
|
name="Local",
|
|
70
74
|
transport="stdio",
|
|
71
75
|
instructions=SERVER_INSTRUCTIONS,
|
|
72
|
-
log_level="
|
|
76
|
+
log_level="INFO",
|
|
73
77
|
)
|
|
74
78
|
|
|
75
79
|
# Filesystem tools
|
|
@@ -10,6 +10,12 @@ os.environ.setdefault("TOKENIZERS_PARALLELISM", "false")
|
|
|
10
10
|
# Suppress pkg_resources deprecation warning from webrtcvad and other packages
|
|
11
11
|
# that haven't migrated to importlib.resources yet.
|
|
12
12
|
# This MUST be set before any imports that might trigger the warning.
|
|
13
|
+
# Note: webrtcvad emits UserWarning, not DeprecationWarning
|
|
14
|
+
warnings.filterwarnings(
|
|
15
|
+
"ignore",
|
|
16
|
+
message=".*pkg_resources is deprecated.*",
|
|
17
|
+
category=UserWarning,
|
|
18
|
+
)
|
|
13
19
|
warnings.filterwarnings(
|
|
14
20
|
"ignore",
|
|
15
21
|
message=".*pkg_resources is deprecated.*",
|
|
@@ -42,7 +48,7 @@ from cadecoder.cli.commands.serve import serve_app
|
|
|
42
48
|
from cadecoder.cli.commands.tasks import tasks_app
|
|
43
49
|
from cadecoder.cli.commands.thread import thread_app
|
|
44
50
|
from cadecoder.cli.commands.tools import tool_app
|
|
45
|
-
from cadecoder.core.config import build_runtime, set_verbose_mode
|
|
51
|
+
from cadecoder.core.config import build_runtime, is_local_only_mode, set_verbose_mode
|
|
46
52
|
from cadecoder.core.logging import setup_logging
|
|
47
53
|
from cadecoder.core.runtime import set_runtime
|
|
48
54
|
|
|
@@ -191,6 +197,30 @@ def doctor_cmd(
|
|
|
191
197
|
raise typer.Exit(1)
|
|
192
198
|
|
|
193
199
|
|
|
200
|
+
@app.command(name="__mcp-local", hidden=True)
|
|
201
|
+
def _mcp_local_cmd() -> None:
|
|
202
|
+
"""Run the bundled local MCP server over stdio."""
|
|
203
|
+
from cadecoder.core.frozen import patch_frozen_return_detection
|
|
204
|
+
|
|
205
|
+
patch_frozen_return_detection()
|
|
206
|
+
|
|
207
|
+
from cade_mcp_local import main
|
|
208
|
+
|
|
209
|
+
main()
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
@app.command(name="__memory-mcp", hidden=True)
|
|
213
|
+
def _memory_mcp_cmd() -> None:
|
|
214
|
+
"""Run the bundled agent-library MCP server over stdio."""
|
|
215
|
+
from cadecoder.core.frozen import patch_frozen_return_detection
|
|
216
|
+
|
|
217
|
+
patch_frozen_return_detection()
|
|
218
|
+
|
|
219
|
+
from librarian.server import app as librarian_app
|
|
220
|
+
|
|
221
|
+
librarian_app.run(transport="stdio")
|
|
222
|
+
|
|
223
|
+
|
|
194
224
|
@app.callback()
|
|
195
225
|
def main_callback(
|
|
196
226
|
ctx: typer.Context,
|
|
@@ -237,7 +267,7 @@ def main_callback(
|
|
|
237
267
|
"--message",
|
|
238
268
|
"-m",
|
|
239
269
|
help="Single message mode: process one message and exit. "
|
|
240
|
-
"
|
|
270
|
+
"When stdin is piped, stdin is appended to this prompt.",
|
|
241
271
|
),
|
|
242
272
|
] = None,
|
|
243
273
|
version: Annotated[
|
|
@@ -275,22 +305,27 @@ def main_callback(
|
|
|
275
305
|
|
|
276
306
|
from cadecoder.ui.session import run_single_message_mode
|
|
277
307
|
|
|
278
|
-
# If
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
308
|
+
# If stdin is piped, append it to the prompt. This makes the natural
|
|
309
|
+
# shell forms work: `cade -m "Explain this" < file` and
|
|
310
|
+
# `git diff | cade -m "Review this diff"`.
|
|
311
|
+
if not sys.stdin.isatty():
|
|
312
|
+
stdin_text = sys.stdin.read().strip()
|
|
313
|
+
if stdin_text:
|
|
314
|
+
message = stdin_text if message == "" else f"{message}\n\n{stdin_text}"
|
|
315
|
+
elif message == "":
|
|
316
|
+
console.print("[red]Error: No message provided. Use -m 'message' or pipe input.[/red]")
|
|
317
|
+
raise typer.Exit(1)
|
|
288
318
|
|
|
289
319
|
if not message:
|
|
290
320
|
console.print("[red]Error: Empty message provided.[/red]")
|
|
291
321
|
raise typer.Exit(1)
|
|
292
322
|
|
|
293
|
-
exit_code = run_single_message_mode(
|
|
323
|
+
exit_code = run_single_message_mode(
|
|
324
|
+
message,
|
|
325
|
+
model=runtime.model,
|
|
326
|
+
local_only=is_local_only_mode(),
|
|
327
|
+
persona=runtime.persona_name,
|
|
328
|
+
)
|
|
294
329
|
raise typer.Exit(exit_code)
|
|
295
330
|
|
|
296
331
|
# Handle eager resume flag before default chat
|
|
@@ -25,7 +25,14 @@ from cadecoder.core.runtime import reset_runtime
|
|
|
25
25
|
|
|
26
26
|
console = Console(stderr=True)
|
|
27
27
|
context_app = typer.Typer(help="Manage activation contexts", invoke_without_command=True)
|
|
28
|
-
_EXPORT_SECRET_KEYS = {
|
|
28
|
+
_EXPORT_SECRET_KEYS = {
|
|
29
|
+
"auth_token",
|
|
30
|
+
"auth_value",
|
|
31
|
+
"oauth_client_secret",
|
|
32
|
+
"oauth_tokens",
|
|
33
|
+
"secret",
|
|
34
|
+
"secret_ref",
|
|
35
|
+
}
|
|
29
36
|
|
|
30
37
|
|
|
31
38
|
@context_app.callback()
|
|
@@ -125,8 +125,20 @@ class TelegramAdapterConfig(BaseModel):
|
|
|
125
125
|
drop_pending_updates: bool = True
|
|
126
126
|
|
|
127
127
|
|
|
128
|
+
class DesktopAdapterConfig(BaseModel):
|
|
129
|
+
model_config = ConfigDict(extra="ignore")
|
|
130
|
+
enabled: bool = False
|
|
131
|
+
host: str = "127.0.0.1"
|
|
132
|
+
port: int = 8765
|
|
133
|
+
auth_token: str = ""
|
|
134
|
+
allowed_senders: list[str] = Field(default_factory=lambda: ["desktop"])
|
|
135
|
+
allowed_conversations: list[str] = Field(default_factory=list)
|
|
136
|
+
speak_replies: bool = False
|
|
137
|
+
|
|
138
|
+
|
|
128
139
|
class AdaptersConfig(BaseModel):
|
|
129
140
|
model_config = ConfigDict(extra="allow")
|
|
141
|
+
desktop: DesktopAdapterConfig = Field(default_factory=DesktopAdapterConfig)
|
|
130
142
|
telegram: TelegramAdapterConfig = Field(default_factory=TelegramAdapterConfig)
|
|
131
143
|
|
|
132
144
|
def get(self, name: str) -> Any | None:
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Helpers for running Cade from frozen/PyInstaller binaries."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def patch_frozen_return_detection() -> None:
|
|
10
|
+
"""Avoid arcade-core source introspection failures in frozen builds.
|
|
11
|
+
|
|
12
|
+
arcade-core inspects function source to decide whether a tool returns a
|
|
13
|
+
value. Frozen binaries do not always expose source text through their
|
|
14
|
+
loader, so fall back to the function's return annotation when source is
|
|
15
|
+
unavailable.
|
|
16
|
+
"""
|
|
17
|
+
try:
|
|
18
|
+
import arcade_core.catalog as catalog
|
|
19
|
+
import arcade_core.utils as utils
|
|
20
|
+
except Exception:
|
|
21
|
+
return
|
|
22
|
+
|
|
23
|
+
original = utils.does_function_return_value
|
|
24
|
+
if getattr(original, "_cade_frozen_safe", False):
|
|
25
|
+
return
|
|
26
|
+
|
|
27
|
+
def _safe_does_function_return_value(function: Callable[..., Any]) -> bool:
|
|
28
|
+
try:
|
|
29
|
+
return original(function)
|
|
30
|
+
except ValueError as exc:
|
|
31
|
+
if str(exc) != "Source code not found":
|
|
32
|
+
raise
|
|
33
|
+
return function.__annotations__.get("return") is not None
|
|
34
|
+
|
|
35
|
+
_safe_does_function_return_value._cade_frozen_safe = True # type: ignore[attr-defined]
|
|
36
|
+
utils.does_function_return_value = _safe_does_function_return_value
|
|
37
|
+
catalog.does_function_return_value = _safe_does_function_return_value
|
|
@@ -36,8 +36,10 @@ TINY_RESULT_BYTES = 1_024
|
|
|
36
36
|
|
|
37
37
|
# Tools whose results must NEVER be externalized — doing so creates infinite
|
|
38
38
|
# retrieval loops (agent retrieves → result externalized → agent retrieves again → ...)
|
|
39
|
+
# or breaks schema-recovery flows that must stay directly visible in context.
|
|
39
40
|
NEVER_EXTERNALIZE_TOOLS: frozenset[str] = frozenset(
|
|
40
41
|
{
|
|
42
|
+
"Arcade_SelectTools",
|
|
41
43
|
"Local_RetrieveToolResult",
|
|
42
44
|
"Local_SearchRecentContext",
|
|
43
45
|
"Local_ToolSchema",
|
|
@@ -326,8 +328,8 @@ def _extract_tool_name(tool_obj: dict[str, Any]) -> str:
|
|
|
326
328
|
func = tool_obj.get("function", {})
|
|
327
329
|
if isinstance(func, dict) and func.get("name"):
|
|
328
330
|
return _strip_version_suffix(func["name"])
|
|
329
|
-
# Flat format: {"name": "..."}
|
|
330
|
-
return _strip_version_suffix(tool_obj.get("name"
|
|
331
|
+
# Flat / Arcade format: {"name": "..."} or {"tool_name": "..."}
|
|
332
|
+
return _strip_version_suffix(tool_obj.get("name") or tool_obj.get("tool_name") or "unknown")
|
|
331
333
|
|
|
332
334
|
|
|
333
335
|
def _extract_tool_description(tool_obj: dict[str, Any]) -> str:
|
|
@@ -135,6 +135,38 @@ class ToolSchemaCache:
|
|
|
135
135
|
)
|
|
136
136
|
return result
|
|
137
137
|
|
|
138
|
+
def iter_cached_tools(self, thread_id: str = "") -> list[CachedToolSchema]:
|
|
139
|
+
"""Return cached schemas for one thread or every thread.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
thread_id: Optional thread id. Empty means merge every thread cache.
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
Cached schemas de-duplicated by name, preferring the newest entry.
|
|
146
|
+
"""
|
|
147
|
+
entries: dict[str, CachedToolSchema] = {}
|
|
148
|
+
paths = (
|
|
149
|
+
[self._thread_path(thread_id)]
|
|
150
|
+
if thread_id
|
|
151
|
+
else self.storage_dir.glob("*/tool_schemas.json")
|
|
152
|
+
)
|
|
153
|
+
for path in paths:
|
|
154
|
+
if not self._disk_ok or not path.exists():
|
|
155
|
+
continue
|
|
156
|
+
try:
|
|
157
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
158
|
+
except (json.JSONDecodeError, OSError):
|
|
159
|
+
continue
|
|
160
|
+
for tool_data in data.get("tools", {}).values():
|
|
161
|
+
try:
|
|
162
|
+
entry = CachedToolSchema(**tool_data)
|
|
163
|
+
except (TypeError, ValueError):
|
|
164
|
+
continue
|
|
165
|
+
existing = entries.get(entry.name)
|
|
166
|
+
if existing is None or entry.cached_at > existing.cached_at:
|
|
167
|
+
entries[entry.name] = entry
|
|
168
|
+
return list(entries.values())
|
|
169
|
+
|
|
138
170
|
def list_all_cached(self) -> list[dict[str, Any]]:
|
|
139
171
|
"""List unique cached tools across every thread.
|
|
140
172
|
|
|
@@ -356,7 +388,7 @@ def _extract_tool_name(tool_obj: dict[str, Any]) -> str:
|
|
|
356
388
|
func = tool_obj.get("function", {})
|
|
357
389
|
if isinstance(func, dict) and func.get("name"):
|
|
358
390
|
return _strip_version_suffix(func["name"])
|
|
359
|
-
return _strip_version_suffix(tool_obj.get("name"
|
|
391
|
+
return _strip_version_suffix(tool_obj.get("name") or tool_obj.get("tool_name") or "unknown")
|
|
360
392
|
|
|
361
393
|
|
|
362
394
|
def _extract_tool_description(tool_obj: dict[str, Any]) -> str:
|
|
@@ -372,4 +404,4 @@ def _extract_tool_parameters(tool_obj: dict[str, Any]) -> dict[str, Any]:
|
|
|
372
404
|
func = tool_obj.get("function", {})
|
|
373
405
|
if isinstance(func, dict) and func.get("parameters"):
|
|
374
406
|
return func["parameters"]
|
|
375
|
-
return tool_obj.get("parameters"
|
|
407
|
+
return tool_obj.get("parameters") or tool_obj.get("input_schema") or {}
|