feat: enhance config migration with new environment variable tracking
Added a system to track environment variables introduced in each config version, allowing migration prompts to only mention new variables since the user's last version. Updated the interactive configuration process to offer users the option to set these new optional keys during migration.
This commit is contained in:
@@ -156,6 +156,15 @@ DEFAULT_CONFIG = {
|
|||||||
# Config Migration System
|
# Config Migration System
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
|
# Track which env vars were introduced in each config version.
|
||||||
|
# Migration only mentions vars new since the user's previous version.
|
||||||
|
ENV_VARS_BY_VERSION: Dict[int, List[str]] = {
|
||||||
|
3: ["FIRECRAWL_API_KEY", "BROWSERBASE_API_KEY", "BROWSERBASE_PROJECT_ID", "FAL_KEY"],
|
||||||
|
4: ["VOICE_TOOLS_OPENAI_KEY", "ELEVENLABS_API_KEY"],
|
||||||
|
5: ["WHATSAPP_ENABLED", "WHATSAPP_MODE", "WHATSAPP_ALLOWED_USERS",
|
||||||
|
"SLACK_BOT_TOKEN", "SLACK_APP_TOKEN", "SLACK_ALLOWED_USERS"],
|
||||||
|
}
|
||||||
|
|
||||||
# Required environment variables with metadata for migration prompts.
|
# Required environment variables with metadata for migration prompts.
|
||||||
# LLM provider is required but handled in the setup wizard's provider
|
# LLM provider is required but handled in the setup wizard's provider
|
||||||
# selection step (Nous Portal / OpenRouter / Custom endpoint), so this
|
# selection step (Nous Portal / OpenRouter / Custom endpoint), so this
|
||||||
@@ -625,34 +634,47 @@ def migrate_config(interactive: bool = True, quiet: bool = False) -> Dict[str, A
|
|||||||
if v["name"] not in required_names and not v.get("advanced")
|
if v["name"] not in required_names and not v.get("advanced")
|
||||||
]
|
]
|
||||||
|
|
||||||
if interactive and missing_optional:
|
# Only offer to configure env vars that are NEW since the user's previous version
|
||||||
print(" Would you like to configure any optional keys now?")
|
new_var_names = set()
|
||||||
try:
|
for ver in range(current_ver + 1, latest_ver + 1):
|
||||||
answer = input(" Configure optional keys? [y/N]: ").strip().lower()
|
new_var_names.update(ENV_VARS_BY_VERSION.get(ver, []))
|
||||||
except (EOFError, KeyboardInterrupt):
|
|
||||||
answer = "n"
|
if new_var_names and interactive and not quiet:
|
||||||
|
new_and_unset = [
|
||||||
if answer in ("y", "yes"):
|
(name, OPTIONAL_ENV_VARS[name])
|
||||||
|
for name in sorted(new_var_names)
|
||||||
|
if not get_env_value(name) and name in OPTIONAL_ENV_VARS
|
||||||
|
]
|
||||||
|
if new_and_unset:
|
||||||
|
print(f"\n {len(new_and_unset)} new optional key(s) in this update:")
|
||||||
|
for name, info in new_and_unset:
|
||||||
|
print(f" • {name} — {info.get('description', '')}")
|
||||||
print()
|
print()
|
||||||
for var in missing_optional:
|
try:
|
||||||
desc = var.get("description", "")
|
answer = input(" Configure new keys? [y/N]: ").strip().lower()
|
||||||
if var.get("url"):
|
except (EOFError, KeyboardInterrupt):
|
||||||
print(f" {desc}")
|
answer = "n"
|
||||||
print(f" Get your key at: {var['url']}")
|
|
||||||
else:
|
if answer in ("y", "yes"):
|
||||||
print(f" {desc}")
|
|
||||||
|
|
||||||
if var.get("password"):
|
|
||||||
import getpass
|
|
||||||
value = getpass.getpass(f" {var['prompt']} (Enter to skip): ")
|
|
||||||
else:
|
|
||||||
value = input(f" {var['prompt']} (Enter to skip): ").strip()
|
|
||||||
|
|
||||||
if value:
|
|
||||||
save_env_value(var["name"], value)
|
|
||||||
results["env_added"].append(var["name"])
|
|
||||||
print(f" ✓ Saved {var['name']}")
|
|
||||||
print()
|
print()
|
||||||
|
for name, info in new_and_unset:
|
||||||
|
if info.get("url"):
|
||||||
|
print(f" {info.get('description', name)}")
|
||||||
|
print(f" Get your key at: {info['url']}")
|
||||||
|
else:
|
||||||
|
print(f" {info.get('description', name)}")
|
||||||
|
if info.get("password"):
|
||||||
|
import getpass
|
||||||
|
value = getpass.getpass(f" {info.get('prompt', name)} (Enter to skip): ")
|
||||||
|
else:
|
||||||
|
value = input(f" {info.get('prompt', name)} (Enter to skip): ").strip()
|
||||||
|
if value:
|
||||||
|
save_env_value(name, value)
|
||||||
|
results["env_added"].append(name)
|
||||||
|
print(f" ✓ Saved {name}")
|
||||||
|
print()
|
||||||
|
else:
|
||||||
|
print(" Set later with: hermes config set KEY VALUE")
|
||||||
|
|
||||||
# Check for missing config fields
|
# Check for missing config fields
|
||||||
missing_config = get_missing_config_fields()
|
missing_config = get_missing_config_fields()
|
||||||
|
|||||||
Reference in New Issue
Block a user