Metadata-Version: 2.4
Name: lexe-wrapper
Version: 1.0.0
Summary: Simple Python wrapper for Lexe Bitcoin Lightning Network wallet integration
Home-page: https://github.com/lexe-app/lexe-wrapper
Author: Lexe Wrapper Team
Author-email: Lexe Wrapper Team <support@example.com>
License: MIT
Project-URL: Homepage, https://github.com/lexe-app/lexe-wrapper
Project-URL: Bug Reports, https://github.com/lexe-app/lexe-wrapper/issues
Project-URL: Source, https://github.com/lexe-app/lexe-wrapper
Keywords: bitcoin,lightning,network,lexe,wallet,cryptocurrency
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Office/Business :: Financial
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.7
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.31.0
Provides-Extra: dev
Requires-Dist: pytest>=6.0; extra == "dev"
Requires-Dist: black; extra == "dev"
Requires-Dist: flake8; extra == "dev"
Dynamic: author
Dynamic: home-page
Dynamic: license-file
Dynamic: requires-python

# Lexe Wrapper

A simple Python package for integrating Bitcoin Lightning Network payments using the Lexe wallet. Install with pip and start building Lightning apps in minutes, not hours.

```bash
pip install lexe-wrapper
```

## What This Package Solves

The Lexe Sidecar API is already clean and simple, but there are several setup gotchas that slow down development:

1. **Binary Management**: Downloading and extracting the correct Lexe sidecar binary
2. **Process Management**: Starting and stopping the sidecar subprocess  
3. **Credentials Handling**: Properly encoding and validating base64 client credentials
4. **Connection Management**: Ensuring the sidecar is healthy and ready to accept requests
5. **Port Configuration**: Managing the correct port (5393) for communication

This package handles all of these automatically, so you can focus on building great Lightning applications.

## Quick Start

### 1. Install the Package

```bash
git clone https://github.com/lexe-app/lexe-wrapper.git
cd lexe-wrapper
pip install -e .
```

### 2. Get Your Lexe Credentials

