152 lines
4.5 KiB
Python
152 lines
4.5 KiB
Python
from typing import List, Dict, Any
|
|
|
|
from cmdlet._shared import Cmdlet, CmdletArg
|
|
from SYS.config import load_config, save_config
|
|
|
|
CMDLET = Cmdlet(
|
|
name=".config",
|
|
summary="Manage configuration settings",
|
|
usage=".config [key] [value]",
|
|
arg=[
|
|
CmdletArg(
|
|
name="key",
|
|
description="Configuration key to update (dot-separated)",
|
|
required=False
|
|
),
|
|
CmdletArg(
|
|
name="value",
|
|
description="New value for the configuration key",
|
|
required=False
|
|
),
|
|
],
|
|
)
|
|
|
|
|
|
def flatten_config(config: Dict[str,
|
|
Any],
|
|
parent_key: str = "",
|
|
sep: str = ".") -> List[Dict[str,
|
|
Any]]:
|
|
items = []
|
|
for k, v in config.items():
|
|
if k.startswith("_"): # Skip internal keys
|
|
continue
|
|
|
|
new_key = f"{parent_key}{sep}{k}" if parent_key else k
|
|
if isinstance(v, dict):
|
|
items.extend(flatten_config(v, new_key, sep=sep))
|
|
else:
|
|
items.append(
|
|
{
|
|
"Key": new_key,
|
|
"Value": str(v),
|
|
"Type": type(v).__name__,
|
|
"_selection_args": [new_key],
|
|
}
|
|
)
|
|
return items
|
|
|
|
|
|
def set_nested_config(config: Dict[str, Any], key: str, value: str) -> bool:
|
|
keys = key.split(".")
|
|
d = config
|
|
|
|
# Navigate to the parent dict
|
|
for k in keys[:-1]:
|
|
if k not in d or not isinstance(d[k], dict):
|
|
d[k] = {}
|
|
d = d[k]
|
|
|
|
last_key = keys[-1]
|
|
|
|
# Try to preserve type if key exists
|
|
if last_key in d:
|
|
current_val = d[last_key]
|
|
if isinstance(current_val, bool):
|
|
if value.lower() in ("true", "yes", "1", "on"):
|
|
d[last_key] = True
|
|
elif value.lower() in ("false", "no", "0", "off"):
|
|
d[last_key] = False
|
|
else:
|
|
# Fallback to boolean conversion of string (usually True for non-empty)
|
|
# But for config, explicit is better.
|
|
print(f"Warning: Could not convert '{value}' to boolean. Using string.")
|
|
d[last_key] = value
|
|
elif isinstance(current_val, int):
|
|
try:
|
|
d[last_key] = int(value)
|
|
except ValueError:
|
|
print(f"Warning: Could not convert '{value}' to int. Using string.")
|
|
d[last_key] = value
|
|
elif isinstance(current_val, float):
|
|
try:
|
|
d[last_key] = float(value)
|
|
except ValueError:
|
|
print(f"Warning: Could not convert '{value}' to float. Using string.")
|
|
d[last_key] = value
|
|
else:
|
|
d[last_key] = value
|
|
else:
|
|
# New key, try to infer type
|
|
if value.lower() in ("true", "false"):
|
|
d[last_key] = value.lower() == "true"
|
|
elif value.isdigit():
|
|
d[last_key] = int(value)
|
|
else:
|
|
d[last_key] = value
|
|
|
|
return True
|
|
|
|
|
|
def _run(piped_result: Any, args: List[str], config: Dict[str, Any]) -> int:
|
|
# Reload config to ensure we have the latest on disk
|
|
# We don't use the passed 'config' because we want to edit the file
|
|
# and 'config' might contain runtime objects (like worker manager)
|
|
# But load_config() returns a fresh dict from disk (or cache)
|
|
# We should use load_config()
|
|
|
|
current_config = load_config()
|
|
|
|
# Parse args
|
|
# We handle args manually because of the potential for spaces in values
|
|
# and the @ expansion logic in CLI.py passing args
|
|
|
|
if not args:
|
|
# List mode
|
|
items = flatten_config(current_config)
|
|
# Sort by key
|
|
items.sort(key=lambda x: x["Key"])
|
|
|
|
# Emit items for ResultTable
|
|
import pipeline as ctx
|
|
|
|
for item in items:
|
|
ctx.emit(item)
|
|
return 0
|
|
|
|
# Update mode
|
|
key = args[0]
|
|
|
|
if len(args) < 2:
|
|
print(f"Error: Value required for key '{key}'")
|
|
return 1
|
|
|
|
value = " ".join(args[1:])
|
|
|
|
# Remove quotes if present
|
|
if (value.startswith('"') and value.endswith('"')) or (value.startswith("'")
|
|
and value.endswith("'")):
|
|
value = value[1:-1]
|
|
|
|
try:
|
|
set_nested_config(current_config, key, value)
|
|
save_config(current_config)
|
|
print(f"Updated '{key}' to '{value}'")
|
|
return 0
|
|
except Exception as e:
|
|
print(f"Error updating config: {e}")
|
|
return 1
|
|
|
|
|
|
CMDLET.exec = _run
|