| """ |
| Atom CLI - Init command for Personal Edition setup. |
| |
| Creates initial configuration for Personal Edition: |
| - .env file with Personal defaults (SQLite) |
| - data/ directory for local storage |
| - Database initialization |
| - First run wizard |
| """ |
|
|
| import os |
| import sys |
| import click |
| import logging |
| from pathlib import Path |
| from datetime import datetime |
|
|
| |
| sys.path.insert(0, str(Path(__file__).parent.parent)) |
|
|
| logger = logging.getLogger(__name__) |
|
|
|
|
| @click.command() |
| @click.option('--force', is_flag=True, help='Overwrite existing configuration') |
| @click.option('--edition', type=click.Choice(['personal', 'enterprise']), default='personal', |
| help='Edition to initialize (default: personal)') |
| @click.option('--database-url', help='Custom database URL (default: SQLite for personal)') |
| @click.option('--no-input', is_flag=True, help='Run non-interactively') |
| def init(force: bool, edition: str, database_url: str, no_input: bool): |
| """ |
| Initialize Atom Personal Edition configuration. |
| |
| Creates .env file with Personal Edition defaults: |
| - SQLite database (./data/atom.db) |
| - Local filesystem storage |
| - Minimal feature set |
| |
| **Examples:** |
| atom init # Interactive setup |
| atom init --edition personal # Personal Edition |
| atom init --edition enterprise # Enterprise Edition |
| atom init --force # Overwrite existing |
| atom init --no-input # Use all defaults |
| |
| **Enterprise Edition:** |
| atom init --edition enterprise \\ |
| --database-url "postgresql://user:pass@localhost/atom" |
| """ |
| click.echo(click.style("Atom Initialization", bold=True)) |
| click.echo("=" * 50) |
|
|
| |
| env_file = Path(".env") |
| if env_file.exists() and not force: |
| click.echo(click.style("Configuration already exists!", fg="yellow")) |
| click.echo("Use --force to overwrite") |
|
|
| |
| from core.package_feature_service import get_package_feature_service |
| service = get_package_feature_service() |
|
|
| click.echo("") |
| click.echo(f"Current Edition: {service.edition.value}") |
| click.echo(f"Database: {os.getenv('DATABASE_URL', 'Not configured')}") |
|
|
| raise SystemExit(1) |
|
|
| |
| if edition == "personal": |
| _init_personal_edition(database_url, no_input) |
| else: |
| _init_enterprise_edition(database_url, no_input, force) |
|
|
| |
| click.echo("") |
| click.echo(click.style("Initialization complete!", fg="green", bold=True)) |
| click.echo("") |
| click.echo("Next steps:") |
| click.echo(" 1. Edit .env and add your API keys:") |
| click.echo(" OPENAI_API_KEY=sk-...") |
| click.echo(" ANTHROPIC_API_KEY=sk-ant-...") |
| click.echo("") |
| click.echo(" 2. Start Atom:") |
| click.echo(" atom start") |
| click.echo("") |
| click.echo(f" 3. Open dashboard:") |
| click.echo(" http://localhost:8000") |
|
|
|
|
| def _init_personal_edition(database_url: str, no_input: bool) -> None: |
| """Initialize Personal Edition configuration.""" |
| click.echo("") |
| click.echo(click.style("Setting up Personal Edition...", fg="blue")) |
|
|
| |
| data_dir = Path("data") |
| data_dir.mkdir(exist_ok=True) |
| click.echo(f" Created data directory: {data_dir}") |
|
|
| |
| import secrets |
| encryption_key = secrets.token_urlsafe(32) |
| jwt_secret = secrets.token_urlsafe(32) |
|
|
| |
| env_content = f"""# Atom Personal Edition Configuration |
| # Generated: {datetime.now().isoformat()} |
| |
| # Edition |
| ATOM_EDITION=personal |
| |
| # Server |
| PORT=8000 |
| HOST=0.0.0.0 |
| WORKERS=1 |
| |
| # Database (SQLite - Personal Edition default) |
| DATABASE_URL=sqlite:///./data/atom.db |
| |
| # Encryption Keys (auto-generated) |
| BYOK_ENCRYPTION_KEY={encryption_key} |
| JWT_SECRET_KEY={jwt_secret} |
| |
| # LLM Providers (add your keys) |
| OPENAI_API_KEY= |
| ANTHROPIC_API_KEY= |
| DEEPSEEK_API_KEY= |
| |
| # Vector Embeddings (local - Personal Edition) |
| EMBEDDING_PROVIDER=fastembed |
| FASTEMBED_MODEL=BAAI/bge-small-en-v1.5 |
| LANCEDB_PATH=./data/lancedb |
| |
| # Agent Governance |
| AGENT_MATURITY_DEFAULT=STUDENT |
| MAX_CONCURRENT_AGENTS=3 |
| |
| # Logging |
| LOG_LEVEL=INFO |
| STRUCTLOG_LEVEL=INFO |
| |
| # Feature Flags (Personal Edition) |
| ATOM_MULTI_USER_ENABLED=false |
| ATOM_MONITORING_ENABLED=false |
| ATOM_SSO_ENABLED=false |
| """ |
|
|
| |
| if database_url: |
| env_content = env_content.replace( |
| "DATABASE_URL=sqlite:///./data/atom.db", |
| f"DATABASE_URL={database_url}" |
| ) |
|
|
| |
| env_file = Path(".env") |
| env_file.write_text(env_content) |
| click.echo(f" Created .env file (Personal Edition)") |
|
|
| |
| (data_dir / "lancedb").mkdir(exist_ok=True) |
| (data_dir / "uploads").mkdir(exist_ok=True) |
| click.echo(f" Created storage directories") |
|
|
| |
| db_url = database_url or "sqlite:///./data/atom.db" |
| click.echo(f" Database: {db_url}") |
|
|
|
|
| def _init_enterprise_edition(database_url: str, no_input: bool, force: bool) -> None: |
| """Initialize Enterprise Edition configuration.""" |
| click.echo("") |
| click.echo(click.style("Setting up Enterprise Edition...", fg="blue")) |
|
|
| |
| if not database_url and not no_input: |
| click.echo("") |
| click.echo("Enterprise Edition requires PostgreSQL.") |
| database_url = click.prompt( |
| "Enter PostgreSQL database URL", |
| default="postgresql://atom:atom@localhost:5432/atom" |
| ) |
|
|
| if not database_url: |
| click.echo(click.style("Error: Database URL required for Enterprise", fg="red"), err=True) |
| raise SystemExit(1) |
|
|
| |
| data_dir = Path("data") |
| data_dir.mkdir(exist_ok=True) |
| click.echo(f" Created data directory: {data_dir}") |
|
|
| |
| import secrets |
| encryption_key = secrets.token_urlsafe(32) |
| jwt_secret = secrets.token_urlsafe(32) |
|
|
| |
| env_content = f"""# Atom Enterprise Edition Configuration |
| # Generated: {datetime.now().isoformat()} |
| |
| # Edition |
| ATOM_EDITION=enterprise |
| |
| # Server |
| PORT=8000 |
| HOST=0.0.0.0 |
| WORKERS=4 |
| |
| # Database (PostgreSQL - Enterprise Edition required) |
| DATABASE_URL={database_url} |
| |
| # Encryption Keys (auto-generated) |
| BYOK_ENCRYPTION_KEY={encryption_key} |
| JWT_SECRET_KEY={jwt_secret} |
| |
| # LLM Providers |
| OPENAI_API_KEY= |
| ANTHROPIC_API_KEY= |
| DEEPSEEK_API_KEY= |
| |
| # Vector Embeddings (OpenAI for Enterprise) |
| EMBEDDING_PROVIDER=openai |
| OPENAI_EMBEDDING_MODEL=text-embedding-3-small |
| LANCEDB_PATH=./data/lancedb |
| |
| # Agent Governance |
| AGENT_MATURITY_DEFAULT=STUDENT |
| MAX_CONCURRENT_AGENTS=10 |
| |
| # Multi-User (Enterprise) |
| ATOM_MULTI_USER_ENABLED=true |
| ATOM_WORKSPACE_ISOLATION=true |
| |
| # SSO (Enterprise) |
| ATOM_SSO_ENABLED=false |
| # SSO_PROVIDER=okta|auth0|saml |
| # OKTA_DOMAIN= |
| # OKTA_CLIENT_ID= |
| # AUTH0_DOMAIN= |
| # AUTH0_CLIENT_ID= |
| |
| # Monitoring (Enterprise) |
| ATOM_MONITORING_ENABLED=true |
| PROMETHEUS_ENABLED=true |
| PROMETHEUS_PORT=9090 |
| |
| # Logging |
| LOG_LEVEL=INFO |
| STRUCTLOG_LEVEL=INFO |
| STRUCTLOG_JSON=true |
| |
| # Redis (Enterprise - for multi-user pub/sub) |
| REDIS_URL=redis://localhost:6379/0 |
| |
| # Audit Trail (Enterprise) |
| ATOM_AUDIT_TRAIL_ENABLED=true |
| AUDIT_LOG_RETENTION_DAYS=90 |
| |
| # Rate Limiting (Enterprise) |
| ATOM_RATE_LIMITING_ENABLED=true |
| RATE_LIMIT_PER_MINUTE=60 |
| """ |
|
|
| |
| env_file = Path(".env") |
| env_file.write_text(env_content) |
| click.echo(f" Created .env file (Enterprise Edition)") |
|
|
| |
| (data_dir / "lancedb").mkdir(exist_ok=True) |
| (data_dir / "uploads").mkdir(exist_ok=True) |
| (data_dir / "audit").mkdir(exist_ok=True) |
| click.echo(f" Created storage directories") |
|
|
| |
| click.echo("") |
| click.echo(click.style("PostgreSQL Setup Required:", fg="yellow")) |
| click.echo(" 1. Ensure PostgreSQL is installed and running") |
| click.echo(" 2. Create database: createdb atom") |
| click.echo(" 3. Or use connection string to existing database") |
| click.echo("") |
| click.echo("Current DATABASE_URL:") |
| click.echo(f" {database_url}") |
|
|
|
|
| def register_init_command(cli_group): |
| """Register init command with CLI group.""" |
| cli_group.add_command(init) |
|
|