mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-06 10:47:12 +08:00
Compare commits
2 Commits
bb/gui
...
feat/tinyf
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
739b30bc02 | ||
|
|
f41ebf7785 |
@@ -1818,6 +1818,29 @@ OPTIONAL_ENV_VARS = {
|
|||||||
"password": False,
|
"password": False,
|
||||||
"category": "tool",
|
"category": "tool",
|
||||||
},
|
},
|
||||||
|
"TINYFISH_API_KEY": {
|
||||||
|
"description": "TinyFish API key for cloud browser, search, fetch, and agent",
|
||||||
|
"prompt": "TinyFish API key",
|
||||||
|
"url": "https://agent.tinyfish.ai/api-keys",
|
||||||
|
"tools": ["browser_navigate", "browser_click"],
|
||||||
|
"password": True,
|
||||||
|
"category": "tool",
|
||||||
|
},
|
||||||
|
"TINYFISH_API_URL": {
|
||||||
|
"description": "TinyFish browser API URL override (optional, for staging/dev)",
|
||||||
|
"prompt": "TinyFish API URL (leave empty for default)",
|
||||||
|
"url": None,
|
||||||
|
"tools": ["browser_navigate", "browser_click"],
|
||||||
|
"password": False,
|
||||||
|
"category": "tool",
|
||||||
|
},
|
||||||
|
"TINYFISH_BROWSER_TIMEOUT": {
|
||||||
|
"description": "TinyFish browser session inactivity timeout in seconds (optional, default 300)",
|
||||||
|
"prompt": "Browser session timeout (seconds)",
|
||||||
|
"tools": ["browser_navigate", "browser_click"],
|
||||||
|
"password": False,
|
||||||
|
"category": "tool",
|
||||||
|
},
|
||||||
"CAMOFOX_URL": {
|
"CAMOFOX_URL": {
|
||||||
"description": "Camofox browser server URL for local anti-detection browsing (e.g. http://localhost:9377)",
|
"description": "Camofox browser server URL for local anti-detection browsing (e.g. http://localhost:9377)",
|
||||||
"prompt": "Camofox server URL",
|
"prompt": "Camofox server URL",
|
||||||
@@ -4433,6 +4456,7 @@ def show_config():
|
|||||||
("TAVILY_API_KEY", "Tavily"),
|
("TAVILY_API_KEY", "Tavily"),
|
||||||
("BROWSERBASE_API_KEY", "Browserbase"),
|
("BROWSERBASE_API_KEY", "Browserbase"),
|
||||||
("BROWSER_USE_API_KEY", "Browser Use"),
|
("BROWSER_USE_API_KEY", "Browser Use"),
|
||||||
|
("TINYFISH_API_KEY", "TinyFish"),
|
||||||
("FAL_KEY", "FAL"),
|
("FAL_KEY", "FAL"),
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -4617,6 +4641,7 @@ def set_config_value(key: str, value: str):
|
|||||||
'FIRECRAWL_GATEWAY_URL', 'TOOL_GATEWAY_DOMAIN', 'TOOL_GATEWAY_SCHEME',
|
'FIRECRAWL_GATEWAY_URL', 'TOOL_GATEWAY_DOMAIN', 'TOOL_GATEWAY_SCHEME',
|
||||||
'TOOL_GATEWAY_USER_TOKEN', 'TAVILY_API_KEY',
|
'TOOL_GATEWAY_USER_TOKEN', 'TAVILY_API_KEY',
|
||||||
'BROWSERBASE_API_KEY', 'BROWSERBASE_PROJECT_ID', 'BROWSER_USE_API_KEY',
|
'BROWSERBASE_API_KEY', 'BROWSERBASE_PROJECT_ID', 'BROWSER_USE_API_KEY',
|
||||||
|
'TINYFISH_API_KEY', 'TINYFISH_API_URL', 'TINYFISH_BROWSER_TIMEOUT',
|
||||||
'FAL_KEY', 'TELEGRAM_BOT_TOKEN', 'DISCORD_BOT_TOKEN',
|
'FAL_KEY', 'TELEGRAM_BOT_TOKEN', 'DISCORD_BOT_TOKEN',
|
||||||
'TERMINAL_SSH_HOST', 'TERMINAL_SSH_USER', 'TERMINAL_SSH_KEY',
|
'TERMINAL_SSH_HOST', 'TERMINAL_SSH_USER', 'TERMINAL_SSH_KEY',
|
||||||
'SUDO_PASSWORD', 'SLACK_BOT_TOKEN', 'SLACK_APP_TOKEN',
|
'SUDO_PASSWORD', 'SLACK_BOT_TOKEN', 'SLACK_APP_TOKEN',
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ def _browser_label(current_provider: str) -> str:
|
|||||||
"browserbase": "Browserbase",
|
"browserbase": "Browserbase",
|
||||||
"browser-use": "Browser Use",
|
"browser-use": "Browser Use",
|
||||||
"firecrawl": "Firecrawl",
|
"firecrawl": "Firecrawl",
|
||||||
|
"tinyfish": "TinyFish",
|
||||||
"camofox": "Camofox",
|
"camofox": "Camofox",
|
||||||
"local": "Local browser",
|
"local": "Local browser",
|
||||||
}
|
}
|
||||||
@@ -169,6 +170,7 @@ def _resolve_browser_feature_state(
|
|||||||
direct_browserbase: bool,
|
direct_browserbase: bool,
|
||||||
direct_browser_use: bool,
|
direct_browser_use: bool,
|
||||||
direct_firecrawl: bool,
|
direct_firecrawl: bool,
|
||||||
|
direct_tinyfish: bool,
|
||||||
managed_browser_available: bool,
|
managed_browser_available: bool,
|
||||||
) -> tuple[str, bool, bool, bool]:
|
) -> tuple[str, bool, bool, bool]:
|
||||||
"""Resolve browser availability using the same precedence as runtime."""
|
"""Resolve browser availability using the same precedence as runtime."""
|
||||||
@@ -196,6 +198,10 @@ def _resolve_browser_feature_state(
|
|||||||
available = bool(browser_local_available and direct_firecrawl)
|
available = bool(browser_local_available and direct_firecrawl)
|
||||||
active = bool(browser_tool_enabled and available)
|
active = bool(browser_tool_enabled and available)
|
||||||
return current_provider, available, active, False
|
return current_provider, available, active, False
|
||||||
|
if current_provider == "tinyfish":
|
||||||
|
available = bool(browser_local_available and direct_tinyfish)
|
||||||
|
active = bool(browser_tool_enabled and available)
|
||||||
|
return current_provider, available, active, False
|
||||||
if current_provider == "camofox":
|
if current_provider == "camofox":
|
||||||
return current_provider, False, False, False
|
return current_provider, False, False, False
|
||||||
|
|
||||||
@@ -286,6 +292,7 @@ def get_nous_subscription_features(
|
|||||||
direct_camofox = bool(get_env_value("CAMOFOX_URL"))
|
direct_camofox = bool(get_env_value("CAMOFOX_URL"))
|
||||||
direct_browserbase = bool(get_env_value("BROWSERBASE_API_KEY") and get_env_value("BROWSERBASE_PROJECT_ID"))
|
direct_browserbase = bool(get_env_value("BROWSERBASE_API_KEY") and get_env_value("BROWSERBASE_PROJECT_ID"))
|
||||||
direct_browser_use = bool(get_env_value("BROWSER_USE_API_KEY"))
|
direct_browser_use = bool(get_env_value("BROWSER_USE_API_KEY"))
|
||||||
|
direct_tinyfish = bool(get_env_value("TINYFISH_API_KEY"))
|
||||||
direct_modal = has_direct_modal_credentials()
|
direct_modal = has_direct_modal_credentials()
|
||||||
|
|
||||||
# When use_gateway is set, suppress direct credentials for managed detection
|
# When use_gateway is set, suppress direct credentials for managed detection
|
||||||
@@ -363,6 +370,7 @@ def get_nous_subscription_features(
|
|||||||
direct_browserbase=direct_browserbase,
|
direct_browserbase=direct_browserbase,
|
||||||
direct_browser_use=direct_browser_use,
|
direct_browser_use=direct_browser_use,
|
||||||
direct_firecrawl=direct_firecrawl,
|
direct_firecrawl=direct_firecrawl,
|
||||||
|
direct_tinyfish=direct_tinyfish,
|
||||||
managed_browser_available=managed_browser_available,
|
managed_browser_available=managed_browser_available,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -384,7 +384,7 @@ def _print_setup_summary(config: dict, hermes_home):
|
|||||||
else:
|
else:
|
||||||
tool_status.append(("Web Search & Extract", False, "EXA_API_KEY, PARALLEL_API_KEY, FIRECRAWL_API_KEY/FIRECRAWL_API_URL, or TAVILY_API_KEY"))
|
tool_status.append(("Web Search & Extract", False, "EXA_API_KEY, PARALLEL_API_KEY, FIRECRAWL_API_KEY/FIRECRAWL_API_URL, or TAVILY_API_KEY"))
|
||||||
|
|
||||||
# Browser tools (local Chromium, Camofox, Browserbase, Browser Use, or Firecrawl)
|
# Browser tools (local Chromium, Camofox, Browserbase, Browser Use, Firecrawl, or TinyFish)
|
||||||
browser_provider = subscription_features.browser.current_provider
|
browser_provider = subscription_features.browser.current_provider
|
||||||
if subscription_features.browser.managed_by_nous:
|
if subscription_features.browser.managed_by_nous:
|
||||||
tool_status.append(("Browser Automation (Nous Browser Use)", True, None))
|
tool_status.append(("Browser Automation (Nous Browser Use)", True, None))
|
||||||
@@ -406,6 +406,10 @@ def _print_setup_summary(config: dict, hermes_home):
|
|||||||
)
|
)
|
||||||
elif browser_provider == "Camofox":
|
elif browser_provider == "Camofox":
|
||||||
missing_browser_hint = "CAMOFOX_URL"
|
missing_browser_hint = "CAMOFOX_URL"
|
||||||
|
elif browser_provider == "TinyFish":
|
||||||
|
missing_browser_hint = (
|
||||||
|
"npm install -g agent-browser and set TINYFISH_API_KEY"
|
||||||
|
)
|
||||||
elif browser_provider == "Local browser":
|
elif browser_provider == "Local browser":
|
||||||
missing_browser_hint = "npm install -g agent-browser"
|
missing_browser_hint = "npm install -g agent-browser"
|
||||||
tool_status.append(
|
tool_status.append(
|
||||||
|
|||||||
@@ -379,6 +379,15 @@ TOOL_CATEGORIES = {
|
|||||||
"browser_provider": "firecrawl",
|
"browser_provider": "firecrawl",
|
||||||
"post_setup": "agent_browser",
|
"post_setup": "agent_browser",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "TinyFish",
|
||||||
|
"tag": "Low latency browser with stealth & proxies",
|
||||||
|
"env_vars": [
|
||||||
|
{"key": "TINYFISH_API_KEY", "prompt": "TinyFish API key", "url": "https://agent.tinyfish.ai/api-keys"},
|
||||||
|
],
|
||||||
|
"browser_provider": "tinyfish",
|
||||||
|
"post_setup": "agent_browser",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "Camofox",
|
"name": "Camofox",
|
||||||
"badge": "free · local",
|
"badge": "free · local",
|
||||||
|
|||||||
@@ -673,6 +673,7 @@ AUTHOR_MAP = {
|
|||||||
"web3blind@gmail.com": "web3blind",
|
"web3blind@gmail.com": "web3blind",
|
||||||
"ztzheng@163.com": "chengoak", # PR #17467
|
"ztzheng@163.com": "chengoak", # PR #17467
|
||||||
"24110240104@m.fudan.edu.cn": "YuShu", # co-author only
|
"24110240104@m.fudan.edu.cn": "YuShu", # co-author only
|
||||||
|
"simantak@mac.local": "simantak-dabhade", # PR #6329
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -58,3 +58,11 @@ class TestCamofoxConfigDefaults:
|
|||||||
|
|
||||||
browser_cfg = DEFAULT_CONFIG["browser"]
|
browser_cfg = DEFAULT_CONFIG["browser"]
|
||||||
assert browser_cfg["camofox"]["managed_persistence"] is False
|
assert browser_cfg["camofox"]["managed_persistence"] is False
|
||||||
|
|
||||||
|
def test_config_version_is_positive_int(self):
|
||||||
|
from hermes_cli.config import DEFAULT_CONFIG
|
||||||
|
|
||||||
|
# Invariant: config version must be a positive integer.
|
||||||
|
# Don't assert a specific number — that's a change-detector.
|
||||||
|
assert isinstance(DEFAULT_CONFIG["_config_version"], int)
|
||||||
|
assert DEFAULT_CONFIG["_config_version"] > 0
|
||||||
|
|||||||
129
tools/browser_providers/tinyfish.py
Normal file
129
tools/browser_providers/tinyfish.py
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
"""TinyFish cloud browser provider."""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import uuid
|
||||||
|
from typing import Any, Dict, Optional
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from tools.browser_providers.base import CloudBrowserProvider
|
||||||
|
from tools.managed_tool_gateway import resolve_managed_tool_gateway
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
_DEFAULT_BASE_URL = "https://api.browser.tinyfish.ai"
|
||||||
|
_DEFAULT_TIMEOUT_SECONDS = 300
|
||||||
|
|
||||||
|
|
||||||
|
class TinyFishBrowserProvider(CloudBrowserProvider):
|
||||||
|
"""TinyFish (https://tinyfish.ai) cloud browser backend."""
|
||||||
|
|
||||||
|
def provider_name(self) -> str:
|
||||||
|
return "TinyFish"
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Config resolution (direct API key OR managed Nous gateway)
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def _get_config_or_none(self) -> Optional[Dict[str, Any]]:
|
||||||
|
api_key = os.environ.get("TINYFISH_API_KEY")
|
||||||
|
if api_key:
|
||||||
|
return {
|
||||||
|
"api_key": api_key,
|
||||||
|
"base_url": os.environ.get("TINYFISH_API_URL", _DEFAULT_BASE_URL).rstrip("/"),
|
||||||
|
"managed_mode": False,
|
||||||
|
}
|
||||||
|
|
||||||
|
managed = resolve_managed_tool_gateway("tinyfish")
|
||||||
|
if managed is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return {
|
||||||
|
"api_key": managed.nous_user_token,
|
||||||
|
"base_url": managed.gateway_origin.rstrip("/"),
|
||||||
|
"managed_mode": True,
|
||||||
|
}
|
||||||
|
|
||||||
|
def _get_config(self) -> Dict[str, Any]:
|
||||||
|
config = self._get_config_or_none()
|
||||||
|
if config is None:
|
||||||
|
raise ValueError(
|
||||||
|
"TinyFish requires a TINYFISH_API_KEY environment variable. "
|
||||||
|
"Get your API key at https://agent.tinyfish.ai/api-keys"
|
||||||
|
)
|
||||||
|
return config
|
||||||
|
|
||||||
|
def is_configured(self) -> bool:
|
||||||
|
return self._get_config_or_none() is not None
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Session lifecycle
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def _headers(self, config: Dict[str, Any]) -> Dict[str, str]:
|
||||||
|
return {
|
||||||
|
"X-API-Key": config["api_key"],
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
}
|
||||||
|
|
||||||
|
def create_session(self, task_id: str) -> Dict[str, object]:
|
||||||
|
config = self._get_config()
|
||||||
|
|
||||||
|
timeout_seconds = _DEFAULT_TIMEOUT_SECONDS
|
||||||
|
try:
|
||||||
|
timeout_seconds = int(os.environ.get("TINYFISH_BROWSER_TIMEOUT", str(_DEFAULT_TIMEOUT_SECONDS)))
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
response = requests.post(
|
||||||
|
config["base_url"],
|
||||||
|
headers=self._headers(config),
|
||||||
|
json={"timeout_seconds": timeout_seconds},
|
||||||
|
timeout=30,
|
||||||
|
)
|
||||||
|
|
||||||
|
if response.status_code in (401, 403):
|
||||||
|
raise ValueError(
|
||||||
|
f"TinyFish authentication failed (HTTP {response.status_code}). "
|
||||||
|
"Check your TINYFISH_API_KEY at https://agent.tinyfish.ai/api-keys"
|
||||||
|
)
|
||||||
|
if response.status_code == 402:
|
||||||
|
raise ValueError(
|
||||||
|
"TinyFish browser session failed: insufficient credits or no active subscription. "
|
||||||
|
"Check your account at https://agent.tinyfish.ai"
|
||||||
|
)
|
||||||
|
if response.status_code == 404:
|
||||||
|
raise ValueError(
|
||||||
|
"TinyFish browser API is not enabled on your plan. "
|
||||||
|
"Contact support or upgrade at https://agent.tinyfish.ai"
|
||||||
|
)
|
||||||
|
if not response.ok:
|
||||||
|
raise RuntimeError(
|
||||||
|
f"Failed to create TinyFish browser session: "
|
||||||
|
f"{response.status_code} {response.text[:200]}"
|
||||||
|
)
|
||||||
|
|
||||||
|
data = response.json()
|
||||||
|
session_name = f"hermes_{task_id}_{uuid.uuid4().hex[:8]}"
|
||||||
|
|
||||||
|
logger.info("Created TinyFish browser session %s", session_name)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"session_name": session_name,
|
||||||
|
"bb_session_id": data["session_id"],
|
||||||
|
"cdp_url": data["cdp_url"],
|
||||||
|
"features": {"tinyfish": True},
|
||||||
|
}
|
||||||
|
|
||||||
|
def close_session(self, session_id: str) -> bool:
|
||||||
|
# TinyFish has no explicit delete endpoint — sessions auto-expire on inactivity timeout.
|
||||||
|
logger.debug(
|
||||||
|
"TinyFish sessions expire automatically on inactivity — no close call needed for %s",
|
||||||
|
session_id,
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def emergency_cleanup(self, session_id: str) -> None:
|
||||||
|
# No-op: TinyFish sessions are cleaned up server-side on inactivity.
|
||||||
|
logger.debug("TinyFish emergency_cleanup skipped for %s — auto-expiry handles cleanup", session_id)
|
||||||
@@ -83,6 +83,7 @@ from tools.browser_providers.base import CloudBrowserProvider
|
|||||||
from tools.browser_providers.browserbase import BrowserbaseProvider
|
from tools.browser_providers.browserbase import BrowserbaseProvider
|
||||||
from tools.browser_providers.browser_use import BrowserUseProvider
|
from tools.browser_providers.browser_use import BrowserUseProvider
|
||||||
from tools.browser_providers.firecrawl import FirecrawlProvider
|
from tools.browser_providers.firecrawl import FirecrawlProvider
|
||||||
|
from tools.browser_providers.tinyfish import TinyFishBrowserProvider
|
||||||
from tools.tool_backend_helpers import normalize_browser_cloud_provider
|
from tools.tool_backend_helpers import normalize_browser_cloud_provider
|
||||||
|
|
||||||
# Camofox local anti-detection browser backend (optional).
|
# Camofox local anti-detection browser backend (optional).
|
||||||
@@ -391,6 +392,7 @@ _PROVIDER_REGISTRY: Dict[str, type] = {
|
|||||||
"browserbase": BrowserbaseProvider,
|
"browserbase": BrowserbaseProvider,
|
||||||
"browser-use": BrowserUseProvider,
|
"browser-use": BrowserUseProvider,
|
||||||
"firecrawl": FirecrawlProvider,
|
"firecrawl": FirecrawlProvider,
|
||||||
|
"tinyfish": TinyFishBrowserProvider,
|
||||||
}
|
}
|
||||||
|
|
||||||
_cached_cloud_provider: Optional[CloudBrowserProvider] = None
|
_cached_cloud_provider: Optional[CloudBrowserProvider] = None
|
||||||
|
|||||||
@@ -118,6 +118,9 @@ For native Anthropic auth, Hermes prefers Claude Code's own credential files whe
|
|||||||
| `BROWSERBASE_PROJECT_ID` | Browserbase project ID |
|
| `BROWSERBASE_PROJECT_ID` | Browserbase project ID |
|
||||||
| `BROWSER_USE_API_KEY` | Browser Use cloud browser API key ([browser-use.com](https://browser-use.com/)) |
|
| `BROWSER_USE_API_KEY` | Browser Use cloud browser API key ([browser-use.com](https://browser-use.com/)) |
|
||||||
| `FIRECRAWL_BROWSER_TTL` | Firecrawl browser session TTL in seconds (default: 300) |
|
| `FIRECRAWL_BROWSER_TTL` | Firecrawl browser session TTL in seconds (default: 300) |
|
||||||
|
| `TINYFISH_API_KEY` | TinyFish API key for cloud browser ([agent.tinyfish.ai](https://agent.tinyfish.ai/api-keys)) |
|
||||||
|
| `TINYFISH_API_URL` | TinyFish browser API URL override for staging/dev (optional) |
|
||||||
|
| `TINYFISH_BROWSER_TIMEOUT` | TinyFish browser session inactivity timeout in seconds (default: 300) |
|
||||||
| `BROWSER_CDP_URL` | Chrome DevTools Protocol URL for local browser (set via `/browser connect`, e.g. `ws://localhost:9222`) |
|
| `BROWSER_CDP_URL` | Chrome DevTools Protocol URL for local browser (set via `/browser connect`, e.g. `ws://localhost:9222`) |
|
||||||
| `CAMOFOX_URL` | Camofox local anti-detection browser URL (default: `http://localhost:9377`) |
|
| `CAMOFOX_URL` | Camofox local anti-detection browser URL (default: `http://localhost:9377`) |
|
||||||
| `BROWSER_INACTIVITY_TIMEOUT` | Browser session inactivity timeout in seconds |
|
| `BROWSER_INACTIVITY_TIMEOUT` | Browser session inactivity timeout in seconds |
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ Hermes Agent includes a full browser automation toolset with multiple backend op
|
|||||||
- **Browserbase cloud mode** via [Browserbase](https://browserbase.com) for managed cloud browsers and anti-bot tooling
|
- **Browserbase cloud mode** via [Browserbase](https://browserbase.com) for managed cloud browsers and anti-bot tooling
|
||||||
- **Browser Use cloud mode** via [Browser Use](https://browser-use.com) as an alternative cloud browser provider
|
- **Browser Use cloud mode** via [Browser Use](https://browser-use.com) as an alternative cloud browser provider
|
||||||
- **Firecrawl cloud mode** via [Firecrawl](https://firecrawl.dev) for cloud browsers with built-in scraping
|
- **Firecrawl cloud mode** via [Firecrawl](https://firecrawl.dev) for cloud browsers with built-in scraping
|
||||||
|
- **TinyFish cloud mode** via [TinyFish](https://tinyfish.ai) for fast cloud CDP browsers
|
||||||
- **Camofox local mode** via [Camofox](https://github.com/jo-inc/camofox-browser) for local anti-detection browsing (Firefox-based fingerprint spoofing)
|
- **Camofox local mode** via [Camofox](https://github.com/jo-inc/camofox-browser) for local anti-detection browsing (Firefox-based fingerprint spoofing)
|
||||||
- **Local Chrome via CDP** — connect browser tools to your own Chrome instance using `/browser connect`
|
- **Local Chrome via CDP** — connect browser tools to your own Chrome instance using `/browser connect`
|
||||||
- **Local browser mode** via the `agent-browser` CLI and a local Chromium installation
|
- **Local browser mode** via the `agent-browser` CLI and a local Chromium installation
|
||||||
@@ -86,6 +87,29 @@ FIRECRAWL_API_URL=http://localhost:3002
|
|||||||
FIRECRAWL_BROWSER_TTL=600
|
FIRECRAWL_BROWSER_TTL=600
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### TinyFish cloud mode
|
||||||
|
|
||||||
|
To use TinyFish as your cloud browser provider, add:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Add to ~/.hermes/.env
|
||||||
|
TINYFISH_API_KEY=your_key_here
|
||||||
|
```
|
||||||
|
|
||||||
|
Get your API key at [agent.tinyfish.ai/api-keys](https://agent.tinyfish.ai/api-keys). Then select TinyFish as your browser provider:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
hermes setup tools
|
||||||
|
# → Browser Automation → TinyFish
|
||||||
|
```
|
||||||
|
|
||||||
|
Optional settings:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Session inactivity timeout in seconds (default: 300, capped to your plan maximum)
|
||||||
|
TINYFISH_BROWSER_TIMEOUT=600
|
||||||
|
```
|
||||||
|
|
||||||
### Hybrid routing: cloud for public URLs, local for LAN/localhost
|
### Hybrid routing: cloud for public URLs, local for LAN/localhost
|
||||||
|
|
||||||
When a cloud provider is configured, Hermes auto-spawns a **local Chromium sidecar**
|
When a cloud provider is configured, Hermes auto-spawns a **local Chromium sidecar**
|
||||||
|
|||||||
Reference in New Issue
Block a user