Browse Source

Rename project from letta-schedules to letta-switchboard

Complete rebrand to better reflect message routing concept:

Modal Changes:
- App name: schedules → switchboard
- Secret: letta-schedules-encryption → letta-switchboard-encryption
- Volume: letta-schedules-volume → letta-switchboard-volume
- API URL: letta--schedules-api.modal.run → letta--switchboard-api.modal.run

CLI Changes:
- Binary: letta-schedules → letta-switchboard
- Module: github.com/letta/letta-schedules-cli → letta-switchboard-cli
- Config dir: ~/.letta-schedules → ~/.letta-switchboard
- All commands updated (letta-switchboard send, etc.)

Environment Variables:
- LETTA_SCHEDULES_DEV_MODE → LETTA_SWITCHBOARD_DEV_MODE
- LETTA_SCHEDULES_ENCRYPTION_KEY → LETTA_SWITCHBOARD_ENCRYPTION_KEY
- LETTA_SCHEDULES_URL → LETTA_SWITCHBOARD_URL

Documentation:
- Updated all READMEs with new name
- Updated CLI help text and descriptions
- Updated test files and scripts
- Updated setup_encryption.sh

The name 'switchboard' better captures the vision of routing messages
between users and agents, especially with future cross-server messaging.

👾 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>
Cameron Pfiffer 5 months ago
parent
commit
171ece0b34

+ 16 - 16
README.md

@@ -1,4 +1,4 @@
-# Letta Schedules
+# Letta Switchboard
 
 A serverless scheduling service for Letta agents built on Modal. Schedule recurring (cron-based) or one-time messages to be sent to your Letta agents at specified times.
 
@@ -22,7 +22,7 @@ A serverless scheduling service for Letta agents built on Modal. Schedule recurr
 
 1. Clone the repository:
 ```bash
-cd letta-schedules
+cd letta-switchboard
 ```
 
 2. Install Modal CLI:
