Fix ruff & mypy errors (#2394)

This commit is contained in:
Shuchang Zheng
2025-05-20 01:00:04 -07:00
committed by GitHub
parent 20fb787cc1
commit 5728b918b5
2 changed files with 262 additions and 214 deletions

View File

@@ -3,26 +3,27 @@ import json
import os import os
import shutil import shutil
import subprocess import subprocess
import sys
import time import time
import webbrowser
import uuid import uuid
import webbrowser
from enum import Enum from enum import Enum
from pathlib import Path from pathlib import Path
from typing import List, Optional, Dict, Any, Tuple from typing import Any, Callable, Optional, cast
from urllib.parse import urlparse
import requests # type: ignore
import typer import typer
from dotenv import load_dotenv, set_key
from rich.console import Console from rich.console import Console
from rich.markdown import Markdown
from rich.panel import Panel from rich.panel import Panel
from rich.progress import Progress, SpinnerColumn, TextColumn from rich.progress import Progress, SpinnerColumn, TextColumn
from rich.table import Table from rich.table import Table
from rich import print as rprint
from rich.markdown import Markdown
from dotenv import load_dotenv, set_key
from skyvern.config import settings from skyvern.config import settings
from skyvern.library import Skyvern
from skyvern.forge.sdk.db.enums import OrganizationAuthTokenType
from skyvern.forge import app from skyvern.forge import app
from skyvern.forge.sdk.db.enums import OrganizationAuthTokenType
from skyvern.library import Skyvern
from skyvern.utils import detect_os, get_windows_appdata_roaming, migrate_db from skyvern.utils import detect_os, get_windows_appdata_roaming, migrate_db
# Initialize Rich console for better formatting # Initialize Rich console for better formatting
@@ -59,30 +60,27 @@ DOCUMENTATION = {
"api": "https://docs.skyvern.com/integrations/api", "api": "https://docs.skyvern.com/integrations/api",
} }
class DeploymentType(str, Enum): class DeploymentType(str, Enum):
LOCAL = "local" LOCAL = "local"
CLOUD = "cloud" CLOUD = "cloud"
class BrowserType(str, Enum): class BrowserType(str, Enum):
HEADLESS = "chromium-headless" HEADLESS = "chromium-headless"
HEADFUL = "chromium-headful" HEADFUL = "chromium-headful"
CDP = "cdp-connect" CDP = "cdp-connect"
#----------------------------------------------------
# ----------------------------------------------------
# 1. Guided Onboarding Flow # 1. Guided Onboarding Flow
#---------------------------------------------------- # ----------------------------------------------------
@cli_app.command(name="init") @cli_app.command(name="init")
def init( def init(
deployment: Optional[DeploymentType] = typer.Option( deployment: Optional[DeploymentType] = typer.Option(None, help="Deployment type: local or cloud"),
None, no_postgres: bool = typer.Option(False, "--no-postgres", help="Skip starting PostgreSQL container"),
help="Deployment type: local or cloud"
),
no_postgres: bool = typer.Option(
False,
"--no-postgres",
help="Skip starting PostgreSQL container"
)
) -> None: ) -> None:
""" """
Initialize Skyvern with a guided setup process. Initialize Skyvern with a guided setup process.
@@ -96,10 +94,11 @@ def init(
- Setting up browser automation - Setting up browser automation
- Configuring integrations - Configuring integrations
""" """
console.print(Panel.fit( console.print(
"[bold blue]Welcome to Skyvern Setup Wizard[/]", Panel.fit(
subtitle="Let's get you started with browser automation" "[bold blue]Welcome to Skyvern Setup Wizard[/]", subtitle="Let's get you started with browser automation"
)) )
)
# Step 1: Choose deployment type # Step 1: Choose deployment type
if deployment is None: if deployment is None:
@@ -111,16 +110,14 @@ def init(
console.print(" • Managed service with no local infrastructure") console.print(" • Managed service with no local infrastructure")
console.print(" • Production-ready with built-in scaling") console.print(" • Production-ready with built-in scaling")
deployment_choice = console.input("\n[bold]Deploy locally or connect to cloud? [cloud/local] [/]").strip().lower() deployment_choice = (
console.input("\n[bold]Deploy locally or connect to cloud? [cloud/local] [/]").strip().lower()
)
run_local = deployment_choice == "local" run_local = deployment_choice == "local"
else: else:
run_local = deployment == DeploymentType.LOCAL run_local = deployment == DeploymentType.LOCAL
with Progress( with Progress(SpinnerColumn(), TextColumn("[progress.description]{task.description}"), console=console) as progress:
SpinnerColumn(),
TextColumn("[progress.description]{task.description}"),
console=console
) as progress:
if run_local: if run_local:
# Step 2: Set up local infrastructure (for local deployment) # Step 2: Set up local infrastructure (for local deployment)
setup_task = progress.add_task("[green]Setting up local infrastructure...", total=1) setup_task = progress.add_task("[green]Setting up local infrastructure...", total=1)
@@ -187,7 +184,9 @@ def init(
# Step 5: Configure integrations # Step 5: Configure integrations
console.print(Markdown("\n## Step 5: Configure Integrations")) console.print(Markdown("\n## Step 5: Configure Integrations"))
configure_mcp = typer.confirm("Would you like to configure AI integrations (Claude, Cursor, Windsurf)?", default=True) configure_mcp = typer.confirm(
"Would you like to configure AI integrations (Claude, Cursor, Windsurf)?", default=True
)
if configure_mcp: if configure_mcp:
setup_mcp() setup_mcp()
console.print("\n[green]AI integrations configured successfully![/]") console.print("\n[green]AI integrations configured successfully![/]")
@@ -195,16 +194,17 @@ def init(
if run_local: if run_local:
# Install required components for local deployment # Install required components for local deployment
console.print(Markdown("\n## Step 6: Installing Components")) console.print(Markdown("\n## Step 6: Installing Components"))
with Progress(SpinnerColumn(), TextColumn("[progress.description]{task.description}"), console=console) as progress: with Progress(
SpinnerColumn(), TextColumn("[progress.description]{task.description}"), console=console
) as progress:
browser_install_task = progress.add_task("[green]Installing Chromium browser...", total=1) browser_install_task = progress.add_task("[green]Installing Chromium browser...", total=1)
subprocess.run(["playwright", "install", "chromium"], check=True) subprocess.run(["playwright", "install", "chromium"], check=True)
progress.update(browser_install_task, completed=1) progress.update(browser_install_task, completed=1)
# Success message and next steps # Success message and next steps
console.print(Panel.fit( console.print(
"[bold green]Skyvern setup complete![/]", Panel.fit("[bold green]Skyvern setup complete![/]", subtitle="You're ready to start automating browsers")
subtitle="You're ready to start automating browsers" )
))
if run_local: if run_local:
console.print("\n[bold]Next steps:[/]") console.print("\n[bold]Next steps:[/]")
@@ -212,20 +212,19 @@ def init(
console.print("2. Start the Skyvern UI: [yellow]skyvern run ui[/]") console.print("2. Start the Skyvern UI: [yellow]skyvern run ui[/]")
else: else:
console.print("\n[bold]Next steps:[/]") console.print("\n[bold]Next steps:[/]")
console.print("1. Visit the Skyvern Cloud dashboard: [link=https://app.skyvern.com]https://app.skyvern.com[/link]") console.print(
"1. Visit the Skyvern Cloud dashboard: [link=https://app.skyvern.com]https://app.skyvern.com[/link]"
)
console.print("2. Try using an AI integration: [yellow]skyvern docs integrations[/]") console.print("2. Try using an AI integration: [yellow]skyvern docs integrations[/]")
#----------------------------------------------------
# ----------------------------------------------------
# 3. Improved Documentation Integration # 3. Improved Documentation Integration
#---------------------------------------------------- # ----------------------------------------------------
@docs_app.command(name="open") @docs_app.command(name="open")
def open_docs( def open_docs(section: str = typer.Argument("quickstart", help="Documentation section to open")) -> None:
section: str = typer.Argument(
"quickstart",
help="Documentation section to open"
)
) -> None:
""" """
Open Skyvern documentation in your web browser. Open Skyvern documentation in your web browser.
@@ -248,17 +247,18 @@ def open_docs(
console.print(f"URL: [link={url}]{url}[/link]") console.print(f"URL: [link={url}]{url}[/link]")
webbrowser.open(url) webbrowser.open(url)
@docs_app.command(name="prompting") @docs_app.command(name="prompting")
def prompting_guide() -> None: def prompting_guide() -> None:
""" """
Show prompting best practices for Skyvern. Show prompting best practices for Skyvern.
""" """
console.print(Panel.fit( console.print(
"[bold blue]Skyvern Prompting Best Practices[/]", Panel.fit("[bold blue]Skyvern Prompting Best Practices[/]", subtitle="Tips for writing effective prompts")
subtitle="Tips for writing effective prompts" )
))
console.print(Markdown(""" console.print(
Markdown("""
## General Guidelines ## General Guidelines
1. **Be specific and detailed** 1. **Be specific and detailed**
@@ -293,21 +293,21 @@ Buy wireless headphones and check out.
## For More Information ## For More Information
Run `skyvern docs open prompting` to see the complete prompting guide online. Run `skyvern docs open prompting` to see the complete prompting guide online.
""")) """)
)
#----------------------------------------------------
# ----------------------------------------------------
# 4. User-Friendly Management Commands # 4. User-Friendly Management Commands
#---------------------------------------------------- # ----------------------------------------------------
@cli_app.command(name="status") @cli_app.command(name="status")
def status() -> None: def status() -> None:
""" """
Check the status of Skyvern services. Check the status of Skyvern services.
""" """
console.print(Panel.fit( console.print(Panel.fit("[bold blue]Skyvern Services Status[/]", subtitle="Checking all system components"))
"[bold blue]Skyvern Services Status[/]",
subtitle="Checking all system components"
))
# Check for .env file # Check for .env file
env_path = Path(".env") env_path = Path(".env")
@@ -318,7 +318,7 @@ def status() -> None:
try: try:
load_dotenv() load_dotenv()
# Simple check - just see if we can run a migrate command without error # Simple check - just see if we can run a migrate command without error
migrate_db(dry_run=True) migrate_db()
db_status = "✅ Connected" db_status = "✅ Connected"
except Exception: except Exception:
db_status = "❌ Not connected" db_status = "❌ Not connected"
@@ -327,24 +327,26 @@ def status() -> None:
server_status = "⏳ Checking..." server_status = "⏳ Checking..."
try: try:
import socket import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(0.5) s.settimeout(0.5)
s.connect(("localhost", 8000)) s.connect(("localhost", 8000))
s.close() s.close()
server_status = "✅ Running" server_status = "✅ Running"
except: except Exception:
server_status = "❌ Not running" server_status = "❌ Not running"
# Check if UI is running (port 8080) # Check if UI is running (port 8080)
ui_status = "⏳ Checking..." ui_status = "⏳ Checking..."
try: try:
import socket import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(0.5) s.settimeout(0.5)
s.connect(("localhost", 8080)) s.connect(("localhost", 8080))
s.close() s.close()
ui_status = "✅ Running" ui_status = "✅ Running"
except: except Exception:
ui_status = "❌ Not running" ui_status = "❌ Not running"
# Check API key # Check API key
@@ -370,15 +372,13 @@ def status() -> None:
else: else:
console.print("\n[bold green]All systems operational![/] Skyvern is ready to use.") console.print("\n[bold green]All systems operational![/] Skyvern is ready to use.")
@tasks_app.command(name="list") @tasks_app.command(name="list")
def list_tasks() -> None: def list_tasks() -> None:
""" """
List recent Skyvern tasks. List recent Skyvern tasks.
""" """
console.print(Panel.fit( console.print(Panel.fit("[bold blue]Recent Skyvern Tasks[/]", subtitle="Retrieving task history"))
"[bold blue]Recent Skyvern Tasks[/]",
subtitle="Retrieving task history"
))
try: try:
# Initialize Skyvern client # Initialize Skyvern client
@@ -389,7 +389,9 @@ def list_tasks() -> None:
) )
# Get tasks # Get tasks
with Progress(SpinnerColumn(), TextColumn("[progress.description]{task.description}"), console=console) as progress: with Progress(
SpinnerColumn(), TextColumn("[progress.description]{task.description}"), console=console
) as progress:
task = progress.add_task("[green]Fetching recent tasks...", total=1) task = progress.add_task("[green]Fetching recent tasks...", total=1)
tasks = asyncio.run(skyvern_agent.get_tasks()) tasks = asyncio.run(skyvern_agent.get_tasks())
progress.update(task, completed=1) progress.update(task, completed=1)
@@ -410,7 +412,7 @@ def list_tasks() -> None:
str(task.id), str(task.id),
task.title or "Untitled", task.title or "Untitled",
task.status or "Unknown", task.status or "Unknown",
task.created_at.strftime("%Y-%m-%d %H:%M:%S") if task.created_at else "Unknown" task.created_at.strftime("%Y-%m-%d %H:%M:%S") if task.created_at else "Unknown",
) )
console.print(table) console.print(table)
@@ -424,20 +426,18 @@ def list_tasks() -> None:
console.print(f"[bold red]Error listing tasks:[/] {str(e)}") console.print(f"[bold red]Error listing tasks:[/] {str(e)}")
console.print("[yellow]Make sure your API key is set correctly in .env[/]") console.print("[yellow]Make sure your API key is set correctly in .env[/]")
@tasks_app.command(name="create") @tasks_app.command(name="create")
def create_task( def create_task(
prompt: str = typer.Option(..., "--prompt", "-p", help="Task prompt"), prompt: str = typer.Option(..., "--prompt", "-p", help="Task prompt"),
url: str = typer.Option(..., "--url", "-u", help="Starting URL"), url: str = typer.Option(..., "--url", "-u", help="Starting URL"),
schema: Optional[str] = typer.Option(None, "--schema", "-s", help="Data extraction schema (JSON)"), schema: Optional[str] = typer.Option(None, "--schema", "-s", help="Data extraction schema (JSON)"),
output_json: bool = typer.Option(False, "--json", help="Output results as JSON") output_json: bool = typer.Option(False, "--json", help="Output results as JSON"),
) -> None: ) -> None:
""" """
Create and run a new Skyvern task. Create and run a new Skyvern task.
""" """
console.print(Panel.fit( console.print(Panel.fit("[bold blue]Creating New Skyvern Task[/]", subtitle="Running browser automation"))
"[bold blue]Creating New Skyvern Task[/]",
subtitle="Running browser automation"
))
console.print(f"[bold]Prompt:[/] {prompt}") console.print(f"[bold]Prompt:[/] {prompt}")
console.print(f"[bold]URL:[/] {url}") console.print(f"[bold]URL:[/] {url}")
@@ -453,15 +453,14 @@ def create_task(
) )
# Create and run task # Create and run task
with Progress(SpinnerColumn(), TextColumn("[progress.description]{task.description}"), console=console) as progress: with Progress(
SpinnerColumn(), TextColumn("[progress.description]{task.description}"), console=console
) as progress:
task = progress.add_task("[green]Running task...", total=1) task = progress.add_task("[green]Running task...", total=1)
result = asyncio.run(skyvern_agent.run_task( result = asyncio.run(
prompt=prompt, skyvern_agent.run_task(prompt=prompt, url=url, data_extraction_schema=schema, user_agent="skyvern-cli")
url=url, )
data_extraction_schema=schema,
user_agent="skyvern-cli"
))
progress.update(task, completed=1) progress.update(task, completed=1)
@@ -474,22 +473,22 @@ def create_task(
# Display path to view results # Display path to view results
base_url = settings.SKYVERN_BASE_URL base_url = settings.SKYVERN_BASE_URL
run_history_url = "https://app.skyvern.com/history" if "skyvern.com" in base_url else "http://localhost:8080/history" run_history_url = (
"https://app.skyvern.com/history" if "skyvern.com" in base_url else "http://localhost:8080/history"
)
console.print(f"\nView details at: [link={run_history_url}]{run_history_url}[/link]") console.print(f"\nView details at: [link={run_history_url}]{run_history_url}[/link]")
except Exception as e: except Exception as e:
console.print(f"[bold red]Error creating task:[/] {str(e)}") console.print(f"[bold red]Error creating task:[/] {str(e)}")
console.print("[yellow]Make sure your API key is set correctly in .env[/]") console.print("[yellow]Make sure your API key is set correctly in .env[/]")
@workflows_app.command(name="list") @workflows_app.command(name="list")
def list_workflows() -> None: def list_workflows() -> None:
""" """
List Skyvern workflows. List Skyvern workflows.
""" """
console.print(Panel.fit( console.print(Panel.fit("[bold blue]Skyvern Workflows[/]", subtitle="Retrieving available workflows"))
"[bold blue]Skyvern Workflows[/]",
subtitle="Retrieving available workflows"
))
try: try:
# Initialize Skyvern client # Initialize Skyvern client
@@ -500,7 +499,9 @@ def list_workflows() -> None:
) )
# Get workflows # Get workflows
with Progress(SpinnerColumn(), TextColumn("[progress.description]{task.description}"), console=console) as progress: with Progress(
SpinnerColumn(), TextColumn("[progress.description]{task.description}"), console=console
) as progress:
task = progress.add_task("[green]Fetching workflows...", total=1) task = progress.add_task("[green]Fetching workflows...", total=1)
workflows = asyncio.run(skyvern_agent.get_workflows()) workflows = asyncio.run(skyvern_agent.get_workflows())
progress.update(task, completed=1) progress.update(task, completed=1)
@@ -521,7 +522,9 @@ def list_workflows() -> None:
str(workflow.id), str(workflow.id),
workflow.title or "Untitled", workflow.title or "Untitled",
workflow.status or "Unknown", workflow.status or "Unknown",
workflow.created_at.strftime("%Y-%m-%d %H:%M:%S") if hasattr(workflow, 'created_at') and workflow.created_at else "Unknown" workflow.created_at.strftime("%Y-%m-%d %H:%M:%S")
if hasattr(workflow, "created_at") and workflow.created_at
else "Unknown",
) )
console.print(table) console.print(table)
@@ -535,9 +538,11 @@ def list_workflows() -> None:
console.print(f"[bold red]Error listing workflows:[/] {str(e)}") console.print(f"[bold red]Error listing workflows:[/] {str(e)}")
console.print("[yellow]Make sure your API key is set correctly in .env[/]") console.print("[yellow]Make sure your API key is set correctly in .env[/]")
#----------------------------------------------------
# ----------------------------------------------------
# 5. Streamlined Configuration (Original functions enhanced) # 5. Streamlined Configuration (Original functions enhanced)
#---------------------------------------------------- # ----------------------------------------------------
def setup_postgresql(no_postgres: bool = False) -> None: def setup_postgresql(no_postgres: bool = False) -> None:
"""Set up PostgreSQL database for Skyvern with improved feedback.""" """Set up PostgreSQL database for Skyvern with improved feedback."""
@@ -569,8 +574,7 @@ def setup_postgresql(no_postgres: bool = False) -> None:
if not no_postgres: if not no_postgres:
console.print("[yellow]![/] No local Postgres detected") console.print("[yellow]![/] No local Postgres detected")
start_postgres = typer.confirm( start_postgres = typer.confirm(
"Start a disposable container now? (Choose 'n' if using Docker Compose)", "Start a disposable container now? (Choose 'n' if using Docker Compose)", default=True
default=True
) )
if not start_postgres: if not start_postgres:
@@ -617,20 +621,27 @@ def setup_llm_providers() -> None:
model_options = [] model_options = []
# Create sections for each provider # Create sections for each provider
providers = [ providers: list[dict[str, Any]] = [
{ {
"name": "OpenAI", "name": "OpenAI",
"env_key": "ENABLE_OPENAI", "env_key": "ENABLE_OPENAI",
"api_key_env": "OPENAI_API_KEY", "api_key_env": "OPENAI_API_KEY",
"models": ["OPENAI_GPT4_1", "OPENAI_GPT4_1_MINI", "OPENAI_GPT4_1_NANO", "OPENAI_GPT4O", "OPENAI_O4_MINI", "OPENAI_O3"], "models": [
"setup_message": "To enable OpenAI, you need an API key from your OpenAI account." "OPENAI_GPT4_1",
"OPENAI_GPT4_1_MINI",
"OPENAI_GPT4_1_NANO",
"OPENAI_GPT4O",
"OPENAI_O4_MINI",
"OPENAI_O3",
],
"setup_message": "To enable OpenAI, you need an API key from your OpenAI account.",
}, },
{ {
"name": "Anthropic", "name": "Anthropic",
"env_key": "ENABLE_ANTHROPIC", "env_key": "ENABLE_ANTHROPIC",
"api_key_env": "ANTHROPIC_API_KEY", "api_key_env": "ANTHROPIC_API_KEY",
"models": ["ANTHROPIC_CLAUDE3.5_SONNET", "ANTHROPIC_CLAUDE3.7_SONNET"], "models": ["ANTHROPIC_CLAUDE3.5_SONNET", "ANTHROPIC_CLAUDE3.7_SONNET"],
"setup_message": "To enable Anthropic, you need an API key from your Anthropic account." "setup_message": "To enable Anthropic, you need an API key from your Anthropic account.",
}, },
{ {
"name": "Azure OpenAI", "name": "Azure OpenAI",
@@ -641,27 +652,39 @@ def setup_llm_providers() -> None:
"extra_fields": { "extra_fields": {
"AZURE_DEPLOYMENT": "Enter your Azure deployment name", "AZURE_DEPLOYMENT": "Enter your Azure deployment name",
"AZURE_API_BASE": "Enter your Azure API base URL", "AZURE_API_BASE": "Enter your Azure API base URL",
"AZURE_API_VERSION": "Enter your Azure API version" "AZURE_API_VERSION": "Enter your Azure API version",
} },
}, },
{ {
"name": "Google Gemini", "name": "Google Gemini",
"env_key": "ENABLE_GEMINI", "env_key": "ENABLE_GEMINI",
"api_key_env": "GEMINI_API_KEY", "api_key_env": "GEMINI_API_KEY",
"models": ["GEMINI_FLASH_2_0", "GEMINI_FLASH_2_0_LITE", "GEMINI_2.5_PRO_PREVIEW_03_25", "GEMINI_2.5_PRO_EXP_03_25"], "models": [
"setup_message": "To enable Gemini, you need an API key from Google AI Studio." "GEMINI_FLASH_2_0",
"GEMINI_FLASH_2_0_LITE",
"GEMINI_2.5_PRO_PREVIEW_03_25",
"GEMINI_2.5_PRO_EXP_03_25",
],
"setup_message": "To enable Gemini, you need an API key from Google AI Studio.",
}, },
{ {
"name": "Novita AI", "name": "Novita AI",
"env_key": "ENABLE_NOVITA", "env_key": "ENABLE_NOVITA",
"api_key_env": "NOVITA_API_KEY", "api_key_env": "NOVITA_API_KEY",
"models": [ "models": [
"NOVITA_DEEPSEEK_R1", "NOVITA_DEEPSEEK_V3", "NOVITA_LLAMA_3_3_70B", "NOVITA_DEEPSEEK_R1",
"NOVITA_LLAMA_3_2_1B", "NOVITA_LLAMA_3_2_3B", "NOVITA_LLAMA_3_2_11B_VISION", "NOVITA_DEEPSEEK_V3",
"NOVITA_LLAMA_3_1_8B", "NOVITA_LLAMA_3_1_70B", "NOVITA_LLAMA_3_1_405B", "NOVITA_LLAMA_3_3_70B",
"NOVITA_LLAMA_3_8B", "NOVITA_LLAMA_3_70B" "NOVITA_LLAMA_3_2_1B",
"NOVITA_LLAMA_3_2_3B",
"NOVITA_LLAMA_3_2_11B_VISION",
"NOVITA_LLAMA_3_1_8B",
"NOVITA_LLAMA_3_1_70B",
"NOVITA_LLAMA_3_1_405B",
"NOVITA_LLAMA_3_8B",
"NOVITA_LLAMA_3_70B",
], ],
"setup_message": "To enable Novita AI, you need an API key from Novita." "setup_message": "To enable Novita AI, you need an API key from Novita.",
}, },
{ {
"name": "OpenAI-compatible", "name": "OpenAI-compatible",
@@ -671,20 +694,18 @@ def setup_llm_providers() -> None:
"extra_fields": { "extra_fields": {
"OPENAI_COMPATIBLE_MODEL_NAME": "Enter the model name (e.g., 'yi-34b', 'mistral-large')", "OPENAI_COMPATIBLE_MODEL_NAME": "Enter the model name (e.g., 'yi-34b', 'mistral-large')",
"OPENAI_COMPATIBLE_API_KEY": "Enter your API key", "OPENAI_COMPATIBLE_API_KEY": "Enter your API key",
"OPENAI_COMPATIBLE_API_BASE": "Enter the API base URL (e.g., 'https://api.together.xyz/v1')" "OPENAI_COMPATIBLE_API_BASE": "Enter the API base URL (e.g., 'https://api.together.xyz/v1')",
}, },
"extra_questions": [ "extra_questions": [
{ {
"question": "Does this model support vision?", "question": "Does this model support vision?",
"env_key": "OPENAI_COMPATIBLE_SUPPORTS_VISION", "env_key": "OPENAI_COMPATIBLE_SUPPORTS_VISION",
"value_if_yes": "true", "value_if_yes": "true",
"value_if_no": "false" "value_if_no": "false",
} }
], ],
"optional_fields": { "optional_fields": {"OPENAI_COMPATIBLE_API_VERSION": "Enter API version (optional, press enter to skip)"},
"OPENAI_COMPATIBLE_API_VERSION": "Enter API version (optional, press enter to skip)" },
}
}
] ]
# Process each provider # Process each provider
@@ -751,10 +772,7 @@ def setup_llm_providers() -> None:
while True: while True:
try: try:
model_choice = typer.prompt( model_choice = typer.prompt(f"Choose a model by number (1-{len(model_options)})", type=int)
f"Choose a model by number (1-{len(model_options)})",
type=int
)
if 1 <= model_choice <= len(model_options): if 1 <= model_choice <= len(model_options):
break break
console.print(f"[red]Please enter a number between 1 and {len(model_options)}[/]") console.print(f"[red]Please enter a number between 1 and {len(model_options)}[/]")
@@ -773,9 +791,17 @@ def setup_browser_config() -> tuple[str, Optional[str], Optional[str]]:
console.print(Markdown("## Browser Configuration")) console.print(Markdown("## Browser Configuration"))
browser_types = [ browser_types = [
{"id": "chromium-headless", "name": "Headless Chrome", "description": "Runs Chrome in the background (no visible window)"}, {
"id": "chromium-headless",
"name": "Headless Chrome",
"description": "Runs Chrome in the background (no visible window)",
},
{"id": "chromium-headful", "name": "Visible Chrome", "description": "Runs Chrome with a visible window"}, {"id": "chromium-headful", "name": "Visible Chrome", "description": "Runs Chrome with a visible window"},
{"id": "cdp-connect", "name": "Connect to Chrome", "description": "Connects to an existing Chrome instance with remote debugging"} {
"id": "cdp-connect",
"name": "Connect to Chrome",
"description": "Connects to an existing Chrome instance with remote debugging",
},
] ]
console.print("Select browser mode:") console.print("Select browser mode:")
@@ -812,7 +838,7 @@ def setup_browser_config() -> tuple[str, Optional[str], Optional[str]]:
console.print("\n[bold]Chrome Remote Debugging Setup:[/]") console.print("\n[bold]Chrome Remote Debugging Setup:[/]")
console.print("Chrome must be running with remote debugging enabled.") console.print("Chrome must be running with remote debugging enabled.")
console.print(f"Example command: [italic]chrome --remote-debugging-port=9222[/]") console.print("Example command: [italic]chrome --remote-debugging-port=9222[/]")
default_port = "9222" default_port = "9222"
remote_debugging_url = f"http://localhost:{default_port}" remote_debugging_url = f"http://localhost:{default_port}"
@@ -867,7 +893,9 @@ def setup_browser_config() -> tuple[str, Optional[str], Optional[str]]:
console.print(f"Connecting to {remote_debugging_url}") console.print(f"Connecting to {remote_debugging_url}")
# Wait for Chrome to start and verify connection # Wait for Chrome to start and verify connection
with Progress(SpinnerColumn(), TextColumn("[progress.description]{task.description}"), console=console) as progress: with Progress(
SpinnerColumn(), TextColumn("[progress.description]{task.description}"), console=console
) as progress:
wait_task = progress.add_task("[green]Waiting for Chrome to initialize...", total=1) wait_task = progress.add_task("[green]Waiting for Chrome to initialize...", total=1)
time.sleep(2) time.sleep(2)
progress.update(wait_task, completed=1) progress.update(wait_task, completed=1)
@@ -877,13 +905,19 @@ def setup_browser_config() -> tuple[str, Optional[str], Optional[str]]:
if verification_response.status_code == 200: if verification_response.status_code == 200:
try: try:
browser_info = verification_response.json() browser_info = verification_response.json()
console.print("[green]✓[/] Connection verified! Chrome is running with remote debugging") console.print(
"[green]✓[/] Connection verified! Chrome is running with remote debugging"
)
if "Browser" in browser_info: if "Browser" in browser_info:
console.print(f" Browser: {browser_info['Browser']}") console.print(f" Browser: {browser_info['Browser']}")
except json.JSONDecodeError: except json.JSONDecodeError:
console.print("[yellow]Warning:[/] Response from Chrome debugging port is not valid JSON") console.print(
"[yellow]Warning:[/] Response from Chrome debugging port is not valid JSON"
)
else: else:
console.print(f"[yellow]Warning:[/] Chrome responded with status code {verification_response.status_code}") console.print(
f"[yellow]Warning:[/] Chrome responded with status code {verification_response.status_code}"
)
except requests.RequestException as e: except requests.RequestException as e:
console.print(f"[yellow]Warning:[/] Could not verify Chrome is running: {e}") console.print(f"[yellow]Warning:[/] Could not verify Chrome is running: {e}")
console.print("You may need to check Chrome manually or try a different port") console.print("You may need to check Chrome manually or try a different port")
@@ -1046,20 +1080,20 @@ def setup_mcp() -> None:
"name": "Claude Desktop", "name": "Claude Desktop",
"check_fn": lambda: is_claude_desktop_installed(host_system), "check_fn": lambda: is_claude_desktop_installed(host_system),
"setup_fn": lambda: setup_claude_desktop_config(host_system, path_to_env), "setup_fn": lambda: setup_claude_desktop_config(host_system, path_to_env),
"not_installed_msg": "Claude Desktop is not installed. Please install it first." "not_installed_msg": "Claude Desktop is not installed. Please install it first.",
}, },
{ {
"name": "Cursor Editor", "name": "Cursor Editor",
"check_fn": lambda: is_cursor_installed(host_system), "check_fn": lambda: is_cursor_installed(host_system),
"setup_fn": lambda: setup_cursor_config(host_system, path_to_env), "setup_fn": lambda: setup_cursor_config(host_system, path_to_env),
"not_installed_msg": "Cursor Editor is not installed. Please install it first." "not_installed_msg": "Cursor Editor is not installed. Please install it first.",
}, },
{ {
"name": "Windsurf", "name": "Windsurf",
"check_fn": lambda: is_windsurf_installed(host_system), "check_fn": lambda: is_windsurf_installed(host_system),
"setup_fn": lambda: setup_windsurf_config(host_system, path_to_env), "setup_fn": lambda: setup_windsurf_config(host_system, path_to_env),
"not_installed_msg": "Windsurf is not installed. Please install it first." "not_installed_msg": "Windsurf is not installed. Please install it first.",
} },
] ]
# Set up each integration # Set up each integration
@@ -1067,7 +1101,8 @@ def setup_mcp() -> None:
console.print(f"\n[bold]Setting up {integration['name']}[/]") console.print(f"\n[bold]Setting up {integration['name']}[/]")
# Check if installed # Check if installed
if not integration["check_fn"](): check_fn = cast(Callable[[], bool], integration["check_fn"])
if not check_fn():
console.print(f"[yellow]![/] {integration['not_installed_msg']}") console.print(f"[yellow]![/] {integration['not_installed_msg']}")
console.print(f"Skipping {integration['name']} integration setup.") console.print(f"Skipping {integration['name']} integration setup.")
continue continue
@@ -1075,7 +1110,8 @@ def setup_mcp() -> None:
# Ask user if they want to set up this integration # Ask user if they want to set up this integration
if typer.confirm(f"Configure {integration['name']} integration?", default=True): if typer.confirm(f"Configure {integration['name']} integration?", default=True):
# Set up the integration # Set up the integration
if integration["setup_fn"](): setup_fn = cast(Callable[[], bool], integration["setup_fn"])
if setup_fn():
console.print(f"[green]✓[/] {integration['name']} integration configured successfully") console.print(f"[green]✓[/] {integration['name']} integration configured successfully")
else: else:
console.print(f"[red]×[/] Error configuring {integration['name']} integration") console.print(f"[red]×[/] Error configuring {integration['name']} integration")
@@ -1097,8 +1133,7 @@ def setup_mcp_config() -> str:
if not python_paths: if not python_paths:
console.print("[yellow]![/] Could not find Python 3.11 installation") console.print("[yellow]![/] Could not find Python 3.11 installation")
path_to_env = typer.prompt( path_to_env = typer.prompt(
"Enter the full path to your Python 3.11 environment", "Enter the full path to your Python 3.11 environment", default="/opt/homebrew/bin/python3.11"
default="/opt/homebrew/bin/python3.11"
) )
else: else:
# Show found Python installations # Show found Python installations
@@ -1112,10 +1147,7 @@ def setup_mcp_config() -> str:
if len(python_paths) > 1: if len(python_paths) > 1:
# Let user choose if multiple were found # Let user choose if multiple were found
choice = typer.prompt( choice = typer.prompt("Which Python installation do you want to use? (Enter number)", default="1")
"Which Python installation do you want to use? (Enter number)",
default="1"
)
try: try:
index = int(choice) - 1 index = int(choice) - 1
if 0 <= index < len(python_paths): if 0 <= index < len(python_paths):
@@ -1210,3 +1242,19 @@ def is_cursor_installed(host_system: str) -> bool:
return False return False
def setup_cursor_config(host_system: str, path_to_env: str) -> bool:
"""Placeholder setup for Cursor integration."""
console.print("[yellow]![/] Cursor integration setup is not implemented yet")
return False
def is_windsurf_installed(host_system: str) -> bool:
"""Check if Windsurf is installed."""
# TODO: Implement actual detection logic
return False
def setup_windsurf_config(host_system: str, path_to_env: str) -> bool:
"""Placeholder setup for Windsurf integration."""
console.print("[yellow]![/] Windsurf integration setup is not implemented yet")
return False

View File

@@ -300,7 +300,7 @@ class Skyvern(AsyncSkyvern):
totp_url: str | None = None, totp_url: str | None = None,
title: str | None = None, title: str | None = None,
error_code_mapping: dict[str, str] | None = None, error_code_mapping: dict[str, str] | None = None,
data_extraction_schema: dict[str, Any] | None = None, data_extraction_schema: dict[str, Any] | str | None = None,
proxy_location: ProxyLocation | None = None, proxy_location: ProxyLocation | None = None,
max_steps: int | None = None, max_steps: int | None = None,
wait_for_completion: bool = True, wait_for_completion: bool = True,