@tiny-fish/hermes 0.2.3 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/package.json +1 -1
- package/plugin.yaml +1 -1
- package/pyproject.toml +1 -1
- package/tinyfish_hermes/rest_client.py +35 -0
- package/tinyfish_hermes/setup_cli.py +18 -0
package/README.md
CHANGED
|
@@ -92,6 +92,10 @@ tinyfish:
|
|
|
92
92
|
|
|
93
93
|
When the `tinyfish` MCP server is also configured in Hermes (`mcp_servers.tinyfish.url: https://agent.tinyfish.ai/mcp`), the plugin injects once-per-context guidance steering the model between the generic web tools and the native MCP tools. Disable with `tinyfish.routing_context: false`.
|
|
94
94
|
|
|
95
|
+
## Telemetry
|
|
96
|
+
|
|
97
|
+
Each `hermes tinyfish setup` run sends one event to TinyFish, whether it succeeds or fails: the outcome (for example `completed` or `key_rejected`), where the key came from (`flag`, `prompt`, `env` or `none`), the plugin version and OS. It sends a one-way hash of the API key, never the key itself, so the event joins your account. Set `TINYFISH_NO_TELEMETRY=1` in your shell or in `~/.hermes/.env` to turn it off.
|
|
98
|
+
|
|
95
99
|
## Development
|
|
96
100
|
|
|
97
101
|
```bash
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tiny-fish/hermes",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "TinyFish web provider plugin for Hermes Agent, delivered via npm for the TinyFish CLI. Installed by `tinyfish connect hermes`; not a JavaScript library.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://docs.tinyfish.ai",
|
package/plugin.yaml
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
name: tinyfish
|
|
2
|
-
version: 0.2.
|
|
2
|
+
version: 0.2.4
|
|
3
3
|
description: "TinyFish: the web, built into Hermes. Free Search and Fetch power Hermes's own web_search and web_extract, and when a site needs clicks, forms or a login, a remote browser and the tinyfish_agent web agent handle it."
|
|
4
4
|
author: TinyFish
|
|
5
5
|
kind: backend
|
package/pyproject.toml
CHANGED
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tinyfish-hermes"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.4"
|
|
8
8
|
description = "TinyFish plugin for Hermes Agent - Search, Fetch, Browser, and the tinyfish_agent web agent"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -2,8 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import hashlib
|
|
5
6
|
import math
|
|
7
|
+
import os
|
|
8
|
+
import sys
|
|
6
9
|
import time
|
|
10
|
+
import uuid
|
|
7
11
|
from typing import Any, NoReturn, cast
|
|
8
12
|
|
|
9
13
|
import httpx
|
|
@@ -17,6 +21,7 @@ AGENT_URL = "https://agent.tinyfish.ai"
|
|
|
17
21
|
WALLET_URL = f"{AGENT_URL}/v1/wallet"
|
|
18
22
|
SEARCH_USAGE_URL = f"{SEARCH_URL}/usage"
|
|
19
23
|
FETCH_USAGE_URL = f"{FETCH_URL}/usage"
|
|
24
|
+
TELEMETRY_URL = f"{AGENT_URL}/api/cli/connect-event"
|
|
20
25
|
|
|
21
26
|
_BROWSER_CLOSE_MAX_ATTEMPTS = 3
|
|
22
27
|
_BROWSER_CLOSE_RETRYABLE_STATUSES = frozenset({409, 429, 500, 503, 504})
|
|
@@ -334,6 +339,36 @@ def start_run(
|
|
|
334
339
|
)
|
|
335
340
|
|
|
336
341
|
|
|
342
|
+
def telemetry_disabled(value: str | None = None) -> bool:
|
|
343
|
+
# Matches the TinyFish CLI: anything but empty, 0 or false opts out.
|
|
344
|
+
if value is None:
|
|
345
|
+
value = os.environ.get("TINYFISH_NO_TELEMETRY", "")
|
|
346
|
+
return value.strip().lower() not in ("", "0", "false")
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def report_setup(outcome: str, *, key_source: str, api_key: str = "") -> None:
|
|
350
|
+
"""Record a ``hermes tinyfish setup`` outcome; never raises."""
|
|
351
|
+
|
|
352
|
+
if telemetry_disabled():
|
|
353
|
+
return
|
|
354
|
+
headers = {"Content-Type": "application/json"}
|
|
355
|
+
if api_key:
|
|
356
|
+
headers["x-api-key-lookup"] = hashlib.sha256(api_key.encode()).hexdigest()
|
|
357
|
+
body = {
|
|
358
|
+
"event": "harness_plugin_setup",
|
|
359
|
+
"harness": "hermes",
|
|
360
|
+
"plugin_version": __version__,
|
|
361
|
+
"outcome": outcome,
|
|
362
|
+
"key_source": key_source,
|
|
363
|
+
"runtime_platform": sys.platform,
|
|
364
|
+
"setup_id": str(uuid.uuid4()),
|
|
365
|
+
}
|
|
366
|
+
try:
|
|
367
|
+
httpx.post(TELEMETRY_URL, headers=headers, json=body, timeout=2.0)
|
|
368
|
+
except Exception:
|
|
369
|
+
pass
|
|
370
|
+
|
|
371
|
+
|
|
337
372
|
def get_run(run_id: str, *, api_key: str) -> dict[str, Any]:
|
|
338
373
|
return _agent_request("GET", f"/v1/runs/{run_id}", api_key=api_key)
|
|
339
374
|
|
|
@@ -257,20 +257,36 @@ def cmd_setup(
|
|
|
257
257
|
provider: TinyFishWebSearchProvider | None = None,
|
|
258
258
|
) -> int:
|
|
259
259
|
api_key = (getattr(args, "api_key", None) or "").strip()
|
|
260
|
+
key_source = "flag" if api_key else "none"
|
|
260
261
|
existing = _api_key_env_var()
|
|
261
262
|
if not api_key and existing:
|
|
262
263
|
print(f"Using the TinyFish API key from {existing}")
|
|
264
|
+
key_source = "env"
|
|
263
265
|
elif not api_key and sys.stdin.isatty():
|
|
264
266
|
api_key = _prompt_for_key()
|
|
267
|
+
key_source = "prompt" if api_key else "none"
|
|
268
|
+
|
|
269
|
+
# Catalog installs skip `tinyfish connect`; this is their install signal.
|
|
270
|
+
def report(outcome: str) -> None:
|
|
271
|
+
# Hermes keeps settings in its .env, which never reaches os.environ.
|
|
272
|
+
if rest_client.telemetry_disabled(_get_env("TINYFISH_NO_TELEMETRY")):
|
|
273
|
+
return
|
|
274
|
+
rest_client.report_setup(
|
|
275
|
+
outcome, key_source=key_source, api_key=api_key or _api_key()
|
|
276
|
+
)
|
|
277
|
+
|
|
265
278
|
if api_key:
|
|
266
279
|
rejection = _validate_key(api_key)
|
|
267
280
|
if rejection:
|
|
268
281
|
print(rejection, file=sys.stderr)
|
|
282
|
+
report("key_rejected")
|
|
269
283
|
return 1
|
|
270
284
|
if not _store_api_key(api_key):
|
|
285
|
+
report("save_failed")
|
|
271
286
|
return 1
|
|
272
287
|
elif not existing:
|
|
273
288
|
print(MISSING_KEY_ERROR, file=sys.stderr)
|
|
289
|
+
report("no_key")
|
|
274
290
|
return 1
|
|
275
291
|
|
|
276
292
|
# Route web tools only once a usable key exists, so a failed setup changes nothing.
|
|
@@ -284,8 +300,10 @@ def cmd_setup(
|
|
|
284
300
|
_save_config(config)
|
|
285
301
|
print("Configured Hermes web backends to use TinyFish")
|
|
286
302
|
if not _enable_toolset("web"):
|
|
303
|
+
report("backend_failed")
|
|
287
304
|
return 1
|
|
288
305
|
|
|
306
|
+
report("completed")
|
|
289
307
|
if getattr(args, "live", False):
|
|
290
308
|
doctor_args = argparse.Namespace(json=False, live=True, live_paid=False)
|
|
291
309
|
return cmd_doctor(doctor_args, provider=provider)
|