You need a Lexe wallet and client credentials. Follow the [Lexe SDK documentation](https://github.com/lexe-app/lexe-sidecar-sdk) to:
1. Download the Lexe mobile app  
2. Create a wallet
3. Export client credentials (base64 encoded string)

### 3. Set Environment Variable

```bash
export LEXE_CLIENT_CREDENTIALS="your_base64_encoded_credentials_here"
```

### 4. Start Building

#### Simple Usage (Context Manager)

```python
from lexe_wrapper import LexeManager
import requests

# Automatic setup and cleanup
with LexeManager() as lexe:
    lexe.start_sidecar()  # Downloads binary, starts process, health checks
    
    # Now use the standard Lexe Sidecar API directly at localhost:5393
    response = requests.post("http://localhost:5393/v1/node/create_invoice", json={
        "amount": "1000", 
        "description": "Test payment"
    })
    invoice = response.json()
    print(f"Created invoice: {invoice['invoice']}")
    
    # Sidecar automatically stops when exiting the context
```

#### Web App Integration

```python
from lexe_wrapper import LexeManager

# Initialize once when your web app starts
lexe = LexeManager()
lexe.start_for_webapp()  # Robust startup with error handling

# Use throughout your app's lifetime
@app.route('/create-invoice', methods=['POST'])
def create_invoice():
    response = requests.post("http://localhost:5393/v1/node/create_invoice", 
                           json=request.json)
    return response.json()

# Stop when app shuts down
lexe.stop_sidecar()
```

#### Manual Management

```python
from lexe_wrapper import LexeManager

lexe = LexeManager()

try:
    lexe.start_sidecar()
    
    if lexe.check_health():
        print("Lexe is ready!")
        
        # Get node info using the wrapper method
        node_info = lexe.get_node_info()
        print(f"Balance: {node_info['balance']} sats")
        
finally:
    lexe.stop_sidecar()
```

## For Coding Agents

When using this package in automated coding environments:

### Essential Setup Steps
1. **Install package**: Clone repo and `pip install -e .`
2. **Set credentials**: `export LEXE_CLIENT_CREDENTIALS="your_credentials"`
3. **Import and start**: Use the context manager pattern for automatic cleanup

### Example Implementation
```python
from lexe_wrapper import LexeManager
import requests

def setup_lexe_integration():
    """Initialize Lexe for your application"""
    lexe = LexeManager()
    lexe.start_sidecar()  # Downloads binary, starts process, waits for health
    return "http://localhost:5393"  # Ready to use Lexe API

def create_lightning_invoice(amount_sats, description):
    """Create a Lightning invoice using Lexe API"""
    response = requests.post("http://localhost:5393/v1/node/create_invoice", json={
        "amount": str(amount_sats),
        "description": description
    })
    return response.json()

# The package handles all the complexity - just use the API directly!
```

### Key Benefits for Automation
- **Standard pip installation** - `pip install lexe-wrapper`
- **Clean imports** - `from lexe_wrapper import LexeManager`
- **Zero configuration files needed** - everything is handled programmatically
- **Automatic binary management** - downloads and extracts the right version
- **Built-in health checks** - ensures the connection is ready before returning
- **Error handling** - clear error messages when credentials are invalid or missing
- **Process lifecycle management** - clean startup and shutdown

## Installation Methods

### Install from Source

Since this package is not yet published to PyPI, install directly from source:

```bash
git clone https://github.com/lexe-app/lexe-wrapper.git
cd lexe-wrapper
pip install -e .
```

*Note: PyPI publishing coming soon for simple `pip install lexe-wrapper` experience.*

## API Reference

### LexeManager Class

#### Constructor
```python
from lexe_wrapper import LexeManager

LexeManager(client_credentials=None, port=5393)
```
- `client_credentials`: Base64 encoded credentials (uses `LEXE_CLIENT_CREDENTIALS` env var if None)
- `port`: Port for sidecar to listen on (default: 5393)

#### Methods

**`start_sidecar(wait_for_health=True, health_timeout=30)`**
- Downloads binary if needed, starts process, optionally waits for health check
- Returns: `bool` - True if started successfully

**`start_for_webapp(health_timeout=30)`**
- Web app specific startup with robust error handling
- Returns: `bool` - True if started successfully
- Raises: `RuntimeError` if startup fails

**`stop_sidecar()`**
- Gracefully stops the sidecar process
- Returns: `bool` - True if stopped successfully

**`check_health()`**
- Checks if sidecar is responding to health checks
- Returns: `bool` - True if healthy

**`ensure_running()`**
- Ensures sidecar is running and healthy (great for health check endpoints)
- Returns: `bool` - True if running and healthy

**`restart_if_needed()`**
- Restarts sidecar if not running or unhealthy
- Returns: `bool` - True if now running and healthy

**`get_node_info()`**
- Gets node information from Lexe API
- Returns: `dict` - Node information including balance, channels, etc.

**`is_running()`**
- Checks if the sidecar process is currently running
- Returns: `bool` - True if running

## CLI Reference

The package includes a command-line interface for testing and development:

```bash
# Using the installed package
python -m lexe_wrapper <command> [options]

# Or directly (if you cloned the repo)
python cli.py <command> [options]

Commands:
  start       Start the sidecar
  stop        Stop the sidecar  
  status      Show sidecar status
  health      Check sidecar health
  node-info   Get node information
  download    Download sidecar binary only

Options:
  --credentials TEXT    Override credentials from env var
  --port INTEGER       Port for sidecar (default: 5393)
  --timeout INTEGER    Health check timeout (default: 30)
  --no-wait           Don't wait for health check when starting
  --verbose           Enable verbose logging
```

## Web App Integration (Long-Lived Connections)

The wrapper is specifically designed to support long-lived connections for web applications. Here's how to integrate it:

### Flask Example

```python
from flask import Flask, jsonify, request
from lexe_manager import LexeManager
import requests
import atexit

app = Flask(__name__)

# Global Lexe manager instance
lexe_manager = None

def init_lexe():
    """Initialize Lexe sidecar when app starts"""
    global lexe_manager
    lexe_manager = LexeManager()
    
    try:
        # Use the web app specific startup method
        lexe_manager.start_for_webapp(health_timeout=30)
        print("✅ Lexe sidecar started successfully")
        return True
    except RuntimeError as e:
        print(f"❌ Failed to start Lexe: {e}")
        return False

def cleanup_lexe():
    """Clean shutdown when app stops"""
    global lexe_manager
    if lexe_manager:
        lexe_manager.stop_sidecar()
        print("🛑 Lexe sidecar stopped")

# Initialize Lexe when app starts
with app.app_context():
    if not init_lexe():
        exit(1)

# Register cleanup function
atexit.register(cleanup_lexe)

@app.route('/health')
def health_check():
    """App health check - includes Lexe status"""
    if lexe_manager and lexe_manager.ensure_running():
        return jsonify({"status": "healthy", "lexe": "connected"})
    else:
        return jsonify({"status": "degraded", "lexe": "disconnected"}), 503

@app.route('/node-info')
def node_info():
    """Get Lexe node information"""
    try:
        # Use direct API call (the whole point of this wrapper!)
        response = requests.get("http://localhost:5393/v1/node/node_info", timeout=10)
        response.raise_for_status()
        return jsonify(response.json())
    except Exception as e:
        return jsonify({"error": str(e)}), 500

@app.route('/create-invoice', methods=['POST'])
def create_invoice():
    """Create a Lightning invoice"""
    try:
        amount = request.json.get('amount', '1000')
        description = request.json.get('description', 'Payment')
        
        # Direct Lexe API usage
        response = requests.post("http://localhost:5393/v1/node/create_invoice", 
                               json={
                                   "amount": str(amount),
                                   "description": description
                               }, timeout=15)
        response.raise_for_status()
        return jsonify(response.json())
    except Exception as e:
        return jsonify({"error": str(e)}), 500

@app.route('/restart-lexe', methods=['POST'])
def restart_lexe():
    """Restart Lexe if having issues"""
    if lexe_manager and lexe_manager.restart_if_needed():
        return jsonify({"status": "restarted"})
    else:
        return jsonify({"error": "restart failed"}), 500

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)
```

### FastAPI Example

```python
from fastapi import FastAPI, HTTPException
from contextlib import asynccontextmanager
from lexe_manager import LexeManager
import requests

# Global Lexe manager
lexe_manager = None

@asynccontextmanager
async def lifespan(app: FastAPI):
    # Startup
    global lexe_manager
    lexe_manager = LexeManager()
    
    try:
        lexe_manager.start_for_webapp()
        print("✅ Lexe sidecar started")
        yield
    except RuntimeError as e:
        print(f"❌ Lexe startup failed: {e}")
        raise
    finally:
        # Shutdown
        if lexe_manager:
            lexe_manager.stop_sidecar()
            print("🛑 Lexe sidecar stopped")

app = FastAPI(lifespan=lifespan)

@app.get("/health")
def health_check():
    if lexe_manager and lexe_manager.ensure_running():
        return {"status": "healthy", "lexe": "connected"}
    raise HTTPException(503, "Lexe not available")

@app.post("/invoices")
def create_invoice(amount: int, description: str = "Payment"):
    try:
        response = requests.post("http://localhost:5393/v1/node/create_invoice",
                               json={"amount": str(amount), "description": description})
        response.raise_for_status()
        return response.json()
    except Exception as e:
        raise HTTPException(500, str(e))
```

### Web App Best Practices

1. **Startup Pattern**: Use `start_for_webapp()` during app initialization
2. **Health Monitoring**: Use `ensure_running()` in your health check endpoints
3. **Recovery**: Use `restart_if_needed()` for automatic recovery
4. **Shutdown**: Ensure `stop_sidecar()` is called when your app shuts down
5. **Direct API Usage**: Once started, use standard HTTP requests to `localhost:5393`

### Process Management for Production

```python
import signal
import sys
from lexe_manager import LexeManager

# Global manager for signal handlers
lexe_manager = None

def signal_handler(signum, frame):
    """Handle shutdown signals gracefully"""
    global lexe_manager
    print("🔄 Received shutdown signal, stopping Lexe...")
    if lexe_manager:
        lexe_manager.stop_sidecar()
    sys.exit(0)

# Register signal handlers for graceful shutdown
signal.signal(signal.SIGINT, signal_handler)   # Ctrl+C
signal.signal(signal.SIGTERM, signal_handler)  # Docker/systemd stop

def main():
    global lexe_manager
    lexe_manager = LexeManager()
    
    # Start for long-lived operation
    lexe_manager.start_for_webapp()
    
    # Your web app code here
    # The sidecar stays running until explicitly stopped
```

## After Starting the Sidecar

Once the wrapper starts the sidecar, you can use the [standard Lexe Sidecar API](https://github.com/lexe-app/lexe-sidecar-sdk#rest-api-reference) directly:

- **Health**: `GET http://localhost:5393/v1/health`
- **Node Info**: `GET http://localhost:5393/v1/node/node_info`
- **Create Invoice**: `POST http://localhost:5393/v1/node/create_invoice`
- **Pay Invoice**: `POST http://localhost:5393/v1/node/pay_invoice`
- **Check Payment**: `GET http://localhost:5393/v1/node/payment?index=<index>`

## Error Handling

The wrapper provides clear error messages for common issues:

- **Missing credentials**: Clear message about setting `LEXE_CLIENT_CREDENTIALS`
- **Invalid credentials**: Validates base64 format and provides specific error
- **Download failures**: Network issues when downloading the binary
- **Health check failures**: When sidecar doesn't respond within timeout
- **Process management**: Issues starting or stopping the sidecar process

## Package Structure

```
lexe-wrapper/
├── lexe_wrapper/           # Main package
│   ├── __init__.py        # Package exports LexeManager
│   ├── manager.py         # Core LexeManager class
│   └── __main__.py        # CLI entry point
├── examples/              # Usage examples
│   └── simple_usage.py    # Simple integration examples
├── setup.py              # Package installation
├── pyproject.toml        # Modern Python packaging
└── README.md             # This documentation
```

## Requirements

- Python 3.7+
- `requests` library (automatically installed as dependency)
- x86_64 Linux environment (where Lexe sidecar runs)
- Valid Lexe client credentials

## Contributing

Contributions are welcome! This package follows standard Python packaging conventions:

1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Add tests if applicable
5. Submit a pull request

## License

MIT License - see LICENSE file for details.

## About

This package is designed to eliminate the friction in getting started with Lexe Bitcoin Lightning Network integration. The Lexe Sidecar API itself is excellent - this package just handles the setup complexity so you can focus on building great Lightning applications.

**Key Design Principles:**
- **Standard Python packaging** - Install with pip, import normally
- **Minimal dependencies** - Only requires `requests`
- **Clean API surface** - Simple import, clear methods
- **Production ready** - Proper error handling, logging, and lifecycle management
- **Direct API access** - No unnecessary abstraction over the Lexe Sidecar API
