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,449 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Recipes CLI - Commands for managing and running Agent-Recipes.
|
|
3
|
+
|
|
4
|
+
Provides commands for:
|
|
5
|
+
- praison recipes list - List available recipes
|
|
6
|
+
- praison recipes info <name> - Show recipe details
|
|
7
|
+
- praison recipes run <name> <input> - Run a recipe
|
|
8
|
+
- praison recipes doctor <name> - Check recipe dependencies
|
|
9
|
+
- praison recipes explain <name> - Show recipe execution plan
|
|
10
|
+
- praison recipes init <name> - Initialize recipe in current directory
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import os
|
|
14
|
+
import sys
|
|
15
|
+
import json
|
|
16
|
+
import yaml
|
|
17
|
+
import argparse
|
|
18
|
+
import logging
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Optional, List, Dict, Any
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
# Recipe discovery paths in order of precedence
|
|
25
|
+
RECIPE_PATHS = [
|
|
26
|
+
Path.home() / ".praison" / "templates",
|
|
27
|
+
Path.home() / ".config" / "praison" / "templates",
|
|
28
|
+
Path.cwd() / ".praison" / "templates",
|
|
29
|
+
Path("/Users/praison/Agent-Recipes/agent_recipes/templates"),
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def find_recipe_paths() -> List[Path]:
|
|
34
|
+
"""Find all valid recipe directories."""
|
|
35
|
+
paths = []
|
|
36
|
+
for p in RECIPE_PATHS:
|
|
37
|
+
if p.exists() and p.is_dir():
|
|
38
|
+
paths.append(p)
|
|
39
|
+
return paths
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def list_recipes() -> List[Dict[str, Any]]:
|
|
43
|
+
"""List all available recipes."""
|
|
44
|
+
recipes = []
|
|
45
|
+
seen = set()
|
|
46
|
+
|
|
47
|
+
for base_path in find_recipe_paths():
|
|
48
|
+
for recipe_dir in base_path.iterdir():
|
|
49
|
+
if not recipe_dir.is_dir():
|
|
50
|
+
continue
|
|
51
|
+
|
|
52
|
+
template_file = recipe_dir / "TEMPLATE.yaml"
|
|
53
|
+
if not template_file.exists():
|
|
54
|
+
continue
|
|
55
|
+
|
|
56
|
+
name = recipe_dir.name
|
|
57
|
+
if name in seen:
|
|
58
|
+
continue
|
|
59
|
+
seen.add(name)
|
|
60
|
+
|
|
61
|
+
try:
|
|
62
|
+
with open(template_file) as f:
|
|
63
|
+
template = yaml.safe_load(f)
|
|
64
|
+
recipes.append({
|
|
65
|
+
"name": name,
|
|
66
|
+
"description": template.get("description", ""),
|
|
67
|
+
"version": template.get("version", "1.0.0"),
|
|
68
|
+
"tags": template.get("tags", []),
|
|
69
|
+
"path": str(recipe_dir),
|
|
70
|
+
})
|
|
71
|
+
except Exception as e:
|
|
72
|
+
logger.warning(f"Failed to load recipe {name}: {e}")
|
|
73
|
+
|
|
74
|
+
return sorted(recipes, key=lambda x: x["name"])
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def get_recipe(name: str) -> Optional[Dict[str, Any]]:
|
|
78
|
+
"""Get recipe by name."""
|
|
79
|
+
for base_path in find_recipe_paths():
|
|
80
|
+
recipe_dir = base_path / name
|
|
81
|
+
template_file = recipe_dir / "TEMPLATE.yaml"
|
|
82
|
+
|
|
83
|
+
if template_file.exists():
|
|
84
|
+
with open(template_file) as f:
|
|
85
|
+
template = yaml.safe_load(f)
|
|
86
|
+
template["path"] = str(recipe_dir)
|
|
87
|
+
return template
|
|
88
|
+
|
|
89
|
+
return None
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def check_dependencies(recipe: Dict[str, Any]) -> Dict[str, bool]:
|
|
93
|
+
"""Check if recipe dependencies are satisfied."""
|
|
94
|
+
results = {}
|
|
95
|
+
requires = recipe.get("requires", {})
|
|
96
|
+
|
|
97
|
+
# Check environment variables
|
|
98
|
+
for env_var in requires.get("env", []):
|
|
99
|
+
results[f"env:{env_var}"] = bool(os.environ.get(env_var))
|
|
100
|
+
|
|
101
|
+
# Check Python packages
|
|
102
|
+
for package in requires.get("packages", []):
|
|
103
|
+
try:
|
|
104
|
+
__import__(package.replace("-", "_").split("[")[0])
|
|
105
|
+
results[f"package:{package}"] = True
|
|
106
|
+
except ImportError:
|
|
107
|
+
results[f"package:{package}"] = False
|
|
108
|
+
|
|
109
|
+
# Check external tools
|
|
110
|
+
import shutil
|
|
111
|
+
for tool in requires.get("external", []):
|
|
112
|
+
results[f"external:{tool}"] = shutil.which(tool) is not None
|
|
113
|
+
|
|
114
|
+
# Check recipe tools
|
|
115
|
+
for tool in requires.get("tools", []):
|
|
116
|
+
try:
|
|
117
|
+
from praisonai_tools.recipe_tools import __all__ as available_tools
|
|
118
|
+
# Check if tool class exists
|
|
119
|
+
tool_class = tool.replace("_tool", "").title() + "Tool"
|
|
120
|
+
results[f"tool:{tool}"] = tool_class in available_tools or tool in str(available_tools)
|
|
121
|
+
except ImportError:
|
|
122
|
+
results[f"tool:{tool}"] = False
|
|
123
|
+
|
|
124
|
+
return results
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def cmd_list(args: argparse.Namespace) -> int:
|
|
128
|
+
"""List available recipes."""
|
|
129
|
+
recipes = list_recipes()
|
|
130
|
+
|
|
131
|
+
if args.json:
|
|
132
|
+
print(json.dumps(recipes, indent=2))
|
|
133
|
+
return 0
|
|
134
|
+
|
|
135
|
+
if not recipes:
|
|
136
|
+
print("No recipes found.")
|
|
137
|
+
return 0
|
|
138
|
+
|
|
139
|
+
# Group by first tag if available
|
|
140
|
+
if args.group:
|
|
141
|
+
groups = {}
|
|
142
|
+
for r in recipes:
|
|
143
|
+
tag = r["tags"][0] if r["tags"] else "other"
|
|
144
|
+
if tag not in groups:
|
|
145
|
+
groups[tag] = []
|
|
146
|
+
groups[tag].append(r)
|
|
147
|
+
|
|
148
|
+
for group, group_recipes in sorted(groups.items()):
|
|
149
|
+
print(f"\n{group.upper()}:")
|
|
150
|
+
for r in group_recipes:
|
|
151
|
+
print(f" {r['name']:<35} {r['description'][:50]}")
|
|
152
|
+
else:
|
|
153
|
+
print(f"{'Recipe':<35} {'Description':<50} {'Version'}")
|
|
154
|
+
print("-" * 95)
|
|
155
|
+
for r in recipes:
|
|
156
|
+
desc = r["description"][:47] + "..." if len(r["description"]) > 50 else r["description"]
|
|
157
|
+
print(f"{r['name']:<35} {desc:<50} {r['version']}")
|
|
158
|
+
|
|
159
|
+
print(f"\nTotal: {len(recipes)} recipes")
|
|
160
|
+
return 0
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def cmd_info(args: argparse.Namespace) -> int:
|
|
164
|
+
"""Show recipe details."""
|
|
165
|
+
recipe = get_recipe(args.name)
|
|
166
|
+
|
|
167
|
+
if not recipe:
|
|
168
|
+
print(f"Recipe not found: {args.name}")
|
|
169
|
+
return 1
|
|
170
|
+
|
|
171
|
+
if args.json:
|
|
172
|
+
print(json.dumps(recipe, indent=2, default=str))
|
|
173
|
+
return 0
|
|
174
|
+
|
|
175
|
+
print(f"Name: {recipe.get('name', args.name)}")
|
|
176
|
+
print(f"Version: {recipe.get('version', '1.0.0')}")
|
|
177
|
+
print(f"Description: {recipe.get('description', '')}")
|
|
178
|
+
print(f"Author: {recipe.get('author', 'unknown')}")
|
|
179
|
+
print(f"License: {recipe.get('license', 'unknown')}")
|
|
180
|
+
print(f"Path: {recipe.get('path', '')}")
|
|
181
|
+
|
|
182
|
+
if recipe.get("tags"):
|
|
183
|
+
print(f"Tags: {', '.join(recipe['tags'])}")
|
|
184
|
+
|
|
185
|
+
requires = recipe.get("requires", {})
|
|
186
|
+
if requires:
|
|
187
|
+
print("\nRequirements:")
|
|
188
|
+
if requires.get("env"):
|
|
189
|
+
print(f" Environment: {', '.join(requires['env'])}")
|
|
190
|
+
if requires.get("packages"):
|
|
191
|
+
print(f" Packages: {', '.join(requires['packages'])}")
|
|
192
|
+
if requires.get("external"):
|
|
193
|
+
print(f" External: {', '.join(requires['external'])}")
|
|
194
|
+
if requires.get("tools"):
|
|
195
|
+
print(f" Tools: {', '.join(requires['tools'])}")
|
|
196
|
+
|
|
197
|
+
cli = recipe.get("cli", {})
|
|
198
|
+
if cli:
|
|
199
|
+
print("\nCLI:")
|
|
200
|
+
print(f" Command: {cli.get('command', '')}")
|
|
201
|
+
if cli.get("examples"):
|
|
202
|
+
print(" Examples:")
|
|
203
|
+
for ex in cli["examples"]:
|
|
204
|
+
print(f" {ex}")
|
|
205
|
+
|
|
206
|
+
safety = recipe.get("safety", {})
|
|
207
|
+
if safety:
|
|
208
|
+
print("\nSafety:")
|
|
209
|
+
print(f" Dry-run default: {safety.get('dry_run_default', False)}")
|
|
210
|
+
if safety.get("requires_consent"):
|
|
211
|
+
print(f" Requires consent: {safety.get('consent_message', 'Yes')}")
|
|
212
|
+
if safety.get("legal_disclaimer"):
|
|
213
|
+
print(f" Legal disclaimer: {safety.get('legal_disclaimer')}")
|
|
214
|
+
|
|
215
|
+
return 0
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def cmd_doctor(args: argparse.Namespace) -> int:
|
|
219
|
+
"""Check recipe dependencies."""
|
|
220
|
+
recipe = get_recipe(args.name)
|
|
221
|
+
|
|
222
|
+
if not recipe:
|
|
223
|
+
print(f"Recipe not found: {args.name}")
|
|
224
|
+
return 1
|
|
225
|
+
|
|
226
|
+
print(f"Checking dependencies for: {args.name}")
|
|
227
|
+
print("-" * 50)
|
|
228
|
+
|
|
229
|
+
deps = check_dependencies(recipe)
|
|
230
|
+
all_ok = True
|
|
231
|
+
|
|
232
|
+
for dep, status in sorted(deps.items()):
|
|
233
|
+
icon = "✓" if status else "✗"
|
|
234
|
+
color = "\033[92m" if status else "\033[91m"
|
|
235
|
+
reset = "\033[0m"
|
|
236
|
+
print(f" {color}{icon}{reset} {dep}")
|
|
237
|
+
if not status:
|
|
238
|
+
all_ok = False
|
|
239
|
+
|
|
240
|
+
print("-" * 50)
|
|
241
|
+
if all_ok:
|
|
242
|
+
print("All dependencies satisfied!")
|
|
243
|
+
return 0
|
|
244
|
+
else:
|
|
245
|
+
print("\nMissing dependencies. Install with:")
|
|
246
|
+
requires = recipe.get("requires", {})
|
|
247
|
+
|
|
248
|
+
if requires.get("packages"):
|
|
249
|
+
missing_pkgs = [p for p in requires["packages"] if not deps.get(f"package:{p}", True)]
|
|
250
|
+
if missing_pkgs:
|
|
251
|
+
print(f" pip install {' '.join(missing_pkgs)}")
|
|
252
|
+
|
|
253
|
+
if requires.get("external"):
|
|
254
|
+
missing_ext = [e for e in requires["external"] if not deps.get(f"external:{e}", True)]
|
|
255
|
+
if missing_ext:
|
|
256
|
+
print(f" # Install external tools: {', '.join(missing_ext)}")
|
|
257
|
+
|
|
258
|
+
if requires.get("env"):
|
|
259
|
+
missing_env = [e for e in requires["env"] if not deps.get(f"env:{e}", True)]
|
|
260
|
+
if missing_env:
|
|
261
|
+
print(f" # Set environment variables: {', '.join(missing_env)}")
|
|
262
|
+
|
|
263
|
+
return 1
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def cmd_run(args: argparse.Namespace) -> int:
|
|
267
|
+
"""Run a recipe."""
|
|
268
|
+
recipe = get_recipe(args.name)
|
|
269
|
+
|
|
270
|
+
if not recipe:
|
|
271
|
+
print(f"Recipe not found: {args.name}")
|
|
272
|
+
return 1
|
|
273
|
+
|
|
274
|
+
# Check safety requirements
|
|
275
|
+
safety = recipe.get("safety", {})
|
|
276
|
+
|
|
277
|
+
if safety.get("requires_consent") and not args.consent:
|
|
278
|
+
print(f"This recipe requires consent: {safety.get('consent_message', '')}")
|
|
279
|
+
print("Use --consent flag to acknowledge.")
|
|
280
|
+
return 1
|
|
281
|
+
|
|
282
|
+
if safety.get("legal_disclaimer"):
|
|
283
|
+
print(f"LEGAL DISCLAIMER: {safety['legal_disclaimer']}")
|
|
284
|
+
|
|
285
|
+
# Check dependencies
|
|
286
|
+
if not args.skip_checks:
|
|
287
|
+
deps = check_dependencies(recipe)
|
|
288
|
+
missing = [k for k, v in deps.items() if not v]
|
|
289
|
+
if missing:
|
|
290
|
+
print(f"Missing dependencies: {', '.join(missing)}")
|
|
291
|
+
print("Run 'praison recipes doctor {args.name}' for details.")
|
|
292
|
+
if not args.force:
|
|
293
|
+
return 1
|
|
294
|
+
|
|
295
|
+
# Dry run mode
|
|
296
|
+
if args.dry_run or (safety.get("dry_run_default") and not args.write):
|
|
297
|
+
print(f"DRY RUN: Would execute recipe '{args.name}'")
|
|
298
|
+
print(f" Input: {args.input}")
|
|
299
|
+
print(f" Output: {args.output or 'default'}")
|
|
300
|
+
print("\nUse --write to execute.")
|
|
301
|
+
return 0
|
|
302
|
+
|
|
303
|
+
# Execute recipe
|
|
304
|
+
print(f"Running recipe: {args.name}")
|
|
305
|
+
print(f"Input: {args.input}")
|
|
306
|
+
|
|
307
|
+
# TODO: Implement actual recipe execution via praisonaiagents
|
|
308
|
+
# For now, show what would be executed
|
|
309
|
+
print("\nRecipe execution would involve:")
|
|
310
|
+
for tool in recipe.get("requires", {}).get("tools", []):
|
|
311
|
+
print(f" - Using {tool}")
|
|
312
|
+
|
|
313
|
+
print("\nRecipe execution completed (placeholder).")
|
|
314
|
+
return 0
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def cmd_explain(args: argparse.Namespace) -> int:
|
|
318
|
+
"""Explain recipe execution plan."""
|
|
319
|
+
recipe = get_recipe(args.name)
|
|
320
|
+
|
|
321
|
+
if not recipe:
|
|
322
|
+
print(f"Recipe not found: {args.name}")
|
|
323
|
+
return 1
|
|
324
|
+
|
|
325
|
+
print(f"Execution Plan for: {args.name}")
|
|
326
|
+
print("=" * 50)
|
|
327
|
+
|
|
328
|
+
print("\n1. Dependency Check:")
|
|
329
|
+
for tool in recipe.get("requires", {}).get("tools", []):
|
|
330
|
+
print(f" - Load {tool}")
|
|
331
|
+
|
|
332
|
+
print("\n2. Input Processing:")
|
|
333
|
+
inputs = recipe.get("inputs", {})
|
|
334
|
+
for name, spec in inputs.items():
|
|
335
|
+
print(f" - {name}: {spec.get('type', 'unknown')}")
|
|
336
|
+
|
|
337
|
+
print("\n3. Execution:")
|
|
338
|
+
print(f" - Run recipe workflow")
|
|
339
|
+
|
|
340
|
+
print("\n4. Output:")
|
|
341
|
+
outputs = recipe.get("outputs", {})
|
|
342
|
+
for name, spec in outputs.items():
|
|
343
|
+
print(f" - {name}: {spec.get('type', 'unknown')}")
|
|
344
|
+
|
|
345
|
+
return 0
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def cmd_init(args: argparse.Namespace) -> int:
|
|
349
|
+
"""Initialize recipe in current directory."""
|
|
350
|
+
recipe = get_recipe(args.name)
|
|
351
|
+
|
|
352
|
+
if not recipe:
|
|
353
|
+
print(f"Recipe not found: {args.name}")
|
|
354
|
+
return 1
|
|
355
|
+
|
|
356
|
+
target_dir = Path(args.output) if args.output else Path.cwd() / args.name
|
|
357
|
+
|
|
358
|
+
if target_dir.exists() and not args.force:
|
|
359
|
+
print(f"Directory already exists: {target_dir}")
|
|
360
|
+
print("Use --force to overwrite.")
|
|
361
|
+
return 1
|
|
362
|
+
|
|
363
|
+
import shutil
|
|
364
|
+
source_dir = Path(recipe["path"])
|
|
365
|
+
|
|
366
|
+
if args.dry_run:
|
|
367
|
+
print(f"DRY RUN: Would copy {source_dir} to {target_dir}")
|
|
368
|
+
return 0
|
|
369
|
+
|
|
370
|
+
target_dir.mkdir(parents=True, exist_ok=True)
|
|
371
|
+
|
|
372
|
+
# Copy recipe files
|
|
373
|
+
for item in source_dir.iterdir():
|
|
374
|
+
if item.is_file():
|
|
375
|
+
shutil.copy2(item, target_dir / item.name)
|
|
376
|
+
|
|
377
|
+
print(f"Initialized recipe '{args.name}' in {target_dir}")
|
|
378
|
+
return 0
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
def setup_parser(subparsers: argparse._SubParsersAction) -> None:
|
|
382
|
+
"""Setup recipes subcommand parser."""
|
|
383
|
+
recipes_parser = subparsers.add_parser(
|
|
384
|
+
"recipes",
|
|
385
|
+
help="Manage and run Agent-Recipes",
|
|
386
|
+
description="Commands for managing and running Agent-Recipes templates.",
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
recipes_subparsers = recipes_parser.add_subparsers(dest="recipes_command")
|
|
390
|
+
|
|
391
|
+
# List command
|
|
392
|
+
list_parser = recipes_subparsers.add_parser("list", help="List available recipes")
|
|
393
|
+
list_parser.add_argument("--json", action="store_true", help="Output as JSON")
|
|
394
|
+
list_parser.add_argument("--group", action="store_true", help="Group by category")
|
|
395
|
+
list_parser.set_defaults(func=cmd_list)
|
|
396
|
+
|
|
397
|
+
# Info command
|
|
398
|
+
info_parser = recipes_subparsers.add_parser("info", help="Show recipe details")
|
|
399
|
+
info_parser.add_argument("name", help="Recipe name")
|
|
400
|
+
info_parser.add_argument("--json", action="store_true", help="Output as JSON")
|
|
401
|
+
info_parser.set_defaults(func=cmd_info)
|
|
402
|
+
|
|
403
|
+
# Doctor command
|
|
404
|
+
doctor_parser = recipes_subparsers.add_parser("doctor", help="Check recipe dependencies")
|
|
405
|
+
doctor_parser.add_argument("name", help="Recipe name")
|
|
406
|
+
doctor_parser.set_defaults(func=cmd_doctor)
|
|
407
|
+
|
|
408
|
+
# Run command
|
|
409
|
+
run_parser = recipes_subparsers.add_parser("run", help="Run a recipe")
|
|
410
|
+
run_parser.add_argument("name", help="Recipe name")
|
|
411
|
+
run_parser.add_argument("input", nargs="?", help="Input file or value")
|
|
412
|
+
run_parser.add_argument("--output", "-o", help="Output directory")
|
|
413
|
+
run_parser.add_argument("--dry-run", action="store_true", help="Show what would be done")
|
|
414
|
+
run_parser.add_argument("--write", action="store_true", help="Actually execute (for dry-run-default recipes)")
|
|
415
|
+
run_parser.add_argument("--force", action="store_true", help="Force execution despite missing deps")
|
|
416
|
+
run_parser.add_argument("--consent", action="store_true", help="Acknowledge consent requirements")
|
|
417
|
+
run_parser.add_argument("--skip-checks", action="store_true", help="Skip dependency checks")
|
|
418
|
+
run_parser.add_argument("--json", action="store_true", help="Output as JSON")
|
|
419
|
+
run_parser.set_defaults(func=cmd_run)
|
|
420
|
+
|
|
421
|
+
# Explain command
|
|
422
|
+
explain_parser = recipes_subparsers.add_parser("explain", help="Explain recipe execution plan")
|
|
423
|
+
explain_parser.add_argument("name", help="Recipe name")
|
|
424
|
+
explain_parser.set_defaults(func=cmd_explain)
|
|
425
|
+
|
|
426
|
+
# Init command
|
|
427
|
+
init_parser = recipes_subparsers.add_parser("init", help="Initialize recipe in directory")
|
|
428
|
+
init_parser.add_argument("name", help="Recipe name")
|
|
429
|
+
init_parser.add_argument("--output", "-o", help="Target directory")
|
|
430
|
+
init_parser.add_argument("--force", action="store_true", help="Overwrite existing")
|
|
431
|
+
init_parser.add_argument("--dry-run", action="store_true", help="Show what would be done")
|
|
432
|
+
init_parser.set_defaults(func=cmd_init)
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
def handle_recipes_command(args: argparse.Namespace) -> int:
|
|
436
|
+
"""Handle recipes subcommand."""
|
|
437
|
+
if not hasattr(args, "func"):
|
|
438
|
+
# No subcommand specified, show help
|
|
439
|
+
print("Usage: praison recipes <command> [options]")
|
|
440
|
+
print("\nCommands:")
|
|
441
|
+
print(" list List available recipes")
|
|
442
|
+
print(" info Show recipe details")
|
|
443
|
+
print(" doctor Check recipe dependencies")
|
|
444
|
+
print(" run Run a recipe")
|
|
445
|
+
print(" explain Explain recipe execution plan")
|
|
446
|
+
print(" init Initialize recipe in directory")
|
|
447
|
+
return 0
|
|
448
|
+
|
|
449
|
+
return args.func(args)
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Registry CLI Feature Handler
|
|
3
|
+
|
|
4
|
+
Provides CLI commands for recipe registry management:
|
|
5
|
+
- serve: Start local HTTP registry server
|
|
6
|
+
- status: Check registry status
|
|
7
|
+
|
|
8
|
+
All commands use the canonical `praisonai registry` prefix.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import os
|
|
12
|
+
import sys
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Any, Dict, List
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class RegistryHandler:
|
|
18
|
+
"""
|
|
19
|
+
CLI handler for registry operations.
|
|
20
|
+
|
|
21
|
+
Commands:
|
|
22
|
+
- serve: Start local HTTP registry server
|
|
23
|
+
- status: Check registry server status
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
# Stable exit codes
|
|
27
|
+
EXIT_SUCCESS = 0
|
|
28
|
+
EXIT_GENERAL_ERROR = 1
|
|
29
|
+
EXIT_VALIDATION_ERROR = 2
|
|
30
|
+
EXIT_NETWORK_ERROR = 10
|
|
31
|
+
EXIT_AUTH_ERROR = 9
|
|
32
|
+
|
|
33
|
+
def __init__(self):
|
|
34
|
+
"""Initialize the handler."""
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
def handle(self, args: List[str]) -> int:
|
|
38
|
+
"""
|
|
39
|
+
Handle registry subcommand.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
args: Command arguments
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
Exit code
|
|
46
|
+
"""
|
|
47
|
+
if not args:
|
|
48
|
+
self._print_help()
|
|
49
|
+
return self.EXIT_SUCCESS
|
|
50
|
+
|
|
51
|
+
command = args[0]
|
|
52
|
+
remaining = args[1:]
|
|
53
|
+
|
|
54
|
+
commands = {
|
|
55
|
+
"serve": self.cmd_serve,
|
|
56
|
+
"status": self.cmd_status,
|
|
57
|
+
"help": lambda _: self._print_help() or self.EXIT_SUCCESS,
|
|
58
|
+
"--help": lambda _: self._print_help() or self.EXIT_SUCCESS,
|
|
59
|
+
"-h": lambda _: self._print_help() or self.EXIT_SUCCESS,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
handler = commands.get(command)
|
|
63
|
+
if handler:
|
|
64
|
+
return handler(remaining)
|
|
65
|
+
|
|
66
|
+
self._print_error(f"Unknown command: {command}")
|
|
67
|
+
self._print_help()
|
|
68
|
+
return self.EXIT_VALIDATION_ERROR
|
|
69
|
+
|
|
70
|
+
def _print_help(self):
|
|
71
|
+
"""Print help message."""
|
|
72
|
+
print("""
|
|
73
|
+
PraisonAI Registry Commands
|
|
74
|
+
|
|
75
|
+
Usage: praisonai registry <command> [options]
|
|
76
|
+
|
|
77
|
+
Commands:
|
|
78
|
+
serve Start local HTTP registry server
|
|
79
|
+
status Check registry server status
|
|
80
|
+
|
|
81
|
+
Examples:
|
|
82
|
+
praisonai registry serve
|
|
83
|
+
praisonai registry serve --port 7777 --token mysecret
|
|
84
|
+
praisonai registry serve --read-only
|
|
85
|
+
praisonai registry status --registry http://localhost:7777
|
|
86
|
+
|
|
87
|
+
Options for 'serve':
|
|
88
|
+
--host HOST Host to bind to (default: 127.0.0.1)
|
|
89
|
+
--port PORT Port to bind to (default: 7777)
|
|
90
|
+
--dir PATH Registry directory (default: ~/.praison/registry)
|
|
91
|
+
--token TOKEN Require token for write operations
|
|
92
|
+
--read-only Disable all write operations
|
|
93
|
+
--json Output in JSON format
|
|
94
|
+
|
|
95
|
+
Options for 'status':
|
|
96
|
+
--registry URL Registry URL to check (default: http://localhost:7777)
|
|
97
|
+
--json Output in JSON format
|
|
98
|
+
""")
|
|
99
|
+
|
|
100
|
+
def _print_error(self, message: str):
|
|
101
|
+
"""Print error message."""
|
|
102
|
+
print(f"Error: {message}", file=sys.stderr)
|
|
103
|
+
|
|
104
|
+
def _print_success(self, message: str):
|
|
105
|
+
"""Print success message."""
|
|
106
|
+
print(f"✓ {message}")
|
|
107
|
+
|
|
108
|
+
def _print_json(self, data: Any):
|
|
109
|
+
"""Print JSON output."""
|
|
110
|
+
import json
|
|
111
|
+
print(json.dumps(data, indent=2))
|
|
112
|
+
|
|
113
|
+
def _parse_args(self, args: List[str], spec: Dict[str, Any]) -> Dict[str, Any]:
|
|
114
|
+
"""Parse command arguments based on spec."""
|
|
115
|
+
result = {k: v.get("default") for k, v in spec.items()}
|
|
116
|
+
|
|
117
|
+
i = 0
|
|
118
|
+
while i < len(args):
|
|
119
|
+
arg = args[i]
|
|
120
|
+
|
|
121
|
+
if arg.startswith("--"):
|
|
122
|
+
key = arg[2:].replace("-", "_")
|
|
123
|
+
if key in spec:
|
|
124
|
+
if spec[key].get("flag"):
|
|
125
|
+
result[key] = True
|
|
126
|
+
elif i + 1 < len(args):
|
|
127
|
+
result[key] = args[i + 1]
|
|
128
|
+
i += 1
|
|
129
|
+
elif arg.startswith("-") and len(arg) == 2:
|
|
130
|
+
# Short flag
|
|
131
|
+
for key, val in spec.items():
|
|
132
|
+
if val.get("short") == arg:
|
|
133
|
+
if val.get("flag"):
|
|
134
|
+
result[key] = True
|
|
135
|
+
elif i + 1 < len(args):
|
|
136
|
+
result[key] = args[i + 1]
|
|
137
|
+
i += 1
|
|
138
|
+
break
|
|
139
|
+
i += 1
|
|
140
|
+
|
|
141
|
+
return result
|
|
142
|
+
|
|
143
|
+
def cmd_serve(self, args: List[str]) -> int:
|
|
144
|
+
"""Start local HTTP registry server."""
|
|
145
|
+
spec = {
|
|
146
|
+
"host": {"default": "127.0.0.1"},
|
|
147
|
+
"port": {"default": "7777"},
|
|
148
|
+
"dir": {"default": None},
|
|
149
|
+
"token": {"default": None},
|
|
150
|
+
"read_only": {"flag": True, "default": False},
|
|
151
|
+
"json": {"flag": True, "default": False},
|
|
152
|
+
}
|
|
153
|
+
parsed = self._parse_args(args, spec)
|
|
154
|
+
|
|
155
|
+
try:
|
|
156
|
+
from praisonai.recipe.server import run_server
|
|
157
|
+
from praisonai.recipe.registry import DEFAULT_REGISTRY_PATH
|
|
158
|
+
|
|
159
|
+
host = parsed["host"]
|
|
160
|
+
port = int(parsed["port"])
|
|
161
|
+
registry_path = Path(parsed["dir"]) if parsed["dir"] else DEFAULT_REGISTRY_PATH
|
|
162
|
+
token = parsed["token"] or os.environ.get("PRAISONAI_REGISTRY_TOKEN")
|
|
163
|
+
read_only = parsed["read_only"]
|
|
164
|
+
|
|
165
|
+
if parsed["json"]:
|
|
166
|
+
self._print_json({
|
|
167
|
+
"ok": True,
|
|
168
|
+
"message": "Starting registry server",
|
|
169
|
+
"host": host,
|
|
170
|
+
"port": port,
|
|
171
|
+
"registry_path": str(registry_path),
|
|
172
|
+
"read_only": read_only,
|
|
173
|
+
"auth_required": bool(token),
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
run_server(
|
|
177
|
+
host=host,
|
|
178
|
+
port=port,
|
|
179
|
+
registry_path=registry_path,
|
|
180
|
+
token=token,
|
|
181
|
+
read_only=read_only,
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
return self.EXIT_SUCCESS
|
|
185
|
+
|
|
186
|
+
except KeyboardInterrupt:
|
|
187
|
+
print("\nServer stopped.")
|
|
188
|
+
return self.EXIT_SUCCESS
|
|
189
|
+
except Exception as e:
|
|
190
|
+
self._print_error(str(e))
|
|
191
|
+
return self.EXIT_GENERAL_ERROR
|
|
192
|
+
|
|
193
|
+
def cmd_status(self, args: List[str]) -> int:
|
|
194
|
+
"""Check registry server status."""
|
|
195
|
+
spec = {
|
|
196
|
+
"registry": {"default": "http://localhost:7777"},
|
|
197
|
+
"json": {"flag": True, "default": False},
|
|
198
|
+
}
|
|
199
|
+
parsed = self._parse_args(args, spec)
|
|
200
|
+
|
|
201
|
+
try:
|
|
202
|
+
from praisonai.recipe.registry import HttpRegistry
|
|
203
|
+
|
|
204
|
+
registry = HttpRegistry(parsed["registry"])
|
|
205
|
+
health = registry.health()
|
|
206
|
+
|
|
207
|
+
if parsed["json"]:
|
|
208
|
+
self._print_json(health)
|
|
209
|
+
else:
|
|
210
|
+
status = "healthy" if health.get("ok") else "unhealthy"
|
|
211
|
+
print(f"Registry: {parsed['registry']}")
|
|
212
|
+
print(f"Status: {status}")
|
|
213
|
+
if health.get("read_only"):
|
|
214
|
+
print("Mode: read-only")
|
|
215
|
+
if health.get("auth_required"):
|
|
216
|
+
print("Auth: required for writes")
|
|
217
|
+
|
|
218
|
+
return self.EXIT_SUCCESS
|
|
219
|
+
|
|
220
|
+
except Exception as e:
|
|
221
|
+
if parsed["json"]:
|
|
222
|
+
self._print_json({
|
|
223
|
+
"ok": False,
|
|
224
|
+
"error": str(e),
|
|
225
|
+
"registry": parsed["registry"],
|
|
226
|
+
})
|
|
227
|
+
else:
|
|
228
|
+
self._print_error(f"Cannot connect to registry: {e}")
|
|
229
|
+
return self.EXIT_NETWORK_ERROR
|