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 shutil
import subprocess
import sys
import time
import webbrowser
import uuid
import webbrowser
from enum import Enum
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
from dotenv import load_dotenv, set_key
from rich.console import Console
from rich.markdown import Markdown
from rich.panel import Panel
from rich.progress import Progress, SpinnerColumn, TextColumn
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.library import Skyvern
from skyvern.forge.sdk.db.enums import OrganizationAuthTokenType
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
# Initialize Rich console for better formatting
@@ -59,30 +60,27 @@ DOCUMENTATION = {
"api": "https://docs.skyvern.com/integrations/api",
}
class DeploymentType(str, Enum):
LOCAL = "local"
CLOUD = "cloud"
class BrowserType(str, Enum):
HEADLESS = "chromium-headless"
HEADFUL = "chromium-headful"
CDP = "cdp-connect"
#----------------------------------------------------
# ----------------------------------------------------
# 1. Guided Onboarding Flow
#----------------------------------------------------
# ----------------------------------------------------
@cli_app.command(name="init")
def init(
deployment: Optional[DeploymentType] = typer.Option(
None,
help="Deployment type: local or cloud"
),
no_postgres: bool = typer.Option(
False,
"--no-postgres",
help="Skip starting PostgreSQL container"
)
deployment: Optional[DeploymentType] = typer.Option(None, help="Deployment type: local or cloud"),
no_postgres: bool = typer.Option(False, "--no-postgres", help="Skip starting PostgreSQL container"),
) -> None:
"""
Initialize Skyvern with a guided setup process.
@@ -96,10 +94,11 @@ def init(
- Setting up browser automation
- Configuring integrations
"""
console.print(Panel.fit(
"[bold blue]Welcome to Skyvern Setup Wizard[/]",
subtitle="Let's get you started with browser automation"
))
console.print(
Panel.fit(
"[bold blue]Welcome to Skyvern Setup Wizard[/]", subtitle="Let's get you started with browser automation"
)
)
# Step 1: Choose deployment type
if deployment is None:
@@ -111,16 +110,14 @@ def init(
console.print(" • Managed service with no local infrastructure")
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"
else:
run_local = deployment == DeploymentType.LOCAL
with Progress(
SpinnerColumn(),
TextColumn("[progress.description]{task.description}"),
console=console
) as progress:
with Progress(SpinnerColumn(), TextColumn("[progress.description]{task.description}"), console=console) as progress:
if run_local:
# Step 2: Set up local infrastructure (for local deployment)
setup_task = progress.add_task("[green]Setting up local infrastructure...", total=1)
@@ -187,7 +184,9 @@ def init(
# 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:
setup_mcp()
console.print("\n[green]AI integrations configured successfully![/]")
@@ -195,16 +194,17 @@ def init(
if run_local:
# Install required components for local deployment
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)
subprocess.run(["playwright", "install", "chromium"], check=True)
progress.update(browser_install_task, completed=1)
# Success message and next steps
console.print(Panel.fit(
"[bold green]Skyvern setup complete![/]",
subtitle="You're ready to start automating browsers"
))
console.print(
Panel.fit("[bold green]Skyvern setup complete![/]", subtitle="You're ready to start automating browsers")
)
if run_local:
console.print("\n[bold]Next steps:[/]")
@@ -212,20 +212,19 @@ def init(
console.print("2. Start the Skyvern UI: [yellow]skyvern run ui[/]")
else:
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[/]")
#----------------------------------------------------
# ----------------------------------------------------
# 3. Improved Documentation Integration
#----------------------------------------------------
# ----------------------------------------------------
@docs_app.command(name="open")
def open_docs(
section: str = typer.Argument(
"quickstart",
help="Documentation section to open"
)
) -> None:
def open_docs(section: str = typer.Argument("quickstart", help="Documentation section to open")) -> None:
"""
Open Skyvern documentation in your web browser.
@@ -248,17 +247,18 @@ def open_docs(
console.print(f"URL: [link={url}]{url}[/link]")
webbrowser.open(url)
@docs_app.command(name="prompting")
def prompting_guide() -> None:
"""
Show prompting best practices for Skyvern.
"""
console.print(Panel.fit(
"[bold blue]Skyvern Prompting Best Practices[/]",
subtitle="Tips for writing effective prompts"
))
console.print(
Panel.fit("[bold blue]Skyvern Prompting Best Practices[/]", subtitle="Tips for writing effective prompts")
)
console.print(Markdown("""
console.print(
Markdown("""
## General Guidelines
1. **Be specific and detailed**
@@ -293,21 +293,21 @@ Buy wireless headphones and check out.
## For More Information
Run `skyvern docs open prompting` to see the complete prompting guide online.
"""))
""")
)
#----------------------------------------------------
# ----------------------------------------------------
# 4. User-Friendly Management Commands
#----------------------------------------------------
# ----------------------------------------------------
@cli_app.command(name="status")
def status() -> None:
"""
Check the status of Skyvern services.
"""
console.print(Panel.fit(
"[bold blue]Skyvern Services Status[/]",
subtitle="Checking all system components"
))
console.print(Panel.fit("[bold blue]Skyvern Services Status[/]", subtitle="Checking all system components"))
# Check for .env file
env_path = Path(".env")
@@ -318,7 +318,7 @@ def status() -> None:
try:
load_dotenv()
# Simple check - just see if we can run a migrate command without error
migrate_db(dry_run=True)
migrate_db()
db_status = "✅ Connected"
except Exception:
db_status = "❌ Not connected"
@@ -327,24 +327,26 @@ def status() -> None:
server_status = "⏳ Checking..."
try:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(0.5)
s.connect(("localhost", 8000))
s.close()
server_status = "✅ Running"
except:
except Exception:
server_status = "❌ Not running"
# Check if UI is running (port 8080)
ui_status = "⏳ Checking..."
try:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(0.5)
s.connect(("localhost", 8080))
s.close()
ui_status = "✅ Running"
except:
except Exception:
ui_status = "❌ Not running"
# Check API key
@@ -370,15 +372,13 @@ def status() -> None:
else:
console.print("\n[bold green]All systems operational![/] Skyvern is ready to use.")
@tasks_app.command(name="list")
def list_tasks() -> None:
"""
List recent Skyvern tasks.
"""
console.print(Panel.fit(
"[bold blue]Recent Skyvern Tasks[/]",
subtitle="Retrieving task history"
))
console.print(Panel.fit("[bold blue]Recent Skyvern Tasks[/]", subtitle="Retrieving task history"))
try:
# Initialize Skyvern client
@@ -389,7 +389,9 @@ def list_tasks() -> None:
)
# 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)
tasks = asyncio.run(skyvern_agent.get_tasks())
progress.update(task, completed=1)
@@ -410,7 +412,7 @@ def list_tasks() -> None:
str(task.id),
task.title or "Untitled",
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)
@@ -424,20 +426,18 @@ def list_tasks() -> None:
console.print(f"[bold red]Error listing tasks:[/] {str(e)}")
console.print("[yellow]Make sure your API key is set correctly in .env[/]")
@tasks_app.command(name="create")
def create_task(
prompt: str = typer.Option(..., "--prompt", "-p", help="Task prompt"),
url: str = typer.Option(..., "--url", "-u", help="Starting URL"),
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:
"""
Create and run a new Skyvern task.
"""
console.print(Panel.fit(
"[bold blue]Creating New Skyvern Task[/]",
subtitle="Running browser automation"
))
console.print(Panel.fit("[bold blue]Creating New Skyvern Task[/]", subtitle="Running browser automation"))
console.print(f"[bold]Prompt:[/] {prompt}")
console.print(f"[bold]URL:[/] {url}")
@@ -453,15 +453,14 @@ def create_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)
result = asyncio.run(skyvern_agent.run_task(
prompt=prompt,
url=url,
data_extraction_schema=schema,
user_agent="skyvern-cli"
))
result = asyncio.run(
skyvern_agent.run_task(prompt=prompt, url=url, data_extraction_schema=schema, user_agent="skyvern-cli")
)
progress.update(task, completed=1)
@@ -474,22 +473,22 @@ def create_task(
# Display path to view results
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]")
except Exception as e:
console.print(f"[bold red]Error creating task:[/] {str(e)}")
console.print("[yellow]Make sure your API key is set correctly in .env[/]")
@workflows_app.command(name="list")
def list_workflows() -> None:
"""
List Skyvern workflows.
"""
console.print(Panel.fit(
"[bold blue]Skyvern Workflows[/]",
subtitle="Retrieving available workflows"
))
console.print(Panel.fit("[bold blue]Skyvern Workflows[/]", subtitle="Retrieving available workflows"))
try:
# Initialize Skyvern client
@@ -500,7 +499,9 @@ def list_workflows() -> None:
)
# 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)
workflows = asyncio.run(skyvern_agent.get_workflows())
progress.update(task, completed=1)
@@ -521,7 +522,9 @@ def list_workflows() -> None:
str(workflow.id),
workflow.title or "Untitled",
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)
@@ -535,9 +538,11 @@ def list_workflows() -> None:
console.print(f"[bold red]Error listing workflows:[/] {str(e)}")
console.print("[yellow]Make sure your API key is set correctly in .env[/]")
#----------------------------------------------------
# ----------------------------------------------------
# 5. Streamlined Configuration (Original functions enhanced)
#----------------------------------------------------
# ----------------------------------------------------
def setup_postgresql(no_postgres: bool = False) -> None:
"""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:
console.print("[yellow]![/] No local Postgres detected")
start_postgres = typer.confirm(
"Start a disposable container now? (Choose 'n' if using Docker Compose)",
default=True
"Start a disposable container now? (Choose 'n' if using Docker Compose)", default=True
)
if not start_postgres:
@@ -617,20 +621,27 @@ def setup_llm_providers() -> None:
model_options = []
# Create sections for each provider
providers = [
providers: list[dict[str, Any]] = [
{
"name": "OpenAI",
"env_key": "ENABLE_OPENAI",
"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"],
"setup_message": "To enable OpenAI, you need an API key from your OpenAI account."
"models": [
"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",
"env_key": "ENABLE_ANTHROPIC",
"api_key_env": "ANTHROPIC_API_KEY",
"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",
@@ -641,27 +652,39 @@ def setup_llm_providers() -> None:
"extra_fields": {
"AZURE_DEPLOYMENT": "Enter your Azure deployment name",
"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",
"env_key": "ENABLE_GEMINI",
"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"],
"setup_message": "To enable Gemini, you need an API key from Google AI Studio."
"models": [
"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",
"env_key": "ENABLE_NOVITA",
"api_key_env": "NOVITA_API_KEY",
"models": [
"NOVITA_DEEPSEEK_R1", "NOVITA_DEEPSEEK_V3", "NOVITA_LLAMA_3_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"
"NOVITA_DEEPSEEK_R1",
"NOVITA_DEEPSEEK_V3",
"NOVITA_LLAMA_3_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",
@@ -671,20 +694,18 @@ def setup_llm_providers() -> None:
"extra_fields": {
"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_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": [
{
"question": "Does this model support vision?",
"env_key": "OPENAI_COMPATIBLE_SUPPORTS_VISION",
"value_if_yes": "true",
"value_if_no": "false"
"value_if_no": "false",
}
],
"optional_fields": {
"OPENAI_COMPATIBLE_API_VERSION": "Enter API version (optional, press enter to skip)"
}
}
"optional_fields": {"OPENAI_COMPATIBLE_API_VERSION": "Enter API version (optional, press enter to skip)"},
},
]
# Process each provider
@@ -751,10 +772,7 @@ def setup_llm_providers() -> None:
while True:
try:
model_choice = typer.prompt(
f"Choose a model by number (1-{len(model_options)})",
type=int
)
model_choice = typer.prompt(f"Choose a model by number (1-{len(model_options)})", type=int)
if 1 <= model_choice <= len(model_options):
break
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"))
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": "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:")
@@ -812,7 +838,7 @@ def setup_browser_config() -> tuple[str, Optional[str], Optional[str]]:
console.print("\n[bold]Chrome Remote Debugging Setup:[/]")
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"
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}")
# 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)
time.sleep(2)
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:
try:
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:
console.print(f" Browser: {browser_info['Browser']}")
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:
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:
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")
@@ -1046,20 +1080,20 @@ def setup_mcp() -> None:
"name": "Claude Desktop",
"check_fn": lambda: is_claude_desktop_installed(host_system),
"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",
"check_fn": lambda: is_cursor_installed(host_system),
"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",
"check_fn": lambda: is_windsurf_installed(host_system),
"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
@@ -1067,7 +1101,8 @@ def setup_mcp() -> None:
console.print(f"\n[bold]Setting up {integration['name']}[/]")
# 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"Skipping {integration['name']} integration setup.")
continue
@@ -1075,7 +1110,8 @@ def setup_mcp() -> None:
# Ask user if they want to set up this integration
if typer.confirm(f"Configure {integration['name']} integration?", default=True):
# 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")
else:
console.print(f"[red]×[/] Error configuring {integration['name']} integration")
@@ -1097,8 +1133,7 @@ def setup_mcp_config() -> str:
if not python_paths:
console.print("[yellow]![/] Could not find Python 3.11 installation")
path_to_env = typer.prompt(
"Enter the full path to your Python 3.11 environment",
default="/opt/homebrew/bin/python3.11"
"Enter the full path to your Python 3.11 environment", default="/opt/homebrew/bin/python3.11"
)
else:
# Show found Python installations
@@ -1112,10 +1147,7 @@ def setup_mcp_config() -> str:
if len(python_paths) > 1:
# Let user choose if multiple were found
choice = typer.prompt(
"Which Python installation do you want to use? (Enter number)",
default="1"
)
choice = typer.prompt("Which Python installation do you want to use? (Enter number)", default="1")
try:
index = int(choice) - 1
if 0 <= index < len(python_paths):
@@ -1210,3 +1242,19 @@ def is_cursor_installed(host_system: str) -> bool:
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,
title: 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,
max_steps: int | None = None,
wait_for_completion: bool = True,