herm-tui 1.9.0-dev.1 → 1.9.0-dev.3
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.
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Eikon install plugin."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from .schemas import EIKON_INSTALL_SCHEMA
|
|
6
|
+
from .tools import _handle_eikon_install, check_herm_available
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def register(ctx) -> None:
|
|
10
|
+
ctx.register_tool(
|
|
11
|
+
name="eikon_install",
|
|
12
|
+
toolset="eikon",
|
|
13
|
+
schema=EIKON_INSTALL_SCHEMA,
|
|
14
|
+
handler=_handle_eikon_install,
|
|
15
|
+
check_fn=check_herm_available,
|
|
16
|
+
emoji="⬡",
|
|
17
|
+
)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Tool schemas for the eikon plugin."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
EIKON_INSTALL_SCHEMA = {
|
|
6
|
+
"name": "eikon_install",
|
|
7
|
+
"description": (
|
|
8
|
+
"Install a Herm eikon/avatar from the public catalog, a manifest URL, "
|
|
9
|
+
"a git repository, or a local directory. Uses `herm eikon install`; "
|
|
10
|
+
"no separate eikon executable is required."
|
|
11
|
+
),
|
|
12
|
+
"parameters": {
|
|
13
|
+
"type": "object",
|
|
14
|
+
"properties": {
|
|
15
|
+
"source": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"description": "Catalog name, HTTPS manifest/base URL, git URL, or local directory.",
|
|
18
|
+
},
|
|
19
|
+
"name": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"description": "Optional installed name override.",
|
|
22
|
+
},
|
|
23
|
+
"media": {
|
|
24
|
+
"type": "boolean",
|
|
25
|
+
"description": "Whether to fetch source media into the profile. Default true.",
|
|
26
|
+
"default": True,
|
|
27
|
+
},
|
|
28
|
+
"no_source": {
|
|
29
|
+
"type": "boolean",
|
|
30
|
+
"description": "Alias for media=false.",
|
|
31
|
+
"default": False,
|
|
32
|
+
},
|
|
33
|
+
"set_active": {
|
|
34
|
+
"type": "boolean",
|
|
35
|
+
"description": "Set the installed eikon as the active Herm avatar. Default true.",
|
|
36
|
+
"default": True,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
"required": ["source"],
|
|
40
|
+
},
|
|
41
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Handlers for the eikon plugin."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import shutil
|
|
8
|
+
import subprocess
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _json(data: dict[str, Any]) -> str:
|
|
14
|
+
return json.dumps(data)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _herm_bin() -> str | None:
|
|
18
|
+
override = os.getenv("HERM_EIKON_HERM_BIN", "").strip()
|
|
19
|
+
if override:
|
|
20
|
+
return override
|
|
21
|
+
return shutil.which("herm")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def check_herm_available() -> bool:
|
|
25
|
+
binary = _herm_bin()
|
|
26
|
+
if not binary:
|
|
27
|
+
return False
|
|
28
|
+
if os.path.sep in binary:
|
|
29
|
+
return Path(binary).is_file() and os.access(binary, os.X_OK)
|
|
30
|
+
return shutil.which(binary) is not None
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _handle_eikon_install(args: dict[str, Any], **_: Any) -> str:
|
|
34
|
+
source = str(args.get("source") or "").strip()
|
|
35
|
+
if not source:
|
|
36
|
+
return _json({"ok": False, "error": "source is required"})
|
|
37
|
+
|
|
38
|
+
binary = _herm_bin()
|
|
39
|
+
if not binary:
|
|
40
|
+
return _json({"ok": False, "error": "herm executable not found on PATH"})
|
|
41
|
+
|
|
42
|
+
cmd = [binary, "eikon", "install", source, "--json"]
|
|
43
|
+
name = str(args.get("name") or "").strip()
|
|
44
|
+
if name:
|
|
45
|
+
cmd.extend(["--name", name])
|
|
46
|
+
if args.get("media") is False or args.get("no_source") is True:
|
|
47
|
+
cmd.append("--no-source")
|
|
48
|
+
if args.get("set_active") is False:
|
|
49
|
+
cmd.append("--no-use")
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
proc = subprocess.run(cmd, capture_output=True, text=True, timeout=120, check=False)
|
|
53
|
+
except FileNotFoundError:
|
|
54
|
+
return _json({"ok": False, "error": f"herm executable not found: {binary}"})
|
|
55
|
+
except subprocess.TimeoutExpired:
|
|
56
|
+
return _json({"ok": False, "error": "herm eikon install timed out"})
|
|
57
|
+
|
|
58
|
+
stdout = (proc.stdout or "").strip()
|
|
59
|
+
stderr = (proc.stderr or "").strip()
|
|
60
|
+
if proc.returncode != 0:
|
|
61
|
+
detail = stderr or stdout or f"exit {proc.returncode}"
|
|
62
|
+
return _json({"ok": False, "error": f"herm eikon install failed: {detail}"})
|
|
63
|
+
|
|
64
|
+
line = next((ln for ln in reversed(stdout.splitlines()) if ln.strip()), "")
|
|
65
|
+
try:
|
|
66
|
+
payload = json.loads(line)
|
|
67
|
+
except json.JSONDecodeError:
|
|
68
|
+
return _json({"ok": False, "error": "herm eikon install returned non-JSON output", "output": stdout})
|
|
69
|
+
return _json(payload)
|