Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 66 additions & 37 deletions deadend_cli/src/deadend_cli/init.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@
import os
import time
from pathlib import Path

import docker
import toml
import typer
import docker
from docker.errors import DockerException, NotFound
from rich.console import Console

Expand All @@ -22,10 +23,10 @@

def check_docker(client: docker.DockerClient) -> bool:
"""Check if Docker daemon is running using the Docker Python API.

Args:
client: Docker client instance

Returns:
bool: True if Docker daemon is available and running, False otherwise
"""
Expand All @@ -45,10 +46,10 @@ def check_docker(client: docker.DockerClient) -> bool:

def check_pgvector_container(client: docker.DockerClient) -> bool:
"""Check if pgvector container is running.

Args:
client: Docker client instance

Returns:
bool: True if pgvector container is running, False otherwise
"""
Expand All @@ -58,16 +59,18 @@ def check_pgvector_container(client: docker.DockerClient) -> bool:
except NotFound:
return False
except DockerException as e:
console.print(f"[yellow]Warning: Could not check pgvector container status: {e}[/yellow]")
console.print(
f"[yellow]Warning: Could not check pgvector container status: {e}[/yellow]"
)
return False


def setup_pgvector_database(client: docker.DockerClient) -> bool:
"""Setup pgvector database using Docker API.

Args:
client: Docker client instance

Returns:
bool: True if setup successful, False otherwise
"""
Expand All @@ -79,7 +82,9 @@ def setup_pgvector_database(client: docker.DockerClient) -> bool:
console.print("[green]pgvector database is already running.[/green]")
return True
else:
console.print("[yellow]Found existing pgvector container, starting it...[/yellow]")
console.print(
"[yellow]Found existing pgvector container, starting it...[/yellow]"
)
existing_container.start()
# Wait for container to be ready
time.sleep(5)
Expand All @@ -105,13 +110,18 @@ def setup_pgvector_database(client: docker.DockerClient) -> bool:
name="deadend_pg",
environment={
"POSTGRES_DB": "codeindexerdb",
"POSTGRES_USER": "postgres",
"POSTGRES_PASSWORD": "postgres"
"POSTGRES_USER": "postgres",
"POSTGRES_PASSWORD": "postgres",
},
ports={"5432/tcp": 54320},
volumes={str(postgres_data_dir): {"bind": "/var/lib/postgresql/data", "mode": "rw"}},
volumes={
str(postgres_data_dir): {
"bind": "/var/lib/postgresql/data",
"mode": "rw",
}
},
detach=True,
remove=False
remove=False,
)

# Wait for container to be ready
Expand All @@ -121,13 +131,19 @@ def setup_pgvector_database(client: docker.DockerClient) -> bool:
# Check if container is running
container.reload()
if container.status == "running":
console.print("[green]pgvector database setup completed successfully.[/green]")
console.print("[blue]Database connection: postgresql://postgres:postgres@localhost:54320/codeindexerdb[/blue]")
console.print(
"[green]pgvector database setup completed successfully.[/green]"
)
console.print(
"[blue]Database connection: postgresql://postgres:postgres@localhost:54320/codeindexerdb[/blue]"
)
return True
else:
console.print(f"[red]Failed to start pgvector container. Status: {container.status}[/red]")
console.print(
f"[red]Failed to start pgvector container. Status: {container.status}[/red]"
)
return False

except DockerException as e:
console.print(f"[red]Error setting up pgvector database: {e}[/red]")
return False
Expand All @@ -138,10 +154,10 @@ def setup_pgvector_database(client: docker.DockerClient) -> bool:

def pull_sandboxed_kali_image(client: docker.DockerClient) -> bool:
"""Pull the sandboxed Kali image.

Args:
client: Docker client instance

Returns:
bool: True if pull successful, False otherwise
"""
Expand All @@ -160,10 +176,10 @@ def pull_sandboxed_kali_image(client: docker.DockerClient) -> bool:

