calvyn-code 0.14.5 → 0.14.6
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/cli.py +65 -20
- package/hermes_cli/__init__.py +1 -1
- package/hermes_cli/config.py +21 -17
- package/hermes_cli/doctor.py +2 -2
- package/package.json +3 -2
- package/pyproject.toml +1 -1
- package/tools/browser_tool.py +12 -7
package/cli.py
CHANGED
|
@@ -23,15 +23,17 @@ except ModuleNotFoundError:
|
|
|
23
23
|
# means UTF-8 stdio setup is skipped on Windows; POSIX is unaffected.
|
|
24
24
|
pass
|
|
25
25
|
|
|
26
|
-
import logging
|
|
27
|
-
import os
|
|
28
|
-
import shutil
|
|
29
|
-
import sys
|
|
30
|
-
import json
|
|
31
|
-
import re
|
|
32
|
-
import concurrent.futures
|
|
33
|
-
import base64
|
|
34
|
-
import
|
|
26
|
+
import logging
|
|
27
|
+
import os
|
|
28
|
+
import shutil
|
|
29
|
+
import sys
|
|
30
|
+
import json
|
|
31
|
+
import re
|
|
32
|
+
import concurrent.futures
|
|
33
|
+
import base64
|
|
34
|
+
import hashlib
|
|
35
|
+
import secrets
|
|
36
|
+
import atexit
|
|
35
37
|
import errno
|
|
36
38
|
import tempfile
|
|
37
39
|
import time
|
|
@@ -2452,7 +2454,7 @@ def _parse_skills_argument(skills: str | list[str] | tuple[str, ...] | None) ->
|
|
|
2452
2454
|
return parsed
|
|
2453
2455
|
|
|
2454
2456
|
|
|
2455
|
-
def save_config_value(key_path: str, value: any) -> bool:
|
|
2457
|
+
def save_config_value(key_path: str, value: any) -> bool:
|
|
2456
2458
|
"""
|
|
2457
2459
|
Save a value to the active config file at the specified key path.
|
|
2458
2460
|
|
|
@@ -2488,9 +2490,50 @@ def save_config_value(key_path: str, value: any) -> bool:
|
|
|
2488
2490
|
pass
|
|
2489
2491
|
|
|
2490
2492
|
return True
|
|
2491
|
-
except Exception as e:
|
|
2492
|
-
logger.error("Failed to save config: %s", e)
|
|
2493
|
-
return False
|
|
2493
|
+
except Exception as e:
|
|
2494
|
+
logger.error("Failed to save config: %s", e)
|
|
2495
|
+
return False
|
|
2496
|
+
|
|
2497
|
+
|
|
2498
|
+
def _ensure_dev_access_gate() -> None:
|
|
2499
|
+
"""Require a one-time dev access code and persist the approval."""
|
|
2500
|
+
try:
|
|
2501
|
+
cfg = load_cli_config() or {}
|
|
2502
|
+
security_cfg = cfg.get("security", {}) if isinstance(cfg, dict) else {}
|
|
2503
|
+
if not isinstance(security_cfg, dict):
|
|
2504
|
+
security_cfg = {}
|
|
2505
|
+
if not security_cfg.get("dev_access_required", True):
|
|
2506
|
+
return
|
|
2507
|
+
if security_cfg.get("dev_access_granted", False):
|
|
2508
|
+
return
|
|
2509
|
+
|
|
2510
|
+
access_code_hash = str(security_cfg.get("dev_access_code_hash") or "").strip().lower()
|
|
2511
|
+
generated_code = ""
|
|
2512
|
+
if not access_code_hash:
|
|
2513
|
+
generated_code = f"CALVYN-{secrets.token_hex(3).upper()}-{secrets.token_hex(3).upper()}"
|
|
2514
|
+
access_code_hash = hashlib.sha256(generated_code.encode("utf-8")).hexdigest()
|
|
2515
|
+
save_config_value("security.dev_access_code_hash", access_code_hash)
|
|
2516
|
+
|
|
2517
|
+
print()
|
|
2518
|
+
print("╔══════════════════════════════════════════════════════════════════════════════╗")
|
|
2519
|
+
print("║ CALVYN DEV АВТОРИЗАЦИЯ ║")
|
|
2520
|
+
print("╠══════════════════════════════════════════════════════════════════════════════╣")
|
|
2521
|
+
print("║ Доступ к режиму разработки защищен одноразовым кодом. ║")
|
|
2522
|
+
if generated_code:
|
|
2523
|
+
print("║ Новый секретный код сгенерирован и сохранен в виде хэша. ║")
|
|
2524
|
+
print(f"║ Код для первого входа: {generated_code:<42}║")
|
|
2525
|
+
else:
|
|
2526
|
+
print("║ Код уже сохранен в системе. Введите секретный ключ для разблокировки. ║")
|
|
2527
|
+
print("╚══════════════════════════════════════════════════════════════════════════════╝")
|
|
2528
|
+
entered = input("Код доступа: ").strip()
|
|
2529
|
+
entered_hash = hashlib.sha256(entered.encode("utf-8")).hexdigest()
|
|
2530
|
+
if entered_hash != access_code_hash:
|
|
2531
|
+
raise SystemExit("Неверный код доступа.")
|
|
2532
|
+
save_config_value("security.dev_access_granted", True)
|
|
2533
|
+
except SystemExit:
|
|
2534
|
+
raise
|
|
2535
|
+
except Exception as exc:
|
|
2536
|
+
print(f"⚠ Dev-доступ не проверен: {exc}")
|
|
2494
2537
|
|
|
2495
2538
|
|
|
2496
2539
|
|
|
@@ -13925,13 +13968,15 @@ def main(
|
|
|
13925
13968
|
# This enables interactive sudo password prompts with timeout
|
|
13926
13969
|
os.environ["HERMES_INTERACTIVE"] = "1"
|
|
13927
13970
|
|
|
13928
|
-
# Handle gateway mode (messaging + cron)
|
|
13929
|
-
if gateway:
|
|
13930
|
-
import asyncio
|
|
13931
|
-
from gateway.run import start_gateway
|
|
13932
|
-
print("Starting Hermes Gateway (messaging platforms)...")
|
|
13933
|
-
asyncio.run(start_gateway())
|
|
13934
|
-
return
|
|
13971
|
+
# Handle gateway mode (messaging + cron)
|
|
13972
|
+
if gateway:
|
|
13973
|
+
import asyncio
|
|
13974
|
+
from gateway.run import start_gateway
|
|
13975
|
+
print("Starting Hermes Gateway (messaging platforms)...")
|
|
13976
|
+
asyncio.run(start_gateway())
|
|
13977
|
+
return
|
|
13978
|
+
|
|
13979
|
+
_ensure_dev_access_gate()
|
|
13935
13980
|
|
|
13936
13981
|
# Skip worktree for list commands (they exit immediately)
|
|
13937
13982
|
if not list_tools and not list_toolsets:
|
package/hermes_cli/__init__.py
CHANGED
package/hermes_cli/config.py
CHANGED
|
@@ -473,7 +473,7 @@ DEFAULT_CONFIG = {
|
|
|
473
473
|
"providers": {},
|
|
474
474
|
"fallback_providers": [],
|
|
475
475
|
"credential_pool_strategies": {},
|
|
476
|
-
"toolsets": ["hermes-cli"],
|
|
476
|
+
"toolsets": ["hermes-cli", "web", "browser"],
|
|
477
477
|
"agent": {
|
|
478
478
|
"max_turns": 90,
|
|
479
479
|
# Inactivity timeout for gateway agent execution (seconds).
|
|
@@ -650,10 +650,11 @@ DEFAULT_CONFIG = {
|
|
|
650
650
|
"allow_private_urls": False, # Allow navigating to private/internal IPs (localhost, 192.168.x.x, etc.)
|
|
651
651
|
# Browser engine for local mode. Passed as ``--engine <value>`` to
|
|
652
652
|
# agent-browser v0.25.3+.
|
|
653
|
-
# "auto"
|
|
654
|
-
# "lightpanda"
|
|
655
|
-
# "chrome"
|
|
656
|
-
#
|
|
653
|
+
# "auto" - use Chrome (default, don't pass --engine at all)
|
|
654
|
+
# "lightpanda" - use Lightpanda (1.3-5.8x faster navigation, no screenshots)
|
|
655
|
+
# "chrome" - explicitly request Chrome
|
|
656
|
+
# "puppeteer" - route local browser execution through Puppeteer Chrome
|
|
657
|
+
# Also settable via AGENT_BROWSER_ENGINE env var.
|
|
657
658
|
"engine": "auto",
|
|
658
659
|
"auto_local_for_private_urls": True, # When a cloud provider is set, auto-spawn local Chromium for LAN/localhost URLs instead of sending them to the cloud
|
|
659
660
|
"cdp_url": "", # Optional persistent CDP endpoint for attaching to an existing Chromium/Chrome
|
|
@@ -1395,18 +1396,21 @@ DEFAULT_CONFIG = {
|
|
|
1395
1396
|
"personalities": {},
|
|
1396
1397
|
|
|
1397
1398
|
# Pre-exec security scanning via tirith
|
|
1398
|
-
"security": {
|
|
1399
|
-
"allow_private_urls": False, # Allow requests to private/internal IPs (for OpenWrt, proxies, VPNs)
|
|
1400
|
-
"redact_secrets": True,
|
|
1401
|
-
"tirith_enabled": True,
|
|
1402
|
-
"tirith_path": "tirith",
|
|
1403
|
-
"tirith_timeout": 5,
|
|
1404
|
-
"tirith_fail_open": True,
|
|
1405
|
-
"
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1399
|
+
"security": {
|
|
1400
|
+
"allow_private_urls": False, # Allow requests to private/internal IPs (for OpenWrt, proxies, VPNs)
|
|
1401
|
+
"redact_secrets": True,
|
|
1402
|
+
"tirith_enabled": True,
|
|
1403
|
+
"tirith_path": "tirith",
|
|
1404
|
+
"tirith_timeout": 5,
|
|
1405
|
+
"tirith_fail_open": True,
|
|
1406
|
+
"dev_access_required": True,
|
|
1407
|
+
"dev_access_code_hash": "",
|
|
1408
|
+
"dev_access_granted": False,
|
|
1409
|
+
"website_blocklist": {
|
|
1410
|
+
"enabled": False,
|
|
1411
|
+
"domains": [],
|
|
1412
|
+
"shared_files": [],
|
|
1413
|
+
},
|
|
1410
1414
|
# Acknowledged supply-chain security advisories. Each entry is the
|
|
1411
1415
|
# ID of an advisory the user has read and acted on (uninstalled the
|
|
1412
1416
|
# compromised package, rotated credentials). Acked advisories no
|
package/hermes_cli/doctor.py
CHANGED
|
@@ -1214,12 +1214,12 @@ def run_doctor(args):
|
|
|
1214
1214
|
if sys.platform == "win32":
|
|
1215
1215
|
check_info(
|
|
1216
1216
|
f"Install with: cd {PROJECT_ROOT} && "
|
|
1217
|
-
|
|
1217
|
+
"npx playwright install chromium && npm install puppeteer"
|
|
1218
1218
|
)
|
|
1219
1219
|
else:
|
|
1220
1220
|
check_info(
|
|
1221
1221
|
f"Install with: cd {PROJECT_ROOT} && "
|
|
1222
|
-
|
|
1222
|
+
"npx playwright install --with-deps chromium && npm install puppeteer"
|
|
1223
1223
|
)
|
|
1224
1224
|
elif _is_termux():
|
|
1225
1225
|
check_info("Node.js not found (browser tools are optional in the tested Termux path)")
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "calvyn-code",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.6",
|
|
4
4
|
"description": "Calvyn Code — AI агент с инструментами, мессенджерами и локальным CLI",
|
|
5
5
|
"bin": {
|
|
6
6
|
"calvyn": "./bin/calvyn.js",
|
|
@@ -33,7 +33,8 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@askjo/camofox-browser": "^1.5.2",
|
|
36
|
-
"agent-browser": "^0.26.0"
|
|
36
|
+
"agent-browser": "^0.26.0",
|
|
37
|
+
"puppeteer": "^25.0.2"
|
|
37
38
|
},
|
|
38
39
|
"overrides": {
|
|
39
40
|
"lodash": "4.18.1"
|
package/pyproject.toml
CHANGED
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "calvyn-code"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.1.0"
|
|
8
8
|
description = "The self-improving AI agent — creates skills from experience, improves them during use, and runs anywhere"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
package/tools/browser_tool.py
CHANGED
|
@@ -520,15 +520,15 @@ _auto_local_for_private_urls_resolved = False
|
|
|
520
520
|
_cached_auto_local_for_private_urls: bool = True
|
|
521
521
|
|
|
522
522
|
|
|
523
|
-
def _get_browser_engine() -> str:
|
|
524
|
-
"""Return the configured browser engine (``auto``, ``lightpanda``, or ``
|
|
523
|
+
def _get_browser_engine() -> str:
|
|
524
|
+
"""Return the configured browser engine (``auto``, ``lightpanda``, ``chrome``, or ``puppeteer``).
|
|
525
525
|
|
|
526
526
|
Reads ``config["browser"]["engine"]`` once and caches the result.
|
|
527
527
|
Falls back to the ``AGENT_BROWSER_ENGINE`` env var, then ``auto``.
|
|
528
528
|
|
|
529
529
|
``auto`` means: don't pass ``--engine`` at all (agent-browser defaults to
|
|
530
|
-
Chrome). ``lightpanda`` or ``
|
|
531
|
-
``--engine <value>`` to agent-browser v0.25.3+.
|
|
530
|
+
Chrome). ``lightpanda``, ``chrome``, or ``puppeteer`` are forwarded as
|
|
531
|
+
``--engine <value>`` to agent-browser v0.25.3+.
|
|
532
532
|
|
|
533
533
|
Lightpanda is 1.3-5.8x faster on navigation but has no graphical
|
|
534
534
|
renderer (no screenshots).
|
|
@@ -556,8 +556,10 @@ def _get_browser_engine() -> str:
|
|
|
556
556
|
if env_val:
|
|
557
557
|
_cached_browser_engine = env_val
|
|
558
558
|
|
|
559
|
-
# Validate
|
|
560
|
-
|
|
559
|
+
# Validate against the engines we intentionally expose in Calvyn.
|
|
560
|
+
# ``puppeteer`` currently maps to the Chrome local path while the Node
|
|
561
|
+
# dependency is managed explicitly in package.json for npm installs.
|
|
562
|
+
_VALID_ENGINES = {"auto", "lightpanda", "chrome", "puppeteer"}
|
|
561
563
|
if _cached_browser_engine not in _VALID_ENGINES:
|
|
562
564
|
logger.warning(
|
|
563
565
|
"Unknown browser engine %r (valid: %s), falling back to 'auto'",
|
|
@@ -565,7 +567,10 @@ def _get_browser_engine() -> str:
|
|
|
565
567
|
)
|
|
566
568
|
_cached_browser_engine = "auto"
|
|
567
569
|
|
|
568
|
-
|
|
570
|
+
if _cached_browser_engine == "puppeteer":
|
|
571
|
+
return "chrome"
|
|
572
|
+
|
|
573
|
+
return _cached_browser_engine
|
|
569
574
|
|
|
570
575
|
|
|
571
576
|
def _should_inject_engine(engine: str) -> bool:
|