kader 2.7.2__tar.gz → 2.7.4__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.
- {kader-2.7.2 → kader-2.7.4}/PKG-INFO +5 -3
- {kader-2.7.2 → kader-2.7.4}/README.md +4 -2
- {kader-2.7.2 → kader-2.7.4}/cli/README.md +10 -3
- {kader-2.7.2 → kader-2.7.4}/cli/app.py +12 -123
- {kader-2.7.2 → kader-2.7.4}/cli/llm_factory.py +53 -2
- {kader-2.7.2 → kader-2.7.4}/docs/cli/index.md +8 -1
- {kader-2.7.2 → kader-2.7.4}/docs/configuration.md +12 -1
- {kader-2.7.2 → kader-2.7.4}/docs/guide.md +3 -0
- {kader-2.7.2 → kader-2.7.4}/kader/README.md +26 -2
- {kader-2.7.2 → kader-2.7.4}/kader/providers/ollama.py +34 -7
- {kader-2.7.2 → kader-2.7.4}/pyproject.toml +1 -1
- {kader-2.7.2 → kader-2.7.4}/uv.lock +1 -1
- {kader-2.7.2 → kader-2.7.4}/.github/workflows/ci.yml +0 -0
- {kader-2.7.2 → kader-2.7.4}/.github/workflows/pages.yml +0 -0
- {kader-2.7.2 → kader-2.7.4}/.github/workflows/release.yml +0 -0
- {kader-2.7.2 → kader-2.7.4}/.gitignore +0 -0
- {kader-2.7.2 → kader-2.7.4}/.kader/KADER.md +0 -0
- {kader-2.7.2 → kader-2.7.4}/.kader/commands/lint-test/CONTENT.md +0 -0
- {kader-2.7.2 → kader-2.7.4}/.kader/skills/contributing-to-kader/SKILL.md +0 -0
- {kader-2.7.2 → kader-2.7.4}/.kader/skills/contributing-to-kader/assets/contributor_checklist.md +0 -0
- {kader-2.7.2 → kader-2.7.4}/.kader/skills/contributing-to-kader/references/kader_agent_instructions.md +0 -0
- {kader-2.7.2 → kader-2.7.4}/.kader/skills/contributing-to-kader/scripts/dev_helper.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/.opencode/skills/contributing-to-kader/SKILL.md +0 -0
- {kader-2.7.2 → kader-2.7.4}/.opencode/skills/contributing-to-kader/assets/contributor_checklist.md +0 -0
- {kader-2.7.2 → kader-2.7.4}/.opencode/skills/contributing-to-kader/references/kader_agent_instructions.md +0 -0
- {kader-2.7.2 → kader-2.7.4}/.opencode/skills/contributing-to-kader/scripts/dev_helper.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/.python-version +0 -0
- {kader-2.7.2 → kader-2.7.4}/AGENTS.md +0 -0
- {kader-2.7.2 → kader-2.7.4}/CONTRIBUTING.md +0 -0
- {kader-2.7.2 → kader-2.7.4}/LICENSE +0 -0
- {kader-2.7.2 → kader-2.7.4}/assets/architecture/cli_integration.mmd +0 -0
- {kader-2.7.2 → kader-2.7.4}/assets/architecture/cli_message_flow.mmd +0 -0
- {kader-2.7.2 → kader-2.7.4}/assets/architecture/cli_tool_confirmation.mmd +0 -0
- {kader-2.7.2 → kader-2.7.4}/assets/architecture/memory_flow.mmd +0 -0
- {kader-2.7.2 → kader-2.7.4}/assets/architecture/planner_executor_sequence.mmd +0 -0
- {kader-2.7.2 → kader-2.7.4}/assets/architecture/planner_executor_workflow.mmd +0 -0
- {kader-2.7.2 → kader-2.7.4}/assets/design/v2/code.html +0 -0
- {kader-2.7.2 → kader-2.7.4}/assets/design/v2/code1.html +0 -0
- {kader-2.7.2 → kader-2.7.4}/assets/design/v2/code2.html +0 -0
- {kader-2.7.2 → kader-2.7.4}/assets/design/v2/code3.html +0 -0
- {kader-2.7.2 → kader-2.7.4}/assets/design/v2/screen.png +0 -0
- {kader-2.7.2 → kader-2.7.4}/assets/design/v2/screen1.png +0 -0
- {kader-2.7.2 → kader-2.7.4}/assets/design/v2/screen2.png +0 -0
- {kader-2.7.2 → kader-2.7.4}/assets/design/v2/screen3.png +0 -0
- {kader-2.7.2 → kader-2.7.4}/cli/__init__.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/cli/__main__.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/cli/commands/__init__.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/cli/commands/base.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/cli/commands/initialize.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/cli/utils.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/docs/assets/imgs/kader-cli.png +0 -0
- {kader-2.7.2 → kader-2.7.4}/docs/core-framework/agents.md +0 -0
- {kader-2.7.2 → kader-2.7.4}/docs/core-framework/index.md +0 -0
- {kader-2.7.2 → kader-2.7.4}/docs/core-framework/memory.md +0 -0
- {kader-2.7.2 → kader-2.7.4}/docs/core-framework/providers.md +0 -0
- {kader-2.7.2 → kader-2.7.4}/docs/core-framework/tools.md +0 -0
- {kader-2.7.2 → kader-2.7.4}/docs/index.md +0 -0
- {kader-2.7.2 → kader-2.7.4}/examples/.gitignore +0 -0
- {kader-2.7.2 → kader-2.7.4}/examples/README.md +0 -0
- {kader-2.7.2 → kader-2.7.4}/examples/anthropic_example.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/examples/google_example.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/examples/memory_example.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/examples/mistral_example.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/examples/ollama_example.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/examples/openai_compatible_example.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/examples/planner_executor_example.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/examples/planning_agent_example.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/examples/python_developer/main.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/examples/python_developer/template.yaml +0 -0
- {kader-2.7.2 → kader-2.7.4}/examples/react_agent_example.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/examples/simple_agent.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/examples/skills/hello_example.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/examples/skills/react_agent.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/examples/skills/skills/calculator/SKILL.md +0 -0
- {kader-2.7.2 → kader-2.7.4}/examples/skills/skills/calculator/scripts/calculate.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/examples/skills/skills/github/SKILL.md +0 -0
- {kader-2.7.2 → kader-2.7.4}/examples/skills/skills/hello/SKILL.md +0 -0
- {kader-2.7.2 → kader-2.7.4}/examples/skills/skills/hello/scripts/hello.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/examples/skills/skills/joke/SKILL.md +0 -0
- {kader-2.7.2 → kader-2.7.4}/examples/skills/skills/visualization/SKILL.md +0 -0
- {kader-2.7.2 → kader-2.7.4}/examples/todo_agent/main.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/examples/tools_example.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/__init__.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/agent/__init__.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/agent/agents.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/agent/base.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/agent/logger.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/config.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/memory/__init__.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/memory/compression.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/memory/conversation.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/memory/session.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/memory/state.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/memory/summarization.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/memory/types.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/prompts/__init__.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/prompts/agent_prompts.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/prompts/base.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/prompts/cli_prompts.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/prompts/templates/command_agent.j2 +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/prompts/templates/executor_agent.j2 +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/prompts/templates/init_command_prompt.j2 +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/prompts/templates/kader_planner.j2 +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/prompts/templates/planning_agent.j2 +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/prompts/templates/react_agent.j2 +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/providers/__init__.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/providers/anthropic.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/providers/base.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/providers/google.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/providers/mistral.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/providers/mock.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/providers/openai_compatible.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/tools/README.md +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/tools/__init__.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/tools/agent.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/tools/base.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/tools/commands.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/tools/exec_commands.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/tools/filesys.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/tools/filesystem.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/tools/protocol.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/tools/rag.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/tools/skills.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/tools/todo.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/tools/utils.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/tools/web.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/utils/__init__.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/utils/checkpointer.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/utils/context_aggregator.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/utils/ignore.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/workflows/__init__.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/workflows/base.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/kader/workflows/planner_executor.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/mkdocs.yml +0 -0
- {kader-2.7.2 → kader-2.7.4}/tests/conftest.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/tests/providers/test_anthropic_provider.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/tests/providers/test_google.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/tests/providers/test_mistral_provider.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/tests/providers/test_mock.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/tests/providers/test_ollama.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/tests/providers/test_openai_compatible_provider.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/tests/providers/test_providers_base.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/tests/test_agent_logger.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/tests/test_agent_logger_integration.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/tests/test_base_agent.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/tests/test_file_memory.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/tests/test_hierarchical_memory.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/tests/test_todo_tool.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/tests/tools/test_agent_tool.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/tests/tools/test_agent_tool_persistence.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/tests/tools/test_agent_tool_skills.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/tests/tools/test_exec_commands.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/tests/tools/test_filesys_tools.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/tests/tools/test_filesystem_tools.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/tests/tools/test_rag.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/tests/tools/test_skills.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/tests/tools/test_tools_base.py +0 -0
- {kader-2.7.2 → kader-2.7.4}/tests/tools/test_web.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kader
|
|
3
|
-
Version: 2.7.
|
|
3
|
+
Version: 2.7.4
|
|
4
4
|
Summary: kader coding agent
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Requires-Python: >=3.11
|
|
@@ -33,6 +33,7 @@ Kader is an intelligent coding agent designed to assist with software developmen
|
|
|
33
33
|
|
|
34
34
|
- 🤖 **AI-powered Code Assistance** - Support for multiple LLM providers:
|
|
35
35
|
- **Ollama**: Local LLM execution for privacy and speed.
|
|
36
|
+
- **Ollama Cloud**: Cloud-based models via [ollama.com](https://ollama.com).
|
|
36
37
|
- **Google Gemini**: Cloud-based powerful models via the Google GenAI SDK.
|
|
37
38
|
- **Anthropic**: High-quality Claude models via the Anthropic SDK.
|
|
38
39
|
- 🖥️ **Interactive CLI** - Modern terminal interface built with Rich & prompt_toolkit:
|
|
@@ -125,7 +126,7 @@ When the kader module is imported for the first time, it automatically creates a
|
|
|
125
126
|
### Environment Variables
|
|
126
127
|
|
|
127
128
|
The application automatically loads environment variables from `~/.kader/.env`:
|
|
128
|
-
- `OLLAMA_API_KEY`: API key for Ollama
|
|
129
|
+
- `OLLAMA_API_KEY`: API key for Ollama Cloud (for cloud models at ollama.com). Get your key from https://ollama.com/settings
|
|
129
130
|
- `GOOGLE_API_KEY`: API key for Google Gemini (required for Google Provider).
|
|
130
131
|
- `ANTHROPIC_API_KEY`: API key for Anthropic Claude (required for Anthropic Provider).
|
|
131
132
|
- Additional variables can be added to the `.env` file and will be automatically loaded.
|
|
@@ -143,7 +144,7 @@ Kader stores data in `~/.kader/`:
|
|
|
143
144
|
| Command | Description |
|
|
144
145
|
|---------|-------------|
|
|
145
146
|
| `/help` | Show command reference |
|
|
146
|
-
| `/models` | Show available models (Ollama, Google & Anthropic) |
|
|
147
|
+
| `/models` | Show available models (Ollama local & cloud, Google & Anthropic) |
|
|
147
148
|
| `/clear` | Clear conversation |
|
|
148
149
|
| `/save` | Save current session |
|
|
149
150
|
| `/load <id>` | Load a saved session |
|
|
@@ -211,6 +212,7 @@ Kader provides a robust agent architecture:
|
|
|
211
212
|
|
|
212
213
|
Kader supports multiple backends:
|
|
213
214
|
- **OllamaProvider**: Connects to locally running Ollama instances.
|
|
215
|
+
- **OllamaProvider (Cloud)**: Connects to cloud models at ollama.com (requires OLLAMA_API_KEY).
|
|
214
216
|
- **GoogleProvider**: High-performance access to Gemini models.
|
|
215
217
|
- **AnthropicProvider**: Full support for Claude models.
|
|
216
218
|
|
|
@@ -6,6 +6,7 @@ Kader is an intelligent coding agent designed to assist with software developmen
|
|
|
6
6
|
|
|
7
7
|
- 🤖 **AI-powered Code Assistance** - Support for multiple LLM providers:
|
|
8
8
|
- **Ollama**: Local LLM execution for privacy and speed.
|
|
9
|
+
- **Ollama Cloud**: Cloud-based models via [ollama.com](https://ollama.com).
|
|
9
10
|
- **Google Gemini**: Cloud-based powerful models via the Google GenAI SDK.
|
|
10
11
|
- **Anthropic**: High-quality Claude models via the Anthropic SDK.
|
|
11
12
|
- 🖥️ **Interactive CLI** - Modern terminal interface built with Rich & prompt_toolkit:
|
|
@@ -98,7 +99,7 @@ When the kader module is imported for the first time, it automatically creates a
|
|
|
98
99
|
### Environment Variables
|
|
99
100
|
|
|
100
101
|
The application automatically loads environment variables from `~/.kader/.env`:
|
|
101
|
-
- `OLLAMA_API_KEY`: API key for Ollama
|
|
102
|
+
- `OLLAMA_API_KEY`: API key for Ollama Cloud (for cloud models at ollama.com). Get your key from https://ollama.com/settings
|
|
102
103
|
- `GOOGLE_API_KEY`: API key for Google Gemini (required for Google Provider).
|
|
103
104
|
- `ANTHROPIC_API_KEY`: API key for Anthropic Claude (required for Anthropic Provider).
|
|
104
105
|
- Additional variables can be added to the `.env` file and will be automatically loaded.
|
|
@@ -116,7 +117,7 @@ Kader stores data in `~/.kader/`:
|
|
|
116
117
|
| Command | Description |
|
|
117
118
|
|---------|-------------|
|
|
118
119
|
| `/help` | Show command reference |
|
|
119
|
-
| `/models` | Show available models (Ollama, Google & Anthropic) |
|
|
120
|
+
| `/models` | Show available models (Ollama local & cloud, Google & Anthropic) |
|
|
120
121
|
| `/clear` | Clear conversation |
|
|
121
122
|
| `/save` | Save current session |
|
|
122
123
|
| `/load <id>` | Load a saved session |
|
|
@@ -184,6 +185,7 @@ Kader provides a robust agent architecture:
|
|
|
184
185
|
|
|
185
186
|
Kader supports multiple backends:
|
|
186
187
|
- **OllamaProvider**: Connects to locally running Ollama instances.
|
|
188
|
+
- **OllamaProvider (Cloud)**: Connects to cloud models at ollama.com (requires OLLAMA_API_KEY).
|
|
187
189
|
- **GoogleProvider**: High-performance access to Gemini models.
|
|
188
190
|
- **AnthropicProvider**: Full support for Claude models.
|
|
189
191
|
|
|
@@ -16,7 +16,7 @@ A modern terminal-based AI coding assistant built with [Rich](https://github.com
|
|
|
16
16
|
|
|
17
17
|
## Prerequisites
|
|
18
18
|
|
|
19
|
-
- [Ollama](https://ollama.ai/) running locally (for local models)
|
|
19
|
+
- [Ollama](https://ollama.ai/) running locally (for local models) or [Ollama Cloud](https://ollama.com) (for cloud models)
|
|
20
20
|
- Python 3.11 or higher
|
|
21
21
|
- [uv](https://docs.astral.sh/uv/) package manager (recommended) or [pip](https://pypi.org/project/pip/)
|
|
22
22
|
- API keys for cloud providers (optional, based on model selection)
|
|
@@ -207,7 +207,8 @@ DEFAULT_MODEL = "minimax-m2.5:cloud" # Default model
|
|
|
207
207
|
|
|
208
208
|
| Provider | Format | Example |
|
|
209
209
|
|----------|--------|---------|
|
|
210
|
-
| Ollama | `ollama:model` | `ollama:llama3` |
|
|
210
|
+
| Ollama (local) | `ollama:model` | `ollama:llama3` |
|
|
211
|
+
| Ollama (cloud) | `ollama:model:cloud` | `ollama:minimax-m2.5:cloud` |
|
|
211
212
|
| Google Gemini | `google:model` | `google:gemini-2.5-flash` |
|
|
212
213
|
| Mistral | `mistral:model` | `mistral:small-3.1` |
|
|
213
214
|
| Anthropic | `anthropic:model` | `anthropic:claude-3.5-sonnet` |
|
|
@@ -223,10 +224,16 @@ DEFAULT_MODEL = "minimax-m2.5:cloud" # Default model
|
|
|
223
224
|
Set API keys for cloud providers:
|
|
224
225
|
|
|
225
226
|
```bash
|
|
227
|
+
# Ollama Cloud (get from https://ollama.com/settings)
|
|
228
|
+
export OLLAMA_API_KEY="your-ollama-api-key"
|
|
229
|
+
|
|
230
|
+
# Google Gemini
|
|
226
231
|
export GOOGLE_API_KEY="your-google-api-key"
|
|
232
|
+
|
|
233
|
+
# Other providers...
|
|
227
234
|
export ANTHROPIC_API_KEY="your-anthropic-api-key"
|
|
228
235
|
export MISTRAL_API_KEY="your-mistral-api-key"
|
|
229
|
-
export OPENAI_API_KEY="your-
|
|
236
|
+
export OPENAI_API_KEY="your-openapi-key"
|
|
230
237
|
export MOONSHOT_API_KEY="your-kimi-api-key"
|
|
231
238
|
export ZAI_API_KEY="your-glm-api-key"
|
|
232
239
|
export OPENROUTER_API_KEY="your-openrouter-api-key"
|
|
@@ -612,139 +612,28 @@ class KaderApp:
|
|
|
612
612
|
self.console.print(f"\n [kader.orange]\\[>] Executing:[/kader.orange] `{cmd}`")
|
|
613
613
|
|
|
614
614
|
try:
|
|
615
|
-
output = await self.
|
|
615
|
+
output = await self._run_terminal_command_direct(cmd)
|
|
616
616
|
|
|
617
617
|
if not output:
|
|
618
|
-
|
|
619
|
-
|
|
618
|
+
output = "Command executed successfully with no output."
|
|
619
|
+
|
|
620
|
+
self.console.print()
|
|
621
|
+
self.console.print(
|
|
622
|
+
Panel(
|
|
623
|
+
output,
|
|
624
|
+
title="[kader.orange]Terminal Output[/kader.orange]",
|
|
625
|
+
border_style="dark_orange",
|
|
626
|
+
padding=(0, 1),
|
|
620
627
|
)
|
|
628
|
+
)
|
|
621
629
|
|
|
622
630
|
except Exception as e:
|
|
623
631
|
self.console.print(
|
|
624
632
|
rf" [kader.red]\[-][/kader.red] Error executing command: {e}"
|
|
625
633
|
)
|
|
626
634
|
|
|
627
|
-
async def _run_terminal_command_pty(self, command: str) -> str:
|
|
628
|
-
"""Run a terminal command using PTY for interactive support."""
|
|
629
|
-
import platform
|
|
630
|
-
|
|
631
|
-
system = platform.system().lower()
|
|
632
|
-
|
|
633
|
-
if system == "windows":
|
|
634
|
-
return await self._run_terminal_command_winpty(command)
|
|
635
|
-
else:
|
|
636
|
-
return await self._run_terminal_command_unix_pty(command)
|
|
637
|
-
|
|
638
|
-
async def _run_terminal_command_unix_pty(self, command: str) -> str:
|
|
639
|
-
"""Run a terminal command using PTY on Unix."""
|
|
640
|
-
import os
|
|
641
|
-
import pty
|
|
642
|
-
|
|
643
|
-
master_fd, slave_fd = pty.openpty()
|
|
644
|
-
|
|
645
|
-
env = os.environ.copy()
|
|
646
|
-
env["TERM"] = "xterm-256color"
|
|
647
|
-
|
|
648
|
-
shell = "/bin/bash"
|
|
649
|
-
|
|
650
|
-
try:
|
|
651
|
-
process = await asyncio.create_subprocess_exec(
|
|
652
|
-
shell,
|
|
653
|
-
"-c",
|
|
654
|
-
command,
|
|
655
|
-
stdin=slave_fd,
|
|
656
|
-
stdout=asyncio.subprocess.PIPE,
|
|
657
|
-
stderr=asyncio.subprocess.STDOUT,
|
|
658
|
-
env=env,
|
|
659
|
-
)
|
|
660
|
-
finally:
|
|
661
|
-
os.close(slave_fd)
|
|
662
|
-
os.close(master_fd)
|
|
663
|
-
|
|
664
|
-
output_parts = []
|
|
665
|
-
stdout = process.stdout
|
|
666
|
-
assert stdout is not None
|
|
667
|
-
|
|
668
|
-
while True:
|
|
669
|
-
line = await asyncio.wait_for(stdout.readline(), timeout=0.1)
|
|
670
|
-
if line:
|
|
671
|
-
text = line.decode("utf-8", errors="replace")
|
|
672
|
-
output_parts.append(text)
|
|
673
|
-
self.console.print(text, end="")
|
|
674
|
-
elif process.returncode is not None:
|
|
675
|
-
break
|
|
676
|
-
|
|
677
|
-
await process.wait()
|
|
678
|
-
return "".join(output_parts).strip()
|
|
679
|
-
|
|
680
|
-
async def _run_terminal_command_winpty(self, command: str) -> str:
|
|
681
|
-
"""Run a terminal command using pywinpty on Windows."""
|
|
682
|
-
|
|
683
|
-
try:
|
|
684
|
-
import pywinpty
|
|
685
|
-
except ImportError:
|
|
686
|
-
return await self._run_terminal_command_direct(command)
|
|
687
|
-
|
|
688
|
-
command_lower = command.lower().strip()
|
|
689
|
-
is_powershell = command_lower.startswith("pwsh") or command_lower.startswith(
|
|
690
|
-
"powershell"
|
|
691
|
-
)
|
|
692
|
-
|
|
693
|
-
if is_powershell:
|
|
694
|
-
shell = "powershell.exe"
|
|
695
|
-
shell_args = ["-Command", command]
|
|
696
|
-
else:
|
|
697
|
-
shell = "cmd.exe"
|
|
698
|
-
shell_args = ["/c", command]
|
|
699
|
-
|
|
700
|
-
try:
|
|
701
|
-
pty_obj = pywinpty.PTY(width=80, height=24, visible=False)
|
|
702
|
-
except Exception:
|
|
703
|
-
return await self._run_terminal_command_direct(command)
|
|
704
|
-
|
|
705
|
-
try:
|
|
706
|
-
process = pty_obj.spawn(shell, shell_args)
|
|
707
|
-
except Exception:
|
|
708
|
-
return await self._run_terminal_command_direct(command)
|
|
709
|
-
|
|
710
|
-
output_parts = []
|
|
711
|
-
max_attempts = 100
|
|
712
|
-
attempts = 0
|
|
713
|
-
|
|
714
|
-
while attempts < max_attempts:
|
|
715
|
-
try:
|
|
716
|
-
data = process.read(blocking=False)
|
|
717
|
-
if data:
|
|
718
|
-
output_parts.append(data)
|
|
719
|
-
self.console.print(data, end="")
|
|
720
|
-
attempts = 0
|
|
721
|
-
except Exception:
|
|
722
|
-
pass
|
|
723
|
-
|
|
724
|
-
if not process.isalive():
|
|
725
|
-
await asyncio.sleep(0.1)
|
|
726
|
-
try:
|
|
727
|
-
data = process.read(blocking=False)
|
|
728
|
-
if data:
|
|
729
|
-
output_parts.append(data)
|
|
730
|
-
self.console.print(data, end="")
|
|
731
|
-
except Exception:
|
|
732
|
-
pass
|
|
733
|
-
if not process.isalive():
|
|
734
|
-
break
|
|
735
|
-
|
|
736
|
-
attempts += 1
|
|
737
|
-
await asyncio.sleep(0.05)
|
|
738
|
-
|
|
739
|
-
result = "".join(output_parts).strip()
|
|
740
|
-
|
|
741
|
-
if not result:
|
|
742
|
-
return await self._run_terminal_command_direct(command)
|
|
743
|
-
|
|
744
|
-
return result
|
|
745
|
-
|
|
746
635
|
async def _run_terminal_command_direct(self, command: str) -> str:
|
|
747
|
-
"""Run a terminal command directly without PTY
|
|
636
|
+
"""Run a terminal command directly without PTY."""
|
|
748
637
|
|
|
749
638
|
try:
|
|
750
639
|
process = await asyncio.create_subprocess_shell(
|
|
@@ -16,6 +16,7 @@ from kader.providers import (
|
|
|
16
16
|
OpenAIProviderConfig,
|
|
17
17
|
)
|
|
18
18
|
from kader.providers.base import BaseLLMProvider, ModelConfig
|
|
19
|
+
from kader.providers.ollama import DEFAULT_CLOUD_HOST, DEFAULT_LOCAL_HOST
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
class LLMProviderFactory:
|
|
@@ -168,6 +169,23 @@ class LLMProviderFactory:
|
|
|
168
169
|
default_config=config,
|
|
169
170
|
)
|
|
170
171
|
|
|
172
|
+
# Handle Ollama provider (needs api_key for cloud models)
|
|
173
|
+
if provider_name == "ollama":
|
|
174
|
+
ollama_api_key = os.environ.get("OLLAMA_API_KEY")
|
|
175
|
+
|
|
176
|
+
# Strip :cloud suffix if present (we add it for display purposes)
|
|
177
|
+
actual_model_name = model_name.replace(":cloud", "")
|
|
178
|
+
|
|
179
|
+
# Use cloud host if API key is available
|
|
180
|
+
host = DEFAULT_CLOUD_HOST if ollama_api_key else None
|
|
181
|
+
|
|
182
|
+
return OllamaProvider(
|
|
183
|
+
model=actual_model_name,
|
|
184
|
+
host=host,
|
|
185
|
+
api_key=ollama_api_key,
|
|
186
|
+
default_config=config,
|
|
187
|
+
)
|
|
188
|
+
|
|
171
189
|
return provider_class(model=model_name, default_config=config)
|
|
172
190
|
|
|
173
191
|
@classmethod
|
|
@@ -226,13 +244,29 @@ class LLMProviderFactory:
|
|
|
226
244
|
"""
|
|
227
245
|
models: dict[str, list[str]] = {}
|
|
228
246
|
|
|
229
|
-
# Get Ollama models
|
|
247
|
+
# Get Ollama models (local first)
|
|
230
248
|
try:
|
|
231
|
-
ollama_models = OllamaProvider.get_supported_models()
|
|
249
|
+
ollama_models = OllamaProvider.get_supported_models(host=DEFAULT_LOCAL_HOST)
|
|
232
250
|
models["ollama"] = [f"ollama:{m}" for m in ollama_models]
|
|
233
251
|
except Exception:
|
|
234
252
|
models["ollama"] = []
|
|
235
253
|
|
|
254
|
+
# Also try Ollama Cloud if API key is available
|
|
255
|
+
ollama_api_key = os.environ.get("OLLAMA_API_KEY")
|
|
256
|
+
if ollama_api_key:
|
|
257
|
+
try:
|
|
258
|
+
cloud_models = OllamaProvider.get_supported_models(
|
|
259
|
+
host=DEFAULT_CLOUD_HOST, api_key=ollama_api_key
|
|
260
|
+
)
|
|
261
|
+
existing = set(models.get("ollama", []))
|
|
262
|
+
for m in cloud_models:
|
|
263
|
+
# Add :cloud suffix to distinguish from local models
|
|
264
|
+
prefixed = f"ollama:{m}:cloud"
|
|
265
|
+
if prefixed.replace(":cloud", "") not in existing:
|
|
266
|
+
models["ollama"].append(prefixed)
|
|
267
|
+
except Exception:
|
|
268
|
+
pass # Cloud failed, but local models still available
|
|
269
|
+
|
|
236
270
|
# Get Google models
|
|
237
271
|
try:
|
|
238
272
|
google_models = GoogleProvider.get_supported_models()
|
|
@@ -339,6 +373,23 @@ class LLMProviderFactory:
|
|
|
339
373
|
# Try to get models to verify provider is working
|
|
340
374
|
try:
|
|
341
375
|
provider_class = cls.PROVIDERS[provider_name]
|
|
376
|
+
|
|
377
|
+
# Special handling for Ollama: check both local and cloud
|
|
378
|
+
if provider_name == "ollama":
|
|
379
|
+
local_models = provider_class.get_supported_models(
|
|
380
|
+
host=DEFAULT_LOCAL_HOST
|
|
381
|
+
)
|
|
382
|
+
if local_models:
|
|
383
|
+
return True
|
|
384
|
+
# Also try cloud if API key is available
|
|
385
|
+
ollama_api_key = os.environ.get("OLLAMA_API_KEY")
|
|
386
|
+
if ollama_api_key:
|
|
387
|
+
cloud_models = provider_class.get_supported_models(
|
|
388
|
+
host=DEFAULT_CLOUD_HOST, api_key=ollama_api_key
|
|
389
|
+
)
|
|
390
|
+
return len(cloud_models) > 0
|
|
391
|
+
return False
|
|
392
|
+
|
|
342
393
|
models = provider_class.get_supported_models()
|
|
343
394
|
return len(models) > 0
|
|
344
395
|
except Exception:
|
|
@@ -158,7 +158,8 @@ The model selection interface allows you to:
|
|
|
158
158
|
|
|
159
159
|
| Provider | Format | Example |
|
|
160
160
|
|----------|--------|---------|
|
|
161
|
-
| Ollama | `ollama:model` | `ollama:llama3` |
|
|
161
|
+
| Ollama (local) | `ollama:model` | `ollama:llama3` |
|
|
162
|
+
| Ollama (cloud) | `ollama:model:cloud` | `ollama:minimax-m2.5:cloud` |
|
|
162
163
|
| Google Gemini | `google:model` | `google:gemini-2.5-flash` |
|
|
163
164
|
| Mistral | `mistral:model` | `mistral:small-3.1` |
|
|
164
165
|
| Anthropic | `anthropic:model` | `anthropic:claude-3.5-sonnet` |
|
|
@@ -172,7 +173,13 @@ The model selection interface allows you to:
|
|
|
172
173
|
### Setting API Keys
|
|
173
174
|
|
|
174
175
|
```bash
|
|
176
|
+
# Ollama Cloud (get from https://ollama.com/settings)
|
|
177
|
+
export OLLAMA_API_KEY="your-ollama-api-key"
|
|
178
|
+
|
|
179
|
+
# Google Gemini
|
|
175
180
|
export GOOGLE_API_KEY="your-google-api-key"
|
|
181
|
+
|
|
182
|
+
# Other providers...
|
|
176
183
|
export ANTHROPIC_API_KEY="your-anthropic-api-key"
|
|
177
184
|
export MISTRAL_API_KEY="your-mistral-api-key"
|
|
178
185
|
export OPENAI_API_KEY="your-openai-api-key"
|
|
@@ -7,6 +7,7 @@ Kader can be configured through environment variables, YAML files, and the `.kad
|
|
|
7
7
|
| Variable | Description | Required |
|
|
8
8
|
|----------|-------------|----------|
|
|
9
9
|
| `KADER_DIR` | Kader config directory (default: `~/.kader`) | No |
|
|
10
|
+
| `OLLAMA_API_KEY` | Ollama Cloud API key (get from https://ollama.com/settings) | For Ollama Cloud |
|
|
10
11
|
| `GEMINI_API_KEY` | Google Gemini API key | For Google Provider |
|
|
11
12
|
| `MISTRAL_API_KEY` | Mistral API key | For Mistral Provider |
|
|
12
13
|
| `ANTHROPIC_API_KEY` | Anthropic API key | For Anthropic Provider |
|
|
@@ -57,7 +58,7 @@ agent = BaseAgent.from_yaml("agent.yaml")
|
|
|
57
58
|
|
|
58
59
|
## Provider Configuration Format
|
|
59
60
|
|
|
60
|
-
### Ollama
|
|
61
|
+
### Ollama (Local)
|
|
61
62
|
|
|
62
63
|
```yaml
|
|
63
64
|
provider:
|
|
@@ -67,6 +68,16 @@ provider:
|
|
|
67
68
|
timeout: 120
|
|
68
69
|
```
|
|
69
70
|
|
|
71
|
+
### Ollama (Cloud)
|
|
72
|
+
|
|
73
|
+
```yaml
|
|
74
|
+
provider:
|
|
75
|
+
provider: ollama
|
|
76
|
+
model: minimax-m2.5
|
|
77
|
+
base_url: "https://ollama.com"
|
|
78
|
+
# api_key: "your-ollama-api-key" # Or set OLLAMA_API_KEY env var
|
|
79
|
+
```
|
|
80
|
+
|
|
70
81
|
### Google Gemini
|
|
71
82
|
|
|
72
83
|
```yaml
|
|
@@ -41,6 +41,9 @@ pip install -e .
|
|
|
41
41
|
For cloud providers, create a `.env` file in `~/.kader/.env`:
|
|
42
42
|
|
|
43
43
|
```bash
|
|
44
|
+
# Ollama Cloud (get from https://ollama.com/settings)
|
|
45
|
+
OLLAMA_API_KEY='your-api-key'
|
|
46
|
+
|
|
44
47
|
# Google Gemini
|
|
45
48
|
GEMINI_API_KEY='your-api-key'
|
|
46
49
|
|
|
@@ -66,6 +69,14 @@ ollama pull llama3.2
|
|
|
66
69
|
ollama pull qwen2.5
|
|
67
70
|
```
|
|
68
71
|
|
|
72
|
+
For cloud models via Ollama Cloud, get an API key from https://ollama.com/settings and set it in your environment:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
export OLLAMA_API_KEY="your-api-key"
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Then use cloud models with the `OllamaProvider` by specifying the cloud host:
|
|
79
|
+
|
|
69
80
|
## Core Concepts
|
|
70
81
|
|
|
71
82
|
### 1. Providers
|
|
@@ -302,10 +313,22 @@ For local LLM inference. Best for privacy, speed, and offline capability.
|
|
|
302
313
|
```python
|
|
303
314
|
from kader.providers import OllamaProvider, Message
|
|
304
315
|
|
|
316
|
+
# Local model (default - connects to localhost:11434)
|
|
305
317
|
provider = OllamaProvider(
|
|
306
318
|
model="llama3.2", # Model name
|
|
307
|
-
|
|
308
|
-
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
# Cloud model (requires OLLAMA_API_KEY environment variable)
|
|
322
|
+
provider = OllamaProvider(
|
|
323
|
+
model="minimax-m2.5", # Cloud model name
|
|
324
|
+
host="https://ollama.com", # Cloud endpoint
|
|
325
|
+
api_key="your-api-key", # Or set OLLAMA_API_KEY env var
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
# Custom local endpoint
|
|
329
|
+
provider = OllamaProvider(
|
|
330
|
+
model="llama3.2",
|
|
331
|
+
host="http://localhost:11434",
|
|
309
332
|
)
|
|
310
333
|
|
|
311
334
|
# Basic invocation
|
|
@@ -907,6 +930,7 @@ python ollama_example.py
|
|
|
907
930
|
| Variable | Description |
|
|
908
931
|
|----------|-------------|
|
|
909
932
|
| `KADER_DIR` | Kader config directory (default: `~/.kader`) |
|
|
933
|
+
| `OLLAMA_API_KEY` | Ollama Cloud API key (for cloud models at ollama.com) |
|
|
910
934
|
| `GEMINI_API_KEY` | Google Gemini API key |
|
|
911
935
|
| `MISTRAL_API_KEY` | Mistral API key |
|
|
912
936
|
| `OPENAI_API_KEY` | OpenAI API key |
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Ollama LLM Provider implementation.
|
|
3
3
|
|
|
4
|
-
Provides synchronous and asynchronous access to Ollama models
|
|
4
|
+
Provides synchronous and asynchronous access to Ollama models,
|
|
5
|
+
including both local and cloud (ollama.com) models.
|
|
5
6
|
"""
|
|
6
7
|
|
|
8
|
+
import os
|
|
7
9
|
from typing import AsyncIterator, Iterator
|
|
8
10
|
|
|
9
11
|
from ollama import AsyncClient, Client
|
|
@@ -20,6 +22,9 @@ from .base import (
|
|
|
20
22
|
Usage,
|
|
21
23
|
)
|
|
22
24
|
|
|
25
|
+
DEFAULT_LOCAL_HOST = "http://localhost:11434"
|
|
26
|
+
DEFAULT_CLOUD_HOST = "https://ollama.com"
|
|
27
|
+
|
|
23
28
|
|
|
24
29
|
class OllamaProvider(BaseLLMProvider):
|
|
25
30
|
"""
|
|
@@ -38,6 +43,7 @@ class OllamaProvider(BaseLLMProvider):
|
|
|
38
43
|
self,
|
|
39
44
|
model: str,
|
|
40
45
|
host: str | None = None,
|
|
46
|
+
api_key: str | None = None,
|
|
41
47
|
default_config: ModelConfig | None = None,
|
|
42
48
|
) -> None:
|
|
43
49
|
"""
|
|
@@ -46,12 +52,23 @@ class OllamaProvider(BaseLLMProvider):
|
|
|
46
52
|
Args:
|
|
47
53
|
model: The Ollama model identifier (e.g., "llama3.2", "gpt-oss:120b-cloud")
|
|
48
54
|
host: Optional Ollama server host (default: http://localhost:11434)
|
|
55
|
+
api_key: Optional API key for Ollama Cloud (ollama.com)
|
|
49
56
|
default_config: Default configuration for all requests
|
|
50
57
|
"""
|
|
51
58
|
super().__init__(model=model, default_config=default_config)
|
|
52
59
|
self._host = host
|
|
53
|
-
self.
|
|
54
|
-
|
|
60
|
+
self._api_key = api_key or os.environ.get("OLLAMA_API_KEY")
|
|
61
|
+
|
|
62
|
+
headers = None
|
|
63
|
+
if self._api_key:
|
|
64
|
+
headers = {"Authorization": f"Bearer {self._api_key}"}
|
|
65
|
+
|
|
66
|
+
if host:
|
|
67
|
+
self._client = Client(host=host, headers=headers)
|
|
68
|
+
self._async_client = AsyncClient(host=host, headers=headers)
|
|
69
|
+
else:
|
|
70
|
+
self._client = Client()
|
|
71
|
+
self._async_client = AsyncClient()
|
|
55
72
|
|
|
56
73
|
def _convert_messages(self, messages: list[Message]) -> list[dict]:
|
|
57
74
|
"""Convert Message objects to Ollama format."""
|
|
@@ -416,18 +433,28 @@ class OllamaProvider(BaseLLMProvider):
|
|
|
416
433
|
return None
|
|
417
434
|
|
|
418
435
|
@classmethod
|
|
419
|
-
def get_supported_models(
|
|
436
|
+
def get_supported_models(
|
|
437
|
+
cls, host: str | None = None, api_key: str | None = None
|
|
438
|
+
) -> list[str]:
|
|
420
439
|
"""
|
|
421
440
|
Get list of models available on the Ollama server.
|
|
422
441
|
|
|
423
442
|
Args:
|
|
424
|
-
host: Optional Ollama server host
|
|
443
|
+
host: Optional Ollama server host (e.g., "http://localhost:11434" or "https://ollama.com")
|
|
444
|
+
api_key: Optional API key for Ollama Cloud
|
|
425
445
|
|
|
426
446
|
Returns:
|
|
427
447
|
List of available model names
|
|
428
448
|
"""
|
|
449
|
+
api_key = api_key or os.environ.get("OLLAMA_API_KEY")
|
|
450
|
+
headers = None
|
|
451
|
+
if api_key:
|
|
452
|
+
headers = {"Authorization": f"Bearer {api_key}"}
|
|
453
|
+
|
|
429
454
|
try:
|
|
430
|
-
client =
|
|
455
|
+
client = (
|
|
456
|
+
Client(host=host, headers=headers) if host else Client(headers=headers)
|
|
457
|
+
)
|
|
431
458
|
response = client.list()
|
|
432
459
|
models = [model.model for model in response.models]
|
|
433
460
|
models_config = {}
|
|
@@ -444,4 +471,4 @@ class OllamaProvider(BaseLLMProvider):
|
|
|
444
471
|
|
|
445
472
|
def list_models(self) -> list[str]:
|
|
446
473
|
"""List all available models on the Ollama server."""
|
|
447
|
-
return self.get_supported_models(self._host)
|
|
474
|
+
return self.get_supported_models(self._host, self._api_key)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kader-2.7.2 → kader-2.7.4}/.kader/skills/contributing-to-kader/assets/contributor_checklist.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{kader-2.7.2 → kader-2.7.4}/.opencode/skills/contributing-to-kader/assets/contributor_checklist.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|