PraisonAI 3.0.0__py3-none-any.whl
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.
- praisonai/__init__.py +54 -0
- praisonai/__main__.py +15 -0
- praisonai/acp/__init__.py +54 -0
- praisonai/acp/config.py +159 -0
- praisonai/acp/server.py +587 -0
- praisonai/acp/session.py +219 -0
- praisonai/adapters/__init__.py +50 -0
- praisonai/adapters/readers.py +395 -0
- praisonai/adapters/rerankers.py +315 -0
- praisonai/adapters/retrievers.py +394 -0
- praisonai/adapters/vector_stores.py +409 -0
- praisonai/agent_scheduler.py +337 -0
- praisonai/agents_generator.py +903 -0
- praisonai/api/call.py +292 -0
- praisonai/auto.py +1197 -0
- praisonai/capabilities/__init__.py +275 -0
- praisonai/capabilities/a2a.py +140 -0
- praisonai/capabilities/assistants.py +283 -0
- praisonai/capabilities/audio.py +320 -0
- praisonai/capabilities/batches.py +469 -0
- praisonai/capabilities/completions.py +336 -0
- praisonai/capabilities/container_files.py +155 -0
- praisonai/capabilities/containers.py +93 -0
- praisonai/capabilities/embeddings.py +158 -0
- praisonai/capabilities/files.py +467 -0
- praisonai/capabilities/fine_tuning.py +293 -0
- praisonai/capabilities/guardrails.py +182 -0
- praisonai/capabilities/images.py +330 -0
- praisonai/capabilities/mcp.py +190 -0
- praisonai/capabilities/messages.py +270 -0
- praisonai/capabilities/moderations.py +154 -0
- praisonai/capabilities/ocr.py +217 -0
- praisonai/capabilities/passthrough.py +204 -0
- praisonai/capabilities/rag.py +207 -0
- praisonai/capabilities/realtime.py +160 -0
- praisonai/capabilities/rerank.py +165 -0
- praisonai/capabilities/responses.py +266 -0
- praisonai/capabilities/search.py +109 -0
- praisonai/capabilities/skills.py +133 -0
- praisonai/capabilities/vector_store_files.py +334 -0
- praisonai/capabilities/vector_stores.py +304 -0
- praisonai/capabilities/videos.py +141 -0
- praisonai/chainlit_ui.py +304 -0
- praisonai/chat/__init__.py +106 -0
- praisonai/chat/app.py +125 -0
- praisonai/cli/__init__.py +26 -0
- praisonai/cli/app.py +213 -0
- praisonai/cli/commands/__init__.py +75 -0
- praisonai/cli/commands/acp.py +70 -0
- praisonai/cli/commands/completion.py +333 -0
- praisonai/cli/commands/config.py +166 -0
- praisonai/cli/commands/debug.py +142 -0
- praisonai/cli/commands/diag.py +55 -0
- praisonai/cli/commands/doctor.py +166 -0
- praisonai/cli/commands/environment.py +179 -0
- praisonai/cli/commands/lsp.py +112 -0
- praisonai/cli/commands/mcp.py +210 -0
- praisonai/cli/commands/profile.py +457 -0
- praisonai/cli/commands/run.py +228 -0
- praisonai/cli/commands/schedule.py +150 -0
- praisonai/cli/commands/serve.py +97 -0
- praisonai/cli/commands/session.py +212 -0
- praisonai/cli/commands/traces.py +145 -0
- praisonai/cli/commands/version.py +101 -0
- praisonai/cli/configuration/__init__.py +18 -0
- praisonai/cli/configuration/loader.py +353 -0
- praisonai/cli/configuration/paths.py +114 -0
- praisonai/cli/configuration/schema.py +164 -0
- praisonai/cli/features/__init__.py +268 -0
- praisonai/cli/features/acp.py +236 -0
- praisonai/cli/features/action_orchestrator.py +546 -0
- praisonai/cli/features/agent_scheduler.py +773 -0
- praisonai/cli/features/agent_tools.py +474 -0
- praisonai/cli/features/agents.py +375 -0
- praisonai/cli/features/at_mentions.py +471 -0
- praisonai/cli/features/auto_memory.py +182 -0
- praisonai/cli/features/autonomy_mode.py +490 -0
- praisonai/cli/features/background.py +356 -0
- praisonai/cli/features/base.py +168 -0
- praisonai/cli/features/capabilities.py +1326 -0
- praisonai/cli/features/checkpoints.py +338 -0
- praisonai/cli/features/code_intelligence.py +652 -0
- praisonai/cli/features/compaction.py +294 -0
- praisonai/cli/features/compare.py +534 -0
- praisonai/cli/features/cost_tracker.py +514 -0
- praisonai/cli/features/debug.py +810 -0
- praisonai/cli/features/deploy.py +517 -0
- praisonai/cli/features/diag.py +289 -0
- praisonai/cli/features/doctor/__init__.py +63 -0
- praisonai/cli/features/doctor/checks/__init__.py +24 -0
- praisonai/cli/features/doctor/checks/acp_checks.py +240 -0
- praisonai/cli/features/doctor/checks/config_checks.py +366 -0
- praisonai/cli/features/doctor/checks/db_checks.py +366 -0
- praisonai/cli/features/doctor/checks/env_checks.py +543 -0
- praisonai/cli/features/doctor/checks/lsp_checks.py +199 -0
- praisonai/cli/features/doctor/checks/mcp_checks.py +349 -0
- praisonai/cli/features/doctor/checks/memory_checks.py +268 -0
- praisonai/cli/features/doctor/checks/network_checks.py +251 -0
- praisonai/cli/features/doctor/checks/obs_checks.py +328 -0
- praisonai/cli/features/doctor/checks/performance_checks.py +235 -0
- praisonai/cli/features/doctor/checks/permissions_checks.py +259 -0
- praisonai/cli/features/doctor/checks/selftest_checks.py +322 -0
- praisonai/cli/features/doctor/checks/serve_checks.py +426 -0
- praisonai/cli/features/doctor/checks/skills_checks.py +231 -0
- praisonai/cli/features/doctor/checks/tools_checks.py +371 -0
- praisonai/cli/features/doctor/engine.py +266 -0
- praisonai/cli/features/doctor/formatters.py +310 -0
- praisonai/cli/features/doctor/handler.py +397 -0
- praisonai/cli/features/doctor/models.py +264 -0
- praisonai/cli/features/doctor/registry.py +239 -0
- praisonai/cli/features/endpoints.py +1019 -0
- praisonai/cli/features/eval.py +560 -0
- praisonai/cli/features/external_agents.py +231 -0
- praisonai/cli/features/fast_context.py +410 -0
- praisonai/cli/features/flow_display.py +566 -0
- praisonai/cli/features/git_integration.py +651 -0
- praisonai/cli/features/guardrail.py +171 -0
- praisonai/cli/features/handoff.py +185 -0
- praisonai/cli/features/hooks.py +583 -0
- praisonai/cli/features/image.py +384 -0
- praisonai/cli/features/interactive_runtime.py +585 -0
- praisonai/cli/features/interactive_tools.py +380 -0
- praisonai/cli/features/interactive_tui.py +603 -0
- praisonai/cli/features/jobs.py +632 -0
- praisonai/cli/features/knowledge.py +531 -0
- praisonai/cli/features/lite.py +244 -0
- praisonai/cli/features/lsp_cli.py +225 -0
- praisonai/cli/features/mcp.py +169 -0
- praisonai/cli/features/message_queue.py +587 -0
- praisonai/cli/features/metrics.py +211 -0
- praisonai/cli/features/n8n.py +673 -0
- praisonai/cli/features/observability.py +293 -0
- praisonai/cli/features/ollama.py +361 -0
- praisonai/cli/features/output_style.py +273 -0
- praisonai/cli/features/package.py +631 -0
- praisonai/cli/features/performance.py +308 -0
- praisonai/cli/features/persistence.py +636 -0
- praisonai/cli/features/profile.py +226 -0
- praisonai/cli/features/profiler/__init__.py +81 -0
- praisonai/cli/features/profiler/core.py +558 -0
- praisonai/cli/features/profiler/optimizations.py +652 -0
- praisonai/cli/features/profiler/suite.py +386 -0
- praisonai/cli/features/profiling.py +350 -0
- praisonai/cli/features/queue/__init__.py +73 -0
- praisonai/cli/features/queue/manager.py +395 -0
- praisonai/cli/features/queue/models.py +286 -0
- praisonai/cli/features/queue/persistence.py +564 -0
- praisonai/cli/features/queue/scheduler.py +484 -0
- praisonai/cli/features/queue/worker.py +372 -0
- praisonai/cli/features/recipe.py +1723 -0
- praisonai/cli/features/recipes.py +449 -0
- praisonai/cli/features/registry.py +229 -0
- praisonai/cli/features/repo_map.py +860 -0
- praisonai/cli/features/router.py +466 -0
- praisonai/cli/features/sandbox_executor.py +515 -0
- praisonai/cli/features/serve.py +829 -0
- praisonai/cli/features/session.py +222 -0
- praisonai/cli/features/skills.py +856 -0
- praisonai/cli/features/slash_commands.py +650 -0
- praisonai/cli/features/telemetry.py +179 -0
- praisonai/cli/features/templates.py +1384 -0
- praisonai/cli/features/thinking.py +305 -0
- praisonai/cli/features/todo.py +334 -0
- praisonai/cli/features/tools.py +680 -0
- praisonai/cli/features/tui/__init__.py +83 -0
- praisonai/cli/features/tui/app.py +580 -0
- praisonai/cli/features/tui/cli.py +566 -0
- praisonai/cli/features/tui/debug.py +511 -0
- praisonai/cli/features/tui/events.py +99 -0
- praisonai/cli/features/tui/mock_provider.py +328 -0
- praisonai/cli/features/tui/orchestrator.py +652 -0
- praisonai/cli/features/tui/screens/__init__.py +50 -0
- praisonai/cli/features/tui/screens/main.py +245 -0
- praisonai/cli/features/tui/screens/queue.py +174 -0
- praisonai/cli/features/tui/screens/session.py +124 -0
- praisonai/cli/features/tui/screens/settings.py +148 -0
- praisonai/cli/features/tui/widgets/__init__.py +56 -0
- praisonai/cli/features/tui/widgets/chat.py +261 -0
- praisonai/cli/features/tui/widgets/composer.py +224 -0
- praisonai/cli/features/tui/widgets/queue_panel.py +200 -0
- praisonai/cli/features/tui/widgets/status.py +167 -0
- praisonai/cli/features/tui/widgets/tool_panel.py +248 -0
- praisonai/cli/features/workflow.py +720 -0
- praisonai/cli/legacy.py +236 -0
- praisonai/cli/main.py +5559 -0
- praisonai/cli/schedule_cli.py +54 -0
- praisonai/cli/state/__init__.py +31 -0
- praisonai/cli/state/identifiers.py +161 -0
- praisonai/cli/state/sessions.py +313 -0
- praisonai/code/__init__.py +93 -0
- praisonai/code/agent_tools.py +344 -0
- praisonai/code/diff/__init__.py +21 -0
- praisonai/code/diff/diff_strategy.py +432 -0
- praisonai/code/tools/__init__.py +27 -0
- praisonai/code/tools/apply_diff.py +221 -0
- praisonai/code/tools/execute_command.py +275 -0
- praisonai/code/tools/list_files.py +274 -0
- praisonai/code/tools/read_file.py +206 -0
- praisonai/code/tools/search_replace.py +248 -0
- praisonai/code/tools/write_file.py +217 -0
- praisonai/code/utils/__init__.py +46 -0
- praisonai/code/utils/file_utils.py +307 -0
- praisonai/code/utils/ignore_utils.py +308 -0
- praisonai/code/utils/text_utils.py +276 -0
- praisonai/db/__init__.py +64 -0
- praisonai/db/adapter.py +531 -0
- praisonai/deploy/__init__.py +62 -0
- praisonai/deploy/api.py +231 -0
- praisonai/deploy/docker.py +454 -0
- praisonai/deploy/doctor.py +367 -0
- praisonai/deploy/main.py +327 -0
- praisonai/deploy/models.py +179 -0
- praisonai/deploy/providers/__init__.py +33 -0
- praisonai/deploy/providers/aws.py +331 -0
- praisonai/deploy/providers/azure.py +358 -0
- praisonai/deploy/providers/base.py +101 -0
- praisonai/deploy/providers/gcp.py +314 -0
- praisonai/deploy/schema.py +208 -0
- praisonai/deploy.py +185 -0
- praisonai/endpoints/__init__.py +53 -0
- praisonai/endpoints/a2u_server.py +410 -0
- praisonai/endpoints/discovery.py +165 -0
- praisonai/endpoints/providers/__init__.py +28 -0
- praisonai/endpoints/providers/a2a.py +253 -0
- praisonai/endpoints/providers/a2u.py +208 -0
- praisonai/endpoints/providers/agents_api.py +171 -0
- praisonai/endpoints/providers/base.py +231 -0
- praisonai/endpoints/providers/mcp.py +263 -0
- praisonai/endpoints/providers/recipe.py +206 -0
- praisonai/endpoints/providers/tools_mcp.py +150 -0
- praisonai/endpoints/registry.py +131 -0
- praisonai/endpoints/server.py +161 -0
- praisonai/inbuilt_tools/__init__.py +24 -0
- praisonai/inbuilt_tools/autogen_tools.py +117 -0
- praisonai/inc/__init__.py +2 -0
- praisonai/inc/config.py +96 -0
- praisonai/inc/models.py +155 -0
- praisonai/integrations/__init__.py +56 -0
- praisonai/integrations/base.py +303 -0
- praisonai/integrations/claude_code.py +270 -0
- praisonai/integrations/codex_cli.py +255 -0
- praisonai/integrations/cursor_cli.py +195 -0
- praisonai/integrations/gemini_cli.py +222 -0
- praisonai/jobs/__init__.py +67 -0
- praisonai/jobs/executor.py +425 -0
- praisonai/jobs/models.py +230 -0
- praisonai/jobs/router.py +314 -0
- praisonai/jobs/server.py +186 -0
- praisonai/jobs/store.py +203 -0
- praisonai/llm/__init__.py +66 -0
- praisonai/llm/registry.py +382 -0
- praisonai/mcp_server/__init__.py +152 -0
- praisonai/mcp_server/adapters/__init__.py +74 -0
- praisonai/mcp_server/adapters/agents.py +128 -0
- praisonai/mcp_server/adapters/capabilities.py +168 -0
- praisonai/mcp_server/adapters/cli_tools.py +568 -0
- praisonai/mcp_server/adapters/extended_capabilities.py +462 -0
- praisonai/mcp_server/adapters/knowledge.py +93 -0
- praisonai/mcp_server/adapters/memory.py +104 -0
- praisonai/mcp_server/adapters/prompts.py +306 -0
- praisonai/mcp_server/adapters/resources.py +124 -0
- praisonai/mcp_server/adapters/tools_bridge.py +280 -0
- praisonai/mcp_server/auth/__init__.py +48 -0
- praisonai/mcp_server/auth/api_key.py +291 -0
- praisonai/mcp_server/auth/oauth.py +460 -0
- praisonai/mcp_server/auth/oidc.py +289 -0
- praisonai/mcp_server/auth/scopes.py +260 -0
- praisonai/mcp_server/cli.py +852 -0
- praisonai/mcp_server/elicitation.py +445 -0
- praisonai/mcp_server/icons.py +302 -0
- praisonai/mcp_server/recipe_adapter.py +573 -0
- praisonai/mcp_server/recipe_cli.py +824 -0
- praisonai/mcp_server/registry.py +703 -0
- praisonai/mcp_server/sampling.py +422 -0
- praisonai/mcp_server/server.py +490 -0
- praisonai/mcp_server/tasks.py +443 -0
- praisonai/mcp_server/transports/__init__.py +18 -0
- praisonai/mcp_server/transports/http_stream.py +376 -0
- praisonai/mcp_server/transports/stdio.py +132 -0
- praisonai/persistence/__init__.py +84 -0
- praisonai/persistence/config.py +238 -0
- praisonai/persistence/conversation/__init__.py +25 -0
- praisonai/persistence/conversation/async_mysql.py +427 -0
- praisonai/persistence/conversation/async_postgres.py +410 -0
- praisonai/persistence/conversation/async_sqlite.py +371 -0
- praisonai/persistence/conversation/base.py +151 -0
- praisonai/persistence/conversation/json_store.py +250 -0
- praisonai/persistence/conversation/mysql.py +387 -0
- praisonai/persistence/conversation/postgres.py +401 -0
- praisonai/persistence/conversation/singlestore.py +240 -0
- praisonai/persistence/conversation/sqlite.py +341 -0
- praisonai/persistence/conversation/supabase.py +203 -0
- praisonai/persistence/conversation/surrealdb.py +287 -0
- praisonai/persistence/factory.py +301 -0
- praisonai/persistence/hooks/__init__.py +18 -0
- praisonai/persistence/hooks/agent_hooks.py +297 -0
- praisonai/persistence/knowledge/__init__.py +26 -0
- praisonai/persistence/knowledge/base.py +144 -0
- praisonai/persistence/knowledge/cassandra.py +232 -0
- praisonai/persistence/knowledge/chroma.py +295 -0
- praisonai/persistence/knowledge/clickhouse.py +242 -0
- praisonai/persistence/knowledge/cosmosdb_vector.py +438 -0
- praisonai/persistence/knowledge/couchbase.py +286 -0
- praisonai/persistence/knowledge/lancedb.py +216 -0
- praisonai/persistence/knowledge/langchain_adapter.py +291 -0
- praisonai/persistence/knowledge/lightrag_adapter.py +212 -0
- praisonai/persistence/knowledge/llamaindex_adapter.py +256 -0
- praisonai/persistence/knowledge/milvus.py +277 -0
- praisonai/persistence/knowledge/mongodb_vector.py +306 -0
- praisonai/persistence/knowledge/pgvector.py +335 -0
- praisonai/persistence/knowledge/pinecone.py +253 -0
- praisonai/persistence/knowledge/qdrant.py +301 -0
- praisonai/persistence/knowledge/redis_vector.py +291 -0
- praisonai/persistence/knowledge/singlestore_vector.py +299 -0
- praisonai/persistence/knowledge/surrealdb_vector.py +309 -0
- praisonai/persistence/knowledge/upstash_vector.py +266 -0
- praisonai/persistence/knowledge/weaviate.py +223 -0
- praisonai/persistence/migrations/__init__.py +10 -0
- praisonai/persistence/migrations/manager.py +251 -0
- praisonai/persistence/orchestrator.py +406 -0
- praisonai/persistence/state/__init__.py +21 -0
- praisonai/persistence/state/async_mongodb.py +200 -0
- praisonai/persistence/state/base.py +107 -0
- praisonai/persistence/state/dynamodb.py +226 -0
- praisonai/persistence/state/firestore.py +175 -0
- praisonai/persistence/state/gcs.py +155 -0
- praisonai/persistence/state/memory.py +245 -0
- praisonai/persistence/state/mongodb.py +158 -0
- praisonai/persistence/state/redis.py +190 -0
- praisonai/persistence/state/upstash.py +144 -0
- praisonai/persistence/tests/__init__.py +3 -0
- praisonai/persistence/tests/test_all_backends.py +633 -0
- praisonai/profiler.py +1214 -0
- praisonai/recipe/__init__.py +134 -0
- praisonai/recipe/bridge.py +278 -0
- praisonai/recipe/core.py +893 -0
- praisonai/recipe/exceptions.py +54 -0
- praisonai/recipe/history.py +402 -0
- praisonai/recipe/models.py +266 -0
- praisonai/recipe/operations.py +440 -0
- praisonai/recipe/policy.py +422 -0
- praisonai/recipe/registry.py +849 -0
- praisonai/recipe/runtime.py +214 -0
- praisonai/recipe/security.py +711 -0
- praisonai/recipe/serve.py +859 -0
- praisonai/recipe/server.py +613 -0
- praisonai/scheduler/__init__.py +45 -0
- praisonai/scheduler/agent_scheduler.py +552 -0
- praisonai/scheduler/base.py +124 -0
- praisonai/scheduler/daemon_manager.py +225 -0
- praisonai/scheduler/state_manager.py +155 -0
- praisonai/scheduler/yaml_loader.py +193 -0
- praisonai/scheduler.py +194 -0
- praisonai/setup/__init__.py +1 -0
- praisonai/setup/build.py +21 -0
- praisonai/setup/post_install.py +23 -0
- praisonai/setup/setup_conda_env.py +25 -0
- praisonai/setup.py +16 -0
- praisonai/templates/__init__.py +116 -0
- praisonai/templates/cache.py +364 -0
- praisonai/templates/dependency_checker.py +358 -0
- praisonai/templates/discovery.py +391 -0
- praisonai/templates/loader.py +564 -0
- praisonai/templates/registry.py +511 -0
- praisonai/templates/resolver.py +206 -0
- praisonai/templates/security.py +327 -0
- praisonai/templates/tool_override.py +498 -0
- praisonai/templates/tools_doctor.py +256 -0
- praisonai/test.py +105 -0
- praisonai/train.py +562 -0
- praisonai/train_vision.py +306 -0
- praisonai/ui/agents.py +824 -0
- praisonai/ui/callbacks.py +57 -0
- praisonai/ui/chainlit_compat.py +246 -0
- praisonai/ui/chat.py +532 -0
- praisonai/ui/code.py +717 -0
- praisonai/ui/colab.py +474 -0
- praisonai/ui/colab_chainlit.py +81 -0
- praisonai/ui/components/aicoder.py +284 -0
- praisonai/ui/context.py +283 -0
- praisonai/ui/database_config.py +56 -0
- praisonai/ui/db.py +294 -0
- praisonai/ui/realtime.py +488 -0
- praisonai/ui/realtimeclient/__init__.py +756 -0
- praisonai/ui/realtimeclient/tools.py +242 -0
- praisonai/ui/sql_alchemy.py +710 -0
- praisonai/upload_vision.py +140 -0
- praisonai/version.py +1 -0
- praisonai-3.0.0.dist-info/METADATA +3493 -0
- praisonai-3.0.0.dist-info/RECORD +393 -0
- praisonai-3.0.0.dist-info/WHEEL +5 -0
- praisonai-3.0.0.dist-info/entry_points.txt +4 -0
- praisonai-3.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Execute Command Tool for PraisonAI Code.
|
|
3
|
+
|
|
4
|
+
Provides functionality to execute shell commands safely.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import subprocess
|
|
9
|
+
import shlex
|
|
10
|
+
from typing import Optional, Dict, Any
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# Commands that are generally safe to auto-run
|
|
14
|
+
SAFE_COMMANDS = {
|
|
15
|
+
'ls', 'dir', 'pwd', 'echo', 'cat', 'head', 'tail', 'wc',
|
|
16
|
+
'grep', 'find', 'which', 'whereis', 'type', 'file',
|
|
17
|
+
'date', 'whoami', 'hostname', 'uname',
|
|
18
|
+
'python', 'python3', 'node', 'npm', 'npx', 'pip', 'pip3',
|
|
19
|
+
'git', 'cargo', 'go', 'ruby', 'java', 'javac',
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
# Commands that should never be auto-run
|
|
23
|
+
DANGEROUS_COMMANDS = {
|
|
24
|
+
'rm', 'rmdir', 'del', 'format', 'mkfs',
|
|
25
|
+
'dd', 'shred', 'chmod', 'chown', 'chgrp',
|
|
26
|
+
'kill', 'killall', 'pkill',
|
|
27
|
+
'shutdown', 'reboot', 'halt', 'poweroff',
|
|
28
|
+
'sudo', 'su', 'doas',
|
|
29
|
+
'curl', 'wget', 'ssh', 'scp', 'rsync',
|
|
30
|
+
'mv', 'cp', # Can be destructive
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def execute_command(
|
|
35
|
+
command: str,
|
|
36
|
+
cwd: Optional[str] = None,
|
|
37
|
+
workspace: Optional[str] = None,
|
|
38
|
+
timeout: int = 120,
|
|
39
|
+
capture_output: bool = True,
|
|
40
|
+
shell: bool = True,
|
|
41
|
+
env: Optional[Dict[str, str]] = None,
|
|
42
|
+
) -> Dict[str, Any]:
|
|
43
|
+
"""
|
|
44
|
+
Execute a shell command and return the result.
|
|
45
|
+
|
|
46
|
+
This tool executes shell commands with safety checks and output capture.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
command: The command to execute
|
|
50
|
+
cwd: Working directory for the command
|
|
51
|
+
workspace: Workspace root (for security validation)
|
|
52
|
+
timeout: Command timeout in seconds
|
|
53
|
+
capture_output: Whether to capture stdout/stderr
|
|
54
|
+
shell: Whether to run through shell
|
|
55
|
+
env: Additional environment variables
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
Dictionary with:
|
|
59
|
+
- success: bool
|
|
60
|
+
- exit_code: int
|
|
61
|
+
- stdout: str
|
|
62
|
+
- stderr: str
|
|
63
|
+
- command: str
|
|
64
|
+
- error: str (if success is False)
|
|
65
|
+
|
|
66
|
+
Example:
|
|
67
|
+
>>> result = execute_command("python --version")
|
|
68
|
+
>>> print(result['stdout'])
|
|
69
|
+
"""
|
|
70
|
+
if not command or not command.strip():
|
|
71
|
+
return {
|
|
72
|
+
'success': False,
|
|
73
|
+
'error': "Empty command",
|
|
74
|
+
'command': command,
|
|
75
|
+
'exit_code': -1,
|
|
76
|
+
'stdout': '',
|
|
77
|
+
'stderr': '',
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
# Resolve working directory
|
|
81
|
+
if cwd:
|
|
82
|
+
if workspace and not os.path.isabs(cwd):
|
|
83
|
+
work_dir = os.path.abspath(os.path.join(workspace, cwd))
|
|
84
|
+
else:
|
|
85
|
+
work_dir = os.path.abspath(cwd)
|
|
86
|
+
elif workspace:
|
|
87
|
+
work_dir = workspace
|
|
88
|
+
else:
|
|
89
|
+
work_dir = os.getcwd()
|
|
90
|
+
|
|
91
|
+
# Validate working directory exists
|
|
92
|
+
if not os.path.isdir(work_dir):
|
|
93
|
+
return {
|
|
94
|
+
'success': False,
|
|
95
|
+
'error': f"Working directory not found: {cwd}",
|
|
96
|
+
'command': command,
|
|
97
|
+
'exit_code': -1,
|
|
98
|
+
'stdout': '',
|
|
99
|
+
'stderr': '',
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
# Prepare environment
|
|
103
|
+
cmd_env = os.environ.copy()
|
|
104
|
+
if env:
|
|
105
|
+
cmd_env.update(env)
|
|
106
|
+
|
|
107
|
+
# Set PAGER to cat to avoid interactive pagers
|
|
108
|
+
cmd_env['PAGER'] = 'cat'
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
# Execute command
|
|
112
|
+
if shell:
|
|
113
|
+
result = subprocess.run(
|
|
114
|
+
command,
|
|
115
|
+
shell=True,
|
|
116
|
+
cwd=work_dir,
|
|
117
|
+
capture_output=capture_output,
|
|
118
|
+
timeout=timeout,
|
|
119
|
+
env=cmd_env,
|
|
120
|
+
text=True,
|
|
121
|
+
)
|
|
122
|
+
else:
|
|
123
|
+
# Parse command for non-shell execution
|
|
124
|
+
args = shlex.split(command)
|
|
125
|
+
result = subprocess.run(
|
|
126
|
+
args,
|
|
127
|
+
cwd=work_dir,
|
|
128
|
+
capture_output=capture_output,
|
|
129
|
+
timeout=timeout,
|
|
130
|
+
env=cmd_env,
|
|
131
|
+
text=True,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
'success': result.returncode == 0,
|
|
136
|
+
'exit_code': result.returncode,
|
|
137
|
+
'stdout': result.stdout if capture_output else '',
|
|
138
|
+
'stderr': result.stderr if capture_output else '',
|
|
139
|
+
'command': command,
|
|
140
|
+
'cwd': work_dir,
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
except subprocess.TimeoutExpired:
|
|
144
|
+
return {
|
|
145
|
+
'success': False,
|
|
146
|
+
'error': f"Command timed out after {timeout} seconds",
|
|
147
|
+
'command': command,
|
|
148
|
+
'exit_code': -1,
|
|
149
|
+
'stdout': '',
|
|
150
|
+
'stderr': '',
|
|
151
|
+
}
|
|
152
|
+
except FileNotFoundError as e:
|
|
153
|
+
return {
|
|
154
|
+
'success': False,
|
|
155
|
+
'error': f"Command not found: {str(e)}",
|
|
156
|
+
'command': command,
|
|
157
|
+
'exit_code': -1,
|
|
158
|
+
'stdout': '',
|
|
159
|
+
'stderr': '',
|
|
160
|
+
}
|
|
161
|
+
except PermissionError:
|
|
162
|
+
return {
|
|
163
|
+
'success': False,
|
|
164
|
+
'error': "Permission denied",
|
|
165
|
+
'command': command,
|
|
166
|
+
'exit_code': -1,
|
|
167
|
+
'stdout': '',
|
|
168
|
+
'stderr': '',
|
|
169
|
+
}
|
|
170
|
+
except Exception as e:
|
|
171
|
+
return {
|
|
172
|
+
'success': False,
|
|
173
|
+
'error': f"Error executing command: {str(e)}",
|
|
174
|
+
'command': command,
|
|
175
|
+
'exit_code': -1,
|
|
176
|
+
'stdout': '',
|
|
177
|
+
'stderr': '',
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def is_safe_command(command: str) -> bool:
|
|
182
|
+
"""
|
|
183
|
+
Check if a command is considered safe to auto-run.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
command: The command to check
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
True if the command is considered safe
|
|
190
|
+
"""
|
|
191
|
+
if not command:
|
|
192
|
+
return False
|
|
193
|
+
|
|
194
|
+
# Get the base command (first word)
|
|
195
|
+
parts = shlex.split(command)
|
|
196
|
+
if not parts:
|
|
197
|
+
return False
|
|
198
|
+
|
|
199
|
+
base_cmd = os.path.basename(parts[0]).lower()
|
|
200
|
+
|
|
201
|
+
# Check against dangerous commands
|
|
202
|
+
if base_cmd in DANGEROUS_COMMANDS:
|
|
203
|
+
return False
|
|
204
|
+
|
|
205
|
+
# Check against safe commands
|
|
206
|
+
if base_cmd in SAFE_COMMANDS:
|
|
207
|
+
return True
|
|
208
|
+
|
|
209
|
+
# Default to unsafe
|
|
210
|
+
return False
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def get_command_info(command: str) -> Dict[str, Any]:
|
|
214
|
+
"""
|
|
215
|
+
Get information about a command without executing it.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
command: The command to analyze
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
Dictionary with command analysis
|
|
222
|
+
"""
|
|
223
|
+
if not command:
|
|
224
|
+
return {
|
|
225
|
+
'valid': False,
|
|
226
|
+
'error': "Empty command",
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
try:
|
|
230
|
+
parts = shlex.split(command)
|
|
231
|
+
base_cmd = parts[0] if parts else ''
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
'valid': True,
|
|
235
|
+
'base_command': base_cmd,
|
|
236
|
+
'arguments': parts[1:] if len(parts) > 1 else [],
|
|
237
|
+
'is_safe': is_safe_command(command),
|
|
238
|
+
'is_dangerous': os.path.basename(base_cmd).lower() in DANGEROUS_COMMANDS,
|
|
239
|
+
}
|
|
240
|
+
except ValueError as e:
|
|
241
|
+
return {
|
|
242
|
+
'valid': False,
|
|
243
|
+
'error': f"Invalid command syntax: {str(e)}",
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def run_python(
|
|
248
|
+
code: str,
|
|
249
|
+
cwd: Optional[str] = None,
|
|
250
|
+
timeout: int = 60,
|
|
251
|
+
) -> Dict[str, Any]:
|
|
252
|
+
"""
|
|
253
|
+
Execute Python code and return the result.
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
code: Python code to execute
|
|
257
|
+
cwd: Working directory
|
|
258
|
+
timeout: Execution timeout
|
|
259
|
+
|
|
260
|
+
Returns:
|
|
261
|
+
Dictionary with execution result
|
|
262
|
+
"""
|
|
263
|
+
# Create a temporary command to run the code
|
|
264
|
+
import sys
|
|
265
|
+
python_cmd = sys.executable
|
|
266
|
+
|
|
267
|
+
# Escape the code for command line
|
|
268
|
+
escaped_code = code.replace('\\', '\\\\').replace('"', '\\"')
|
|
269
|
+
command = f'{python_cmd} -c "{escaped_code}"'
|
|
270
|
+
|
|
271
|
+
return execute_command(
|
|
272
|
+
command=command,
|
|
273
|
+
cwd=cwd,
|
|
274
|
+
timeout=timeout,
|
|
275
|
+
)
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
"""
|
|
2
|
+
List Files Tool for PraisonAI Code.
|
|
3
|
+
|
|
4
|
+
Provides functionality to list directory contents with filtering options.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
from typing import Optional, List, Dict, Any
|
|
9
|
+
|
|
10
|
+
from ..utils.ignore_utils import (
|
|
11
|
+
should_ignore_path,
|
|
12
|
+
load_gitignore_patterns,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def list_files(
|
|
17
|
+
path: str,
|
|
18
|
+
recursive: bool = False,
|
|
19
|
+
workspace: Optional[str] = None,
|
|
20
|
+
max_files: int = 200,
|
|
21
|
+
include_hidden: bool = False,
|
|
22
|
+
extensions: Optional[List[str]] = None,
|
|
23
|
+
respect_gitignore: bool = True,
|
|
24
|
+
) -> Dict[str, Any]:
|
|
25
|
+
"""
|
|
26
|
+
List files and directories in a given path.
|
|
27
|
+
|
|
28
|
+
This tool lists the contents of a directory, optionally recursively,
|
|
29
|
+
with support for filtering and gitignore patterns.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
path: Path to the directory (absolute or relative to workspace)
|
|
33
|
+
recursive: Whether to list recursively
|
|
34
|
+
workspace: Workspace root directory (for relative paths)
|
|
35
|
+
max_files: Maximum number of files to return
|
|
36
|
+
include_hidden: Whether to include hidden files (starting with .)
|
|
37
|
+
extensions: List of file extensions to include (e.g., ['py', 'js'])
|
|
38
|
+
respect_gitignore: Whether to respect .gitignore patterns
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
Dictionary with:
|
|
42
|
+
- success: bool
|
|
43
|
+
- files: List of file entries with path, type, size
|
|
44
|
+
- directories: List of directory entries
|
|
45
|
+
- total_count: int (total items found)
|
|
46
|
+
- truncated: bool (True if max_files was reached)
|
|
47
|
+
- error: str (if success is False)
|
|
48
|
+
|
|
49
|
+
Example:
|
|
50
|
+
>>> result = list_files("src", recursive=True, extensions=['py'])
|
|
51
|
+
>>> for f in result['files']:
|
|
52
|
+
... print(f['path'])
|
|
53
|
+
"""
|
|
54
|
+
# Resolve path
|
|
55
|
+
if workspace and not os.path.isabs(path):
|
|
56
|
+
abs_path = os.path.abspath(os.path.join(workspace, path))
|
|
57
|
+
else:
|
|
58
|
+
abs_path = os.path.abspath(path)
|
|
59
|
+
|
|
60
|
+
# Check if directory exists
|
|
61
|
+
if not os.path.isdir(abs_path):
|
|
62
|
+
return {
|
|
63
|
+
'success': False,
|
|
64
|
+
'error': f"Directory not found: {path}",
|
|
65
|
+
'files': [],
|
|
66
|
+
'directories': [],
|
|
67
|
+
'total_count': 0,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
# Load gitignore patterns
|
|
71
|
+
ignore_patterns = []
|
|
72
|
+
if respect_gitignore:
|
|
73
|
+
# Load from workspace root if available
|
|
74
|
+
if workspace:
|
|
75
|
+
ignore_patterns = load_gitignore_patterns(workspace)
|
|
76
|
+
# Also load from the target directory
|
|
77
|
+
ignore_patterns.extend(load_gitignore_patterns(abs_path))
|
|
78
|
+
|
|
79
|
+
files = []
|
|
80
|
+
directories = []
|
|
81
|
+
total_count = 0
|
|
82
|
+
truncated = False
|
|
83
|
+
|
|
84
|
+
try:
|
|
85
|
+
if recursive:
|
|
86
|
+
# Walk directory tree
|
|
87
|
+
for root, dirs, filenames in os.walk(abs_path):
|
|
88
|
+
# Filter hidden directories
|
|
89
|
+
if not include_hidden:
|
|
90
|
+
dirs[:] = [d for d in dirs if not d.startswith('.')]
|
|
91
|
+
|
|
92
|
+
# Filter ignored directories
|
|
93
|
+
if respect_gitignore:
|
|
94
|
+
base_dir = workspace or abs_path
|
|
95
|
+
dirs[:] = [
|
|
96
|
+
d for d in dirs
|
|
97
|
+
if not should_ignore_path(
|
|
98
|
+
os.path.join(root, d),
|
|
99
|
+
ignore_patterns,
|
|
100
|
+
base_dir,
|
|
101
|
+
is_directory=True
|
|
102
|
+
)
|
|
103
|
+
]
|
|
104
|
+
|
|
105
|
+
# Process files
|
|
106
|
+
for filename in filenames:
|
|
107
|
+
if total_count >= max_files:
|
|
108
|
+
truncated = True
|
|
109
|
+
break
|
|
110
|
+
|
|
111
|
+
# Skip hidden files
|
|
112
|
+
if not include_hidden and filename.startswith('.'):
|
|
113
|
+
continue
|
|
114
|
+
|
|
115
|
+
file_path = os.path.join(root, filename)
|
|
116
|
+
rel_path = os.path.relpath(file_path, abs_path)
|
|
117
|
+
|
|
118
|
+
# Check gitignore
|
|
119
|
+
if respect_gitignore:
|
|
120
|
+
base_dir = workspace or abs_path
|
|
121
|
+
if should_ignore_path(file_path, ignore_patterns, base_dir):
|
|
122
|
+
continue
|
|
123
|
+
|
|
124
|
+
# Filter by extension
|
|
125
|
+
if extensions:
|
|
126
|
+
ext = os.path.splitext(filename)[1].lstrip('.')
|
|
127
|
+
if ext.lower() not in [e.lower() for e in extensions]:
|
|
128
|
+
continue
|
|
129
|
+
|
|
130
|
+
# Get file info
|
|
131
|
+
try:
|
|
132
|
+
stat = os.stat(file_path)
|
|
133
|
+
files.append({
|
|
134
|
+
'path': rel_path,
|
|
135
|
+
'name': filename,
|
|
136
|
+
'size': stat.st_size,
|
|
137
|
+
'modified': stat.st_mtime,
|
|
138
|
+
})
|
|
139
|
+
total_count += 1
|
|
140
|
+
except OSError:
|
|
141
|
+
continue
|
|
142
|
+
|
|
143
|
+
if truncated:
|
|
144
|
+
break
|
|
145
|
+
|
|
146
|
+
# Add directories
|
|
147
|
+
for dirname in dirs:
|
|
148
|
+
if total_count >= max_files:
|
|
149
|
+
truncated = True
|
|
150
|
+
break
|
|
151
|
+
|
|
152
|
+
dir_path = os.path.join(root, dirname)
|
|
153
|
+
rel_path = os.path.relpath(dir_path, abs_path)
|
|
154
|
+
|
|
155
|
+
directories.append({
|
|
156
|
+
'path': rel_path,
|
|
157
|
+
'name': dirname,
|
|
158
|
+
})
|
|
159
|
+
total_count += 1
|
|
160
|
+
else:
|
|
161
|
+
# List only top level
|
|
162
|
+
for entry in os.listdir(abs_path):
|
|
163
|
+
if total_count >= max_files:
|
|
164
|
+
truncated = True
|
|
165
|
+
break
|
|
166
|
+
|
|
167
|
+
# Skip hidden files
|
|
168
|
+
if not include_hidden and entry.startswith('.'):
|
|
169
|
+
continue
|
|
170
|
+
|
|
171
|
+
entry_path = os.path.join(abs_path, entry)
|
|
172
|
+
|
|
173
|
+
# Check gitignore
|
|
174
|
+
if respect_gitignore:
|
|
175
|
+
base_dir = workspace or abs_path
|
|
176
|
+
is_dir = os.path.isdir(entry_path)
|
|
177
|
+
if should_ignore_path(entry_path, ignore_patterns, base_dir, is_dir):
|
|
178
|
+
continue
|
|
179
|
+
|
|
180
|
+
if os.path.isfile(entry_path):
|
|
181
|
+
# Filter by extension
|
|
182
|
+
if extensions:
|
|
183
|
+
ext = os.path.splitext(entry)[1].lstrip('.')
|
|
184
|
+
if ext.lower() not in [e.lower() for e in extensions]:
|
|
185
|
+
continue
|
|
186
|
+
|
|
187
|
+
try:
|
|
188
|
+
stat = os.stat(entry_path)
|
|
189
|
+
files.append({
|
|
190
|
+
'path': entry,
|
|
191
|
+
'name': entry,
|
|
192
|
+
'size': stat.st_size,
|
|
193
|
+
'modified': stat.st_mtime,
|
|
194
|
+
})
|
|
195
|
+
total_count += 1
|
|
196
|
+
except OSError:
|
|
197
|
+
continue
|
|
198
|
+
|
|
199
|
+
elif os.path.isdir(entry_path):
|
|
200
|
+
directories.append({
|
|
201
|
+
'path': entry,
|
|
202
|
+
'name': entry,
|
|
203
|
+
})
|
|
204
|
+
total_count += 1
|
|
205
|
+
|
|
206
|
+
# Sort results
|
|
207
|
+
files.sort(key=lambda x: x['path'])
|
|
208
|
+
directories.sort(key=lambda x: x['path'])
|
|
209
|
+
|
|
210
|
+
return {
|
|
211
|
+
'success': True,
|
|
212
|
+
'files': files,
|
|
213
|
+
'directories': directories,
|
|
214
|
+
'total_count': total_count,
|
|
215
|
+
'truncated': truncated,
|
|
216
|
+
'path': path,
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
except PermissionError:
|
|
220
|
+
return {
|
|
221
|
+
'success': False,
|
|
222
|
+
'error': f"Permission denied: {path}",
|
|
223
|
+
'files': [],
|
|
224
|
+
'directories': [],
|
|
225
|
+
'total_count': 0,
|
|
226
|
+
}
|
|
227
|
+
except Exception as e:
|
|
228
|
+
return {
|
|
229
|
+
'success': False,
|
|
230
|
+
'error': f"Error listing {path}: {str(e)}",
|
|
231
|
+
'files': [],
|
|
232
|
+
'directories': [],
|
|
233
|
+
'total_count': 0,
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def format_file_list(result: Dict[str, Any], show_size: bool = True) -> str:
|
|
238
|
+
"""
|
|
239
|
+
Format a list_files result as a readable string.
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
result: Result from list_files()
|
|
243
|
+
show_size: Whether to show file sizes
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
Formatted string representation
|
|
247
|
+
"""
|
|
248
|
+
if not result['success']:
|
|
249
|
+
return f"Error: {result.get('error', 'Unknown error')}"
|
|
250
|
+
|
|
251
|
+
lines = []
|
|
252
|
+
|
|
253
|
+
# Add directories first
|
|
254
|
+
for d in result['directories']:
|
|
255
|
+
lines.append(f"📁 {d['path']}/")
|
|
256
|
+
|
|
257
|
+
# Add files
|
|
258
|
+
for f in result['files']:
|
|
259
|
+
if show_size:
|
|
260
|
+
size = f.get('size', 0)
|
|
261
|
+
if size < 1024:
|
|
262
|
+
size_str = f"{size}B"
|
|
263
|
+
elif size < 1024 * 1024:
|
|
264
|
+
size_str = f"{size / 1024:.1f}KB"
|
|
265
|
+
else:
|
|
266
|
+
size_str = f"{size / (1024 * 1024):.1f}MB"
|
|
267
|
+
lines.append(f"📄 {f['path']} ({size_str})")
|
|
268
|
+
else:
|
|
269
|
+
lines.append(f"📄 {f['path']}")
|
|
270
|
+
|
|
271
|
+
if result.get('truncated'):
|
|
272
|
+
lines.append(f"\n... (truncated, showing {result['total_count']} of more items)")
|
|
273
|
+
|
|
274
|
+
return '\n'.join(lines)
|