Files
mytoolkit/mycli/commands/env.py
T
Zhengshou Lai bd167cfb6d feat: add API keys management and config CRUD operations
- Add api_keys and feishu_apps to config.json
- Add env export/init commands for shell integration
- Add config CRUD: list, set, delete, init
- Add config.json.example template
- Add config.json to .gitignore for security
- Update README with new commands

Usage:
  mycli env export              # Export all env vars
  mycli config set key value    # Set config value
  mycli config delete key       # Delete config key
2026-04-13 11:41:57 +08:00

183 lines
5.3 KiB
Python

"""Environment paths and quick navigation."""
import functools
import os
import subprocess
from pathlib import Path
import click
from mycli.config import config
from mycli.utils import handle_errors
@functools.lru_cache(maxsize=1)
def _get_paths() -> dict[str, Path]:
"""Lazy load paths from config."""
return config.get_all_paths()
@click.group(name="env")
def env_cmd():
"""Environment paths and navigation."""
pass
@env_cmd.command("goto")
@click.argument("name")
@handle_errors
def goto(name):
"""Print path for cd (use: cd $(mycli env goto <name>))."""
paths = _get_paths()
if name not in paths:
click.echo(f"Unknown path: {name}", err=True)
click.echo(f"Available: {', '.join(paths.keys())}", err=True)
raise click.Exit(1)
click.echo(paths[name])
@env_cmd.command("list")
@handle_errors
def list_paths():
"""List all configured paths."""
paths = _get_paths()
max_len = max(len(k) for k in paths.keys())
for name, path in sorted(paths.items()):
exists = "✓" if path.exists() else "✗"
click.echo(f"{name:{max_len}} {exists} {path}")
@env_cmd.command("cd")
@click.argument("name")
@handle_errors
def cd_path(name):
"""Change directory (launches new shell)."""
paths = _get_paths()
if name not in paths:
click.echo(f"Unknown path: {name}", err=True)
raise click.Exit(1)
target = paths[name]
if not target.exists():
click.echo(f"Path does not exist: {target}", err=True)
raise click.Exit(1)
shell = os.environ.get("SHELL", "/bin/zsh")
click.echo(f"Starting shell in: {target}")
subprocess.run([shell], cwd=target)
@env_cmd.command("status")
@handle_errors
def status():
"""Show environment status."""
paths = _get_paths()
click.secho("=== Paths ===", fg="cyan")
for name, path in sorted(paths.items()):
exists = "✓" if path.exists() else "✗"
click.echo(f" {name:12} [{exists}] {path}")
click.secho("\n=== OpenFOAM ===", fg="cyan")
openfoam_path = "/Volumes/OpenFOAM/openfoam/build"
if Path("/Volumes/OpenFOAM").exists():
click.secho(f" Mounted: {openfoam_path}", fg="green")
else:
click.secho(" Not mounted", fg="red")
# API Keys status (without showing actual values)
click.secho("\n=== API Keys ===", fg="cyan")
api_keys = config.get("api_keys", {})
for name in api_keys.keys():
env_var = f"{name.upper()}_API_KEY"
is_set = "✓" if os.environ.get(env_var) else "✗"
click.echo(f" {name:12} [{is_set}] {env_var}")
# Feishu Apps status
click.secho("\n=== Feishu Apps ===", fg="cyan")
feishu_apps = config.get("feishu_apps", {})
for app_name in feishu_apps.keys():
app_id_var = f"{app_name.upper()}_APP_ID"
is_set = "✓" if os.environ.get(app_id_var) else "✗"
click.echo(f" {app_name:12} [{is_set}] {app_id_var}")
@env_cmd.command("export")
@click.argument("name", required=False)
def export_env(name):
"""Print export statements for environment variables.
Examples:
mycli env export # Export all
mycli env export deepseek # Export specific API key
mycli env export feishu # Export all Feishu apps
eval "$(mycli env export)" # Load in current shell
"""
if name is None:
# Export all
_export_api_keys()
_export_feishu_apps()
elif name == "feishu":
_export_feishu_apps()
elif name in config.get("api_keys", {}):
_export_api_key(name)
elif name in config.get("feishu_apps", {}):
_export_feishu_app(name)
else:
click.echo(f"Unknown export target: {name}", err=True)
click.echo(f"Available: api_keys ({', '.join(config.get('api_keys', {}).keys())}), "
f"feishu_apps ({', '.join(config.get('feishu_apps', {}).keys())})", err=True)
raise click.Exit(1)
def _export_api_key(name: str):
"""Export a single API key."""
api_keys = config.get("api_keys", {})
if name not in api_keys:
return
env_var = f"{name.upper()}_API_KEY"
value = api_keys[name]
click.echo(f"export {env_var}={value}")
def _export_api_keys():
"""Export all API keys."""
api_keys = config.get("api_keys", {})
for name in api_keys.keys():
_export_api_key(name)
def _export_feishu_app(name: str):
"""Export a single Feishu app."""
feishu_apps = config.get("feishu_apps", {})
if name not in feishu_apps:
return
app = feishu_apps[name]
prefix = name.upper()
click.echo(f"export {prefix}_APP_ID={app['app_id']}")
click.echo(f"export {prefix}_APP_SECRET={app['app_secret']}")
def _export_feishu_apps():
"""Export all Feishu apps."""
feishu_apps = config.get("feishu_apps", {})
for name in feishu_apps.keys():
_export_feishu_app(name)
@env_cmd.command("init")
@click.option("--shell", "-s", "shell_type", type=click.Choice(["bash", "zsh", "fish"]), default="zsh")
def init_shell(shell_type):
"""Print shell initialization snippet.
Add this to your ~/.zshrc or ~/.bashrc:
eval "$(mycli env init)"
"""
snippet = f"""# MyCLI environment initialization
# Add this line to your ~/.{shell_type}rc:
# eval "$(mycli env init)"
# Export API keys and app credentials
eval "$(mycli env export 2>/dev/null)"
"""
click.echo(snippet)