flock-core 0.5.0b28__py3-none-any.whl → 0.5.56b0__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.
Potentially problematic release.
This version of flock-core might be problematic. Click here for more details.
- flock/__init__.py +12 -217
- flock/agent.py +678 -0
- flock/api/themes.py +71 -0
- flock/artifacts.py +79 -0
- flock/cli.py +75 -0
- flock/components.py +173 -0
- flock/dashboard/__init__.py +28 -0
- flock/dashboard/collector.py +283 -0
- flock/dashboard/events.py +182 -0
- flock/dashboard/launcher.py +230 -0
- flock/dashboard/service.py +537 -0
- flock/dashboard/websocket.py +235 -0
- flock/engines/__init__.py +6 -0
- flock/engines/dspy_engine.py +856 -0
- flock/examples.py +128 -0
- flock/{core/util → helper}/cli_helper.py +4 -3
- flock/{core/logging → logging}/__init__.py +2 -3
- flock/{core/logging → logging}/formatters/enum_builder.py +3 -4
- flock/{core/logging → logging}/formatters/theme_builder.py +19 -44
- flock/{core/logging → logging}/formatters/themed_formatter.py +69 -115
- flock/{core/logging → logging}/logging.py +77 -61
- flock/{core/logging → logging}/telemetry.py +20 -26
- flock/{core/logging → logging}/telemetry_exporter/base_exporter.py +2 -2
- flock/{core/logging → logging}/telemetry_exporter/file_exporter.py +6 -9
- flock/{core/logging → logging}/telemetry_exporter/sqlite_exporter.py +2 -3
- flock/{core/logging → logging}/trace_and_logged.py +20 -24
- flock/mcp/__init__.py +91 -0
- flock/{core/mcp/mcp_client.py → mcp/client.py} +103 -154
- flock/{core/mcp/mcp_config.py → mcp/config.py} +62 -117
- flock/mcp/manager.py +255 -0
- flock/mcp/servers/sse/__init__.py +1 -1
- flock/mcp/servers/sse/flock_sse_server.py +11 -53
- flock/mcp/servers/stdio/__init__.py +1 -1
- flock/mcp/servers/stdio/flock_stdio_server.py +8 -48
- flock/mcp/servers/streamable_http/flock_streamable_http_server.py +17 -62
- flock/mcp/servers/websockets/flock_websocket_server.py +7 -40
- flock/{core/mcp/flock_mcp_tool.py → mcp/tool.py} +16 -26
- flock/mcp/types/__init__.py +42 -0
- flock/{core/mcp → mcp}/types/callbacks.py +9 -15
- flock/{core/mcp → mcp}/types/factories.py +7 -6
- flock/{core/mcp → mcp}/types/handlers.py +13 -18
- flock/{core/mcp → mcp}/types/types.py +70 -74
- flock/{core/mcp → mcp}/util/helpers.py +1 -1
- flock/orchestrator.py +645 -0
- flock/registry.py +148 -0
- flock/runtime.py +262 -0
- flock/service.py +140 -0
- flock/store.py +69 -0
- flock/subscription.py +111 -0
- flock/themes/andromeda.toml +1 -1
- flock/themes/apple-system-colors.toml +1 -1
- flock/themes/arcoiris.toml +1 -1
- flock/themes/atomonelight.toml +1 -1
- flock/themes/ayu copy.toml +1 -1
- flock/themes/ayu-light.toml +1 -1
- flock/themes/belafonte-day.toml +1 -1
- flock/themes/belafonte-night.toml +1 -1
- flock/themes/blulocodark.toml +1 -1
- flock/themes/breeze.toml +1 -1
- flock/themes/broadcast.toml +1 -1
- flock/themes/brogrammer.toml +1 -1
- flock/themes/builtin-dark.toml +1 -1
- flock/themes/builtin-pastel-dark.toml +1 -1
- flock/themes/catppuccin-latte.toml +1 -1
- flock/themes/catppuccin-macchiato.toml +1 -1
- flock/themes/catppuccin-mocha.toml +1 -1
- flock/themes/cga.toml +1 -1
- flock/themes/chalk.toml +1 -1
- flock/themes/ciapre.toml +1 -1
- flock/themes/coffee-theme.toml +1 -1
- flock/themes/cyberpunkscarletprotocol.toml +1 -1
- flock/themes/dark+.toml +1 -1
- flock/themes/darkermatrix.toml +1 -1
- flock/themes/darkside.toml +1 -1
- flock/themes/desert.toml +1 -1
- flock/themes/django.toml +1 -1
- flock/themes/djangosmooth.toml +1 -1
- flock/themes/doomone.toml +1 -1
- flock/themes/dotgov.toml +1 -1
- flock/themes/dracula+.toml +1 -1
- flock/themes/duckbones.toml +1 -1
- flock/themes/encom.toml +1 -1
- flock/themes/espresso.toml +1 -1
- flock/themes/everblush.toml +1 -1
- flock/themes/fairyfloss.toml +1 -1
- flock/themes/fideloper.toml +1 -1
- flock/themes/fishtank.toml +1 -1
- flock/themes/flexoki-light.toml +1 -1
- flock/themes/floraverse.toml +1 -1
- flock/themes/framer.toml +1 -1
- flock/themes/galizur.toml +1 -1
- flock/themes/github.toml +1 -1
- flock/themes/grass.toml +1 -1
- flock/themes/grey-green.toml +1 -1
- flock/themes/gruvboxlight.toml +1 -1
- flock/themes/guezwhoz.toml +1 -1
- flock/themes/harper.toml +1 -1
- flock/themes/hax0r-blue.toml +1 -1
- flock/themes/hopscotch.256.toml +1 -1
- flock/themes/ic-green-ppl.toml +1 -1
- flock/themes/iceberg-dark.toml +1 -1
- flock/themes/japanesque.toml +1 -1
- flock/themes/jubi.toml +1 -1
- flock/themes/kibble.toml +1 -1
- flock/themes/kolorit.toml +1 -1
- flock/themes/kurokula.toml +1 -1
- flock/themes/materialdesigncolors.toml +1 -1
- flock/themes/matrix.toml +1 -1
- flock/themes/mellifluous.toml +1 -1
- flock/themes/midnight-in-mojave.toml +1 -1
- flock/themes/monokai-remastered.toml +1 -1
- flock/themes/monokai-soda.toml +1 -1
- flock/themes/neon.toml +1 -1
- flock/themes/neopolitan.toml +1 -1
- flock/themes/nord-light.toml +1 -1
- flock/themes/ocean.toml +1 -1
- flock/themes/onehalfdark.toml +1 -1
- flock/themes/onehalflight.toml +1 -1
- flock/themes/palenighthc.toml +1 -1
- flock/themes/paulmillr.toml +1 -1
- flock/themes/pencildark.toml +1 -1
- flock/themes/pnevma.toml +1 -1
- flock/themes/purple-rain.toml +1 -1
- flock/themes/purplepeter.toml +1 -1
- flock/themes/raycast-dark.toml +1 -1
- flock/themes/red-sands.toml +1 -1
- flock/themes/relaxed.toml +1 -1
- flock/themes/retro.toml +1 -1
- flock/themes/rose-pine.toml +1 -1
- flock/themes/royal.toml +1 -1
- flock/themes/ryuuko.toml +1 -1
- flock/themes/sakura.toml +1 -1
- flock/themes/scarlet-protocol.toml +1 -1
- flock/themes/seoulbones-dark.toml +1 -1
- flock/themes/shades-of-purple.toml +1 -1
- flock/themes/smyck.toml +1 -1
- flock/themes/softserver.toml +1 -1
- flock/themes/solarized-darcula.toml +1 -1
- flock/themes/square.toml +1 -1
- flock/themes/sugarplum.toml +1 -1
- flock/themes/thayer-bright.toml +1 -1
- flock/themes/tokyonight.toml +1 -1
- flock/themes/tomorrow.toml +1 -1
- flock/themes/ubuntu.toml +1 -1
- flock/themes/ultradark.toml +1 -1
- flock/themes/ultraviolent.toml +1 -1
- flock/themes/unikitty.toml +1 -1
- flock/themes/urple.toml +1 -1
- flock/themes/vesper.toml +1 -1
- flock/themes/vimbones.toml +1 -1
- flock/themes/wildcherry.toml +1 -1
- flock/themes/wilmersdorf.toml +1 -1
- flock/themes/wryan.toml +1 -1
- flock/themes/xcodedarkhc.toml +1 -1
- flock/themes/xcodelight.toml +1 -1
- flock/themes/zenbones-light.toml +1 -1
- flock/themes/zenwritten-dark.toml +1 -1
- flock/utilities.py +301 -0
- flock/{components/utility → utility}/output_utility_component.py +68 -53
- flock/visibility.py +107 -0
- flock_core-0.5.56b0.dist-info/METADATA +747 -0
- flock_core-0.5.56b0.dist-info/RECORD +398 -0
- flock_core-0.5.56b0.dist-info/entry_points.txt +2 -0
- {flock_core-0.5.0b28.dist-info → flock_core-0.5.56b0.dist-info}/licenses/LICENSE +1 -1
- flock/adapter/__init__.py +0 -14
- flock/adapter/azure_adapter.py +0 -68
- flock/adapter/chroma_adapter.py +0 -73
- flock/adapter/faiss_adapter.py +0 -97
- flock/adapter/pinecone_adapter.py +0 -51
- flock/adapter/vector_base.py +0 -47
- flock/cli/assets/release_notes.md +0 -140
- flock/cli/config.py +0 -8
- flock/cli/constants.py +0 -36
- flock/cli/create_agent.py +0 -1
- flock/cli/create_flock.py +0 -280
- flock/cli/execute_flock.py +0 -620
- flock/cli/load_agent.py +0 -1
- flock/cli/load_examples.py +0 -1
- flock/cli/load_flock.py +0 -192
- flock/cli/load_release_notes.py +0 -20
- flock/cli/loaded_flock_cli.py +0 -254
- flock/cli/manage_agents.py +0 -459
- flock/cli/registry_management.py +0 -889
- flock/cli/runner.py +0 -41
- flock/cli/settings.py +0 -857
- flock/cli/utils.py +0 -135
- flock/cli/view_results.py +0 -29
- flock/cli/yaml_editor.py +0 -396
- flock/components/__init__.py +0 -30
- flock/components/evaluation/__init__.py +0 -9
- flock/components/evaluation/declarative_evaluation_component.py +0 -606
- flock/components/routing/__init__.py +0 -15
- flock/components/routing/conditional_routing_component.py +0 -494
- flock/components/routing/default_routing_component.py +0 -103
- flock/components/routing/llm_routing_component.py +0 -206
- flock/components/utility/__init__.py +0 -22
- flock/components/utility/example_utility_component.py +0 -250
- flock/components/utility/feedback_utility_component.py +0 -206
- flock/components/utility/memory_utility_component.py +0 -550
- flock/components/utility/metrics_utility_component.py +0 -700
- flock/config.py +0 -61
- flock/core/__init__.py +0 -110
- flock/core/agent/__init__.py +0 -16
- flock/core/agent/default_agent.py +0 -216
- flock/core/agent/flock_agent_components.py +0 -104
- flock/core/agent/flock_agent_execution.py +0 -101
- flock/core/agent/flock_agent_integration.py +0 -260
- flock/core/agent/flock_agent_lifecycle.py +0 -186
- flock/core/agent/flock_agent_serialization.py +0 -381
- flock/core/api/__init__.py +0 -10
- flock/core/api/custom_endpoint.py +0 -45
- flock/core/api/endpoints.py +0 -254
- flock/core/api/main.py +0 -162
- flock/core/api/models.py +0 -97
- flock/core/api/run_store.py +0 -224
- flock/core/api/runner.py +0 -44
- flock/core/api/service.py +0 -214
- flock/core/component/__init__.py +0 -15
- flock/core/component/agent_component_base.py +0 -309
- flock/core/component/evaluation_component.py +0 -62
- flock/core/component/routing_component.py +0 -74
- flock/core/component/utility_component.py +0 -69
- flock/core/config/flock_agent_config.py +0 -58
- flock/core/config/scheduled_agent_config.py +0 -40
- flock/core/context/context.py +0 -213
- flock/core/context/context_manager.py +0 -37
- flock/core/context/context_vars.py +0 -10
- flock/core/evaluation/utils.py +0 -396
- flock/core/execution/batch_executor.py +0 -369
- flock/core/execution/evaluation_executor.py +0 -438
- flock/core/execution/local_executor.py +0 -31
- flock/core/execution/opik_executor.py +0 -103
- flock/core/execution/temporal_executor.py +0 -164
- flock/core/flock.py +0 -634
- flock/core/flock_agent.py +0 -336
- flock/core/flock_factory.py +0 -613
- flock/core/flock_scheduler.py +0 -166
- flock/core/flock_server_manager.py +0 -136
- flock/core/interpreter/python_interpreter.py +0 -689
- flock/core/mcp/__init__.py +0 -1
- flock/core/mcp/flock_mcp_server.py +0 -680
- flock/core/mcp/mcp_client_manager.py +0 -201
- flock/core/mcp/types/__init__.py +0 -1
- flock/core/mixin/dspy_integration.py +0 -403
- flock/core/mixin/prompt_parser.py +0 -125
- flock/core/orchestration/__init__.py +0 -15
- flock/core/orchestration/flock_batch_processor.py +0 -94
- flock/core/orchestration/flock_evaluator.py +0 -113
- flock/core/orchestration/flock_execution.py +0 -295
- flock/core/orchestration/flock_initialization.py +0 -149
- flock/core/orchestration/flock_server_manager.py +0 -67
- flock/core/orchestration/flock_web_server.py +0 -117
- flock/core/registry/__init__.py +0 -45
- flock/core/registry/agent_registry.py +0 -69
- flock/core/registry/callable_registry.py +0 -139
- flock/core/registry/component_discovery.py +0 -142
- flock/core/registry/component_registry.py +0 -64
- flock/core/registry/config_mapping.py +0 -64
- flock/core/registry/decorators.py +0 -137
- flock/core/registry/registry_hub.py +0 -205
- flock/core/registry/server_registry.py +0 -57
- flock/core/registry/type_registry.py +0 -86
- flock/core/serialization/__init__.py +0 -13
- flock/core/serialization/callable_registry.py +0 -52
- flock/core/serialization/flock_serializer.py +0 -832
- flock/core/serialization/json_encoder.py +0 -41
- flock/core/serialization/secure_serializer.py +0 -175
- flock/core/serialization/serializable.py +0 -342
- flock/core/serialization/serialization_utils.py +0 -412
- flock/core/util/file_path_utils.py +0 -223
- flock/core/util/hydrator.py +0 -309
- flock/core/util/input_resolver.py +0 -164
- flock/core/util/loader.py +0 -59
- flock/core/util/splitter.py +0 -219
- flock/di.py +0 -27
- flock/platform/docker_tools.py +0 -49
- flock/platform/jaeger_install.py +0 -86
- flock/webapp/__init__.py +0 -1
- flock/webapp/app/__init__.py +0 -0
- flock/webapp/app/api/__init__.py +0 -0
- flock/webapp/app/api/agent_management.py +0 -241
- flock/webapp/app/api/execution.py +0 -709
- flock/webapp/app/api/flock_management.py +0 -129
- flock/webapp/app/api/registry_viewer.py +0 -30
- flock/webapp/app/chat.py +0 -665
- flock/webapp/app/config.py +0 -104
- flock/webapp/app/dependencies.py +0 -117
- flock/webapp/app/main.py +0 -1070
- flock/webapp/app/middleware.py +0 -113
- flock/webapp/app/models_ui.py +0 -7
- flock/webapp/app/services/__init__.py +0 -0
- flock/webapp/app/services/feedback_file_service.py +0 -363
- flock/webapp/app/services/flock_service.py +0 -337
- flock/webapp/app/services/sharing_models.py +0 -81
- flock/webapp/app/services/sharing_store.py +0 -762
- flock/webapp/app/templates/theme_mapper.html +0 -326
- flock/webapp/app/theme_mapper.py +0 -812
- flock/webapp/app/utils.py +0 -85
- flock/webapp/run.py +0 -215
- flock/webapp/static/css/chat.css +0 -301
- flock/webapp/static/css/components.css +0 -167
- flock/webapp/static/css/header.css +0 -39
- flock/webapp/static/css/layout.css +0 -46
- flock/webapp/static/css/sidebar.css +0 -127
- flock/webapp/static/css/two-pane.css +0 -48
- flock/webapp/templates/base.html +0 -200
- flock/webapp/templates/chat.html +0 -152
- flock/webapp/templates/chat_settings.html +0 -19
- flock/webapp/templates/flock_editor.html +0 -16
- flock/webapp/templates/index.html +0 -12
- flock/webapp/templates/partials/_agent_detail_form.html +0 -93
- flock/webapp/templates/partials/_agent_list.html +0 -18
- flock/webapp/templates/partials/_agent_manager_view.html +0 -51
- flock/webapp/templates/partials/_agent_tools_checklist.html +0 -14
- flock/webapp/templates/partials/_chat_container.html +0 -15
- flock/webapp/templates/partials/_chat_messages.html +0 -57
- flock/webapp/templates/partials/_chat_settings_form.html +0 -85
- flock/webapp/templates/partials/_create_flock_form.html +0 -50
- flock/webapp/templates/partials/_dashboard_flock_detail.html +0 -17
- flock/webapp/templates/partials/_dashboard_flock_file_list.html +0 -16
- flock/webapp/templates/partials/_dashboard_flock_properties_preview.html +0 -28
- flock/webapp/templates/partials/_dashboard_upload_flock_form.html +0 -16
- flock/webapp/templates/partials/_dynamic_input_form_content.html +0 -22
- flock/webapp/templates/partials/_env_vars_table.html +0 -23
- flock/webapp/templates/partials/_execution_form.html +0 -118
- flock/webapp/templates/partials/_execution_view_container.html +0 -28
- flock/webapp/templates/partials/_flock_file_list.html +0 -23
- flock/webapp/templates/partials/_flock_properties_form.html +0 -52
- flock/webapp/templates/partials/_flock_upload_form.html +0 -16
- flock/webapp/templates/partials/_header_flock_status.html +0 -5
- flock/webapp/templates/partials/_load_manager_view.html +0 -49
- flock/webapp/templates/partials/_registry_table.html +0 -25
- flock/webapp/templates/partials/_registry_viewer_content.html +0 -70
- flock/webapp/templates/partials/_results_display.html +0 -78
- flock/webapp/templates/partials/_settings_env_content.html +0 -9
- flock/webapp/templates/partials/_settings_theme_content.html +0 -14
- flock/webapp/templates/partials/_settings_view.html +0 -36
- flock/webapp/templates/partials/_share_chat_link_snippet.html +0 -11
- flock/webapp/templates/partials/_share_link_snippet.html +0 -35
- flock/webapp/templates/partials/_sidebar.html +0 -74
- flock/webapp/templates/partials/_streaming_results_container.html +0 -195
- flock/webapp/templates/partials/_structured_data_view.html +0 -40
- flock/webapp/templates/partials/_theme_preview.html +0 -36
- flock/webapp/templates/registry_viewer.html +0 -84
- flock/webapp/templates/shared_run_page.html +0 -140
- flock/workflow/__init__.py +0 -0
- flock/workflow/activities.py +0 -196
- flock/workflow/agent_activities.py +0 -24
- flock/workflow/agent_execution_activity.py +0 -202
- flock/workflow/flock_workflow.py +0 -214
- flock/workflow/temporal_config.py +0 -96
- flock/workflow/temporal_setup.py +0 -68
- flock_core-0.5.0b28.dist-info/METADATA +0 -274
- flock_core-0.5.0b28.dist-info/RECORD +0 -561
- flock_core-0.5.0b28.dist-info/entry_points.txt +0 -2
- /flock/{core/logging → logging}/formatters/themes.py +0 -0
- /flock/{core/logging → logging}/span_middleware/baggage_span_processor.py +0 -0
- /flock/{core/mcp → mcp}/util/__init__.py +0 -0
- {flock_core-0.5.0b28.dist-info → flock_core-0.5.56b0.dist-info}/WHEEL +0 -0
flock/cli/settings.py
DELETED
|
@@ -1,857 +0,0 @@
|
|
|
1
|
-
"""Settings editor for the Flock CLI.
|
|
2
|
-
|
|
3
|
-
This module provides functionality to view, edit, add, and delete
|
|
4
|
-
environment variables in the .env file.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
import os
|
|
8
|
-
import shutil
|
|
9
|
-
from pathlib import Path
|
|
10
|
-
from typing import Dict, List, Optional, Tuple
|
|
11
|
-
import math
|
|
12
|
-
|
|
13
|
-
import questionary
|
|
14
|
-
from rich.console import Console
|
|
15
|
-
from rich.panel import Panel
|
|
16
|
-
from rich.table import Table
|
|
17
|
-
from rich.text import Text
|
|
18
|
-
|
|
19
|
-
from flock.core.util.cli_helper import init_console
|
|
20
|
-
|
|
21
|
-
# Constants
|
|
22
|
-
ENV_FILE = ".env"
|
|
23
|
-
ENV_TEMPLATE_FILE = ".env_template"
|
|
24
|
-
ENV_PROFILE_PREFIX = ".env_"
|
|
25
|
-
DEFAULT_PROFILE_COMMENT = "# Profile: {profile_name}"
|
|
26
|
-
SHOW_SECRETS_KEY = "SHOW_SECRETS"
|
|
27
|
-
VARS_PER_PAGE_KEY = "VARS_PER_PAGE"
|
|
28
|
-
DEFAULT_VARS_PER_PAGE = 20
|
|
29
|
-
|
|
30
|
-
console = Console()
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def settings_editor():
|
|
34
|
-
"""Main entry point for the settings editor."""
|
|
35
|
-
while True:
|
|
36
|
-
init_console()
|
|
37
|
-
console.print(Panel("[bold green]Environment Settings Editor[/]"), justify="center")
|
|
38
|
-
|
|
39
|
-
# Get current profile name
|
|
40
|
-
current_profile = get_current_profile()
|
|
41
|
-
if current_profile:
|
|
42
|
-
console.print(f"Current Profile: [bold cyan]{current_profile}[/]")
|
|
43
|
-
else:
|
|
44
|
-
console.print("No profile detected")
|
|
45
|
-
|
|
46
|
-
console.line()
|
|
47
|
-
|
|
48
|
-
choice = questionary.select(
|
|
49
|
-
"What would you like to do?",
|
|
50
|
-
choices=[
|
|
51
|
-
questionary.Separator(line=" "),
|
|
52
|
-
"View all environment variables",
|
|
53
|
-
"Edit an environment variable",
|
|
54
|
-
"Add a new environment variable",
|
|
55
|
-
"Delete an environment variable",
|
|
56
|
-
questionary.Separator(),
|
|
57
|
-
"Manage environment profiles",
|
|
58
|
-
questionary.Separator(),
|
|
59
|
-
"Toggle show secrets",
|
|
60
|
-
"Change variables per page",
|
|
61
|
-
questionary.Separator(),
|
|
62
|
-
"Back to main menu",
|
|
63
|
-
],
|
|
64
|
-
).ask()
|
|
65
|
-
|
|
66
|
-
if choice == "View all environment variables":
|
|
67
|
-
view_env_variables()
|
|
68
|
-
elif choice == "Edit an environment variable":
|
|
69
|
-
edit_env_variable()
|
|
70
|
-
elif choice == "Add a new environment variable":
|
|
71
|
-
add_env_variable()
|
|
72
|
-
elif choice == "Delete an environment variable":
|
|
73
|
-
delete_env_variable()
|
|
74
|
-
elif choice == "Manage environment profiles":
|
|
75
|
-
manage_profiles()
|
|
76
|
-
elif choice == "Toggle show secrets":
|
|
77
|
-
toggle_show_secrets()
|
|
78
|
-
elif choice == "Change variables per page":
|
|
79
|
-
change_vars_per_page()
|
|
80
|
-
elif choice == "Back to main menu":
|
|
81
|
-
break
|
|
82
|
-
|
|
83
|
-
if choice != "Back to main menu":
|
|
84
|
-
input("\nPress Enter to continue...")
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
def view_env_variables(page: int = 1, page_size: Optional[int] = None):
|
|
88
|
-
"""View all environment variables with pagination.
|
|
89
|
-
|
|
90
|
-
Args:
|
|
91
|
-
page: Page number to display
|
|
92
|
-
page_size: Number of variables per page (if None, use the setting in .env)
|
|
93
|
-
"""
|
|
94
|
-
env_vars = load_env_file()
|
|
95
|
-
|
|
96
|
-
# If page_size is not specified, get it from settings
|
|
97
|
-
if page_size is None:
|
|
98
|
-
page_size = get_vars_per_page_setting(env_vars)
|
|
99
|
-
|
|
100
|
-
# Calculate pagination
|
|
101
|
-
total_vars = len(env_vars)
|
|
102
|
-
total_pages = math.ceil(total_vars / page_size) if total_vars > 0 else 1
|
|
103
|
-
|
|
104
|
-
# Validate page number
|
|
105
|
-
page = min(max(1, page), total_pages)
|
|
106
|
-
|
|
107
|
-
start_idx = (page - 1) * page_size
|
|
108
|
-
end_idx = min(start_idx + page_size, total_vars)
|
|
109
|
-
|
|
110
|
-
# Get current page variables
|
|
111
|
-
current_page_vars = list(env_vars.items())[start_idx:end_idx]
|
|
112
|
-
|
|
113
|
-
# Check if secrets should be shown
|
|
114
|
-
show_secrets = get_show_secrets_setting(env_vars)
|
|
115
|
-
|
|
116
|
-
# Create table
|
|
117
|
-
table = Table(title=f"Environment Variables (Page {page}/{total_pages}, {page_size} per page)")
|
|
118
|
-
table.add_column("Name", style="cyan")
|
|
119
|
-
table.add_column("Value", style="green")
|
|
120
|
-
|
|
121
|
-
# Show secrets status
|
|
122
|
-
secrets_status = "[green]ON[/]" if show_secrets else "[red]OFF[/]"
|
|
123
|
-
init_console()
|
|
124
|
-
console.print(f"Show Secrets: {secrets_status}")
|
|
125
|
-
|
|
126
|
-
for key, value in current_page_vars:
|
|
127
|
-
# Skip lines that are comments or empty
|
|
128
|
-
if key.startswith('#') or not key:
|
|
129
|
-
continue
|
|
130
|
-
|
|
131
|
-
# Mask sensitive values if show_secrets is False
|
|
132
|
-
if is_sensitive(key) and not show_secrets:
|
|
133
|
-
masked_value = mask_sensitive_value(value)
|
|
134
|
-
table.add_row(key, masked_value)
|
|
135
|
-
else:
|
|
136
|
-
table.add_row(key, value)
|
|
137
|
-
|
|
138
|
-
console.print(table)
|
|
139
|
-
|
|
140
|
-
# Pagination controls with more intuitive shortcuts
|
|
141
|
-
console.print("\nNavigation: ", end="")
|
|
142
|
-
if page > 1:
|
|
143
|
-
console.print("[bold]Previous (p)[/] | ", end="")
|
|
144
|
-
if page < total_pages:
|
|
145
|
-
console.print("[bold]Next (n)[/] | ", end="")
|
|
146
|
-
if show_secrets:
|
|
147
|
-
console.print("[bold]Hide secrets (h)[/] | ", end="")
|
|
148
|
-
else:
|
|
149
|
-
console.print("[bold]Show secrets (s)[/] | ", end="")
|
|
150
|
-
console.print("[bold]Change variables per page (v)[/] | ", end="")
|
|
151
|
-
console.print("[bold]Back (b)[/]")
|
|
152
|
-
|
|
153
|
-
# Handle navigation
|
|
154
|
-
while True:
|
|
155
|
-
key = input("Enter option: ").lower()
|
|
156
|
-
if key == 'p' and page > 1:
|
|
157
|
-
view_env_variables(page - 1, page_size)
|
|
158
|
-
break
|
|
159
|
-
elif key == 'n' and page < total_pages:
|
|
160
|
-
view_env_variables(page + 1, page_size)
|
|
161
|
-
break
|
|
162
|
-
elif key == 's' and not show_secrets:
|
|
163
|
-
# Confirm showing secrets
|
|
164
|
-
confirm = questionary.confirm("Are you sure you want to show sensitive values?").ask()
|
|
165
|
-
if confirm:
|
|
166
|
-
set_show_secrets_setting(True)
|
|
167
|
-
view_env_variables(page, page_size)
|
|
168
|
-
break
|
|
169
|
-
elif key == 'h' and show_secrets:
|
|
170
|
-
set_show_secrets_setting(False)
|
|
171
|
-
view_env_variables(page, page_size)
|
|
172
|
-
break
|
|
173
|
-
elif key == 'v':
|
|
174
|
-
new_page_size = change_vars_per_page()
|
|
175
|
-
if new_page_size:
|
|
176
|
-
view_env_variables(1, new_page_size) # Reset to first page with new page size
|
|
177
|
-
break
|
|
178
|
-
elif key == 'b':
|
|
179
|
-
break
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
def change_vars_per_page():
|
|
183
|
-
"""Change the number of variables displayed per page.
|
|
184
|
-
|
|
185
|
-
Returns:
|
|
186
|
-
The new page size or None if cancelled
|
|
187
|
-
"""
|
|
188
|
-
env_vars = load_env_file()
|
|
189
|
-
current_setting = get_vars_per_page_setting(env_vars)
|
|
190
|
-
|
|
191
|
-
console.print(f"Current variables per page: [cyan]{current_setting}[/]")
|
|
192
|
-
|
|
193
|
-
# Predefined options plus custom option
|
|
194
|
-
page_size_options = ["10", "20", "30", "50", "Custom", "Cancel"]
|
|
195
|
-
|
|
196
|
-
choice = questionary.select(
|
|
197
|
-
"Select number of variables per page:",
|
|
198
|
-
choices=page_size_options,
|
|
199
|
-
).ask()
|
|
200
|
-
|
|
201
|
-
if choice == "Cancel":
|
|
202
|
-
return None
|
|
203
|
-
|
|
204
|
-
if choice == "Custom":
|
|
205
|
-
while True:
|
|
206
|
-
try:
|
|
207
|
-
custom_size = questionary.text(
|
|
208
|
-
"Enter custom page size (5-100):",
|
|
209
|
-
default=str(current_setting)
|
|
210
|
-
).ask()
|
|
211
|
-
|
|
212
|
-
if not custom_size:
|
|
213
|
-
return None
|
|
214
|
-
|
|
215
|
-
new_size = int(custom_size)
|
|
216
|
-
if 5 <= new_size <= 100:
|
|
217
|
-
break
|
|
218
|
-
else:
|
|
219
|
-
console.print("[yellow]Page size must be between 5 and 100.[/]")
|
|
220
|
-
except ValueError:
|
|
221
|
-
console.print("[yellow]Please enter a valid number.[/]")
|
|
222
|
-
else:
|
|
223
|
-
new_size = int(choice)
|
|
224
|
-
|
|
225
|
-
# Save the setting
|
|
226
|
-
set_vars_per_page_setting(new_size)
|
|
227
|
-
console.print(f"[green]Variables per page set to {new_size}.[/]")
|
|
228
|
-
|
|
229
|
-
return new_size
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
def get_vars_per_page_setting(env_vars: Dict[str, str] = None) -> int:
|
|
233
|
-
"""Get the current variables per page setting.
|
|
234
|
-
|
|
235
|
-
Args:
|
|
236
|
-
env_vars: Optional dictionary of environment variables
|
|
237
|
-
|
|
238
|
-
Returns:
|
|
239
|
-
Number of variables per page
|
|
240
|
-
"""
|
|
241
|
-
if env_vars is None:
|
|
242
|
-
env_vars = load_env_file()
|
|
243
|
-
|
|
244
|
-
if VARS_PER_PAGE_KEY in env_vars:
|
|
245
|
-
try:
|
|
246
|
-
page_size = int(env_vars[VARS_PER_PAGE_KEY])
|
|
247
|
-
# Ensure the value is within reasonable bounds
|
|
248
|
-
if 5 <= page_size <= 100:
|
|
249
|
-
return page_size
|
|
250
|
-
except ValueError:
|
|
251
|
-
pass
|
|
252
|
-
|
|
253
|
-
return DEFAULT_VARS_PER_PAGE
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
def set_vars_per_page_setting(page_size: int):
|
|
257
|
-
"""Set the variables per page setting.
|
|
258
|
-
|
|
259
|
-
Args:
|
|
260
|
-
page_size: Number of variables to display per page
|
|
261
|
-
"""
|
|
262
|
-
env_vars = load_env_file()
|
|
263
|
-
env_vars[VARS_PER_PAGE_KEY] = str(page_size)
|
|
264
|
-
save_env_file(env_vars)
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
def toggle_show_secrets():
|
|
268
|
-
"""Toggle the show secrets setting."""
|
|
269
|
-
env_vars = load_env_file()
|
|
270
|
-
current_setting = get_show_secrets_setting(env_vars)
|
|
271
|
-
|
|
272
|
-
if current_setting:
|
|
273
|
-
console.print("Currently showing sensitive values. Do you want to hide them?")
|
|
274
|
-
confirm = questionary.confirm("Hide sensitive values?").ask()
|
|
275
|
-
if confirm:
|
|
276
|
-
set_show_secrets_setting(False)
|
|
277
|
-
console.print("[green]Sensitive values will now be masked.[/]")
|
|
278
|
-
else:
|
|
279
|
-
console.print("[yellow]Warning:[/] Showing sensitive values can expose sensitive information.")
|
|
280
|
-
confirm = questionary.confirm("Are you sure you want to show sensitive values?").ask()
|
|
281
|
-
if confirm:
|
|
282
|
-
set_show_secrets_setting(True)
|
|
283
|
-
console.print("[green]Sensitive values will now be shown.[/]")
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
def get_show_secrets_setting(env_vars: Dict[str, str] = None) -> bool:
|
|
287
|
-
"""Get the current show secrets setting.
|
|
288
|
-
|
|
289
|
-
Args:
|
|
290
|
-
env_vars: Optional dictionary of environment variables
|
|
291
|
-
|
|
292
|
-
Returns:
|
|
293
|
-
True if secrets should be shown, False otherwise
|
|
294
|
-
"""
|
|
295
|
-
if env_vars is None:
|
|
296
|
-
env_vars = load_env_file()
|
|
297
|
-
|
|
298
|
-
if SHOW_SECRETS_KEY in env_vars:
|
|
299
|
-
return env_vars[SHOW_SECRETS_KEY].lower() == 'true'
|
|
300
|
-
|
|
301
|
-
return False
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
def set_show_secrets_setting(show_secrets: bool):
|
|
305
|
-
"""Set the show secrets setting.
|
|
306
|
-
|
|
307
|
-
Args:
|
|
308
|
-
show_secrets: Whether to show secrets
|
|
309
|
-
"""
|
|
310
|
-
env_vars = load_env_file()
|
|
311
|
-
env_vars[SHOW_SECRETS_KEY] = str(show_secrets)
|
|
312
|
-
save_env_file(env_vars)
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
def edit_env_variable():
|
|
316
|
-
"""Edit an environment variable."""
|
|
317
|
-
# Get list of variables
|
|
318
|
-
env_vars = load_env_file()
|
|
319
|
-
|
|
320
|
-
if not env_vars:
|
|
321
|
-
console.print("[yellow]No environment variables found to edit.[/]")
|
|
322
|
-
return
|
|
323
|
-
|
|
324
|
-
# Filter out comments
|
|
325
|
-
variables = [k for k in env_vars.keys() if not k.startswith('#') and k]
|
|
326
|
-
|
|
327
|
-
# Display variables with selection
|
|
328
|
-
init_console()
|
|
329
|
-
console.print("Select a variable to edit:")
|
|
330
|
-
|
|
331
|
-
# Let user select a variable to edit
|
|
332
|
-
var_name = questionary.select(
|
|
333
|
-
"Select a variable to edit:",
|
|
334
|
-
choices=variables + ["Cancel"],
|
|
335
|
-
).ask()
|
|
336
|
-
|
|
337
|
-
if var_name == "Cancel":
|
|
338
|
-
return
|
|
339
|
-
|
|
340
|
-
current_value = env_vars[var_name]
|
|
341
|
-
is_sensitive_var = is_sensitive(var_name)
|
|
342
|
-
|
|
343
|
-
if is_sensitive_var:
|
|
344
|
-
console.print(f"[yellow]Warning:[/] You are editing a sensitive value: {var_name}")
|
|
345
|
-
confirm = questionary.confirm("Are you sure you want to continue?").ask()
|
|
346
|
-
if not confirm:
|
|
347
|
-
return
|
|
348
|
-
|
|
349
|
-
# Show current value (masked if sensitive and show_secrets is False)
|
|
350
|
-
show_secrets = get_show_secrets_setting(env_vars)
|
|
351
|
-
if is_sensitive_var and not show_secrets:
|
|
352
|
-
console.print(f"Current value: {mask_sensitive_value(current_value)}")
|
|
353
|
-
else:
|
|
354
|
-
console.print(f"Current value: {current_value}")
|
|
355
|
-
|
|
356
|
-
# Get new value with hint
|
|
357
|
-
console.print("[italic]Enter new value (or leave empty to cancel)[/]")
|
|
358
|
-
new_value = questionary.text("Enter new value:", default=current_value).ask()
|
|
359
|
-
|
|
360
|
-
if new_value is None:
|
|
361
|
-
console.print("[yellow]Edit cancelled.[/]")
|
|
362
|
-
return
|
|
363
|
-
|
|
364
|
-
if new_value == "":
|
|
365
|
-
# Confirm if user wants to set an empty value or cancel
|
|
366
|
-
confirm = questionary.confirm("Do you want to set an empty value? Select No to cancel.", default=False).ask()
|
|
367
|
-
if not confirm:
|
|
368
|
-
console.print("[yellow]Edit cancelled.[/]")
|
|
369
|
-
return
|
|
370
|
-
|
|
371
|
-
if new_value == current_value:
|
|
372
|
-
console.print("[yellow]No changes made.[/]")
|
|
373
|
-
return
|
|
374
|
-
|
|
375
|
-
# Update the value
|
|
376
|
-
env_vars[var_name] = new_value
|
|
377
|
-
save_env_file(env_vars)
|
|
378
|
-
console.print(f"[green]Updated {var_name} successfully.[/]")
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
def add_env_variable():
|
|
382
|
-
"""Add a new environment variable."""
|
|
383
|
-
env_vars = load_env_file()
|
|
384
|
-
|
|
385
|
-
console.print("[italic]Enter variable name (or leave empty to go back)[/]")
|
|
386
|
-
|
|
387
|
-
# Get variable name
|
|
388
|
-
while True:
|
|
389
|
-
var_name = questionary.text("Enter variable name:").ask()
|
|
390
|
-
|
|
391
|
-
if not var_name:
|
|
392
|
-
# Ask if user wants to go back
|
|
393
|
-
go_back = questionary.confirm("Do you want to go back to the settings menu?", default=True).ask()
|
|
394
|
-
if go_back:
|
|
395
|
-
return
|
|
396
|
-
else:
|
|
397
|
-
console.print("[italic]Please enter a variable name (or leave empty to go back)[/]")
|
|
398
|
-
continue
|
|
399
|
-
|
|
400
|
-
if var_name in env_vars and not var_name.startswith('#'):
|
|
401
|
-
console.print(f"[yellow]Variable {var_name} already exists. Please use edit instead.[/]")
|
|
402
|
-
continue
|
|
403
|
-
|
|
404
|
-
break
|
|
405
|
-
|
|
406
|
-
# Get variable value
|
|
407
|
-
var_value = questionary.text("Enter variable value:").ask()
|
|
408
|
-
|
|
409
|
-
# Add to env_vars
|
|
410
|
-
env_vars[var_name] = var_value
|
|
411
|
-
save_env_file(env_vars)
|
|
412
|
-
console.print(f"[green]Added {var_name} successfully.[/]")
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
def delete_env_variable():
|
|
416
|
-
"""Delete an environment variable."""
|
|
417
|
-
# Get list of variables
|
|
418
|
-
env_vars = load_env_file()
|
|
419
|
-
|
|
420
|
-
if not env_vars:
|
|
421
|
-
console.print("[yellow]No environment variables found to delete.[/]")
|
|
422
|
-
return
|
|
423
|
-
|
|
424
|
-
# Filter out comments
|
|
425
|
-
variables = [k for k in env_vars.keys() if not k.startswith('#') and k]
|
|
426
|
-
|
|
427
|
-
# Display variables with selection
|
|
428
|
-
init_console()
|
|
429
|
-
console.print("Select a variable to delete:")
|
|
430
|
-
|
|
431
|
-
# Let user select a variable to delete with hint
|
|
432
|
-
var_name = questionary.select(
|
|
433
|
-
"Select a variable to delete:",
|
|
434
|
-
choices=variables + ["Cancel"],
|
|
435
|
-
).ask()
|
|
436
|
-
|
|
437
|
-
if var_name == "Cancel":
|
|
438
|
-
return
|
|
439
|
-
|
|
440
|
-
# Confirm deletion
|
|
441
|
-
confirm = questionary.confirm(f"Are you sure you want to delete {var_name}?").ask()
|
|
442
|
-
if not confirm:
|
|
443
|
-
console.print("[yellow]Deletion cancelled.[/]")
|
|
444
|
-
return
|
|
445
|
-
|
|
446
|
-
# Delete the variable
|
|
447
|
-
del env_vars[var_name]
|
|
448
|
-
save_env_file(env_vars)
|
|
449
|
-
console.print(f"[green]Deleted {var_name} successfully.[/]")
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
def manage_profiles():
|
|
453
|
-
"""Manage environment profiles."""
|
|
454
|
-
init_console()
|
|
455
|
-
console.print(Panel("[bold green]Environment Profile Management[/]"), justify="center")
|
|
456
|
-
|
|
457
|
-
# Get current profile and available profiles
|
|
458
|
-
current_profile = get_current_profile()
|
|
459
|
-
available_profiles = get_available_profiles()
|
|
460
|
-
|
|
461
|
-
if current_profile:
|
|
462
|
-
console.print(f"Current Profile: [bold cyan]{current_profile}[/]")
|
|
463
|
-
|
|
464
|
-
if not available_profiles:
|
|
465
|
-
console.print("[yellow]No profiles found.[/]")
|
|
466
|
-
else:
|
|
467
|
-
console.print("Available Profiles:")
|
|
468
|
-
for profile in available_profiles:
|
|
469
|
-
if profile == current_profile:
|
|
470
|
-
console.print(f" [bold cyan]{profile} (active)[/]")
|
|
471
|
-
else:
|
|
472
|
-
console.print(f" {profile}")
|
|
473
|
-
|
|
474
|
-
console.line()
|
|
475
|
-
|
|
476
|
-
# Profile management options
|
|
477
|
-
choice = questionary.select(
|
|
478
|
-
"What would you like to do?",
|
|
479
|
-
choices=[
|
|
480
|
-
questionary.Separator(line=" "),
|
|
481
|
-
"Switch to a different profile",
|
|
482
|
-
"Create a new profile",
|
|
483
|
-
"Rename a profile",
|
|
484
|
-
"Delete a profile",
|
|
485
|
-
"Back to settings menu",
|
|
486
|
-
],
|
|
487
|
-
).ask()
|
|
488
|
-
|
|
489
|
-
if choice == "Switch to a different profile":
|
|
490
|
-
switch_profile()
|
|
491
|
-
elif choice == "Create a new profile":
|
|
492
|
-
create_profile()
|
|
493
|
-
elif choice == "Rename a profile":
|
|
494
|
-
rename_profile()
|
|
495
|
-
elif choice == "Delete a profile":
|
|
496
|
-
delete_profile()
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
def switch_profile():
|
|
500
|
-
"""Switch to a different environment profile."""
|
|
501
|
-
available_profiles = get_available_profiles()
|
|
502
|
-
current_profile = get_current_profile()
|
|
503
|
-
|
|
504
|
-
if not available_profiles:
|
|
505
|
-
console.print("[yellow]No profiles available to switch to.[/]")
|
|
506
|
-
return
|
|
507
|
-
|
|
508
|
-
# Remove current profile from the list to avoid switching to the same profile
|
|
509
|
-
selectable_profiles = [p for p in available_profiles if p != current_profile]
|
|
510
|
-
|
|
511
|
-
if not selectable_profiles:
|
|
512
|
-
console.print("[yellow]No other profiles available to switch to.[/]")
|
|
513
|
-
return
|
|
514
|
-
|
|
515
|
-
target_profile = questionary.select(
|
|
516
|
-
"Select a profile to switch to:",
|
|
517
|
-
choices=selectable_profiles + ["Cancel"],
|
|
518
|
-
).ask()
|
|
519
|
-
|
|
520
|
-
if target_profile == "Cancel":
|
|
521
|
-
return
|
|
522
|
-
|
|
523
|
-
# Confirm switch
|
|
524
|
-
confirm = questionary.confirm(f"Are you sure you want to switch to the {target_profile} profile?").ask()
|
|
525
|
-
if not confirm:
|
|
526
|
-
return
|
|
527
|
-
|
|
528
|
-
# Backup current .env file
|
|
529
|
-
backup_env_file()
|
|
530
|
-
|
|
531
|
-
# Copy selected profile to .env
|
|
532
|
-
source_file = f"{ENV_PROFILE_PREFIX}{target_profile}"
|
|
533
|
-
if os.path.exists(source_file):
|
|
534
|
-
shutil.copy2(source_file, ENV_FILE)
|
|
535
|
-
console.print(f"[green]Switched to {target_profile} profile successfully.[/]")
|
|
536
|
-
else:
|
|
537
|
-
console.print(f"[red]Error: Could not find profile file {source_file}.[/]")
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
def create_profile():
|
|
541
|
-
"""Create a new environment profile."""
|
|
542
|
-
profile_name = questionary.text("Enter new profile name:").ask()
|
|
543
|
-
|
|
544
|
-
if not profile_name:
|
|
545
|
-
console.print("[yellow]Profile name cannot be empty.[/]")
|
|
546
|
-
return
|
|
547
|
-
|
|
548
|
-
# Check if profile already exists
|
|
549
|
-
target_file = f"{ENV_PROFILE_PREFIX}{profile_name}"
|
|
550
|
-
if os.path.exists(target_file):
|
|
551
|
-
console.print(f"[yellow]Profile {profile_name} already exists.[/]")
|
|
552
|
-
return
|
|
553
|
-
|
|
554
|
-
# Determine source file - use current .env or template
|
|
555
|
-
source_choices = ["Current environment (.env)", ".env_template"]
|
|
556
|
-
if os.path.exists(ENV_TEMPLATE_FILE):
|
|
557
|
-
source_choices.append(ENV_TEMPLATE_FILE)
|
|
558
|
-
|
|
559
|
-
source_choice = questionary.select(
|
|
560
|
-
"Create profile based on:",
|
|
561
|
-
choices=source_choices + ["Cancel"],
|
|
562
|
-
).ask()
|
|
563
|
-
|
|
564
|
-
if source_choice == "Cancel":
|
|
565
|
-
return
|
|
566
|
-
|
|
567
|
-
source_file = ENV_FILE if source_choice == "Current environment (.env)" else ENV_TEMPLATE_FILE
|
|
568
|
-
|
|
569
|
-
if not os.path.exists(source_file):
|
|
570
|
-
console.print(f"[red]Error: Source file {source_file} not found.[/]")
|
|
571
|
-
return
|
|
572
|
-
|
|
573
|
-
# Create new profile file
|
|
574
|
-
try:
|
|
575
|
-
# Copy source file
|
|
576
|
-
shutil.copy2(source_file, target_file)
|
|
577
|
-
|
|
578
|
-
# Add profile header if it doesn't exist
|
|
579
|
-
with open(target_file, 'r') as file:
|
|
580
|
-
content = file.read()
|
|
581
|
-
|
|
582
|
-
if not content.startswith("# Profile:"):
|
|
583
|
-
with open(target_file, 'w') as file:
|
|
584
|
-
profile_header = DEFAULT_PROFILE_COMMENT.format(profile_name=profile_name)
|
|
585
|
-
file.write(f"{profile_header}\n{content}")
|
|
586
|
-
|
|
587
|
-
console.print(f"[green]Created {profile_name} profile successfully.[/]")
|
|
588
|
-
except Exception as e:
|
|
589
|
-
console.print(f"[red]Error creating profile: {str(e)}[/]")
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
def rename_profile():
|
|
593
|
-
"""Rename an existing profile."""
|
|
594
|
-
available_profiles = get_available_profiles()
|
|
595
|
-
current_profile = get_current_profile()
|
|
596
|
-
|
|
597
|
-
if not available_profiles:
|
|
598
|
-
console.print("[yellow]No profiles available to rename.[/]")
|
|
599
|
-
return
|
|
600
|
-
|
|
601
|
-
# Let user select a profile to rename
|
|
602
|
-
profile_to_rename = questionary.select(
|
|
603
|
-
"Select a profile to rename:",
|
|
604
|
-
choices=available_profiles + ["Cancel"],
|
|
605
|
-
).ask()
|
|
606
|
-
|
|
607
|
-
if profile_to_rename == "Cancel":
|
|
608
|
-
return
|
|
609
|
-
|
|
610
|
-
# Get new name
|
|
611
|
-
new_name = questionary.text("Enter new profile name:").ask()
|
|
612
|
-
|
|
613
|
-
if not new_name:
|
|
614
|
-
console.print("[yellow]New profile name cannot be empty.[/]")
|
|
615
|
-
return
|
|
616
|
-
|
|
617
|
-
if new_name in available_profiles:
|
|
618
|
-
console.print(f"[yellow]Profile {new_name} already exists.[/]")
|
|
619
|
-
return
|
|
620
|
-
|
|
621
|
-
# Rename profile file
|
|
622
|
-
source_file = f"{ENV_PROFILE_PREFIX}{profile_to_rename}"
|
|
623
|
-
target_file = f"{ENV_PROFILE_PREFIX}{new_name}"
|
|
624
|
-
|
|
625
|
-
try:
|
|
626
|
-
# Read content of the source file
|
|
627
|
-
with open(source_file, 'r') as file:
|
|
628
|
-
content = file.readlines()
|
|
629
|
-
|
|
630
|
-
# Update profile header if it exists
|
|
631
|
-
if content and content[0].startswith("# Profile:"):
|
|
632
|
-
content[0] = DEFAULT_PROFILE_COMMENT.format(profile_name=new_name) + "\n"
|
|
633
|
-
|
|
634
|
-
# Write to new file
|
|
635
|
-
with open(target_file, 'w') as file:
|
|
636
|
-
file.writelines(content)
|
|
637
|
-
|
|
638
|
-
# Remove old file
|
|
639
|
-
os.remove(source_file)
|
|
640
|
-
|
|
641
|
-
# If this was the current profile, update .env as well
|
|
642
|
-
if profile_to_rename == current_profile:
|
|
643
|
-
with open(ENV_FILE, 'r') as file:
|
|
644
|
-
content = file.readlines()
|
|
645
|
-
|
|
646
|
-
if content and content[0].startswith("# Profile:"):
|
|
647
|
-
content[0] = DEFAULT_PROFILE_COMMENT.format(profile_name=new_name) + "\n"
|
|
648
|
-
|
|
649
|
-
with open(ENV_FILE, 'w') as file:
|
|
650
|
-
file.writelines(content)
|
|
651
|
-
|
|
652
|
-
console.print(f"[green]Renamed {profile_to_rename} to {new_name} successfully.[/]")
|
|
653
|
-
except Exception as e:
|
|
654
|
-
console.print(f"[red]Error renaming profile: {str(e)}[/]")
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
def delete_profile():
|
|
658
|
-
"""Delete an existing profile."""
|
|
659
|
-
available_profiles = get_available_profiles()
|
|
660
|
-
current_profile = get_current_profile()
|
|
661
|
-
|
|
662
|
-
if not available_profiles:
|
|
663
|
-
console.print("[yellow]No profiles available to delete.[/]")
|
|
664
|
-
return
|
|
665
|
-
|
|
666
|
-
# Let user select a profile to delete
|
|
667
|
-
profile_to_delete = questionary.select(
|
|
668
|
-
"Select a profile to delete:",
|
|
669
|
-
choices=available_profiles + ["Cancel"],
|
|
670
|
-
).ask()
|
|
671
|
-
|
|
672
|
-
if profile_to_delete == "Cancel":
|
|
673
|
-
return
|
|
674
|
-
|
|
675
|
-
# Confirm deletion
|
|
676
|
-
confirm = questionary.confirm(
|
|
677
|
-
f"Are you sure you want to delete the {profile_to_delete} profile? This cannot be undone."
|
|
678
|
-
).ask()
|
|
679
|
-
|
|
680
|
-
if not confirm:
|
|
681
|
-
return
|
|
682
|
-
|
|
683
|
-
# Delete profile file
|
|
684
|
-
profile_file = f"{ENV_PROFILE_PREFIX}{profile_to_delete}"
|
|
685
|
-
|
|
686
|
-
try:
|
|
687
|
-
os.remove(profile_file)
|
|
688
|
-
|
|
689
|
-
# Warn if deleting the current profile
|
|
690
|
-
if profile_to_delete == current_profile:
|
|
691
|
-
console.print(
|
|
692
|
-
f"[yellow]Warning: You deleted the currently active profile. "
|
|
693
|
-
f"The .env file still contains those settings but is no longer marked as a profile.[/]"
|
|
694
|
-
)
|
|
695
|
-
|
|
696
|
-
# Remove profile header from .env
|
|
697
|
-
with open(ENV_FILE, 'r') as file:
|
|
698
|
-
content = file.readlines()
|
|
699
|
-
|
|
700
|
-
if content and content[0].startswith("# Profile:"):
|
|
701
|
-
content = content[1:]
|
|
702
|
-
with open(ENV_FILE, 'w') as file:
|
|
703
|
-
file.writelines(content)
|
|
704
|
-
|
|
705
|
-
console.print(f"[green]Deleted {profile_to_delete} profile successfully.[/]")
|
|
706
|
-
except Exception as e:
|
|
707
|
-
console.print(f"[red]Error deleting profile: {str(e)}[/]")
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
def is_sensitive(key: str) -> bool:
|
|
711
|
-
"""Check if a variable is considered sensitive.
|
|
712
|
-
|
|
713
|
-
Args:
|
|
714
|
-
key: The variable name
|
|
715
|
-
|
|
716
|
-
Returns:
|
|
717
|
-
True if sensitive, False otherwise
|
|
718
|
-
"""
|
|
719
|
-
sensitive_patterns = ['key', 'token', 'secret', 'password', 'api', 'pat']
|
|
720
|
-
key_lower = key.lower()
|
|
721
|
-
return any(pattern in key_lower for pattern in sensitive_patterns)
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
def mask_sensitive_value(value: str) -> str:
|
|
725
|
-
"""Mask a sensitive value.
|
|
726
|
-
|
|
727
|
-
Args:
|
|
728
|
-
value: The sensitive value
|
|
729
|
-
|
|
730
|
-
Returns:
|
|
731
|
-
Masked value
|
|
732
|
-
"""
|
|
733
|
-
if not value:
|
|
734
|
-
return value
|
|
735
|
-
|
|
736
|
-
if len(value) <= 4:
|
|
737
|
-
return "••••"
|
|
738
|
-
|
|
739
|
-
# Show first 2 and last 2 characters
|
|
740
|
-
return value[:2] + "•" * (len(value) - 4) + value[-2:]
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
def get_current_profile() -> Optional[str]:
|
|
744
|
-
"""Get the name of the current active profile.
|
|
745
|
-
|
|
746
|
-
Returns:
|
|
747
|
-
Profile name or None if no profile is active
|
|
748
|
-
"""
|
|
749
|
-
if not os.path.exists(ENV_FILE):
|
|
750
|
-
return None
|
|
751
|
-
|
|
752
|
-
try:
|
|
753
|
-
with open(ENV_FILE, 'r') as file:
|
|
754
|
-
first_line = file.readline().strip()
|
|
755
|
-
|
|
756
|
-
if first_line.startswith("# Profile:"):
|
|
757
|
-
return first_line.replace("# Profile:", "").strip()
|
|
758
|
-
except Exception:
|
|
759
|
-
pass
|
|
760
|
-
|
|
761
|
-
return None
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
def get_available_profiles() -> List[str]:
|
|
765
|
-
"""Get a list of available profiles.
|
|
766
|
-
|
|
767
|
-
Returns:
|
|
768
|
-
List of profile names
|
|
769
|
-
"""
|
|
770
|
-
profiles = []
|
|
771
|
-
|
|
772
|
-
for file in os.listdir():
|
|
773
|
-
if file.startswith(ENV_PROFILE_PREFIX):
|
|
774
|
-
profile_name = file[len(ENV_PROFILE_PREFIX):]
|
|
775
|
-
profiles.append(profile_name)
|
|
776
|
-
|
|
777
|
-
return profiles
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
def backup_env_file():
|
|
781
|
-
"""Create a backup of the current .env file."""
|
|
782
|
-
if not os.path.exists(ENV_FILE):
|
|
783
|
-
return
|
|
784
|
-
|
|
785
|
-
backup_file = f"{ENV_FILE}.bak"
|
|
786
|
-
shutil.copy2(ENV_FILE, backup_file)
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
def load_env_file() -> Dict[str, str]:
|
|
790
|
-
"""Load the .env file into a dictionary.
|
|
791
|
-
|
|
792
|
-
Returns:
|
|
793
|
-
Dictionary of environment variables
|
|
794
|
-
"""
|
|
795
|
-
env_vars = {}
|
|
796
|
-
|
|
797
|
-
if not os.path.exists(ENV_FILE):
|
|
798
|
-
console.print(f"[yellow]Warning: {ENV_FILE} file not found.[/]")
|
|
799
|
-
return env_vars
|
|
800
|
-
|
|
801
|
-
try:
|
|
802
|
-
with open(ENV_FILE, 'r') as file:
|
|
803
|
-
lines = file.readlines()
|
|
804
|
-
|
|
805
|
-
# Process each line
|
|
806
|
-
for line in lines:
|
|
807
|
-
line = line.strip()
|
|
808
|
-
|
|
809
|
-
# Skip empty lines
|
|
810
|
-
if not line:
|
|
811
|
-
env_vars[""] = ""
|
|
812
|
-
continue
|
|
813
|
-
|
|
814
|
-
# Handle comments
|
|
815
|
-
if line.startswith('#'):
|
|
816
|
-
env_vars[line] = ""
|
|
817
|
-
continue
|
|
818
|
-
|
|
819
|
-
# Handle regular variables
|
|
820
|
-
if '=' in line:
|
|
821
|
-
key, value = line.split('=', 1)
|
|
822
|
-
env_vars[key] = value
|
|
823
|
-
else:
|
|
824
|
-
# Handle lines without equals sign
|
|
825
|
-
env_vars[line] = ""
|
|
826
|
-
|
|
827
|
-
except Exception as e:
|
|
828
|
-
console.print(f"[red]Error loading .env file: {str(e)}[/]")
|
|
829
|
-
|
|
830
|
-
return env_vars
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
def save_env_file(env_vars: Dict[str, str]):
|
|
834
|
-
"""Save environment variables back to the .env file.
|
|
835
|
-
|
|
836
|
-
Args:
|
|
837
|
-
env_vars: Dictionary of environment variables
|
|
838
|
-
"""
|
|
839
|
-
# Create backup
|
|
840
|
-
backup_env_file()
|
|
841
|
-
|
|
842
|
-
try:
|
|
843
|
-
with open(ENV_FILE, 'w') as file:
|
|
844
|
-
for key, value in env_vars.items():
|
|
845
|
-
if key.startswith('#'):
|
|
846
|
-
# Write comments as is
|
|
847
|
-
file.write(f"{key}\n")
|
|
848
|
-
elif not key:
|
|
849
|
-
# Write empty lines
|
|
850
|
-
file.write("\n")
|
|
851
|
-
else:
|
|
852
|
-
# Write regular variables
|
|
853
|
-
file.write(f"{key}={value}\n")
|
|
854
|
-
|
|
855
|
-
console.print("[green]Settings saved successfully.[/]")
|
|
856
|
-
except Exception as e:
|
|
857
|
-
console.print(f"[red]Error saving .env file: {str(e)}[/]")
|