186 lines
6.0 KiB
Python
186 lines
6.0 KiB
Python
"""Configuration commands."""
|
|
|
|
import socket
|
|
import sys
|
|
import time
|
|
|
|
from ..config import load_config, save_config, set_registry_token, get_registry_url
|
|
|
|
|
|
def cmd_config(args):
|
|
"""Manage CmdForge configuration."""
|
|
if args.config_cmd == "show":
|
|
return _cmd_config_show(args)
|
|
elif args.config_cmd == "set-token":
|
|
return _cmd_config_set_token(args)
|
|
elif args.config_cmd == "set":
|
|
return _cmd_config_set(args)
|
|
elif args.config_cmd == "connect":
|
|
return _cmd_config_connect(args)
|
|
elif args.config_cmd == "disconnect":
|
|
return _cmd_config_disconnect(args)
|
|
else:
|
|
print("Config commands:")
|
|
print(" show Show current configuration")
|
|
print(" connect <username> Connect this app to your CmdForge account")
|
|
print(" disconnect Disconnect from registry (clear token)")
|
|
print(" set-token <token> Set registry authentication token")
|
|
print(" set <key> <value> Set a configuration value")
|
|
return 0
|
|
|
|
|
|
def _cmd_config_show(args):
|
|
"""Show current configuration."""
|
|
config = load_config()
|
|
print("CmdForge Configuration:")
|
|
print(f" Registry URL: {config.registry.url}")
|
|
print(f" Token: {'***' if config.registry.token else '(not set)'}")
|
|
print(f" Client ID: {config.client_id}")
|
|
print(f" Auto-fetch: {config.auto_fetch_from_registry}")
|
|
if config.default_provider:
|
|
print(f" Default provider: {config.default_provider}")
|
|
return 0
|
|
|
|
|
|
def _cmd_config_set_token(args):
|
|
"""Set registry authentication token."""
|
|
token = args.token
|
|
set_registry_token(token)
|
|
print("Registry token saved.")
|
|
return 0
|
|
|
|
|
|
def _cmd_config_set(args):
|
|
"""Set a configuration value."""
|
|
config = load_config()
|
|
key = args.key
|
|
value = args.value
|
|
|
|
if key == "auto_fetch":
|
|
config.auto_fetch_from_registry = value.lower() in ("true", "1", "yes")
|
|
elif key == "default_provider":
|
|
config.default_provider = value if value else None
|
|
elif key == "registry_url":
|
|
config.registry.url = value
|
|
else:
|
|
print(f"Unknown config key: {key}")
|
|
print("Available keys: auto_fetch, default_provider, registry_url")
|
|
return 1
|
|
|
|
save_config(config)
|
|
print(f"Set {key} = {value}")
|
|
return 0
|
|
|
|
|
|
def _cmd_config_connect(args):
|
|
"""Connect this app to a CmdForge account via web pairing."""
|
|
try:
|
|
import requests
|
|
except ImportError:
|
|
print("Error: requests library required. Install with: pip install requests")
|
|
return 1
|
|
|
|
username = args.username
|
|
hostname = socket.gethostname()
|
|
registry_url = get_registry_url()
|
|
|
|
# Remove trailing /api/v1 if present to get base URL
|
|
base_url = registry_url.rstrip("/")
|
|
if base_url.endswith("/api/v1"):
|
|
base_url = base_url[:-7]
|
|
|
|
pairing_url = f"{base_url}/api/v1/pairing/check/{username}"
|
|
|
|
print(f"Connecting to CmdForge as @{username}...")
|
|
print(f"Device: {hostname}")
|
|
print()
|
|
print("Waiting for approval from the web interface...")
|
|
print("Go to https://cmdforge.brrd.tech/dashboard/connected-apps")
|
|
print("and click 'Connect New App', then 'I've Run the Command'")
|
|
print()
|
|
print("Press Ctrl+C to cancel")
|
|
print()
|
|
|
|
# Poll for pairing status
|
|
max_attempts = 150 # 5 minutes at 2-second intervals
|
|
attempt = 0
|
|
|
|
try:
|
|
while attempt < max_attempts:
|
|
attempt += 1
|
|
try:
|
|
response = requests.get(
|
|
pairing_url,
|
|
params={"hostname": hostname},
|
|
timeout=10
|
|
)
|
|
|
|
if response.status_code == 200:
|
|
data = response.json()
|
|
status = data.get("data", {}).get("status")
|
|
|
|
if status == "connected":
|
|
token = data.get("data", {}).get("token")
|
|
if token:
|
|
set_registry_token(token)
|
|
print("\nConnected successfully!")
|
|
print(f"Your device '{hostname}' is now linked to @{username}")
|
|
print("\nYou can now publish tools with: cmdforge registry publish")
|
|
return 0
|
|
else:
|
|
print("\nError: Pairing completed but no token received")
|
|
return 1
|
|
elif status == "pending":
|
|
# Still waiting, continue polling
|
|
pass
|
|
elif status == "expired":
|
|
print("\nPairing request expired. Please try again.")
|
|
return 1
|
|
elif status == "not_found":
|
|
# No pending pairing yet, continue waiting
|
|
pass
|
|
else:
|
|
# Unknown status, keep waiting
|
|
pass
|
|
|
|
elif response.status_code == 404:
|
|
# No pairing found yet, keep waiting
|
|
pass
|
|
else:
|
|
# Server error, but keep trying
|
|
pass
|
|
|
|
except requests.exceptions.RequestException:
|
|
# Network error, keep trying
|
|
pass
|
|
|
|
# Show progress indicator
|
|
dots = "." * ((attempt % 3) + 1)
|
|
sys.stdout.write(f"\rWaiting{dots} ")
|
|
sys.stdout.flush()
|
|
|
|
time.sleep(2)
|
|
|
|
print("\n\nTimed out waiting for approval.")
|
|
print("Please initiate the connection from the web interface first.")
|
|
return 1
|
|
|
|
except KeyboardInterrupt:
|
|
print("\n\nConnection cancelled.")
|
|
return 1
|
|
|
|
|
|
def _cmd_config_disconnect(args):
|
|
"""Disconnect from registry by clearing the token."""
|
|
config = load_config()
|
|
|
|
if not config.registry.token:
|
|
print("Not connected to registry (no token set).")
|
|
return 0
|
|
|
|
# Clear the token
|
|
set_registry_token(None)
|
|
print("Disconnected from registry.")
|
|
print("Token has been cleared from local configuration.")
|
|
return 0
|