@@ -60,7 +60,7 @@ ENCRYPTION_KEY=$(python -c "from cryptography.fernet import Fernet; print(Fernet
 echo "Your encryption key: $ENCRYPTION_KEY"
 
 # Create Modal secret
-modal secret create letta-schedules-encryption \
+modal secret create letta-switchboard-encryption \
   LETTA_SCHEDULES_ENCRYPTION_KEY="$ENCRYPTION_KEY"
 ```
 
@@ -83,7 +83,7 @@ This will:
 modal app list
 ```
 
-Look for `letta-schedules` and note the API endpoint URL.
+Look for `letta-switchboard` and note the API endpoint URL.
 
 ## Local Development
 
@@ -109,13 +109,13 @@ This starts a local development server with auto-reload on file changes.
 **Inspecting files in dev mode:**
 ```bash
 # View a schedule
-cat /tmp/letta-schedules-volume/schedules/recurring/abc123/uuid.json | jq
+cat /tmp/letta-switchboard-volume/schedules/recurring/abc123/uuid.json | jq
 
 # View an execution result
-cat /tmp/letta-schedules-volume/results/abc123/uuid.json | jq
+cat /tmp/letta-switchboard-volume/results/abc123/uuid.json | jq
 
 # List all schedules for a user
-ls -la /tmp/letta-schedules-volume/schedules/recurring/abc123/
+ls -la /tmp/letta-switchboard-volume/schedules/recurring/abc123/
 ```
 
 ## Testing
@@ -161,24 +161,24 @@ LETTA_API_KEY=sk-xxx LETTA_AGENT_ID=agent-yyy python test_api.py
 
 ## CLI Usage (Recommended)
 
-The easiest way to interact with letta-schedules is via the CLI:
+The easiest way to interact with letta-switchboard is via the CLI:
 
 ```bash
 # Send a message immediately
-letta-schedules send --agent-id agent-xxx --message "Hello!"
+letta-switchboard send --agent-id agent-xxx --message "Hello!"
 
 # Send a message later
-letta-schedules send --agent-id agent-xxx --message "Reminder" --execute-at "tomorrow at 9am"
+letta-switchboard send --agent-id agent-xxx --message "Reminder" --execute-at "tomorrow at 9am"
 
 # Create recurring schedule
-letta-schedules recurring create --agent-id agent-xxx --message "Daily standup" --cron "every weekday at 9am"
+letta-switchboard recurring create --agent-id agent-xxx --message "Daily standup" --cron "every weekday at 9am"
 
 # List schedules
-letta-schedules onetime list
-letta-schedules recurring list
+letta-switchboard onetime list
+letta-switchboard recurring list
 
 # View results
-letta-schedules results list
+letta-switchboard results list
 ```
 
 See [CLI Documentation](cli/README.md) for installation and full usage guide.
@@ -417,12 +417,12 @@ Schedules and execution results are stored in a hash-based directory structure:
 
 View logs in Modal dashboard:
 ```bash
-modal app logs letta-schedules
+modal app logs letta-switchboard
 ```
 
 Or watch logs in real-time:
 ```bash
-modal app logs letta-schedules --follow
+modal app logs letta-switchboard --follow
 ```
 
 ## Limitations

+ 6 - 6
app.py

@@ -25,26 +25,26 @@ from dateutil import parser as date_parser
 logging.basicConfig(level=logging.INFO)
 logger = logging.getLogger(__name__)
 
-app = modal.App("schedules")
+app = modal.App("switchboard")
 
 import os as local_os
 
 # Read dev mode setting from local environment
-dev_mode_enabled = local_os.getenv("LETTA_SCHEDULES_DEV_MODE", "false")
+dev_mode_enabled = local_os.getenv("LETTA_SWITCHBOARD_DEV_MODE", "false")
 
 image = (
     modal.Image.debian_slim()
     .pip_install_from_requirements("requirements.txt")
-    .env({"LETTA_SCHEDULES_DEV_MODE": dev_mode_enabled})  # Must come before add_local_*
+    .env({"LETTA_SWITCHBOARD_DEV_MODE": dev_mode_enabled})  # Must come before add_local_*
     .add_local_python_source("models", "scheduler", "letta_executor", "crypto_utils")
 )
 
-volume = modal.Volume.from_name("letta-schedules-volume", create_if_missing=True)
+volume = modal.Volume.from_name("letta-switchboard-volume", create_if_missing=True)
 
 try:
-    encryption_secret = modal.Secret.from_name("letta-schedules-encryption")
+    encryption_secret = modal.Secret.from_name("letta-switchboard-encryption")
 except Exception:
-    logger.warning("letta-schedules-encryption secret not found, will use env var or generate temporary key")
+    logger.warning("letta-switchboard-encryption secret not found, will use env var or generate temporary key")
     encryption_secret = None
 
 VOLUME_PATH = "/data"

+ 8 - 8
cli/Makefile

@@ -5,23 +5,23 @@ all: build
 
 # Build for current platform
 build:
-	go build -o letta-schedules
+	go build -o letta-switchboard
 
 # Build for all platforms
 build-all:
-	GOOS=darwin GOARCH=amd64 go build -o dist/letta-schedules-darwin-amd64
-	GOOS=darwin GOARCH=arm64 go build -o dist/letta-schedules-darwin-arm64
-	GOOS=linux GOARCH=amd64 go build -o dist/letta-schedules-linux-amd64
-	GOOS=linux GOARCH=arm64 go build -o dist/letta-schedules-linux-arm64
-	GOOS=windows GOARCH=amd64 go build -o dist/letta-schedules-windows-amd64.exe
+	GOOS=darwin GOARCH=amd64 go build -o dist/letta-switchboard-darwin-amd64
+	GOOS=darwin GOARCH=arm64 go build -o dist/letta-switchboard-darwin-arm64
+	GOOS=linux GOARCH=amd64 go build -o dist/letta-switchboard-linux-amd64
+	GOOS=linux GOARCH=arm64 go build -o dist/letta-switchboard-linux-arm64
+	GOOS=windows GOARCH=amd64 go build -o dist/letta-switchboard-windows-amd64.exe
 
 # Install to /usr/local/bin
 install: build
-	sudo mv letta-schedules /usr/local/bin/
+	sudo mv letta-switchboard /usr/local/bin/
 
 # Clean build artifacts
 clean:
-	rm -f letta-schedules
+	rm -f letta-switchboard
 	rm -rf dist/
 
 # Download dependencies

+ 38 - 38
cli/README.md

@@ -1,4 +1,4 @@
-# Letta Schedules CLI
+# Letta Switchboard CLI
 
 A command-line interface for sending messages to Letta AI agents and managing schedules.
 
@@ -20,31 +20,31 @@ A command-line interface for sending messages to Letta AI agents and managing sc
 cd cli
 
 # Build the binary
-go build -o letta-schedules
+go build -o letta-switchboard
 
 # Move to your PATH (optional)
-sudo mv letta-schedules /usr/local/bin/
+sudo mv letta-switchboard /usr/local/bin/
 ```
 
 ### Using Go Install
 
 ```bash
-go install github.com/letta/letta-schedules-cli@latest
+go install github.com/letta/letta-switchboard-cli@latest
 ```
 
 ### Cross-Platform Build
 
 ```bash
 # macOS
-GOOS=darwin GOARCH=amd64 go build -o letta-schedules-darwin-amd64
-GOOS=darwin GOARCH=arm64 go build -o letta-schedules-darwin-arm64
+GOOS=darwin GOARCH=amd64 go build -o letta-switchboard-darwin-amd64
+GOOS=darwin GOARCH=arm64 go build -o letta-switchboard-darwin-arm64
 
 # Linux
-GOOS=linux GOARCH=amd64 go build -o letta-schedules-linux-amd64
-GOOS=linux GOARCH=arm64 go build -o letta-schedules-linux-arm64
+GOOS=linux GOARCH=amd64 go build -o letta-switchboard-linux-amd64
+GOOS=linux GOARCH=arm64 go build -o letta-switchboard-linux-arm64
 
 # Windows
-GOOS=windows GOARCH=amd64 go build -o letta-schedules-windows-amd64.exe
+GOOS=windows GOARCH=amd64 go build -o letta-switchboard-windows-amd64.exe
 ```
 
 ## Quick Start
@@ -53,25 +53,25 @@ GOOS=windows GOARCH=amd64 go build -o letta-schedules-windows-amd64.exe
 
 ```bash
 # Set your Letta API key
-letta-schedules config set-api-key sk-xxx...
+letta-switchboard config set-api-key sk-xxx...
 
 # Set the API URL (optional, defaults to Modal deployment)
-letta-schedules config set-url https://your-api-url.com
+letta-switchboard config set-url https://your-api-url.com
 
 # View current configuration
-letta-schedules config show
+letta-switchboard config show
 ```
 
 ### 2. Send a Message to an Agent
 
 ```bash
 # Send immediately
-letta-schedules send \
+letta-switchboard send \
   --agent-id agent-xxx \
   --message "Hello! How are you doing?"
 
 # Or schedule for later
-letta-schedules send \
+letta-switchboard send \
   --agent-id agent-xxx \
   --message "Reminder: Follow up on project" \
   --execute-at "tomorrow at 9am"
@@ -80,7 +80,7 @@ letta-schedules send \
 ### 3. Create a Recurring Schedule
 
 ```bash
-letta-schedules recurring create \
+letta-switchboard recurring create \
   --agent-id agent-xxx \
   --message "Daily check-in" \
   --cron "every weekday at 9am"
@@ -89,8 +89,8 @@ letta-schedules recurring create \
 ### 4. List Schedules
 
 ```bash
-letta-schedules onetime list
-letta-schedules recurring list
+letta-switchboard onetime list
+letta-switchboard recurring list
 ```
 
 ## Natural Language Support
@@ -150,33 +150,33 @@ The CLI supports natural language input for both time expressions and cron sched
 
 ```bash
 # Set API key
-letta-schedules config set-api-key <key>
+letta-switchboard config set-api-key <key>
 
 # Set base URL
-letta-schedules config set-url <url>
+letta-switchboard config set-url <url>
 
 # Show configuration
-letta-schedules config show
+letta-switchboard config show
 ```
 
 ### Recurring Schedules
 
 ```bash
 # Create a recurring schedule
-letta-schedules recurring create \
+letta-switchboard recurring create \
   --agent-id <agent-id> \
   --message "Your message" \
   --cron "0 9 * * *" \
   --role user
 
 # List all recurring schedules
-letta-schedules recurring list
+letta-switchboard recurring list
 
 # Get details of a specific schedule
-letta-schedules recurring get <schedule-id>
+letta-switchboard recurring get <schedule-id>
 
 # Delete a schedule
-letta-schedules recurring delete <schedule-id>
+letta-switchboard recurring delete <schedule-id>
 ```
 
 #### Cron Expression Examples
@@ -190,30 +190,30 @@ letta-schedules recurring delete <schedule-id>
 
 ```bash
 # Create a one-time schedule
-letta-schedules onetime create \
+letta-switchboard onetime create \
   --agent-id <agent-id> \
   --message "Reminder message" \
   --execute-at "2025-11-07T10:00:00Z" \
   --role user
 
 # List all one-time schedules
-letta-schedules onetime list
+letta-switchboard onetime list
 
 # Get details of a specific schedule
-letta-schedules onetime get <schedule-id>
+letta-switchboard onetime get <schedule-id>
 
 # Delete a schedule
-letta-schedules onetime delete <schedule-id>
+letta-switchboard onetime delete <schedule-id>
 ```
 
 ### Execution Results
 
 ```bash
 # List all execution results
-letta-schedules results list
+letta-switchboard results list
 
 # Get result for a specific schedule
-letta-schedules results get <schedule-id>
+letta-switchboard results get <schedule-id>
 ```
 
 ## Sending Messages (One-Time Schedules)
@@ -224,7 +224,7 @@ The `send` (alias: `onetime create`) command allows you to send messages to agen
 
 ```bash
 # Send a message right now (executes within 1 minute)
-letta-schedules send \
+letta-switchboard send \
   --agent-id agent-xxx \
   --message "Hey, how's the project going?"
 ```
@@ -233,13 +233,13 @@ letta-schedules send \
 
 ```bash
 # Relative time
-letta-schedules send \
+letta-switchboard send \
   --agent-id agent-xxx \
   --message "Follow up reminder" \
   --execute-at "in 2 hours"
 
 # Specific day/time
-letta-schedules send \
+letta-switchboard send \
   --agent-id agent-xxx \
   --message "Weekly summary time!" \
   --execute-at "next monday at 10am"
@@ -257,7 +257,7 @@ This will enable:
 
 ## Configuration
 
-The CLI stores configuration in `~/.letta-schedules/config.yaml`:
+The CLI stores configuration in `~/.letta-switchboard/config.yaml`:
 
 ```yaml
 api_key: sk-xxx...
@@ -269,7 +269,7 @@ base_url: https://letta--schedules-api.modal.run
 ### Daily Agent Check-in
 
 ```bash
-letta-schedules recurring create \
+letta-switchboard recurring create \
   --agent-id agent-123 \
   --message "Good morning! Please provide a daily summary." \
   --cron "0 9 * * *"
@@ -278,7 +278,7 @@ letta-schedules recurring create \
 ### Hourly Status Update
 
 ```bash
-letta-schedules recurring create \
+letta-switchboard recurring create \
   --agent-id agent-123 \
   --message "Status update please" \
   --cron "0 * * * *"
@@ -287,7 +287,7 @@ letta-schedules recurring create \
 ### One-Time Reminder
 
 ```bash
-letta-schedules onetime create \
+letta-switchboard onetime create \
   --agent-id agent-123 \
   --message "Meeting in 1 hour" \
   --execute-at "2025-11-07T14:00:00Z"
@@ -302,7 +302,7 @@ letta-schedules onetime create \
 ### Build
 
 ```bash
-go build -o letta-schedules
+go build -o letta-switchboard
 ```
 
 ### Run Tests

+ 1 - 1
cli/cmd/config.go

@@ -4,7 +4,7 @@ import (
 	"fmt"
 
 	"github.com/fatih/color"
-	"github.com/letta/letta-schedules-cli/internal/config"
+	"github.com/letta/letta-switchboard-cli/internal/config"
 	"github.com/spf13/cobra"
 )
 

+ 3 - 3
cli/cmd/onetime.go

@@ -5,9 +5,9 @@ import (
 	"os"
 
 	"github.com/fatih/color"
-	"github.com/letta/letta-schedules-cli/internal/client"
-	"github.com/letta/letta-schedules-cli/internal/config"
-	"github.com/letta/letta-schedules-cli/internal/parser"
+	"github.com/letta/letta-switchboard-cli/internal/client"
+	"github.com/letta/letta-switchboard-cli/internal/config"
+	"github.com/letta/letta-switchboard-cli/internal/parser"
 	"github.com/olekukonko/tablewriter"
 	"github.com/spf13/cobra"
 )

+ 3 - 3
cli/cmd/recurring.go

@@ -5,9 +5,9 @@ import (
 	"os"
 
 	"github.com/fatih/color"
-	"github.com/letta/letta-schedules-cli/internal/client"
-	"github.com/letta/letta-schedules-cli/internal/config"
-	"github.com/letta/letta-schedules-cli/internal/parser"
+	"github.com/letta/letta-switchboard-cli/internal/client"
+	"github.com/letta/letta-switchboard-cli/internal/config"
+	"github.com/letta/letta-switchboard-cli/internal/parser"
 	"github.com/olekukonko/tablewriter"
 	"github.com/spf13/cobra"
 )

+ 2 - 2
cli/cmd/results.go

@@ -4,8 +4,8 @@ import (
 	"fmt"
 	"os"
 
-	"github.com/letta/letta-schedules-cli/internal/client"
-	"github.com/letta/letta-schedules-cli/internal/config"
+	"github.com/letta/letta-switchboard-cli/internal/client"
+	"github.com/letta/letta-switchboard-cli/internal/config"
 	"github.com/olekukonko/tablewriter"
 	"github.com/spf13/cobra"
 )

+ 6 - 6
cli/cmd/root.go

@@ -4,16 +4,16 @@ import (
 	"fmt"
 	"os"
 
-	"github.com/letta/letta-schedules-cli/internal/config"
+	"github.com/letta/letta-switchboard-cli/internal/config"
 	"github.com/spf13/cobra"
 )
 
 var rootCmd = &cobra.Command{
-	Use:   "letta-schedules",
-	Short: "CLI for managing Letta scheduled messages",
-	Long: `A command-line interface for managing scheduled messages
-for Letta AI agents. Create recurring and one-time schedules,
-and view execution results.`,
+	Use:   "letta-switchboard",
+	Short: "CLI for routing messages to Letta agents",
+	Long: `Letta Switchboard - Route messages to Letta AI agents
+Send messages immediately or schedule for later. Create recurring
+schedules and view execution results.`,
 }
 
 // Execute runs the root command

+ 1 - 1
cli/go.mod

@@ -1,4 +1,4 @@
-module github.com/letta/letta-schedules-cli
+module github.com/letta/letta-switchboard-cli
 
 go 1.21
 

+ 4 - 4
cli/internal/config/config.go

@@ -9,7 +9,7 @@ import (
 )
 
 const (
-	ConfigDirName  = ".letta-schedules"
+	ConfigDirName  = ".letta-switchboard"
 	ConfigFileName = "config"
 )
 
@@ -45,7 +45,7 @@ func InitConfig() error {
 	viper.AddConfigPath(configDir)
 
 	// Set defaults
-	viper.SetDefault("base_url", "https://letta--schedules-api.modal.run")
+	viper.SetDefault("base_url", "https://letta--switchboard-api.modal.run")
 
 	// Read config file if it exists
 	if err := viper.ReadInConfig(); err != nil {
@@ -96,10 +96,10 @@ func saveConfig() error {
 // Validate checks if the configuration is valid
 func (c *Config) Validate() error {
 	if c.APIKey == "" {
-		return fmt.Errorf("API key not set. Run 'letta-schedules config set-api-key <key>'")
+		return fmt.Errorf("API key not set. Run 'letta-switchboard config set-api-key <key>'")
 	}
 	if c.BaseURL == "" {
-		return fmt.Errorf("base URL not set. Run 'letta-schedules config set-url <url>'")
+		return fmt.Errorf("base URL not set. Run 'letta-switchboard config set-url <url>'")
 	}
 	return nil
 }

BIN
cli/letta-switchboard


+ 1 - 1
cli/main.go

@@ -4,7 +4,7 @@ import (
 	"fmt"
 	"os"
 
-	"github.com/letta/letta-schedules-cli/cmd"
+	"github.com/letta/letta-switchboard-cli/cmd"
 )
 
 func main() {

+ 3 - 3
crypto_utils.py

@@ -14,19 +14,19 @@ def get_api_key_hash(api_key: str) -> str:
 
 def is_dev_mode() -> bool:
     """Check if we're in dev mode (no encryption)."""
-    return os.getenv("LETTA_SCHEDULES_DEV_MODE", "").lower() in ("true", "1", "yes")
+    return os.getenv("LETTA_SWITCHBOARD_DEV_MODE", "").lower() in ("true", "1", "yes")
 
 
 def get_encryption_key() -> bytes:
     """Get or generate encryption key for JSON encryption."""
-    key = os.getenv("LETTA_SCHEDULES_ENCRYPTION_KEY")
+    key = os.getenv("LETTA_SWITCHBOARD_ENCRYPTION_KEY")
     
     if is_dev_mode():
         logger.warning("🔓 DEV MODE: Encryption disabled - files stored in plaintext")
         return b"dev-mode-no-encryption"
     
     if not key:
-        logger.warning("LETTA_SCHEDULES_ENCRYPTION_KEY not set, generating temporary key")
+        logger.warning("LETTA_SWITCHBOARD_ENCRYPTION_KEY not set, generating temporary key")
         key = Fernet.generate_key().decode()
     
     return key.encode()

+ 5 - 5
setup_encryption.sh

@@ -26,17 +26,17 @@ fi
 echo "Generated encryption key: ${ENCRYPTION_KEY:0:20}..."
 echo ""
 
-echo "Creating Modal secret 'letta-schedules-encryption'..."
+echo "Creating Modal secret 'letta-switchboard-encryption'..."
 
 # Check if secret already exists
-if modal secret list | grep -q "letta-schedules-encryption"; then
-    echo "WARNING: Secret 'letta-schedules-encryption' already exists."
-    echo "Delete it first with: modal secret delete letta-schedules-encryption"
+if modal secret list | grep -q "letta-switchboard-encryption"; then
+    echo "WARNING: Secret 'letta-switchboard-encryption' already exists."
+    echo "Delete it first with: modal secret delete letta-switchboard-encryption"
     exit 1
 fi
 
 # Create the secret
-modal secret create letta-schedules-encryption LETTA_SCHEDULES_ENCRYPTION_KEY="$ENCRYPTION_KEY" 2>&1
+modal secret create letta-switchboard-encryption LETTA_SWITCHBOARD_ENCRYPTION_KEY="$ENCRYPTION_KEY" 2>&1
 
 if [ $? -eq 0 ]; then
     echo ""

+ 1 - 1
test_api.py

@@ -4,7 +4,7 @@ import os
 import json
 from datetime import datetime, timedelta, timezone
 
-BASE_URL = os.getenv("LETTA_SCHEDULES_URL", "https://letta--letta-schedules-api-dev.modal.run")
+BASE_URL = os.getenv("LETTA_SWITCHBOARD_URL", "https://letta--letta-switchboard-api-dev.modal.run")
 API_KEY = os.getenv("LETTA_API_KEY")
 AGENT_ID = os.getenv("LETTA_AGENT_ID", "agent-a29146cc-2fb3-452d-8c0c-bf71e5db609a")
 

+ 1 - 1
test_api.sh

@@ -1,6 +1,6 @@
 #!/bin/bash
 
-BASE_URL="${LETTA_SCHEDULES_URL:-https://your-modal-app-url.modal.run}"
+BASE_URL="${LETTA_SWITCHBOARD_URL:-https://your-modal-app-url.modal.run}"
 AGENT_ID="${LETTA_AGENT_ID:-your-agent-id}"
 API_KEY="${LETTA_API_KEY}"
 

+ 3 - 3
tests/conftest.py

@@ -10,7 +10,7 @@ from cryptography.fernet import Fernet
 # Add parent directory to path for imports
 sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
 
-os.environ["LETTA_SCHEDULES_DEV_MODE"] = "true"
+os.environ["LETTA_SWITCHBOARD_DEV_MODE"] = "true"
 
 
 @pytest.fixture
@@ -85,8 +85,8 @@ def past_onetime_schedule_data(mock_api_key, mock_agent_id):
 
 @pytest.fixture
 def api_base_url():
-    """Base URL for API testing. Override with LETTA_SCHEDULES_URL env var."""
-    return os.getenv("LETTA_SCHEDULES_URL", "https://letta--letta-schedules-api-dev.modal.run")
+    """Base URL for API testing. Override with LETTA_SWITCHBOARD_URL env var."""
+    return os.getenv("LETTA_SWITCHBOARD_URL", "https://letta--letta-switchboard-api-dev.modal.run")
 
 
 @pytest.fixture

+ 10 - 10
tests/test_crypto_utils.py

@@ -34,28 +34,28 @@ class TestApiKeyHash:
 class TestDevMode:
     def test_dev_mode_true(self):
         """Dev mode should be enabled when env var is 'true'."""
-        os.environ["LETTA_SCHEDULES_DEV_MODE"] = "true"
+        os.environ["LETTA_SWITCHBOARD_DEV_MODE"] = "true"
         assert is_dev_mode() is True
     
     def test_dev_mode_1(self):
         """Dev mode should be enabled when env var is '1'."""
-        os.environ["LETTA_SCHEDULES_DEV_MODE"] = "1"
+        os.environ["LETTA_SWITCHBOARD_DEV_MODE"] = "1"
         assert is_dev_mode() is True
     
     def test_dev_mode_yes(self):
         """Dev mode should be enabled when env var is 'yes'."""
-        os.environ["LETTA_SCHEDULES_DEV_MODE"] = "yes"
+        os.environ["LETTA_SWITCHBOARD_DEV_MODE"] = "yes"
         assert is_dev_mode() is True
     
     def test_dev_mode_false(self):
         """Dev mode should be disabled when env var is 'false'."""
-        os.environ["LETTA_SCHEDULES_DEV_MODE"] = "false"
+        os.environ["LETTA_SWITCHBOARD_DEV_MODE"] = "false"
         assert is_dev_mode() is False
     
     def test_dev_mode_unset(self):
         """Dev mode should be disabled when env var is not set."""
-        if "LETTA_SCHEDULES_DEV_MODE" in os.environ:
-            del os.environ["LETTA_SCHEDULES_DEV_MODE"]
+        if "LETTA_SWITCHBOARD_DEV_MODE" in os.environ:
+            del os.environ["LETTA_SWITCHBOARD_DEV_MODE"]
         assert is_dev_mode() is False
 
 
@@ -81,7 +81,7 @@ class TestEncryption:
     
     def test_dev_mode_plaintext(self):
         """In dev mode, data should be plaintext JSON."""
-        os.environ["LETTA_SCHEDULES_DEV_MODE"] = "true"
+        os.environ["LETTA_SWITCHBOARD_DEV_MODE"] = "true"
         
         data = {"test": "data", "number": 123}
         key = b"ignored-in-dev-mode"
@@ -96,7 +96,7 @@ class TestEncryption:
     
     def test_dev_mode_decrypt(self):
         """In dev mode, decrypt should parse plaintext JSON."""
-        os.environ["LETTA_SCHEDULES_DEV_MODE"] = "true"
+        os.environ["LETTA_SWITCHBOARD_DEV_MODE"] = "true"
         
         data = {"test": "data"}
         key = b"ignored"
@@ -108,7 +108,7 @@ class TestEncryption:
     
     def test_production_mode_encrypted(self):
         """In production mode, data should be encrypted (not plaintext)."""
-        os.environ["LETTA_SCHEDULES_DEV_MODE"] = "false"
+        os.environ["LETTA_SWITCHBOARD_DEV_MODE"] = "false"
         
         data = {"test": "secret"}
         key = Fernet.generate_key()
@@ -122,7 +122,7 @@ class TestEncryption:
     
     def test_wrong_key_fails(self, encryption_key):
         """Decrypting with wrong key should fail."""
-        os.environ["LETTA_SCHEDULES_DEV_MODE"] = "false"
+        os.environ["LETTA_SWITCHBOARD_DEV_MODE"] = "false"
         
         data = {"test": "data"}
         encrypted = encrypt_json(data, encryption_key)