def stop_pgvector_container(client: docker.DockerClient) -> bool:
"""Stop the pgvector container.

Args:
client: Docker client instance

Returns:
bool: True if stopped successfully, False otherwise
"""
Expand Down Expand Up @@ -192,7 +208,7 @@ def init_cli_config():
"""Initialize CLI config by prompting for env vars and saving to cache TOML.

Writes to ~/.cache/deadend/config.toml

Returns:
Path: The path to the created configuration file
"""
Expand All @@ -204,13 +220,15 @@ def init_cli_config():
console.print("Please install Docker from: https://docs.docker.com/get-docker/")
console.print("Make sure Docker daemon is running.")
raise typer.Exit(1)

# Check Docker availability first - exit if not available
if not check_docker(docker_client):
console.print("\n[red]Docker is required for this application to function properly.[/red]")
console.print(
"\n[red]Docker is required for this application to function properly.[/red]"
)
console.print("Please install and start Docker, then run this command again.")
raise typer.Exit(1)

# Check and setup pgvector database
if not check_pgvector_container(docker_client):
console.print("\n[blue]pgvector database not found. Setting up...[/blue]")
Expand All @@ -220,13 +238,15 @@ def init_cli_config():
raise typer.Exit(1)
else:
console.print("[green]pgvector database is already running.[/green]")

# Pull sandboxed Kali image
console.print("\n[blue]Setting up sandboxed Kali image...[/blue]")
if not pull_sandboxed_kali_image(docker_client):
console.print("\n[yellow]Warning: Failed to pull sandboxed Kali image.[/yellow]")
console.print(
"\n[yellow]Warning: Failed to pull sandboxed Kali image.[/yellow]"
)
console.print("Some features may not work properly. You can try again later.")

cache_dir = Path.home() / ".cache" / "deadend"
cache_dir.mkdir(parents=True, exist_ok=True)
config_file = cache_dir / "config.toml"
Expand All @@ -236,24 +256,31 @@ def init_cli_config():
try:
with config_file.open("r") as f:
existing_config = toml.load(f)

# Check if config has essential keys and values
essential_keys = ["OPENAI_API_KEY", "ANTHROPIC_API_KEY", "GEMINI_API_KEY"]
has_essential_config = any(
existing_config.get(key, "").strip()
for key in essential_keys
existing_config.get(key, "").strip() for key in essential_keys
)

if has_essential_config:
console.print("[green]Configuration file already exists and is populated.[/green]")
console.print(
"[green]Configuration file already exists and is populated.[/green]"
)
console.print(f"Config file: {config_file}")
console.print("If you need to update the configuration, delete the file and run init again.")
console.print(
"If you need to update the configuration, delete the file and run init again."
)
return config_file
else:
console.print("[yellow]Configuration file exists but appears to be empty or incomplete.[/yellow]")
console.print(
"[yellow]Configuration file exists but appears to be empty or incomplete.[/yellow]"
)
console.print("Proceeding with configuration setup...")
except (toml.TomlDecodeError, OSError) as e:
console.print(f"[yellow]Warning: Could not read existing config file: {e}[/yellow]")
console.print(
f"[yellow]Warning: Could not read existing config file: {e}[/yellow]"
)
console.print("Proceeding with configuration setup...")

# Read current environment as defaults
Expand All @@ -265,7 +292,9 @@ def init_cli_config():
"GEMINI_API_KEY": os.getenv("GEMINI_API_KEY", ""),
"GEMINI_MODEL": os.getenv("GEMINI_MODEL", "gemini-2.5-pro"),
"EMBEDDING_MODEL": os.getenv("EMBEDDING_MODEL", ""),
"DB_URL": os.getenv("DB_URL", ""),
"DB_URL": os.getenv(
"DB_URL", "postgresql://postgres:postgres@localhost:54320/codeindexerdb"
),
"ZAP_PROXY_API_KEY": os.getenv("ZAP_PROXY_API_KEY", ""),
"APP_ENV": os.getenv("APP_ENV", "development"),
"LOG_LEVEL": os.getenv("LOG_LEVEL", "INFO"),
Expand Down