devsper 2.1.6__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.
- devsper/__init__.py +14 -0
- devsper/agents/a2a/__init__.py +27 -0
- devsper/agents/a2a/client.py +126 -0
- devsper/agents/a2a/discovery.py +24 -0
- devsper/agents/a2a/server.py +128 -0
- devsper/agents/a2a/tool_adapter.py +68 -0
- devsper/agents/a2a/types.py +49 -0
- devsper/agents/agent.py +602 -0
- devsper/agents/critic.py +80 -0
- devsper/agents/message_bus.py +124 -0
- devsper/agents/roles.py +181 -0
- devsper/agents/run_agent.py +78 -0
- devsper/analytics/__init__.py +5 -0
- devsper/analytics/tool_analytics.py +78 -0
- devsper/audit/__init__.py +5 -0
- devsper/audit/logger.py +214 -0
- devsper/bus/__init__.py +29 -0
- devsper/bus/backends/__init__.py +5 -0
- devsper/bus/backends/base.py +38 -0
- devsper/bus/backends/memory.py +55 -0
- devsper/bus/backends/redis.py +146 -0
- devsper/bus/message.py +56 -0
- devsper/bus/schema_version.py +3 -0
- devsper/bus/topics.py +19 -0
- devsper/cache/__init__.py +6 -0
- devsper/cache/embedding_index.py +98 -0
- devsper/cache/hashing.py +24 -0
- devsper/cache/store.py +153 -0
- devsper/cache/task_cache.py +191 -0
- devsper/cli/__init__.py +6 -0
- devsper/cli/commands/reg.py +733 -0
- devsper/cli/github_oauth.py +157 -0
- devsper/cli/init.py +637 -0
- devsper/cli/main.py +2956 -0
- devsper/cli/run_progress.py +103 -0
- devsper/cli/ui/__init__.py +65 -0
- devsper/cli/ui/components.py +94 -0
- devsper/cli/ui/errors.py +104 -0
- devsper/cli/ui/logging.py +120 -0
- devsper/cli/ui/onboarding.py +102 -0
- devsper/cli/ui/progress.py +43 -0
- devsper/cli/ui/run_view.py +308 -0
- devsper/cli/ui/theme.py +40 -0
- devsper/cluster/__init__.py +29 -0
- devsper/cluster/election.py +84 -0
- devsper/cluster/local.py +97 -0
- devsper/cluster/node_info.py +77 -0
- devsper/cluster/registry.py +71 -0
- devsper/cluster/router.py +117 -0
- devsper/cluster/state_backend.py +105 -0
- devsper/compliance/__init__.py +5 -0
- devsper/compliance/pii.py +147 -0
- devsper/config/__init__.py +52 -0
- devsper/config/config_loader.py +121 -0
- devsper/config/defaults.py +77 -0
- devsper/config/resolver.py +342 -0
- devsper/config/schema.py +237 -0
- devsper/credentials/__init__.py +19 -0
- devsper/credentials/cli.py +197 -0
- devsper/credentials/migration.py +124 -0
- devsper/credentials/store.py +142 -0
- devsper/dashboard/__init__.py +9 -0
- devsper/dashboard/dashboard.py +87 -0
- devsper/dev/__init__.py +25 -0
- devsper/dev/builder.py +195 -0
- devsper/dev/debugger.py +95 -0
- devsper/dev/repo_index.py +138 -0
- devsper/dev/sandbox.py +203 -0
- devsper/dev/scaffold.py +122 -0
- devsper/embeddings/__init__.py +5 -0
- devsper/embeddings/service.py +36 -0
- devsper/explainability/__init__.py +14 -0
- devsper/explainability/decision_tree.py +104 -0
- devsper/explainability/rationale.py +38 -0
- devsper/explainability/simulation.py +56 -0
- devsper/hitl/__init__.py +13 -0
- devsper/hitl/approval.py +160 -0
- devsper/hitl/escalation.py +95 -0
- devsper/intelligence/__init__.py +9 -0
- devsper/intelligence/adaptation.py +88 -0
- devsper/intelligence/analysis/__init__.py +19 -0
- devsper/intelligence/analysis/analyzer.py +71 -0
- devsper/intelligence/analysis/cost_estimator.py +66 -0
- devsper/intelligence/analysis/formatter.py +103 -0
- devsper/intelligence/analysis/run_report.py +402 -0
- devsper/intelligence/learning_engine.py +92 -0
- devsper/intelligence/strategies/__init__.py +23 -0
- devsper/intelligence/strategies/base.py +14 -0
- devsper/intelligence/strategies/code_analysis_strategy.py +33 -0
- devsper/intelligence/strategies/data_science_strategy.py +33 -0
- devsper/intelligence/strategies/document_pipeline_strategy.py +33 -0
- devsper/intelligence/strategies/experiment_strategy.py +33 -0
- devsper/intelligence/strategies/research_strategy.py +34 -0
- devsper/intelligence/strategy_selector.py +84 -0
- devsper/intelligence/synthesis.py +132 -0
- devsper/intelligence/task_optimizer.py +92 -0
- devsper/knowledge/__init__.py +5 -0
- devsper/knowledge/extractor.py +204 -0
- devsper/knowledge/knowledge_graph.py +184 -0
- devsper/knowledge/query.py +285 -0
- devsper/memory/__init__.py +35 -0
- devsper/memory/consolidation.py +138 -0
- devsper/memory/embeddings.py +60 -0
- devsper/memory/memory_index.py +97 -0
- devsper/memory/memory_router.py +62 -0
- devsper/memory/memory_store.py +221 -0
- devsper/memory/memory_types.py +54 -0
- devsper/memory/namespaces.py +45 -0
- devsper/memory/scoring.py +77 -0
- devsper/memory/summarizer.py +52 -0
- devsper/nodes/__init__.py +5 -0
- devsper/nodes/controller.py +449 -0
- devsper/nodes/rpc.py +127 -0
- devsper/nodes/single.py +161 -0
- devsper/nodes/worker.py +506 -0
- devsper/orchestration/__init__.py +19 -0
- devsper/orchestration/meta_planner.py +239 -0
- devsper/orchestration/priority_queue.py +61 -0
- devsper/plugins/__init__.py +19 -0
- devsper/plugins/marketplace/__init__.py +0 -0
- devsper/plugins/plugin_loader.py +70 -0
- devsper/plugins/plugin_registry.py +34 -0
- devsper/plugins/registry.py +83 -0
- devsper/protocols/__init__.py +6 -0
- devsper/providers/__init__.py +17 -0
- devsper/providers/anthropic.py +84 -0
- devsper/providers/base.py +75 -0
- devsper/providers/complexity_router.py +94 -0
- devsper/providers/gemini.py +36 -0
- devsper/providers/github.py +180 -0
- devsper/providers/model_router.py +40 -0
- devsper/providers/openai.py +105 -0
- devsper/providers/router/__init__.py +21 -0
- devsper/providers/router/backends/__init__.py +19 -0
- devsper/providers/router/backends/anthropic_backend.py +111 -0
- devsper/providers/router/backends/custom_backend.py +138 -0
- devsper/providers/router/backends/gemini_backend.py +89 -0
- devsper/providers/router/backends/github_backend.py +165 -0
- devsper/providers/router/backends/ollama_backend.py +104 -0
- devsper/providers/router/backends/openai_backend.py +142 -0
- devsper/providers/router/backends/vllm_backend.py +35 -0
- devsper/providers/router/base.py +60 -0
- devsper/providers/router/factory.py +92 -0
- devsper/providers/router/legacy.py +101 -0
- devsper/providers/router/router.py +135 -0
- devsper/reasoning/__init__.py +12 -0
- devsper/reasoning/graph.py +59 -0
- devsper/reasoning/nodes.py +20 -0
- devsper/reasoning/store.py +67 -0
- devsper/runtime/__init__.py +12 -0
- devsper/runtime/health.py +88 -0
- devsper/runtime/replay.py +53 -0
- devsper/runtime/replay_engine.py +142 -0
- devsper/runtime/run_history.py +204 -0
- devsper/runtime/telemetry.py +116 -0
- devsper/runtime/visualize.py +58 -0
- devsper/sandbox/__init__.py +13 -0
- devsper/sandbox/sandbox.py +161 -0
- devsper/swarm/checkpointer.py +65 -0
- devsper/swarm/executor.py +558 -0
- devsper/swarm/map_reduce.py +44 -0
- devsper/swarm/planner.py +197 -0
- devsper/swarm/prefetcher.py +91 -0
- devsper/swarm/scheduler.py +153 -0
- devsper/swarm/speculation.py +47 -0
- devsper/swarm/swarm.py +562 -0
- devsper/tools/__init__.py +33 -0
- devsper/tools/base.py +29 -0
- devsper/tools/code_intelligence/__init__.py +13 -0
- devsper/tools/code_intelligence/api_surface_extractor.py +73 -0
- devsper/tools/code_intelligence/architecture_analyzer.py +65 -0
- devsper/tools/code_intelligence/codebase_indexer.py +71 -0
- devsper/tools/code_intelligence/dependency_graph_builder.py +67 -0
- devsper/tools/code_intelligence/design_pattern_detector.py +62 -0
- devsper/tools/code_intelligence/large_function_detector.py +68 -0
- devsper/tools/code_intelligence/module_responsibility_mapper.py +56 -0
- devsper/tools/code_intelligence/parallel_codebase_analysis.py +44 -0
- devsper/tools/code_intelligence/refactor_candidate_detector.py +81 -0
- devsper/tools/code_intelligence/repository_semantic_index.py +61 -0
- devsper/tools/code_intelligence/test_coverage_estimator.py +62 -0
- devsper/tools/coding/__init__.py +12 -0
- devsper/tools/coding/analyze_code_complexity.py +48 -0
- devsper/tools/coding/dependency_analyzer.py +42 -0
- devsper/tools/coding/extract_functions.py +38 -0
- devsper/tools/coding/format_python.py +50 -0
- devsper/tools/coding/generate_docstrings.py +40 -0
- devsper/tools/coding/generate_unit_tests.py +42 -0
- devsper/tools/coding/lint_python.py +51 -0
- devsper/tools/coding/refactor_function.py +41 -0
- devsper/tools/coding/repo_structure_map.py +54 -0
- devsper/tools/coding/run_python.py +53 -0
- devsper/tools/data/__init__.py +12 -0
- devsper/tools/data/column_type_detection.py +64 -0
- devsper/tools/data/csv_summary.py +52 -0
- devsper/tools/data/dataframe_filter.py +51 -0
- devsper/tools/data/dataframe_groupby.py +47 -0
- devsper/tools/data/dataframe_stats.py +38 -0
- devsper/tools/data/dataset_sampling.py +55 -0
- devsper/tools/data/dataset_schema.py +45 -0
- devsper/tools/data/json_pretty_print.py +37 -0
- devsper/tools/data/json_query.py +46 -0
- devsper/tools/data/missing_value_report.py +47 -0
- devsper/tools/data_science/__init__.py +13 -0
- devsper/tools/data_science/correlation_heatmap.py +72 -0
- devsper/tools/data_science/dataset_bias_detector.py +49 -0
- devsper/tools/data_science/dataset_distribution_report.py +64 -0
- devsper/tools/data_science/dataset_drift_detector.py +64 -0
- devsper/tools/data_science/dataset_outlier_detector.py +65 -0
- devsper/tools/data_science/dataset_profile.py +76 -0
- devsper/tools/data_science/distributed_dataset_processor.py +54 -0
- devsper/tools/data_science/feature_engineering_suggestions.py +69 -0
- devsper/tools/data_science/feature_importance_estimator.py +82 -0
- devsper/tools/data_science/model_input_validator.py +59 -0
- devsper/tools/data_science/time_series_analyzer.py +57 -0
- devsper/tools/documents/__init__.py +11 -0
- devsper/tools/documents/_docproc.py +56 -0
- devsper/tools/documents/document_to_markdown.py +29 -0
- devsper/tools/documents/extract_document_images.py +39 -0
- devsper/tools/documents/extract_document_text.py +29 -0
- devsper/tools/documents/extract_equations.py +36 -0
- devsper/tools/documents/extract_tables.py +47 -0
- devsper/tools/documents/summarize_document.py +42 -0
- devsper/tools/documents/write_latex_document.py +133 -0
- devsper/tools/documents/write_markdown_document.py +89 -0
- devsper/tools/documents/write_word_document.py +149 -0
- devsper/tools/experiments/__init__.py +13 -0
- devsper/tools/experiments/bootstrap_estimator.py +54 -0
- devsper/tools/experiments/experiment_report_generator.py +50 -0
- devsper/tools/experiments/experiment_tracker.py +36 -0
- devsper/tools/experiments/grid_search_runner.py +50 -0
- devsper/tools/experiments/model_benchmark_runner.py +45 -0
- devsper/tools/experiments/monte_carlo_experiment.py +38 -0
- devsper/tools/experiments/parameter_sweep_runner.py +51 -0
- devsper/tools/experiments/result_comparator.py +58 -0
- devsper/tools/experiments/simulation_runner.py +43 -0
- devsper/tools/experiments/statistical_significance_test.py +56 -0
- devsper/tools/experiments/swarm_map_reduce.py +42 -0
- devsper/tools/filesystem/__init__.py +12 -0
- devsper/tools/filesystem/append_file.py +42 -0
- devsper/tools/filesystem/file_hash.py +40 -0
- devsper/tools/filesystem/file_line_count.py +36 -0
- devsper/tools/filesystem/file_metadata.py +38 -0
- devsper/tools/filesystem/file_preview.py +55 -0
- devsper/tools/filesystem/find_large_files.py +50 -0
- devsper/tools/filesystem/list_directory.py +39 -0
- devsper/tools/filesystem/read_file.py +35 -0
- devsper/tools/filesystem/search_files.py +60 -0
- devsper/tools/filesystem/write_file.py +41 -0
- devsper/tools/flagship/__init__.py +15 -0
- devsper/tools/flagship/distributed_document_analysis.py +77 -0
- devsper/tools/flagship/docproc_corpus_pipeline.py +91 -0
- devsper/tools/flagship/repository_semantic_map.py +99 -0
- devsper/tools/flagship/research_graph_builder.py +111 -0
- devsper/tools/flagship/swarm_experiment_runner.py +86 -0
- devsper/tools/knowledge/__init__.py +10 -0
- devsper/tools/knowledge/citation_graph_builder.py +69 -0
- devsper/tools/knowledge/concept_frequency_analyzer.py +74 -0
- devsper/tools/knowledge/corpus_builder.py +66 -0
- devsper/tools/knowledge/cross_document_entity_linker.py +71 -0
- devsper/tools/knowledge/document_corpus_summary.py +68 -0
- devsper/tools/knowledge/document_topic_extractor.py +58 -0
- devsper/tools/knowledge/knowledge_graph_extractor.py +58 -0
- devsper/tools/knowledge/timeline_extractor.py +59 -0
- devsper/tools/math/__init__.py +12 -0
- devsper/tools/math/calculate_expression.py +52 -0
- devsper/tools/math/correlation.py +44 -0
- devsper/tools/math/distribution_summary.py +39 -0
- devsper/tools/math/histogram.py +53 -0
- devsper/tools/math/linear_regression.py +47 -0
- devsper/tools/math/matrix_multiply.py +38 -0
- devsper/tools/math/mean_std.py +35 -0
- devsper/tools/math/monte_carlo_simulation.py +43 -0
- devsper/tools/math/polynomial_fit.py +40 -0
- devsper/tools/math/random_sample.py +36 -0
- devsper/tools/mcp/__init__.py +23 -0
- devsper/tools/mcp/adapter.py +53 -0
- devsper/tools/mcp/client.py +235 -0
- devsper/tools/mcp/discovery.py +53 -0
- devsper/tools/memory/__init__.py +16 -0
- devsper/tools/memory/delete_memory.py +25 -0
- devsper/tools/memory/list_memory.py +34 -0
- devsper/tools/memory/search_memory.py +36 -0
- devsper/tools/memory/store_memory.py +47 -0
- devsper/tools/memory/summarize_memory.py +41 -0
- devsper/tools/memory/tag_memory.py +47 -0
- devsper/tools/pipelines.py +92 -0
- devsper/tools/registry.py +39 -0
- devsper/tools/research/__init__.py +12 -0
- devsper/tools/research/arxiv_download.py +55 -0
- devsper/tools/research/arxiv_search.py +58 -0
- devsper/tools/research/citation_extractor.py +35 -0
- devsper/tools/research/duckduckgo_search.py +42 -0
- devsper/tools/research/paper_metadata_extractor.py +45 -0
- devsper/tools/research/paper_summarizer.py +41 -0
- devsper/tools/research/research_question_generator.py +39 -0
- devsper/tools/research/topic_cluster.py +46 -0
- devsper/tools/research/web_search.py +47 -0
- devsper/tools/research/wikipedia_lookup.py +50 -0
- devsper/tools/research_advanced/__init__.py +14 -0
- devsper/tools/research_advanced/citation_context_extractor.py +60 -0
- devsper/tools/research_advanced/literature_review_generator.py +79 -0
- devsper/tools/research_advanced/methodology_extractor.py +58 -0
- devsper/tools/research_advanced/paper_contribution_extractor.py +50 -0
- devsper/tools/research_advanced/paper_dataset_identifier.py +49 -0
- devsper/tools/research_advanced/paper_method_comparator.py +62 -0
- devsper/tools/research_advanced/paper_similarity_search.py +69 -0
- devsper/tools/research_advanced/paper_trend_analyzer.py +69 -0
- devsper/tools/research_advanced/parallel_document_analyzer.py +56 -0
- devsper/tools/research_advanced/research_gap_finder.py +71 -0
- devsper/tools/research_advanced/research_topic_mapper.py +69 -0
- devsper/tools/research_advanced/swarm_literature_review.py +58 -0
- devsper/tools/scoring/__init__.py +52 -0
- devsper/tools/scoring/report.py +44 -0
- devsper/tools/scoring/scorer.py +39 -0
- devsper/tools/scoring/selector.py +61 -0
- devsper/tools/scoring/store.py +267 -0
- devsper/tools/selector.py +130 -0
- devsper/tools/system/__init__.py +12 -0
- devsper/tools/system/cpu_usage.py +22 -0
- devsper/tools/system/disk_usage.py +35 -0
- devsper/tools/system/environment_variables.py +29 -0
- devsper/tools/system/memory_usage.py +23 -0
- devsper/tools/system/pip_install.py +44 -0
- devsper/tools/system/pip_search.py +29 -0
- devsper/tools/system/process_list.py +34 -0
- devsper/tools/system/python_package_list.py +40 -0
- devsper/tools/system/run_shell_command.py +51 -0
- devsper/tools/system/system_info.py +26 -0
- devsper/tools/tool_runner.py +122 -0
- devsper/tui/__init__.py +5 -0
- devsper/tui/activity_feed_view.py +73 -0
- devsper/tui/adaptive_tasks_view.py +75 -0
- devsper/tui/agent_role_view.py +35 -0
- devsper/tui/app.py +395 -0
- devsper/tui/dashboard_screen.py +290 -0
- devsper/tui/dev_view.py +99 -0
- devsper/tui/inject_screen.py +73 -0
- devsper/tui/knowledge_graph_view.py +46 -0
- devsper/tui/layout.py +43 -0
- devsper/tui/logs_view.py +83 -0
- devsper/tui/memory_view.py +58 -0
- devsper/tui/performance_view.py +33 -0
- devsper/tui/reasoning_graph_view.py +39 -0
- devsper/tui/results_view.py +139 -0
- devsper/tui/swarm_view.py +37 -0
- devsper/tui/task_detail_screen.py +55 -0
- devsper/tui/task_view.py +103 -0
- devsper/types/event.py +97 -0
- devsper/types/exceptions.py +21 -0
- devsper/types/swarm.py +41 -0
- devsper/types/task.py +80 -0
- devsper/upgrade/__init__.py +21 -0
- devsper/upgrade/changelog.py +124 -0
- devsper/upgrade/cli.py +145 -0
- devsper/upgrade/installer.py +103 -0
- devsper/upgrade/notifier.py +52 -0
- devsper/upgrade/version_check.py +121 -0
- devsper/utils/event_logger.py +88 -0
- devsper/utils/http.py +43 -0
- devsper/utils/models.py +54 -0
- devsper/visualization/__init__.py +5 -0
- devsper/visualization/dag_export.py +67 -0
- devsper/workflow/__init__.py +18 -0
- devsper/workflow/conditions.py +157 -0
- devsper/workflow/context.py +108 -0
- devsper/workflow/loader.py +156 -0
- devsper/workflow/resolver.py +109 -0
- devsper/workflow/runner.py +562 -0
- devsper/workflow/schema.py +63 -0
- devsper/workflow/validator.py +128 -0
- devsper-2.1.6.dist-info/METADATA +346 -0
- devsper-2.1.6.dist-info/RECORD +375 -0
- devsper-2.1.6.dist-info/WHEEL +4 -0
- devsper-2.1.6.dist-info/entry_points.txt +3 -0
- devsper-2.1.6.dist-info/licenses/LICENSE +639 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"""CLI for credential management: set, list, delete, migrate, export."""
|
|
2
|
+
|
|
3
|
+
import getpass
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from rich.console import Console
|
|
8
|
+
from rich.table import Table
|
|
9
|
+
|
|
10
|
+
from devsper.credentials import (
|
|
11
|
+
delete_credential,
|
|
12
|
+
get_credential,
|
|
13
|
+
list_credentials,
|
|
14
|
+
set_credential,
|
|
15
|
+
)
|
|
16
|
+
from devsper.credentials.migration import migrate_from_config
|
|
17
|
+
|
|
18
|
+
KNOWN_CREDENTIALS = {
|
|
19
|
+
"openai": ["api_key"],
|
|
20
|
+
"anthropic": ["api_key"],
|
|
21
|
+
"github": ["token"],
|
|
22
|
+
"gemini": ["api_key"],
|
|
23
|
+
"azure": ["endpoint", "api_key", "deployment", "api_version"],
|
|
24
|
+
"azure_anthropic": ["endpoint", "api_key", "deployment"],
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# Keys that must never be shown in list (only "(stored)"); all others can show value
|
|
28
|
+
SENSITIVE_KEYS = {"api_key", "token"}
|
|
29
|
+
|
|
30
|
+
# (provider, key) -> env var name for export
|
|
31
|
+
PROVIDER_KEY_TO_ENV: dict[tuple[str, str], str] = {
|
|
32
|
+
("openai", "api_key"): "OPENAI_API_KEY",
|
|
33
|
+
("anthropic", "api_key"): "ANTHROPIC_API_KEY",
|
|
34
|
+
("github", "token"): "GITHUB_TOKEN",
|
|
35
|
+
("gemini", "api_key"): "GEMINI_API_KEY",
|
|
36
|
+
("azure", "endpoint"): "AZURE_OPENAI_ENDPOINT",
|
|
37
|
+
("azure", "api_key"): "AZURE_OPENAI_API_KEY",
|
|
38
|
+
("azure", "deployment"): "AZURE_OPENAI_DEPLOYMENT_NAME",
|
|
39
|
+
("azure", "api_version"): "AZURE_OPENAI_API_VERSION",
|
|
40
|
+
("azure_anthropic", "endpoint"): "AZURE_ANTHROPIC_ENDPOINT",
|
|
41
|
+
("azure_anthropic", "api_key"): "AZURE_ANTHROPIC_API_KEY",
|
|
42
|
+
("azure_anthropic", "deployment"): "AZURE_ANTHROPIC_DEPLOYMENT_NAME",
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _env_escape(value: str) -> str:
|
|
47
|
+
"""Escape value for .env / export format: double-quote and escape internal quotes."""
|
|
48
|
+
return '"' + value.replace("\\", "\\\\").replace('"', '\\"').replace("\n", "\\n") + '"'
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _run_credentials_export(provider: str) -> int:
|
|
52
|
+
"""Print provider's credentials in env format (KEY=value, one per line)."""
|
|
53
|
+
if provider not in KNOWN_CREDENTIALS:
|
|
54
|
+
print(f"Unknown provider: {provider}", file=sys.stderr)
|
|
55
|
+
return 1
|
|
56
|
+
lines: list[str] = []
|
|
57
|
+
for key in KNOWN_CREDENTIALS[provider]:
|
|
58
|
+
env_var = PROVIDER_KEY_TO_ENV.get((provider, key))
|
|
59
|
+
if not env_var:
|
|
60
|
+
continue
|
|
61
|
+
val = get_credential(provider, key)
|
|
62
|
+
if val is not None:
|
|
63
|
+
lines.append(f"{env_var}={_env_escape(val)}")
|
|
64
|
+
if not lines:
|
|
65
|
+
print(f"No credentials stored for provider: {provider}", file=sys.stderr)
|
|
66
|
+
return 1
|
|
67
|
+
for line in lines:
|
|
68
|
+
print(line)
|
|
69
|
+
return 0
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _run_credentials_set(provider: str, key: str, value: str | None = None) -> int:
|
|
73
|
+
"""Store credential. Value can be passed inline, read from stdin, or prompted interactively."""
|
|
74
|
+
if provider not in KNOWN_CREDENTIALS:
|
|
75
|
+
print(f"Unknown provider: {provider}", file=sys.stderr)
|
|
76
|
+
return 1
|
|
77
|
+
if key not in KNOWN_CREDENTIALS[provider]:
|
|
78
|
+
print(f"Unknown key for {provider}: {key}", file=sys.stderr)
|
|
79
|
+
return 1
|
|
80
|
+
|
|
81
|
+
if value is not None and value.strip():
|
|
82
|
+
# Inline value from CLI
|
|
83
|
+
pass
|
|
84
|
+
elif not sys.stdin.isatty():
|
|
85
|
+
# Piped input: echo "val" | devsper credentials set ...
|
|
86
|
+
try:
|
|
87
|
+
value = sys.stdin.read().strip()
|
|
88
|
+
except (KeyboardInterrupt, EOFError):
|
|
89
|
+
print("\nCancelled.", file=sys.stderr)
|
|
90
|
+
return 130
|
|
91
|
+
else:
|
|
92
|
+
# Interactive prompt
|
|
93
|
+
try:
|
|
94
|
+
if key in SENSITIVE_KEYS:
|
|
95
|
+
value = getpass.getpass(f"Enter value for {provider}/{key}: ")
|
|
96
|
+
else:
|
|
97
|
+
value = input(f"Enter value for {provider}/{key}: ")
|
|
98
|
+
except (KeyboardInterrupt, EOFError):
|
|
99
|
+
print("\nCancelled.", file=sys.stderr)
|
|
100
|
+
return 130
|
|
101
|
+
|
|
102
|
+
if not value or not value.strip():
|
|
103
|
+
print("Empty value, not stored.", file=sys.stderr)
|
|
104
|
+
return 1
|
|
105
|
+
set_credential(provider, key, value.strip())
|
|
106
|
+
print(f"Stored {provider}/{key}")
|
|
107
|
+
return 0
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _run_credentials_list() -> int:
|
|
111
|
+
"""List credentials as table (provider, key, value). Secret keys show '(stored)', others show value."""
|
|
112
|
+
creds = list_credentials()
|
|
113
|
+
if not creds:
|
|
114
|
+
print("No credentials stored.")
|
|
115
|
+
return 0
|
|
116
|
+
table = Table(title="Stored credentials")
|
|
117
|
+
table.add_column("Provider", style="cyan")
|
|
118
|
+
table.add_column("Key", style="green")
|
|
119
|
+
table.add_column("Value", style="dim")
|
|
120
|
+
for c in creds:
|
|
121
|
+
p, k = c["provider"], c["key"]
|
|
122
|
+
if k in SENSITIVE_KEYS:
|
|
123
|
+
value = "(stored)"
|
|
124
|
+
else:
|
|
125
|
+
val = get_credential(p, k)
|
|
126
|
+
value = (val[:60] + "…") if val and len(val) > 60 else (val or "")
|
|
127
|
+
table.add_row(p, k, value)
|
|
128
|
+
console = Console()
|
|
129
|
+
console.print(table)
|
|
130
|
+
return 0
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _run_credentials_delete(provider: str, key: str) -> int:
|
|
134
|
+
"""Remove a credential."""
|
|
135
|
+
if provider not in KNOWN_CREDENTIALS:
|
|
136
|
+
print(f"Unknown provider: {provider}", file=sys.stderr)
|
|
137
|
+
return 1
|
|
138
|
+
if key not in KNOWN_CREDENTIALS[provider]:
|
|
139
|
+
print(f"Unknown key for {provider}: {key}", file=sys.stderr)
|
|
140
|
+
return 1
|
|
141
|
+
delete_credential(provider, key)
|
|
142
|
+
print(f"Deleted {provider}/{key}")
|
|
143
|
+
return 0
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _run_credentials_migrate() -> int:
|
|
147
|
+
"""Migrate credentials from TOML and env to store."""
|
|
148
|
+
from devsper.config.config_loader import project_config_paths
|
|
149
|
+
|
|
150
|
+
config_path = None
|
|
151
|
+
for p in project_config_paths():
|
|
152
|
+
if p.is_file():
|
|
153
|
+
config_path = p
|
|
154
|
+
break
|
|
155
|
+
if not config_path:
|
|
156
|
+
config_path = Path.cwd() / "devsper.toml"
|
|
157
|
+
|
|
158
|
+
migrated = migrate_from_config(config_path)
|
|
159
|
+
if not migrated:
|
|
160
|
+
print("No credentials found in env or TOML to migrate.")
|
|
161
|
+
return 0
|
|
162
|
+
print(f"Migrated {len(migrated)} credential(s):")
|
|
163
|
+
for item in migrated:
|
|
164
|
+
print(f" - {item}")
|
|
165
|
+
print("\nYou can now remove these from devsper.toml and .env for better security.")
|
|
166
|
+
return 0
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def run_credentials(args: object) -> int:
|
|
170
|
+
"""Dispatch credentials subcommand."""
|
|
171
|
+
sub = getattr(args, "credentials_subcommand", None)
|
|
172
|
+
provider = getattr(args, "provider", None)
|
|
173
|
+
key = getattr(args, "key", None)
|
|
174
|
+
|
|
175
|
+
if sub == "set":
|
|
176
|
+
if not provider or not key:
|
|
177
|
+
print("Usage: devsper credentials set <provider> <key> [value]", file=sys.stderr)
|
|
178
|
+
return 1
|
|
179
|
+
value = getattr(args, "value", None)
|
|
180
|
+
return _run_credentials_set(provider, key, value)
|
|
181
|
+
if sub == "list":
|
|
182
|
+
return _run_credentials_list()
|
|
183
|
+
if sub == "delete":
|
|
184
|
+
if not provider or not key:
|
|
185
|
+
print("Usage: devsper credentials delete <provider> <key>", file=sys.stderr)
|
|
186
|
+
return 1
|
|
187
|
+
return _run_credentials_delete(provider, key)
|
|
188
|
+
if sub == "migrate":
|
|
189
|
+
return _run_credentials_migrate()
|
|
190
|
+
if sub == "export":
|
|
191
|
+
if not provider:
|
|
192
|
+
print("Usage: devsper credentials export <provider>", file=sys.stderr)
|
|
193
|
+
return 1
|
|
194
|
+
return _run_credentials_export(provider)
|
|
195
|
+
|
|
196
|
+
print("Usage: devsper credentials set|list|delete|migrate|export", file=sys.stderr)
|
|
197
|
+
return 1
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""Migrate credentials from devsper.toml and env to the credential store."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from devsper.credentials import set_credential
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _load_dotenv_for_config(config_path: Path) -> None:
|
|
10
|
+
"""Load .env from config directory so env vars are available for migration."""
|
|
11
|
+
try:
|
|
12
|
+
from dotenv import load_dotenv
|
|
13
|
+
|
|
14
|
+
load_dotenv(config_path.parent / ".env")
|
|
15
|
+
except Exception:
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
# Env var -> (provider, key)
|
|
19
|
+
_ENV_TO_CREDENTIAL: dict[str, tuple[str, str]] = {
|
|
20
|
+
"OPENAI_API_KEY": ("openai", "api_key"),
|
|
21
|
+
"ANTHROPIC_API_KEY": ("anthropic", "api_key"),
|
|
22
|
+
"GITHUB_TOKEN": ("github", "token"),
|
|
23
|
+
"GEMINI_API_KEY": ("gemini", "api_key"),
|
|
24
|
+
"GOOGLE_API_KEY": ("gemini", "api_key"),
|
|
25
|
+
"AZURE_OPENAI_API_KEY": ("azure", "api_key"),
|
|
26
|
+
"AZURE_OPENAI_ENDPOINT": ("azure", "endpoint"),
|
|
27
|
+
"AZURE_OPENAI_DEPLOYMENT_NAME": ("azure", "deployment"),
|
|
28
|
+
"AZURE_OPENAI_API_VERSION": ("azure", "api_version"),
|
|
29
|
+
"AZURE_ANTHROPIC_ENDPOINT": ("azure_anthropic", "endpoint"),
|
|
30
|
+
"AZURE_ANTHROPIC_API_KEY": ("azure_anthropic", "api_key"),
|
|
31
|
+
"AZURE_ANTHROPIC_DEPLOYMENT_NAME": ("azure_anthropic", "deployment"),
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def migrate_from_config(config_path: Path) -> list[str]:
|
|
36
|
+
"""
|
|
37
|
+
Scan TOML and env for known credentials, migrate to store.
|
|
38
|
+
Returns list of migrated items (e.g. ["openai/api_key", "azure/endpoint"]).
|
|
39
|
+
Does NOT delete from env — inform user they can remove them.
|
|
40
|
+
"""
|
|
41
|
+
migrated: list[str] = []
|
|
42
|
+
|
|
43
|
+
# Load .env from config directory so all env vars are available
|
|
44
|
+
_load_dotenv_for_config(config_path)
|
|
45
|
+
|
|
46
|
+
# 1. Check env vars
|
|
47
|
+
for env_var, (provider, key) in _ENV_TO_CREDENTIAL.items():
|
|
48
|
+
val = os.environ.get(env_var)
|
|
49
|
+
if val and str(val).strip():
|
|
50
|
+
set_credential(provider, key, str(val).strip())
|
|
51
|
+
migrated.append(f"{provider}/{key}")
|
|
52
|
+
|
|
53
|
+
# 2. Check TOML
|
|
54
|
+
if config_path.is_file():
|
|
55
|
+
try:
|
|
56
|
+
import tomllib
|
|
57
|
+
|
|
58
|
+
with open(config_path, "rb") as f:
|
|
59
|
+
data = tomllib.load(f)
|
|
60
|
+
except Exception:
|
|
61
|
+
return migrated
|
|
62
|
+
|
|
63
|
+
# [providers.azure]
|
|
64
|
+
providers = data.get("providers") or {}
|
|
65
|
+
azure = providers.get("azure")
|
|
66
|
+
if isinstance(azure, dict):
|
|
67
|
+
for toml_key, cred_key in [
|
|
68
|
+
("endpoint", "endpoint"),
|
|
69
|
+
("api_key", "api_key"),
|
|
70
|
+
("deployment", "deployment"),
|
|
71
|
+
("api_version", "api_version"),
|
|
72
|
+
]:
|
|
73
|
+
val = azure.get(toml_key)
|
|
74
|
+
if val is not None and str(val).strip():
|
|
75
|
+
item = f"azure/{cred_key}"
|
|
76
|
+
if item not in migrated:
|
|
77
|
+
set_credential("azure", cred_key, str(val).strip())
|
|
78
|
+
migrated.append(item)
|
|
79
|
+
|
|
80
|
+
# Legacy sections: [openai], [anthropic], [azure_openai], etc.
|
|
81
|
+
for section, (provider, key) in [
|
|
82
|
+
("openai", ("openai", "api_key")),
|
|
83
|
+
("anthropic", ("anthropic", "api_key")),
|
|
84
|
+
("google", ("gemini", "api_key")),
|
|
85
|
+
]:
|
|
86
|
+
block = data.get(section)
|
|
87
|
+
if isinstance(block, dict):
|
|
88
|
+
val = block.get("api_key")
|
|
89
|
+
if val is not None and str(val).strip():
|
|
90
|
+
item = f"{provider}/{key}"
|
|
91
|
+
if item not in migrated:
|
|
92
|
+
set_credential(provider, key, str(val).strip())
|
|
93
|
+
migrated.append(item)
|
|
94
|
+
|
|
95
|
+
azure_block = data.get("azure_openai")
|
|
96
|
+
if isinstance(azure_block, dict):
|
|
97
|
+
for toml_key, cred_key in [
|
|
98
|
+
("endpoint", "endpoint"),
|
|
99
|
+
("api_key", "api_key"),
|
|
100
|
+
("deployment_name", "deployment"),
|
|
101
|
+
("api_version", "api_version"),
|
|
102
|
+
]:
|
|
103
|
+
val = azure_block.get(toml_key)
|
|
104
|
+
if val is not None and str(val).strip():
|
|
105
|
+
item = f"azure/{cred_key}"
|
|
106
|
+
if item not in migrated:
|
|
107
|
+
set_credential("azure", cred_key, str(val).strip())
|
|
108
|
+
migrated.append(item)
|
|
109
|
+
|
|
110
|
+
azure_anthropic_block = data.get("azure_anthropic")
|
|
111
|
+
if isinstance(azure_anthropic_block, dict):
|
|
112
|
+
for toml_key, cred_key in [
|
|
113
|
+
("endpoint", "endpoint"),
|
|
114
|
+
("api_key", "api_key"),
|
|
115
|
+
("deployment_name", "deployment"),
|
|
116
|
+
]:
|
|
117
|
+
val = azure_anthropic_block.get(toml_key)
|
|
118
|
+
if val is not None and str(val).strip():
|
|
119
|
+
item = f"azure_anthropic/{cred_key}"
|
|
120
|
+
if item not in migrated:
|
|
121
|
+
set_credential("azure_anthropic", cred_key, str(val).strip())
|
|
122
|
+
migrated.append(item)
|
|
123
|
+
|
|
124
|
+
return migrated
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""Credential store: OS keychain (keyring) only."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
logger = logging.getLogger(__name__)
|
|
7
|
+
|
|
8
|
+
# (provider, key) -> env var name; used to inject keychain into env for providers (e.g. run_agent subprocess).
|
|
9
|
+
_PROVIDER_KEY_TO_ENV: list[tuple[str, str, str]] = [
|
|
10
|
+
("openai", "api_key", "OPENAI_API_KEY"),
|
|
11
|
+
("anthropic", "api_key", "ANTHROPIC_API_KEY"),
|
|
12
|
+
("github", "token", "GITHUB_TOKEN"),
|
|
13
|
+
("gemini", "api_key", "GEMINI_API_KEY"),
|
|
14
|
+
("gemini", "api_key", "GOOGLE_API_KEY"),
|
|
15
|
+
("azure", "api_key", "AZURE_OPENAI_API_KEY"),
|
|
16
|
+
("azure", "endpoint", "AZURE_OPENAI_ENDPOINT"),
|
|
17
|
+
("azure", "deployment", "AZURE_OPENAI_DEPLOYMENT_NAME"),
|
|
18
|
+
("azure", "api_version", "AZURE_OPENAI_API_VERSION"),
|
|
19
|
+
("azure_anthropic", "api_key", "AZURE_ANTHROPIC_API_KEY"),
|
|
20
|
+
("azure_anthropic", "endpoint", "AZURE_ANTHROPIC_ENDPOINT"),
|
|
21
|
+
("azure_anthropic", "deployment", "AZURE_ANTHROPIC_DEPLOYMENT_NAME"),
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
SERVICE_NAME = "devsper"
|
|
25
|
+
|
|
26
|
+
# Provider/key -> known keys for list_all
|
|
27
|
+
KNOWN_CREDENTIALS = {
|
|
28
|
+
"openai": ["api_key"],
|
|
29
|
+
"anthropic": ["api_key"],
|
|
30
|
+
"github": ["token"],
|
|
31
|
+
"gemini": ["api_key"],
|
|
32
|
+
"azure": ["endpoint", "api_key", "deployment", "api_version"],
|
|
33
|
+
"azure_anthropic": ["endpoint", "api_key", "deployment"],
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _username(provider: str, key: str) -> str:
|
|
38
|
+
return f"{provider}/{key}"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class CredentialStore:
|
|
42
|
+
"""
|
|
43
|
+
Secure credential store using OS keychain (keyring) only.
|
|
44
|
+
Never logs credential values.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
def get(self, provider: str, key: str) -> str | None:
|
|
48
|
+
"""Get credential from keyring. Returns None if not found or keyring unavailable."""
|
|
49
|
+
username = _username(provider, key)
|
|
50
|
+
try:
|
|
51
|
+
import keyring
|
|
52
|
+
from keyring.errors import KeyringError
|
|
53
|
+
|
|
54
|
+
val = keyring.get_password(SERVICE_NAME, username)
|
|
55
|
+
if val is not None:
|
|
56
|
+
logger.debug("Credential %s/%s retrieved from keyring", provider, key)
|
|
57
|
+
return val
|
|
58
|
+
except KeyringError:
|
|
59
|
+
logger.debug("Keyring unavailable")
|
|
60
|
+
return None
|
|
61
|
+
except Exception as e:
|
|
62
|
+
logger.debug("Keyring error: %s", type(e).__name__)
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
def set(self, provider: str, key: str, value: str) -> None:
|
|
66
|
+
"""Store credential in keyring."""
|
|
67
|
+
username = _username(provider, key)
|
|
68
|
+
try:
|
|
69
|
+
import keyring
|
|
70
|
+
from keyring.errors import KeyringError
|
|
71
|
+
|
|
72
|
+
keyring.set_password(SERVICE_NAME, username, value)
|
|
73
|
+
logger.debug("Credential %s/%s stored in keyring", provider, key)
|
|
74
|
+
except KeyringError as e:
|
|
75
|
+
raise RuntimeError("Keyring unavailable; cannot store credentials") from e
|
|
76
|
+
except Exception as e:
|
|
77
|
+
raise RuntimeError(f"Keyring error: {e}") from e
|
|
78
|
+
|
|
79
|
+
def delete(self, provider: str, key: str) -> None:
|
|
80
|
+
"""Delete credential from keyring."""
|
|
81
|
+
username = _username(provider, key)
|
|
82
|
+
try:
|
|
83
|
+
import keyring
|
|
84
|
+
keyring.delete_password(SERVICE_NAME, username)
|
|
85
|
+
except Exception:
|
|
86
|
+
pass
|
|
87
|
+
|
|
88
|
+
def list_all(self) -> list[dict]:
|
|
89
|
+
"""List stored credentials: [{provider, key, source}]. Never returns values."""
|
|
90
|
+
result: list[dict] = []
|
|
91
|
+
try:
|
|
92
|
+
import keyring
|
|
93
|
+
|
|
94
|
+
for provider, keys in KNOWN_CREDENTIALS.items():
|
|
95
|
+
for k in keys:
|
|
96
|
+
username = _username(provider, k)
|
|
97
|
+
val = keyring.get_password(SERVICE_NAME, username)
|
|
98
|
+
if val is not None:
|
|
99
|
+
result.append({"provider": provider, "key": k, "source": "keyring"})
|
|
100
|
+
except Exception:
|
|
101
|
+
pass
|
|
102
|
+
return result
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
_default_store: CredentialStore | None = None
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _get_store() -> CredentialStore:
|
|
109
|
+
global _default_store
|
|
110
|
+
if _default_store is None:
|
|
111
|
+
_default_store = CredentialStore()
|
|
112
|
+
return _default_store
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def get_credential(provider: str, key: str) -> str | None:
|
|
116
|
+
"""Get credential from store. Returns None if not found."""
|
|
117
|
+
return _get_store().get(provider, key)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def set_credential(provider: str, key: str, value: str) -> None:
|
|
121
|
+
"""Store credential in keyring."""
|
|
122
|
+
_get_store().set(provider, key, value)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def delete_credential(provider: str, key: str) -> None:
|
|
126
|
+
"""Remove credential from store."""
|
|
127
|
+
_get_store().delete(provider, key)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def list_credentials() -> list[dict]:
|
|
131
|
+
"""List stored credentials (provider, key, source). Never returns values."""
|
|
132
|
+
return _get_store().list_all()
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def inject_into_env() -> None:
|
|
136
|
+
"""Set os.environ from keychain for each provider key that is not already set.
|
|
137
|
+
Call this at process startup (e.g. run_agent subprocess) so providers see credentials."""
|
|
138
|
+
for provider, key, env_var in _PROVIDER_KEY_TO_ENV:
|
|
139
|
+
if not os.environ.get(env_var):
|
|
140
|
+
val = get_credential(provider, key)
|
|
141
|
+
if val:
|
|
142
|
+
os.environ[env_var] = val
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Minimal visualization: task DAG, swarm replay, memory entries, telemetry.
|
|
3
|
+
|
|
4
|
+
Uses Rich for terminal output when available; falls back to plain text.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from devsper.swarm.scheduler import Scheduler
|
|
11
|
+
from devsper.runtime.visualize import visualize_scheduler_dag
|
|
12
|
+
from devsper.runtime.replay import replay_execution
|
|
13
|
+
from devsper.runtime.telemetry import collect_telemetry, print_telemetry_summary
|
|
14
|
+
from devsper.memory.memory_store import MemoryStore, get_default_store
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _latest_log_path(events_folder: str = ".devsper/events") -> str | None:
|
|
18
|
+
if not os.path.isdir(events_folder):
|
|
19
|
+
return None
|
|
20
|
+
files = list(Path(events_folder).glob("events_*.jsonl"))
|
|
21
|
+
if not files:
|
|
22
|
+
return None
|
|
23
|
+
files.sort(key=lambda p: p.stat().st_mtime, reverse=True)
|
|
24
|
+
return str(files[0])
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def show_dashboard(
|
|
28
|
+
scheduler: Scheduler | None = None,
|
|
29
|
+
log_path: str | None = None,
|
|
30
|
+
memory_store: MemoryStore | None = None,
|
|
31
|
+
events_folder: str = ".devsper/events",
|
|
32
|
+
memory_limit: int = 20,
|
|
33
|
+
) -> str:
|
|
34
|
+
"""
|
|
35
|
+
Print a combined dashboard: task DAG (if scheduler given), swarm replay and telemetry
|
|
36
|
+
(if log_path or latest log in events_folder), and recent memory entries (if store given).
|
|
37
|
+
Returns the full dashboard as a string.
|
|
38
|
+
"""
|
|
39
|
+
try:
|
|
40
|
+
from rich.console import Console
|
|
41
|
+
from rich.panel import Panel
|
|
42
|
+
use_rich = True
|
|
43
|
+
except ImportError:
|
|
44
|
+
use_rich = False
|
|
45
|
+
|
|
46
|
+
lines: list[str] = []
|
|
47
|
+
|
|
48
|
+
if scheduler is not None:
|
|
49
|
+
dag = visualize_scheduler_dag(scheduler)
|
|
50
|
+
lines.append("=== TASK DAG ===")
|
|
51
|
+
lines.append(dag)
|
|
52
|
+
lines.append("")
|
|
53
|
+
|
|
54
|
+
if log_path is None:
|
|
55
|
+
log_path = _latest_log_path(events_folder)
|
|
56
|
+
if log_path and os.path.exists(log_path):
|
|
57
|
+
lines.append("=== SWARM REPLAY ===")
|
|
58
|
+
lines.append(replay_execution(log_path))
|
|
59
|
+
lines.append("")
|
|
60
|
+
lines.append("=== TELEMETRY ===")
|
|
61
|
+
lines.append(print_telemetry_summary(log_path))
|
|
62
|
+
lines.append("")
|
|
63
|
+
else:
|
|
64
|
+
lines.append("=== SWARM REPLAY / TELEMETRY ===")
|
|
65
|
+
lines.append("(no event log found)")
|
|
66
|
+
lines.append("")
|
|
67
|
+
|
|
68
|
+
store = memory_store or get_default_store()
|
|
69
|
+
lines.append("=== MEMORY (recent) ===")
|
|
70
|
+
try:
|
|
71
|
+
records = store.list_memory(limit=memory_limit)
|
|
72
|
+
if not records:
|
|
73
|
+
lines.append("(no memory entries)")
|
|
74
|
+
else:
|
|
75
|
+
for r in records:
|
|
76
|
+
lines.append(f" [{r.memory_type.value}] {r.id}: {r.content[:120]}{'...' if len(r.content) > 120 else ''}")
|
|
77
|
+
except Exception as e:
|
|
78
|
+
lines.append(f"(error listing memory: {e})")
|
|
79
|
+
lines.append("")
|
|
80
|
+
|
|
81
|
+
out = "\n".join(lines)
|
|
82
|
+
if use_rich:
|
|
83
|
+
console = Console()
|
|
84
|
+
console.print(Panel(out, title="Swarm Dashboard", border_style="blue"))
|
|
85
|
+
else:
|
|
86
|
+
print(out)
|
|
87
|
+
return out
|
devsper/dev/__init__.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Autonomous Application Builder: build working repos from app descriptions.
|
|
3
|
+
|
|
4
|
+
Modules:
|
|
5
|
+
- builder: orchestrates architecture → scaffold → implement → test → debug
|
|
6
|
+
- scaffold: repo structure generation (backend, frontend, tests, docker)
|
|
7
|
+
- sandbox: isolated execution with timeout and resource limits
|
|
8
|
+
- debugger: test run → error detection → fix tasks → patch loop
|
|
9
|
+
- repo_index: AST parsing, dependency graph, symbol search (code intelligence)
|
|
10
|
+
- agents: dev-specific roles (architect, backend, frontend, test, review)
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from devsper.dev.builder import run_build
|
|
14
|
+
from devsper.dev.scaffold import scaffold_repo
|
|
15
|
+
from devsper.dev.sandbox import Sandbox
|
|
16
|
+
from devsper.dev.debugger import debug_loop
|
|
17
|
+
from devsper.dev.repo_index import RepoIndex
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"run_build",
|
|
21
|
+
"scaffold_repo",
|
|
22
|
+
"Sandbox",
|
|
23
|
+
"debug_loop",
|
|
24
|
+
"RepoIndex",
|
|
25
|
+
]
|