Fix ruff & mypy errors (#2394)
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user