acra-cli 0.1.4__tar.gz → 0.1.5__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.
- {acra_cli-0.1.4/acra_cli.egg-info → acra_cli-0.1.5}/PKG-INFO +34 -11
- {acra_cli-0.1.4 → acra_cli-0.1.5}/README.md +33 -10
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/__init__.py +1 -1
- acra_cli-0.1.5/acra/agents/__pycache__/llm.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/llm.py +24 -8
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/commands/ask.py +9 -2
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/commands/keys.py +6 -2
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/commands/research.py +4 -1
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/config/__pycache__/defaults.cpython-312.pyc +0 -0
- acra_cli-0.1.5/acra/config/__pycache__/profile_manager.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/config/profile_manager.py +26 -4
- acra_cli-0.1.5/acra/ui/__pycache__/components.cpython-312.pyc +0 -0
- acra_cli-0.1.5/acra/ui/__pycache__/shell.cpython-312.pyc +0 -0
- acra_cli-0.1.5/acra/ui/__pycache__/spinner.cpython-312.pyc +0 -0
- acra_cli-0.1.5/acra/ui/components.py +78 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/ui/shell.py +6 -10
- acra_cli-0.1.5/acra/ui/spinner.py +97 -0
- acra_cli-0.1.5/acra/utils/keyring_manager.py +179 -0
- acra_cli-0.1.5/acra/utils/output_formatter.py +113 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5/acra_cli.egg-info}/PKG-INFO +34 -11
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra_cli.egg-info/SOURCES.txt +2 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/pyproject.toml +1 -1
- acra_cli-0.1.4/acra/agents/__pycache__/llm.cpython-312.pyc +0 -0
- acra_cli-0.1.4/acra/config/__pycache__/profile_manager.cpython-312.pyc +0 -0
- acra_cli-0.1.4/acra/ui/__pycache__/components.cpython-312.pyc +0 -0
- acra_cli-0.1.4/acra/ui/__pycache__/shell.cpython-312.pyc +0 -0
- acra_cli-0.1.4/acra/ui/components.py +0 -32
- acra_cli-0.1.4/acra/utils/keyring_manager.py +0 -83
- acra_cli-0.1.4/acra/utils/output_formatter.py +0 -18
- {acra_cli-0.1.4 → acra_cli-0.1.5}/LICENSE +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/MANIFEST.in +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/__main__.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/__init__.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/__pycache__/__init__.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/__pycache__/llm_utils.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/coder/__init__.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/coder/__pycache__/__init__.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/coder/__pycache__/coder_agent.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/coder/__pycache__/coder_chain.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/coder/coder_agent.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/coder/coder_chain.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/critic/__init__.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/critic/__pycache__/__init__.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/critic/__pycache__/critic_agent.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/critic/__pycache__/critic_chain.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/critic/critic_agent.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/critic/critic_chain.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/executor/__init__.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/executor/__pycache__/__init__.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/executor/__pycache__/docker_runner.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/executor/__pycache__/executor_agent.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/executor/__pycache__/sandbox_runner.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/executor/docker_runner.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/executor/executor_agent.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/executor/sandbox_runner.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/human/__init__.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/human/__pycache__/__init__.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/human/__pycache__/human_node.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/human/human_node.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/llm_utils.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/memory/__init__.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/memory/__pycache__/__init__.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/memory/__pycache__/memory_agent.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/memory/__pycache__/memory_manager.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/memory/__pycache__/retrieval.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/memory/memory_agent.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/memory/memory_manager.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/memory/retrieval.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/planner/__init__.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/planner/__pycache__/__init__.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/planner/__pycache__/planner_agent.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/planner/__pycache__/planner_chain.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/planner/planner_agent.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/planner/planner_chain.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/researcher/__init__.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/researcher/__pycache__/__init__.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/researcher/__pycache__/researcher_agent.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/researcher/__pycache__/researcher_chain.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/researcher/researcher_agent.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/agents/researcher/researcher_chain.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/brain/__init__.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/brain/model_registry.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/brain/provider_manager.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/cli.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/commands/__init__.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/commands/brain.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/commands/config.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/commands/context.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/commands/graph.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/commands/logs.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/commands/memory.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/commands/plugin.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/commands/serve.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/commands/session.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/commands/utils.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/config/__init__.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/config/__pycache__/__init__.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/config/defaults.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/execution/__init__.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/execution/logs/__init__.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/execution/logs/error_logs/__init__.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/execution/logs/execution_logs/__init__.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/execution/sandbox/__init__.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/execution/sandbox/docker_executor.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/execution/sandbox/isolated_runner.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/execution/sandbox/sandbox_manager.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/graph/__init__.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/graph/checkpoint.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/graph/conditional_edges.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/graph/edges.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/graph/graph_visualizer.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/graph/nodes.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/graph/router.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/graph/state.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/graph/workflow.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/memory/__init__.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/memory/checkpoints/__init__.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/memory/checkpoints/data/__init__.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/memory/checkpoints/postgres_checkpoint.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/memory/checkpoints/sqlite_checkpoint.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/memory/chroma_db/__init__.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/memory/conversation_memory/__init__.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/memory/conversation_memory/long_term.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/memory/conversation_memory/short_term.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/memory/storage/__init__.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/memory/vector_store/__init__.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/memory/vector_store/chroma_store.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/memory/vector_store/embeddings.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/observability/__init__.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/observability/langsmith_tracing.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/observability/metrics.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/observability/monitoring.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/observability/token_tracking.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/prompts/__init__.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/schemas/__init__.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/schemas/__pycache__/__init__.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/schemas/__pycache__/coder_schema.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/schemas/__pycache__/critic_schema.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/schemas/__pycache__/execution_schema.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/schemas/__pycache__/planner_schema.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/schemas/__pycache__/researcher_schema.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/schemas/__pycache__/shared_schema.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/schemas/coder_schema.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/schemas/critic_schema.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/schemas/execution_schema.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/schemas/planner_schema.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/schemas/researcher_schema.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/schemas/shared_schema.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/tools/__init__.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/tools/code_tools/__init__.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/tools/code_tools/file_reader.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/tools/code_tools/file_writer.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/tools/code_tools/python_repl.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/tools/code_tools/terminal_runner.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/tools/github_tools/__init__.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/tools/github_tools/commit_generator.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/tools/github_tools/github_search.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/tools/github_tools/repo_analyzer.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/tools/integration_test_helper.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/tools/validation_tools/__init__.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/tools/validation_tools/dependency_checker.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/tools/validation_tools/environment_config_validator.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/tools/validation_tools/http_validator.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/tools/validation_tools/response_validator.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/tools/validation_tools/security_checker.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/tools/validation_tools/syntax_checker.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/tools/validation_tools/web_framework_validator.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/tools/web_tools/__init__.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/tools/web_tools/arxiv_search.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/tools/web_tools/serpapi_search.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/tools/web_tools/tavily_search.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/ui/__init__.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/ui/__pycache__/__init__.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/ui/__pycache__/animated_banner.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/ui/__pycache__/banner.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/ui/__pycache__/theme.cpython-312.pyc +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/ui/animated_banner.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/ui/banner.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/ui/theme.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/utils/__init__.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/utils/context_manager.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra/utils/session_manager.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra_cli.egg-info/dependency_links.txt +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra_cli.egg-info/entry_points.txt +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra_cli.egg-info/requires.txt +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/acra_cli.egg-info/top_level.txt +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/requirements.txt +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/setup.cfg +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/setup.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/tests/test_cli.py +0 -0
- {acra_cli-0.1.4 → acra_cli-0.1.5}/tests/test_interactive_routing.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: acra-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.5
|
|
4
4
|
Summary: Agentic CLI for LLM task execution, research, and workflow automation.
|
|
5
5
|
Author: Raj Tembe
|
|
6
6
|
License: GNU Affero General Public License v3
|
|
@@ -79,6 +79,23 @@ It is built for developers who want a local CLI for asking an LLM-powered agent
|
|
|
79
79
|
- ChromaDB-backed vector memory and JSON workflow memory modules
|
|
80
80
|
- Optional extras for provider-specific dependencies and checkpoint backends
|
|
81
81
|
|
|
82
|
+
## What's New in 0.1.5
|
|
83
|
+
|
|
84
|
+
### New features
|
|
85
|
+
|
|
86
|
+
- Provider credentials can now be stored with `acra keys` and used directly by Gemini, OpenAI, Groq, and HuggingFace Cloud workflows.
|
|
87
|
+
- The configuration wizard stores provider and research credentials outside the plaintext profile file, using the OS keyring when available and a permission-restricted local fallback otherwise.
|
|
88
|
+
- Long-running task and research commands show a live progress spinner.
|
|
89
|
+
- Task and research results are rendered as readable summaries, findings, sources, and generated-file lists instead of raw workflow state.
|
|
90
|
+
- Commands launched from the interactive shell now stream their output live.
|
|
91
|
+
|
|
92
|
+
### Bug fixes
|
|
93
|
+
|
|
94
|
+
- Fixed saved provider keys not being read by LLM initialization.
|
|
95
|
+
- Fixed profile setup writing API and research keys to plaintext configuration.
|
|
96
|
+
- Fixed interactive-shell commands buffering output until completion.
|
|
97
|
+
- Fixed unwieldy raw dictionary and message-object output after task or research runs.
|
|
98
|
+
|
|
82
99
|
## Requirements
|
|
83
100
|
|
|
84
101
|
- Python `>=3.11`
|
|
@@ -157,7 +174,7 @@ acra config show
|
|
|
157
174
|
Store a key in your OS keyring:
|
|
158
175
|
|
|
159
176
|
```bash
|
|
160
|
-
acra keys set
|
|
177
|
+
acra keys set GEMINI_API_KEY
|
|
161
178
|
```
|
|
162
179
|
|
|
163
180
|
Run a research workflow:
|
|
@@ -304,13 +321,13 @@ The setup wizard prompts for:
|
|
|
304
321
|
Set a key interactively:
|
|
305
322
|
|
|
306
323
|
```bash
|
|
307
|
-
acra keys set
|
|
324
|
+
acra keys set OPENAI_API_KEY
|
|
308
325
|
```
|
|
309
326
|
|
|
310
327
|
Set a key directly:
|
|
311
328
|
|
|
312
329
|
```bash
|
|
313
|
-
acra keys set
|
|
330
|
+
acra keys set OPENAI_API_KEY "your-key"
|
|
314
331
|
```
|
|
315
332
|
|
|
316
333
|
List key status:
|
|
@@ -322,9 +339,11 @@ acra keys list
|
|
|
322
339
|
Delete a key:
|
|
323
340
|
|
|
324
341
|
```bash
|
|
325
|
-
acra keys delete
|
|
342
|
+
acra keys delete OPENAI_API_KEY
|
|
326
343
|
```
|
|
327
344
|
|
|
345
|
+
Supported provider key names are `GEMINI_API_KEY`, `OPENAI_API_KEY`, `GROQ_API_KEY`, and `HF_API_KEY`. Keys are stored in your OS keyring when it is available. On systems without a keyring backend, acra uses a local credentials file with owner-only permissions.
|
|
346
|
+
|
|
328
347
|
Research key names:
|
|
329
348
|
|
|
330
349
|
```bash
|
|
@@ -336,7 +355,10 @@ acra keys set research.arxiv
|
|
|
336
355
|
|
|
337
356
|
Environment fallback variables:
|
|
338
357
|
|
|
339
|
-
- `
|
|
358
|
+
- `GEMINI_API_KEY` or `GOOGLE_GEMINI_API_KEY`
|
|
359
|
+
- `OPENAI_API_KEY`
|
|
360
|
+
- `GROQ_API_KEY`
|
|
361
|
+
- `HF_API_KEY`
|
|
340
362
|
- `ACRA_RESEARCH_WEB_KEY`
|
|
341
363
|
- `ACRA_RESEARCH_GITHUB_KEY`
|
|
342
364
|
- `ACRA_RESEARCH_DOCS_KEY`
|
|
@@ -439,9 +461,9 @@ acra config show
|
|
|
439
461
|
Keys:
|
|
440
462
|
|
|
441
463
|
```bash
|
|
442
|
-
acra keys set
|
|
464
|
+
acra keys set GEMINI_API_KEY
|
|
443
465
|
acra keys list
|
|
444
|
-
acra keys delete
|
|
466
|
+
acra keys delete GEMINI_API_KEY
|
|
445
467
|
```
|
|
446
468
|
|
|
447
469
|
Memory:
|
|
@@ -548,6 +570,7 @@ export OMNIAGENT_DATA_DIR=/path/to/acra-data
|
|
|
548
570
|
|
|
549
571
|
Important subdirectories:
|
|
550
572
|
|
|
573
|
+
- `credentials.json`: permission-restricted credential fallback when no OS keyring is available
|
|
551
574
|
- `projects/`: generated project files
|
|
552
575
|
- `memory/storage/`: JSON memory files
|
|
553
576
|
- `memory/chroma_db/`: ChromaDB vector memory
|
|
@@ -555,7 +578,7 @@ Important subdirectories:
|
|
|
555
578
|
|
|
556
579
|
## Current Beta Notes
|
|
557
580
|
|
|
558
|
-
- The package version is `0.1.
|
|
581
|
+
- The package version is `0.1.5`.
|
|
559
582
|
- The top-level CLI currently attaches `serve`, `config`, `keys`, `research`, `memory`, `session`, `graph`, and `workspace`.
|
|
560
583
|
- The codebase contains additional command modules such as `brain`, `context`, `logs`, and `plugin`, but they are not currently attached to the top-level CLI.
|
|
561
584
|
- Some CLI command groups return placeholder output while the Python modules behind them continue to evolve.
|
|
@@ -587,7 +610,7 @@ export HF_API_KEY="your-token"
|
|
|
587
610
|
Or store it with:
|
|
588
611
|
|
|
589
612
|
```bash
|
|
590
|
-
acra keys set
|
|
613
|
+
acra keys set GEMINI_API_KEY
|
|
591
614
|
```
|
|
592
615
|
|
|
593
616
|
### Ollama connection failure
|
|
@@ -616,7 +639,7 @@ export OLLAMA_BASE_URL=http://localhost:11434
|
|
|
616
639
|
- PyPI package name: `acra-cli`
|
|
617
640
|
- Console command: `acra`
|
|
618
641
|
- Python import package: `acra`
|
|
619
|
-
- Version: `0.1.
|
|
642
|
+
- Version: `0.1.5`
|
|
620
643
|
- Python: `>=3.11`
|
|
621
644
|
- Console script: `acra=acra.cli:app_main`
|
|
622
645
|
- Author: Raj Tembe
|
|
@@ -23,6 +23,23 @@ It is built for developers who want a local CLI for asking an LLM-powered agent
|
|
|
23
23
|
- ChromaDB-backed vector memory and JSON workflow memory modules
|
|
24
24
|
- Optional extras for provider-specific dependencies and checkpoint backends
|
|
25
25
|
|
|
26
|
+
## What's New in 0.1.5
|
|
27
|
+
|
|
28
|
+
### New features
|
|
29
|
+
|
|
30
|
+
- Provider credentials can now be stored with `acra keys` and used directly by Gemini, OpenAI, Groq, and HuggingFace Cloud workflows.
|
|
31
|
+
- The configuration wizard stores provider and research credentials outside the plaintext profile file, using the OS keyring when available and a permission-restricted local fallback otherwise.
|
|
32
|
+
- Long-running task and research commands show a live progress spinner.
|
|
33
|
+
- Task and research results are rendered as readable summaries, findings, sources, and generated-file lists instead of raw workflow state.
|
|
34
|
+
- Commands launched from the interactive shell now stream their output live.
|
|
35
|
+
|
|
36
|
+
### Bug fixes
|
|
37
|
+
|
|
38
|
+
- Fixed saved provider keys not being read by LLM initialization.
|
|
39
|
+
- Fixed profile setup writing API and research keys to plaintext configuration.
|
|
40
|
+
- Fixed interactive-shell commands buffering output until completion.
|
|
41
|
+
- Fixed unwieldy raw dictionary and message-object output after task or research runs.
|
|
42
|
+
|
|
26
43
|
## Requirements
|
|
27
44
|
|
|
28
45
|
- Python `>=3.11`
|
|
@@ -101,7 +118,7 @@ acra config show
|
|
|
101
118
|
Store a key in your OS keyring:
|
|
102
119
|
|
|
103
120
|
```bash
|
|
104
|
-
acra keys set
|
|
121
|
+
acra keys set GEMINI_API_KEY
|
|
105
122
|
```
|
|
106
123
|
|
|
107
124
|
Run a research workflow:
|
|
@@ -248,13 +265,13 @@ The setup wizard prompts for:
|
|
|
248
265
|
Set a key interactively:
|
|
249
266
|
|
|
250
267
|
```bash
|
|
251
|
-
acra keys set
|
|
268
|
+
acra keys set OPENAI_API_KEY
|
|
252
269
|
```
|
|
253
270
|
|
|
254
271
|
Set a key directly:
|
|
255
272
|
|
|
256
273
|
```bash
|
|
257
|
-
acra keys set
|
|
274
|
+
acra keys set OPENAI_API_KEY "your-key"
|
|
258
275
|
```
|
|
259
276
|
|
|
260
277
|
List key status:
|
|
@@ -266,9 +283,11 @@ acra keys list
|
|
|
266
283
|
Delete a key:
|
|
267
284
|
|
|
268
285
|
```bash
|
|
269
|
-
acra keys delete
|
|
286
|
+
acra keys delete OPENAI_API_KEY
|
|
270
287
|
```
|
|
271
288
|
|
|
289
|
+
Supported provider key names are `GEMINI_API_KEY`, `OPENAI_API_KEY`, `GROQ_API_KEY`, and `HF_API_KEY`. Keys are stored in your OS keyring when it is available. On systems without a keyring backend, acra uses a local credentials file with owner-only permissions.
|
|
290
|
+
|
|
272
291
|
Research key names:
|
|
273
292
|
|
|
274
293
|
```bash
|
|
@@ -280,7 +299,10 @@ acra keys set research.arxiv
|
|
|
280
299
|
|
|
281
300
|
Environment fallback variables:
|
|
282
301
|
|
|
283
|
-
- `
|
|
302
|
+
- `GEMINI_API_KEY` or `GOOGLE_GEMINI_API_KEY`
|
|
303
|
+
- `OPENAI_API_KEY`
|
|
304
|
+
- `GROQ_API_KEY`
|
|
305
|
+
- `HF_API_KEY`
|
|
284
306
|
- `ACRA_RESEARCH_WEB_KEY`
|
|
285
307
|
- `ACRA_RESEARCH_GITHUB_KEY`
|
|
286
308
|
- `ACRA_RESEARCH_DOCS_KEY`
|
|
@@ -383,9 +405,9 @@ acra config show
|
|
|
383
405
|
Keys:
|
|
384
406
|
|
|
385
407
|
```bash
|
|
386
|
-
acra keys set
|
|
408
|
+
acra keys set GEMINI_API_KEY
|
|
387
409
|
acra keys list
|
|
388
|
-
acra keys delete
|
|
410
|
+
acra keys delete GEMINI_API_KEY
|
|
389
411
|
```
|
|
390
412
|
|
|
391
413
|
Memory:
|
|
@@ -492,6 +514,7 @@ export OMNIAGENT_DATA_DIR=/path/to/acra-data
|
|
|
492
514
|
|
|
493
515
|
Important subdirectories:
|
|
494
516
|
|
|
517
|
+
- `credentials.json`: permission-restricted credential fallback when no OS keyring is available
|
|
495
518
|
- `projects/`: generated project files
|
|
496
519
|
- `memory/storage/`: JSON memory files
|
|
497
520
|
- `memory/chroma_db/`: ChromaDB vector memory
|
|
@@ -499,7 +522,7 @@ Important subdirectories:
|
|
|
499
522
|
|
|
500
523
|
## Current Beta Notes
|
|
501
524
|
|
|
502
|
-
- The package version is `0.1.
|
|
525
|
+
- The package version is `0.1.5`.
|
|
503
526
|
- The top-level CLI currently attaches `serve`, `config`, `keys`, `research`, `memory`, `session`, `graph`, and `workspace`.
|
|
504
527
|
- The codebase contains additional command modules such as `brain`, `context`, `logs`, and `plugin`, but they are not currently attached to the top-level CLI.
|
|
505
528
|
- Some CLI command groups return placeholder output while the Python modules behind them continue to evolve.
|
|
@@ -531,7 +554,7 @@ export HF_API_KEY="your-token"
|
|
|
531
554
|
Or store it with:
|
|
532
555
|
|
|
533
556
|
```bash
|
|
534
|
-
acra keys set
|
|
557
|
+
acra keys set GEMINI_API_KEY
|
|
535
558
|
```
|
|
536
559
|
|
|
537
560
|
### Ollama connection failure
|
|
@@ -560,7 +583,7 @@ export OLLAMA_BASE_URL=http://localhost:11434
|
|
|
560
583
|
- PyPI package name: `acra-cli`
|
|
561
584
|
- Console command: `acra`
|
|
562
585
|
- Python import package: `acra`
|
|
563
|
-
- Version: `0.1.
|
|
586
|
+
- Version: `0.1.5`
|
|
564
587
|
- Python: `>=3.11`
|
|
565
588
|
- Console script: `acra=acra.cli:app_main`
|
|
566
589
|
- Author: Raj Tembe
|
|
Binary file
|
|
@@ -73,10 +73,17 @@ class LLMInitializationError(Exception):
|
|
|
73
73
|
|
|
74
74
|
def _init_gemini():
|
|
75
75
|
"""Initialize Google Gemini LLM."""
|
|
76
|
-
|
|
76
|
+
from acra.utils.keyring_manager import get_key
|
|
77
|
+
|
|
78
|
+
gemini_api_key = (
|
|
79
|
+
os.getenv("GOOGLE_GEMINI_API_KEY")
|
|
80
|
+
or os.getenv("GEMINI_API_KEY")
|
|
81
|
+
or get_key("GEMINI_API_KEY")
|
|
82
|
+
)
|
|
77
83
|
if not gemini_api_key or gemini_api_key.startswith("your_"):
|
|
78
84
|
raise EnvironmentError(
|
|
79
|
-
"GEMINI_API_KEY is not set.
|
|
85
|
+
"GEMINI_API_KEY is not set. Run 'acra keys set GEMINI_API_KEY' "
|
|
86
|
+
"or add GOOGLE_GEMINI_API_KEY to your .env file."
|
|
80
87
|
)
|
|
81
88
|
os.environ["GOOGLE_API_KEY"] = gemini_api_key
|
|
82
89
|
return ChatGoogleGenerativeAI(
|
|
@@ -91,10 +98,13 @@ def _init_openai():
|
|
|
91
98
|
raise LLMInitializationError(
|
|
92
99
|
"OpenAI support is not installed. Install with: pip install langchain-openai"
|
|
93
100
|
)
|
|
94
|
-
|
|
101
|
+
from acra.utils.keyring_manager import get_key
|
|
102
|
+
|
|
103
|
+
openai_api_key = os.getenv("OPENAI_API_KEY") or get_key("OPENAI_API_KEY")
|
|
95
104
|
if not openai_api_key or openai_api_key.startswith("your_"):
|
|
96
105
|
raise LLMInitializationError(
|
|
97
|
-
"OPENAI_API_KEY is not set.
|
|
106
|
+
"OPENAI_API_KEY is not set. Run 'acra keys set OPENAI_API_KEY' "
|
|
107
|
+
"or add OPENAI_API_KEY to your .env file."
|
|
98
108
|
)
|
|
99
109
|
return ChatOpenAI(
|
|
100
110
|
model=OPENAI_MODEL,
|
|
@@ -109,10 +119,13 @@ def _init_groq():
|
|
|
109
119
|
raise LLMInitializationError(
|
|
110
120
|
"Groq support is not installed. Install with: pip install langchain-groq"
|
|
111
121
|
)
|
|
112
|
-
|
|
122
|
+
from acra.utils.keyring_manager import get_key
|
|
123
|
+
|
|
124
|
+
groq_api_key = os.getenv("GROQ_API_KEY") or get_key("GROQ_API_KEY")
|
|
113
125
|
if not groq_api_key or groq_api_key.startswith("your_"):
|
|
114
126
|
raise LLMInitializationError(
|
|
115
|
-
"GROQ_API_KEY is not set.
|
|
127
|
+
"GROQ_API_KEY is not set. Run 'acra keys set GROQ_API_KEY' "
|
|
128
|
+
"or add GROQ_API_KEY to your .env file."
|
|
116
129
|
)
|
|
117
130
|
return ChatGroq(
|
|
118
131
|
model=GROQ_MODEL,
|
|
@@ -150,10 +163,13 @@ def _init_huggingface_cloud():
|
|
|
150
163
|
"HuggingFace not installed. Install with: pip install langchain-huggingface"
|
|
151
164
|
)
|
|
152
165
|
|
|
153
|
-
|
|
166
|
+
from acra.utils.keyring_manager import get_key
|
|
167
|
+
|
|
168
|
+
huggingface_api_key = os.getenv("HF_API_KEY") or get_key("HF_API_KEY")
|
|
154
169
|
if not huggingface_api_key or huggingface_api_key.startswith("your_"):
|
|
155
170
|
raise LLMInitializationError(
|
|
156
|
-
"HF_API_KEY is not set.
|
|
171
|
+
"HF_API_KEY is not set. Run 'acra keys set HF_API_KEY' or add "
|
|
172
|
+
"HF_API_KEY to your .env file. "
|
|
157
173
|
"Get a token at: https://huggingface.co/settings/tokens"
|
|
158
174
|
)
|
|
159
175
|
|
|
@@ -7,6 +7,8 @@ import typer
|
|
|
7
7
|
|
|
8
8
|
from acra.graph.workflow import OmniAgentCallbacks, omniagent_graph
|
|
9
9
|
from acra.ui.components import render_panel
|
|
10
|
+
from acra.ui.spinner import run_with_spinner
|
|
11
|
+
from acra.utils.output_formatter import format_task_result
|
|
10
12
|
|
|
11
13
|
app = typer.Typer(help="Agent task commands for acra.")
|
|
12
14
|
|
|
@@ -35,8 +37,13 @@ def _run_task(
|
|
|
35
37
|
"callbacks": [OmniAgentCallbacks()],
|
|
36
38
|
"configurable": {"thread_id": thread_id},
|
|
37
39
|
}
|
|
38
|
-
result =
|
|
39
|
-
|
|
40
|
+
result = run_with_spinner(
|
|
41
|
+
omniagent_graph.invoke,
|
|
42
|
+
state,
|
|
43
|
+
config=config,
|
|
44
|
+
message=f"Running {task_label}",
|
|
45
|
+
)
|
|
46
|
+
render_panel(format_task_result(result), title=task_label)
|
|
40
47
|
return result
|
|
41
48
|
|
|
42
49
|
|
|
@@ -16,7 +16,11 @@ app = typer.Typer(help="Key management commands for acra.")
|
|
|
16
16
|
|
|
17
17
|
@app.command("set")
|
|
18
18
|
def set_key_cmd(
|
|
19
|
-
name: str = typer.Argument(
|
|
19
|
+
name: str = typer.Argument(
|
|
20
|
+
...,
|
|
21
|
+
help="Key name: GEMINI_API_KEY, OPENAI_API_KEY, GROQ_API_KEY, HF_API_KEY, "
|
|
22
|
+
"or research.<source> (web, github, docs, arxiv).",
|
|
23
|
+
),
|
|
20
24
|
value: Optional[str] = typer.Argument(None, help="API key value. If omitted, you will be prompted."),
|
|
21
25
|
):
|
|
22
26
|
"""Set a provider or research API key."""
|
|
@@ -37,4 +41,4 @@ def list_key_cmd():
|
|
|
37
41
|
def delete_key_cmd(name: str = typer.Argument(..., help="Key name to delete.")):
|
|
38
42
|
"""Delete a configured API key."""
|
|
39
43
|
delete_key(name)
|
|
40
|
-
render_panel(f"Deleted key: {name}")
|
|
44
|
+
render_panel(f"Deleted key: {name}")
|
|
@@ -10,6 +10,7 @@ from acra.config.profile_manager import ProfileManager
|
|
|
10
10
|
from acra.utils.output_formatter import format_research_report
|
|
11
11
|
from acra.utils.keyring_manager import get_research_key_status
|
|
12
12
|
from acra.ui.components import render_panel
|
|
13
|
+
from acra.ui.spinner import run_with_spinner
|
|
13
14
|
|
|
14
15
|
app = typer.Typer(help="Research commands for acra.")
|
|
15
16
|
|
|
@@ -55,12 +56,14 @@ def research(
|
|
|
55
56
|
}
|
|
56
57
|
|
|
57
58
|
thread_id = str(uuid.uuid4())
|
|
58
|
-
result =
|
|
59
|
+
result = run_with_spinner(
|
|
60
|
+
workflow.invoke,
|
|
59
61
|
state,
|
|
60
62
|
config={
|
|
61
63
|
"callbacks": [OmniAgentCallbacks()],
|
|
62
64
|
"configurable": {"thread_id": thread_id},
|
|
63
65
|
},
|
|
66
|
+
message="Researching",
|
|
64
67
|
)
|
|
65
68
|
|
|
66
69
|
report = format_research_report(result, output_format=format)
|
|
Binary file
|
|
Binary file
|
|
@@ -10,6 +10,16 @@ CONFIG_DIR = Path.home() / ".acra"
|
|
|
10
10
|
CONFIG_FILE = CONFIG_DIR / "config.json"
|
|
11
11
|
DEFAULT_PROFILE_NAME = "default"
|
|
12
12
|
|
|
13
|
+
# Maps provider names to the credential name acra/agents/llm.py actually
|
|
14
|
+
# reads via keyring_manager.get_key(). Ollama is local and needs no key.
|
|
15
|
+
_PROVIDER_KEY_NAMES = {
|
|
16
|
+
"gemini": "GEMINI_API_KEY",
|
|
17
|
+
"openai": "OPENAI_API_KEY",
|
|
18
|
+
"groq": "GROQ_API_KEY",
|
|
19
|
+
"huggingface_cloud": "HF_API_KEY",
|
|
20
|
+
"huggingface_local": "HF_API_KEY",
|
|
21
|
+
}
|
|
22
|
+
|
|
13
23
|
|
|
14
24
|
def _ensure_config_dir() -> None:
|
|
15
25
|
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
|
@@ -69,19 +79,31 @@ class ProfileManager:
|
|
|
69
79
|
api_key = getpass("Provider API key (hidden): ")
|
|
70
80
|
theme = input("Theme (dark-orange/dark-blue/dark-green): ").strip() or "dark-orange"
|
|
71
81
|
workspace = input("Workspace path (default=current directory): ").strip() or os.getcwd()
|
|
72
|
-
research_keys = {}
|
|
73
82
|
print("Configure research keys. Press enter to skip a source.")
|
|
83
|
+
research_key_values = {}
|
|
74
84
|
for source in ["web", "github", "docs", "arxiv"]:
|
|
75
85
|
prompt = f"Research {source} API key (if required): "
|
|
76
86
|
value = getpass(prompt) if source != "arxiv" else input(prompt).strip()
|
|
77
|
-
|
|
87
|
+
if value:
|
|
88
|
+
research_key_values[source] = value
|
|
89
|
+
|
|
90
|
+
# Secrets are stored via the OS keyring (falling back to a
|
|
91
|
+
# permission-locked local file when no OS backend is available),
|
|
92
|
+
# never written into the plaintext profile JSON. See
|
|
93
|
+
# acra/utils/keyring_manager.py.
|
|
94
|
+
from acra.utils.keyring_manager import set_key
|
|
95
|
+
|
|
96
|
+
provider_key_name = _PROVIDER_KEY_NAMES.get(provider)
|
|
97
|
+
if api_key and provider_key_name:
|
|
98
|
+
set_key(provider_key_name, api_key)
|
|
99
|
+
for source, value in research_key_values.items():
|
|
100
|
+
set_key(f"research.{source}", value)
|
|
78
101
|
|
|
79
102
|
profile = {
|
|
80
103
|
"provider": provider,
|
|
81
104
|
"model": model,
|
|
82
105
|
"theme": theme,
|
|
83
106
|
"workspace": workspace,
|
|
84
|
-
"research_keys": research_keys,
|
|
85
107
|
}
|
|
86
108
|
self.save_profile(profile_name, profile)
|
|
87
|
-
return profile_name
|
|
109
|
+
return profile_name
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""Shared UI render components for acra."""
|
|
2
|
+
|
|
3
|
+
from rich.console import Console, Group
|
|
4
|
+
from rich.panel import Panel
|
|
5
|
+
from rich.table import Table
|
|
6
|
+
from rich.text import Text
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
console = Console()
|
|
10
|
+
|
|
11
|
+
_MAX_FIELD_LENGTH = 2000
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _stringify(value: Any) -> str:
|
|
15
|
+
if isinstance(value, list) and value and all(isinstance(v, str) for v in value):
|
|
16
|
+
return "\n".join(f"\u2022 {v}" for v in value)
|
|
17
|
+
if isinstance(value, (list, tuple)):
|
|
18
|
+
return ", ".join(str(v) for v in value)
|
|
19
|
+
if isinstance(value, dict):
|
|
20
|
+
return "\n".join(f"{k}: {v}" for k, v in value.items())
|
|
21
|
+
return str(value)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _render_dict(data: dict) -> Group:
|
|
25
|
+
"""Render a dict as a lead answer (if present) plus a clean field table,
|
|
26
|
+
instead of a raw Python repr. Built for the summaries produced by
|
|
27
|
+
acra.utils.output_formatter, but degrades gracefully for any dict."""
|
|
28
|
+
renderables = []
|
|
29
|
+
|
|
30
|
+
answer = data.get("answer")
|
|
31
|
+
if answer:
|
|
32
|
+
renderables.append(Text(str(answer)))
|
|
33
|
+
renderables.append(Text(""))
|
|
34
|
+
|
|
35
|
+
table = Table(show_header=False, box=None, padding=(0, 1, 0, 0))
|
|
36
|
+
table.add_column("Field", style="bold cyan", no_wrap=True)
|
|
37
|
+
table.add_column("Value")
|
|
38
|
+
for key, value in data.items():
|
|
39
|
+
if key == "answer" or value in (None, "", [], {}):
|
|
40
|
+
continue
|
|
41
|
+
value_str = _stringify(value)
|
|
42
|
+
if len(value_str) > _MAX_FIELD_LENGTH:
|
|
43
|
+
value_str = value_str[:_MAX_FIELD_LENGTH] + "\u2026 (truncated)"
|
|
44
|
+
table.add_row(key.replace("_", " ").title(), value_str)
|
|
45
|
+
|
|
46
|
+
if table.rows:
|
|
47
|
+
renderables.append(table)
|
|
48
|
+
|
|
49
|
+
if not renderables:
|
|
50
|
+
renderables.append(Text("(empty)"))
|
|
51
|
+
|
|
52
|
+
return Group(*renderables)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def render_panel(content: Any, title: str = "acra") -> None:
|
|
56
|
+
if isinstance(content, list):
|
|
57
|
+
table = Table(show_header=True, header_style="bold magenta")
|
|
58
|
+
table.add_column("Name")
|
|
59
|
+
table.add_column("Status")
|
|
60
|
+
table.add_column("Value")
|
|
61
|
+
for item in content:
|
|
62
|
+
if isinstance(item, dict):
|
|
63
|
+
table.add_row(
|
|
64
|
+
str(item.get("name", "")),
|
|
65
|
+
str(item.get("status", "")),
|
|
66
|
+
str(item.get("value", "")),
|
|
67
|
+
)
|
|
68
|
+
else:
|
|
69
|
+
table.add_row(str(item), "", "")
|
|
70
|
+
console.print(Panel(table, title=title))
|
|
71
|
+
elif isinstance(content, dict):
|
|
72
|
+
console.print(Panel(_render_dict(content), title=title))
|
|
73
|
+
else:
|
|
74
|
+
console.print(Panel(str(content), title=title))
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def render_message(message: str) -> None:
|
|
78
|
+
console.print(message)
|
|
@@ -70,21 +70,17 @@ def launch_shell(profile: str = None, workspace: str = None, party: bool = False
|
|
|
70
70
|
console.print(f"[dim]→[/dim] {user_input}")
|
|
71
71
|
|
|
72
72
|
cmd = [sys.executable, "-m", "acra"] + shlex.split(command_text)
|
|
73
|
+
# stdout/stderr are inherited (not captured) so the child process
|
|
74
|
+
# sees a real tty and any live output -- the run_with_spinner
|
|
75
|
+
# progress indicator, the animated banner, streamed agent output
|
|
76
|
+
# -- renders in real time instead of being buffered up and
|
|
77
|
+
# dumped all at once after the command finishes.
|
|
73
78
|
completed = subprocess.run(
|
|
74
79
|
cmd,
|
|
75
80
|
cwd=os.getcwd(),
|
|
76
|
-
stdin=sys.stdin,
|
|
77
|
-
stdout=subprocess.PIPE,
|
|
78
|
-
stderr=subprocess.STDOUT,
|
|
79
|
-
text=True,
|
|
80
81
|
)
|
|
81
|
-
if completed.stdout:
|
|
82
|
-
console.print(completed.stdout, soft_wrap=True)
|
|
83
82
|
if completed.returncode != 0:
|
|
84
|
-
|
|
85
|
-
console.print("[dim]Tip:[/dim] try a subcommand such as 'brain models' or 'config init'.")
|
|
86
|
-
else:
|
|
87
|
-
console.print(f"[bold red]Command failed with exit code {completed.returncode}.[/bold red]")
|
|
83
|
+
console.print(f"[bold red]Command failed with exit code {completed.returncode}.[/bold red]")
|
|
88
84
|
except KeyboardInterrupt:
|
|
89
85
|
break
|
|
90
86
|
except EOFError:
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""Live 'running' spinner for long agent tasks.
|
|
2
|
+
|
|
3
|
+
`acra ask/build/fix/review/explain/run` can take anywhere from a few
|
|
4
|
+
seconds to a couple of minutes (planner -> researcher -> coder -> executor
|
|
5
|
+
-> critic, each an LLM call), and until now the terminal went completely
|
|
6
|
+
silent between hitting enter and the result panel appearing. This wraps a
|
|
7
|
+
blocking call with the same braille spinner used in the animated startup
|
|
8
|
+
banner (acra/ui/animated_banner.py), showing live progress and elapsed
|
|
9
|
+
time instead.
|
|
10
|
+
|
|
11
|
+
Kept as a small, self-contained module (rather than importing the
|
|
12
|
+
startup-banner internals) so a bug here can't affect the startup banner
|
|
13
|
+
and vice versa; the animation/color gating logic is intentionally
|
|
14
|
+
duplicated in miniature from animated_banner.py, honoring the same
|
|
15
|
+
conventions (NO_COLOR, FORCE_COLOR, TERM=dumb, CI, ACRA_NO_ANIMATION).
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import os
|
|
19
|
+
import sys
|
|
20
|
+
import threading
|
|
21
|
+
import time
|
|
22
|
+
from typing import Any, Callable
|
|
23
|
+
|
|
24
|
+
RESET = "\033[0m"
|
|
25
|
+
DIM = "\033[2m"
|
|
26
|
+
CYAN = "\033[36m"
|
|
27
|
+
GREEN = "\033[92m"
|
|
28
|
+
|
|
29
|
+
_SPINNER_FRAMES = "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _animations_enabled() -> bool:
|
|
33
|
+
if os.environ.get("ACRA_NO_ANIMATION"):
|
|
34
|
+
return False
|
|
35
|
+
if os.environ.get("CI"):
|
|
36
|
+
return False
|
|
37
|
+
return sys.stdout.isatty()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _supports_color() -> bool:
|
|
41
|
+
if os.environ.get("FORCE_COLOR", "") != "":
|
|
42
|
+
return True
|
|
43
|
+
if os.environ.get("NO_COLOR", "") != "":
|
|
44
|
+
return False
|
|
45
|
+
if os.environ.get("TERM") == "dumb":
|
|
46
|
+
return False
|
|
47
|
+
return True
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def run_with_spinner(fn: Callable[..., Any], *args, message: str = "Working", **kwargs) -> Any:
|
|
51
|
+
"""Run fn(*args, **kwargs) on a background thread, showing a live spinner
|
|
52
|
+
with elapsed time until it finishes. Returns fn's return value, or
|
|
53
|
+
re-raises whatever exception fn raised.
|
|
54
|
+
|
|
55
|
+
Falls back to a single static line and a plain blocking call when
|
|
56
|
+
stdout isn't a real interactive terminal (piped, CI, ACRA_NO_ANIMATION),
|
|
57
|
+
so scripted/piped invocations pay no animation cost and never see
|
|
58
|
+
raw escape codes leaking into logs.
|
|
59
|
+
"""
|
|
60
|
+
if not _animations_enabled():
|
|
61
|
+
print(f"{message}...")
|
|
62
|
+
return fn(*args, **kwargs)
|
|
63
|
+
|
|
64
|
+
outcome: dict = {}
|
|
65
|
+
|
|
66
|
+
def _target():
|
|
67
|
+
try:
|
|
68
|
+
outcome["value"] = fn(*args, **kwargs)
|
|
69
|
+
except BaseException as exc: # re-raised on the main thread below
|
|
70
|
+
outcome["error"] = exc
|
|
71
|
+
|
|
72
|
+
thread = threading.Thread(target=_target, daemon=True)
|
|
73
|
+
thread.start()
|
|
74
|
+
|
|
75
|
+
color = CYAN if _supports_color() else ""
|
|
76
|
+
reset = RESET if _supports_color() else ""
|
|
77
|
+
dim = DIM if _supports_color() else ""
|
|
78
|
+
green = GREEN if _supports_color() else ""
|
|
79
|
+
|
|
80
|
+
start = time.time()
|
|
81
|
+
frame_idx = 0
|
|
82
|
+
while thread.is_alive():
|
|
83
|
+
frame = _SPINNER_FRAMES[frame_idx % len(_SPINNER_FRAMES)]
|
|
84
|
+
elapsed = time.time() - start
|
|
85
|
+
sys.stdout.write(f"\r{color}{frame}{reset} {dim}{message}\u2026 {elapsed:0.1f}s{reset}\033[K")
|
|
86
|
+
sys.stdout.flush()
|
|
87
|
+
time.sleep(0.08)
|
|
88
|
+
frame_idx += 1
|
|
89
|
+
thread.join()
|
|
90
|
+
|
|
91
|
+
elapsed = time.time() - start
|
|
92
|
+
sys.stdout.write(f"\r{green}\u2713{reset} {message} ({elapsed:.1f}s){' ' * 12}\n")
|
|
93
|
+
sys.stdout.flush()
|
|
94
|
+
|
|
95
|
+
if "error" in outcome:
|
|
96
|
+
raise outcome["error"]
|
|
97
|
+
return outcome.get("value")
|