@inline-chat/hermes-agent-adapter 1.0.0 → 1.0.1
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 +7 -4
- package/package.json +6 -6
- package/plugin/inline/adapter.py +144 -38
- package/plugin/inline/cli.py +24 -5
- package/plugin/inline/plugin.yaml +1 -1
package/README.md
CHANGED
|
@@ -60,7 +60,9 @@ one thread per conversation. Each Hermes 0.22 profile keeps its own bot, state
|
|
|
60
60
|
and sidecar. Agents can follow, unfollow and manage chats, and they show Working
|
|
61
61
|
while they work. Status and doctor report whether messages reach the agent. See
|
|
62
62
|
"Upgrading to 1.0" in the [Hermes docs](https://inline.chat/docs/hermes) for every
|
|
63
|
-
change.
|
|
63
|
+
change. Hermes `0.22.6` and later need `1.0.0`: they refuse every message from
|
|
64
|
+
earlier adapters with "Hermes refused this message (invalid_params)". Pinned
|
|
65
|
+
realtime SDK `0.0.20`.
|
|
64
66
|
|
|
65
67
|
```sh
|
|
66
68
|
npm install -g @inline-chat/hermes-agent-adapter@1.0.0
|
|
@@ -195,10 +197,11 @@ mismatch, rerun the same command after rebuilding or upgrading the package.
|
|
|
195
197
|
|
|
196
198
|
- Hermes Agent: requires the external user plugin registry and native platform
|
|
197
199
|
plugin loader available in Hermes Agent `>=0.21.3`. This package was validated
|
|
198
|
-
against Hermes Agent `0.22.
|
|
199
|
-
`v0.22.
|
|
200
|
+
against Hermes Agent `0.22.7` from source commit `b200f5bc60e31588ba2cb60e288029c4a54ba5fa` (tag
|
|
201
|
+
`v0.22.7`), and installs on the minimum `0.21.3`. Hermes `0.22.6` and later refuse
|
|
202
|
+
every message from adapters before `1.0.0`; upgrade as in Release 1.0.0 above.
|
|
200
203
|
- CI checks the minimum supported Hermes release, newest stable source, and upstream
|
|
201
|
-
`main
|
|
204
|
+
`main`, including a full inbound turn through Hermes' production gateway. A six-hour scheduled check also tests the published npm `latest` adapter.
|
|
202
205
|
`inline-hermes doctor` requires successful host loading and compatibility validation;
|
|
203
206
|
unavailable Hermes diagnostics no longer count as healthy.
|
|
204
207
|
- Node.js: `>=20` is required for the bundled sidecar. Hermes-managed Node 22,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inline-chat/hermes-agent-adapter",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Hermes Agent platform adapter for Inline, with a native Python plugin and bundled Inline realtime sidecar.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -91,12 +91,12 @@
|
|
|
91
91
|
},
|
|
92
92
|
"machineSetupProtocol": 1,
|
|
93
93
|
"minHermesVersion": "0.21.3",
|
|
94
|
-
"testedHermesVersion": "0.22.
|
|
95
|
-
"testedHermesCommit": "
|
|
94
|
+
"testedHermesVersion": "0.22.7",
|
|
95
|
+
"testedHermesCommit": "b200f5bc60e31588ba2cb60e288029c4a54ba5fa"
|
|
96
96
|
},
|
|
97
|
-
"gitHead": "
|
|
97
|
+
"gitHead": "ec95e9296b83c13d2179b47019d915b16783c186",
|
|
98
98
|
"inlineRelease": {
|
|
99
|
-
"tag": "hermes-agent-v1.0.
|
|
100
|
-
"sha": "
|
|
99
|
+
"tag": "hermes-agent-v1.0.1",
|
|
100
|
+
"sha": "ec95e9296b83c13d2179b47019d915b16783c186"
|
|
101
101
|
}
|
|
102
102
|
}
|
package/plugin/inline/adapter.py
CHANGED
|
@@ -206,11 +206,42 @@ def _truthy(value: Any, default: bool = False) -> bool:
|
|
|
206
206
|
return str(value).strip().lower() in {"1", "true", "yes", "on"}
|
|
207
207
|
|
|
208
208
|
|
|
209
|
+
_UNAVAILABLE_HERMES_SEAMS: set[str] = set()
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def _hermes_seam_unavailable(seam: str, effect: str, exc: Optional[BaseException] = None) -> None:
|
|
213
|
+
"""Warn once per process that an undocumented Hermes internal is missing or changed.
|
|
214
|
+
|
|
215
|
+
Hermes documents no public API for these seams yet; the Inline repo tracks them in
|
|
216
|
+
plugins/hermes-agent/docs/hermes-upstream-seams.md.
|
|
217
|
+
Callers degrade the one feature that needs the seam; message delivery never depends on one.
|
|
218
|
+
"""
|
|
219
|
+
if seam in _UNAVAILABLE_HERMES_SEAMS:
|
|
220
|
+
return
|
|
221
|
+
_UNAVAILABLE_HERMES_SEAMS.add(seam)
|
|
222
|
+
reason = f"{type(exc).__name__}: {exc}" if exc is not None else "missing"
|
|
223
|
+
logger.warning("[inline] Hermes internal %s is unavailable (%s); %s", seam, reason, effect)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def _display_registration_fields() -> Dict[str, Any]:
|
|
227
|
+
"""Inline's display defaults through the public PlatformEntry fields, when the host has them."""
|
|
228
|
+
try:
|
|
229
|
+
from gateway.platform_registry import PlatformEntry
|
|
230
|
+
|
|
231
|
+
fields = getattr(PlatformEntry, "__dataclass_fields__", {})
|
|
232
|
+
if {"display_tier", "display_overrides"}.issubset(fields):
|
|
233
|
+
# The overrides cover every key a tier sets, so the tier only names the closest base.
|
|
234
|
+
return {"display_tier": "low", "display_overrides": dict(_INLINE_DISPLAY_DEFAULTS)}
|
|
235
|
+
except Exception:
|
|
236
|
+
logger.debug("[inline] Hermes display registration fields are unavailable", exc_info=True)
|
|
237
|
+
return {}
|
|
238
|
+
|
|
239
|
+
|
|
209
240
|
def _install_inline_display_defaults() -> None:
|
|
210
|
-
"""
|
|
241
|
+
"""Seed Inline's display defaults on hosts that predate PlatformEntry.display_overrides.
|
|
211
242
|
|
|
212
|
-
|
|
213
|
-
|
|
243
|
+
Newer hosts take the same defaults from ``ctx.register_platform`` (see
|
|
244
|
+
``_display_registration_fields``); only older ones need the private table core
|
|
214
245
|
platforms use. User config still wins over these defaults.
|
|
215
246
|
"""
|
|
216
247
|
try:
|
|
@@ -218,6 +249,10 @@ def _install_inline_display_defaults() -> None:
|
|
|
218
249
|
|
|
219
250
|
defaults = getattr(_display_config, "_PLATFORM_DEFAULTS", None)
|
|
220
251
|
if not isinstance(defaults, dict):
|
|
252
|
+
_hermes_seam_unavailable(
|
|
253
|
+
"gateway.display_config._PLATFORM_DEFAULTS",
|
|
254
|
+
"Inline uses Hermes' generic display defaults (tool progress may be shown)",
|
|
255
|
+
)
|
|
221
256
|
return
|
|
222
257
|
inline_defaults = dict(_INLINE_DISPLAY_DEFAULTS)
|
|
223
258
|
current = defaults.get("inline")
|
|
@@ -225,8 +260,12 @@ def _install_inline_display_defaults() -> None:
|
|
|
225
260
|
defaults["inline"] = {**inline_defaults, **current}
|
|
226
261
|
else:
|
|
227
262
|
defaults["inline"] = inline_defaults
|
|
228
|
-
except Exception:
|
|
229
|
-
|
|
263
|
+
except Exception as exc:
|
|
264
|
+
_hermes_seam_unavailable(
|
|
265
|
+
"gateway.display_config._PLATFORM_DEFAULTS",
|
|
266
|
+
"Inline uses Hermes' generic display defaults (tool progress may be shown)",
|
|
267
|
+
exc,
|
|
268
|
+
)
|
|
230
269
|
|
|
231
270
|
|
|
232
271
|
def _reply_thread_mode(value: Any, default: str = "auto") -> str:
|
|
@@ -570,7 +609,12 @@ def _normalize_inline_plugin_command_text(text: str) -> str:
|
|
|
570
609
|
from hermes_cli.plugins import get_plugin_commands
|
|
571
610
|
if command not in (get_plugin_commands() or {}):
|
|
572
611
|
return text
|
|
573
|
-
except Exception:
|
|
612
|
+
except Exception as exc:
|
|
613
|
+
_hermes_seam_unavailable(
|
|
614
|
+
"hermes_cli.plugins.get_plugin_commands",
|
|
615
|
+
"hyphenated plugin commands must be typed with hyphens in Inline",
|
|
616
|
+
exc,
|
|
617
|
+
)
|
|
574
618
|
return text
|
|
575
619
|
# Inline menus require underscores, while Hermes registers plugin commands
|
|
576
620
|
# with hyphens. Normalize before gateway access checks as well as dispatch.
|
|
@@ -585,16 +629,30 @@ def _normalize_inline_command_description(raw: str) -> str:
|
|
|
585
629
|
return description
|
|
586
630
|
|
|
587
631
|
|
|
588
|
-
def
|
|
589
|
-
|
|
632
|
+
def _hermes_menu_commands(max_commands: int) -> tuple[list, int]:
|
|
633
|
+
"""Hermes' gateway command menu; empty when the host no longer exposes it."""
|
|
634
|
+
try:
|
|
635
|
+
from hermes_cli.commands_platforms import telegram_menu_commands
|
|
590
636
|
|
|
637
|
+
menu_commands, hidden_count = telegram_menu_commands(max_commands=max_commands)
|
|
638
|
+
return list(menu_commands or []), int(hidden_count or 0)
|
|
639
|
+
except Exception as exc:
|
|
640
|
+
_hermes_seam_unavailable(
|
|
641
|
+
"hermes_cli.commands_platforms.telegram_menu_commands",
|
|
642
|
+
"the Inline command menu lists only Inline's own commands",
|
|
643
|
+
exc,
|
|
644
|
+
)
|
|
645
|
+
return [], 0
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
def _inline_menu_commands(max_commands: int = _INLINE_COMMAND_LIMIT) -> tuple[List[Dict[str, Any]], int]:
|
|
591
649
|
command_specs = _inline_command_specs()
|
|
592
650
|
local_commands = [
|
|
593
651
|
(spec.name, spec.description)
|
|
594
652
|
for spec in command_specs[:max(0, max_commands)]
|
|
595
653
|
]
|
|
596
654
|
remaining = max(0, max_commands - len(local_commands))
|
|
597
|
-
menu_commands, hidden_count =
|
|
655
|
+
menu_commands, hidden_count = _hermes_menu_commands(remaining)
|
|
598
656
|
commands: List[Dict[str, Any]] = []
|
|
599
657
|
seen: set[str] = set()
|
|
600
658
|
skipped = 0
|
|
@@ -620,11 +678,15 @@ def _inline_menu_commands(max_commands: int = _INLINE_COMMAND_LIMIT) -> tuple[Li
|
|
|
620
678
|
|
|
621
679
|
def _inline_skill_catalog() -> List[Dict[str, Any]]:
|
|
622
680
|
"""Return installed, enabled Hermes skills in the Bot API catalog shape."""
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
681
|
+
try:
|
|
682
|
+
# Public since Hermes 0.22.5 (plugins/index.md § Read the skill catalog).
|
|
683
|
+
from tools.skills_tool import find_all_skills as find_all, sort_skills as sort
|
|
684
|
+
except ImportError:
|
|
685
|
+
# Legacy hosts (< 0.22.5) only had the private names. Drop with that host floor.
|
|
686
|
+
import tools.skills_tool as legacy_skills_tool
|
|
687
|
+
|
|
688
|
+
find_all = getattr(legacy_skills_tool, "_find_all_skills")
|
|
689
|
+
sort = getattr(legacy_skills_tool, "_sort_skills")
|
|
628
690
|
published: List[Dict[str, Any]] = []
|
|
629
691
|
seen: set[str] = set()
|
|
630
692
|
for skill in sort(find_all(skip_disabled=False)):
|
|
@@ -1163,12 +1225,14 @@ class InlineAdapter(BasePlatformAdapter):
|
|
|
1163
1225
|
"""Hermes' own slash-command access for this platform; None on hosts without it."""
|
|
1164
1226
|
try:
|
|
1165
1227
|
from gateway.slash_access import policy_from_extra
|
|
1166
|
-
|
|
1167
|
-
return None
|
|
1168
|
-
try:
|
|
1228
|
+
|
|
1169
1229
|
return policy_from_extra(dict(self.config.extra or {}), "dm" if chat_type == "dm" else "group")
|
|
1170
|
-
except Exception:
|
|
1171
|
-
|
|
1230
|
+
except Exception as exc:
|
|
1231
|
+
_hermes_seam_unavailable(
|
|
1232
|
+
"gateway.slash_access.policy_from_extra",
|
|
1233
|
+
"only a DM's owner can change reply threads; group settings are locked",
|
|
1234
|
+
exc,
|
|
1235
|
+
)
|
|
1172
1236
|
return None
|
|
1173
1237
|
|
|
1174
1238
|
def _may_change_reply_threads(self, actor_id: str, chat_type: str) -> bool:
|
|
@@ -1176,7 +1240,9 @@ class InlineAdapter(BasePlatformAdapter):
|
|
|
1176
1240
|
who may run /threads. Without admin lists Hermes allows everyone, and so does this."""
|
|
1177
1241
|
policy = self._slash_access_policy(chat_type)
|
|
1178
1242
|
if policy is None:
|
|
1179
|
-
|
|
1243
|
+
# Every supported host has the policy, so its absence means the seam broke:
|
|
1244
|
+
# keep a DM's owner in control, but never open a group's setting to everyone.
|
|
1245
|
+
return chat_type == "dm"
|
|
1180
1246
|
return policy.can_run(actor_id, "threads")
|
|
1181
1247
|
|
|
1182
1248
|
def note_turn_thread(self, chat_id: str, message_id: str, thread_id: str) -> None:
|
|
@@ -1227,14 +1293,9 @@ class InlineAdapter(BasePlatformAdapter):
|
|
|
1227
1293
|
return "group"
|
|
1228
1294
|
|
|
1229
1295
|
def _bot_settings_runner(self) -> Any:
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
try:
|
|
1234
|
-
from gateway.run import _gateway_runner_ref
|
|
1235
|
-
return _gateway_runner_ref()
|
|
1236
|
-
except Exception:
|
|
1237
|
-
return None
|
|
1296
|
+
# BasePlatformAdapter.gateway_runner is the runner's documented back-reference;
|
|
1297
|
+
# every supported host sets it before the adapter connects.
|
|
1298
|
+
return getattr(self, "gateway_runner", None)
|
|
1238
1299
|
|
|
1239
1300
|
async def _bot_settings_context(
|
|
1240
1301
|
self,
|
|
@@ -1310,10 +1371,11 @@ class InlineAdapter(BasePlatformAdapter):
|
|
|
1310
1371
|
return context
|
|
1311
1372
|
|
|
1312
1373
|
try:
|
|
1313
|
-
from
|
|
1374
|
+
from hermes_cli.config import load_config_readonly
|
|
1314
1375
|
from hermes_cli.model_switch_providers import list_picker_providers
|
|
1315
1376
|
|
|
1316
|
-
|
|
1377
|
+
# Read-only: the returned dict is Hermes' shared config cache.
|
|
1378
|
+
cfg = load_config_readonly() or {}
|
|
1317
1379
|
model_cfg = cfg.get("model") if isinstance(cfg, dict) else {}
|
|
1318
1380
|
if not isinstance(model_cfg, dict):
|
|
1319
1381
|
model_cfg = {"default": str(model_cfg or "")}
|
|
@@ -1415,8 +1477,16 @@ class InlineAdapter(BasePlatformAdapter):
|
|
|
1415
1477
|
{"value": value, "label": value.capitalize()} for value in dict.fromkeys(reasoning_values)
|
|
1416
1478
|
],
|
|
1417
1479
|
})
|
|
1480
|
+
except asyncio.TimeoutError:
|
|
1481
|
+
logger.warning("[inline] agent settings model catalog timed out")
|
|
1418
1482
|
except Exception as exc:
|
|
1419
|
-
|
|
1483
|
+
# Model and reasoning rows lean on GatewayRunner internals and the model picker
|
|
1484
|
+
# catalog; without them the settings sheet still offers reply-thread controls.
|
|
1485
|
+
_hermes_seam_unavailable(
|
|
1486
|
+
"GatewayRunner session model/reasoning settings",
|
|
1487
|
+
"Inline agent settings hide the model and reasoning rows",
|
|
1488
|
+
exc,
|
|
1489
|
+
)
|
|
1420
1490
|
return context
|
|
1421
1491
|
|
|
1422
1492
|
@staticmethod
|
|
@@ -1632,7 +1702,11 @@ class InlineAdapter(BasePlatformAdapter):
|
|
|
1632
1702
|
f"/model {shlex.quote(model)} --provider {shlex.quote(provider)} "
|
|
1633
1703
|
f"{'--global' if item_id == 'model-default' else '--session'}"
|
|
1634
1704
|
)
|
|
1635
|
-
|
|
1705
|
+
handle_model_command = getattr(runner, "_handle_model_command", None)
|
|
1706
|
+
if not callable(handle_model_command):
|
|
1707
|
+
_hermes_seam_unavailable("GatewayRunner._handle_model_command", "Inline settings cannot switch models")
|
|
1708
|
+
raise ValueError("model switching unavailable")
|
|
1709
|
+
result = await handle_model_command(MessageEvent(
|
|
1636
1710
|
text=command,
|
|
1637
1711
|
message_type=MessageType.TEXT,
|
|
1638
1712
|
source=source,
|
|
@@ -1645,7 +1719,13 @@ class InlineAdapter(BasePlatformAdapter):
|
|
|
1645
1719
|
selected = str(value_body.get("stringValue") or "")
|
|
1646
1720
|
if selected not in {option.get("value") for option in context.get("reasoning_options") or []}:
|
|
1647
1721
|
raise ValueError("reasoning unavailable")
|
|
1648
|
-
runner
|
|
1722
|
+
apply_reasoning_selection = getattr(runner, "_apply_reasoning_selection", None)
|
|
1723
|
+
if not callable(apply_reasoning_selection):
|
|
1724
|
+
_hermes_seam_unavailable(
|
|
1725
|
+
"GatewayRunner._apply_reasoning_selection", "Inline settings cannot change reasoning effort",
|
|
1726
|
+
)
|
|
1727
|
+
raise ValueError("reasoning unavailable")
|
|
1728
|
+
apply_reasoning_selection(context["session_key"], "inline", selected)
|
|
1649
1729
|
return
|
|
1650
1730
|
raise ValueError("setting unavailable")
|
|
1651
1731
|
|
|
@@ -4392,6 +4472,11 @@ class InlineAdapter(BasePlatformAdapter):
|
|
|
4392
4472
|
return True
|
|
4393
4473
|
try:
|
|
4394
4474
|
from tools import slash_confirm as slash_confirm_mod
|
|
4475
|
+
except Exception as exc:
|
|
4476
|
+
_hermes_seam_unavailable("tools.slash_confirm.resolve", "Inline slash-confirm buttons cannot answer", exc)
|
|
4477
|
+
await self._finish_action(event, "Unavailable", "This Hermes version cannot record that choice.")
|
|
4478
|
+
return True
|
|
4479
|
+
try:
|
|
4395
4480
|
result = await slash_confirm_mod.resolve(session_key, confirm_id, choice)
|
|
4396
4481
|
self._slash_sessions.pop(confirm_id, None)
|
|
4397
4482
|
text = str(result or ("Cancelled." if choice == "cancel" else "Recorded."))
|
|
@@ -4651,7 +4736,10 @@ class InlineAdapter(BasePlatformAdapter):
|
|
|
4651
4736
|
from hermes_cli.models_catalog_static import PROVIDER_GROUPS
|
|
4652
4737
|
label, _desc, members = PROVIDER_GROUPS.get(group_id, (group_id, "", []))
|
|
4653
4738
|
member_slugs = [str(member) for member in members]
|
|
4654
|
-
except Exception:
|
|
4739
|
+
except Exception as exc:
|
|
4740
|
+
_hermes_seam_unavailable(
|
|
4741
|
+
"hermes_cli.models_catalog_static.PROVIDER_GROUPS", "the model picker cannot open provider groups", exc,
|
|
4742
|
+
)
|
|
4655
4743
|
member_slugs = []
|
|
4656
4744
|
by_slug = {
|
|
4657
4745
|
str(provider.get("slug") or "").strip().lower(): provider
|
|
@@ -4750,8 +4838,17 @@ class InlineAdapter(BasePlatformAdapter):
|
|
|
4750
4838
|
async def _expensive_model_warning(self, model_id: str, provider_slug: str) -> Optional[Any]:
|
|
4751
4839
|
try:
|
|
4752
4840
|
from hermes_cli.model_cost_guard import expensive_model_warning
|
|
4841
|
+
except Exception as exc:
|
|
4842
|
+
_hermes_seam_unavailable(
|
|
4843
|
+
"hermes_cli.model_cost_guard.expensive_model_warning",
|
|
4844
|
+
"the model picker switches without the expensive-model confirmation",
|
|
4845
|
+
exc,
|
|
4846
|
+
)
|
|
4847
|
+
return None
|
|
4848
|
+
try:
|
|
4753
4849
|
return await asyncio.to_thread(expensive_model_warning, model_id, provider=provider_slug)
|
|
4754
4850
|
except Exception:
|
|
4851
|
+
logger.warning("[inline] expensive model check failed", exc_info=True)
|
|
4755
4852
|
return None
|
|
4756
4853
|
|
|
4757
4854
|
async def _edit_action_message(self, event: Dict[str, Any], text: str, actions: Optional[Dict[str, Any]] = None) -> SendResult:
|
|
@@ -5396,7 +5493,8 @@ class InlineAdapter(BasePlatformAdapter):
|
|
|
5396
5493
|
try:
|
|
5397
5494
|
from hermes_cli.providers import get_label
|
|
5398
5495
|
return str(get_label(slug))
|
|
5399
|
-
except Exception:
|
|
5496
|
+
except Exception as exc:
|
|
5497
|
+
_hermes_seam_unavailable("hermes_cli.providers.get_label", "the model picker shows provider slugs", exc)
|
|
5400
5498
|
return str(slug or "")
|
|
5401
5499
|
|
|
5402
5500
|
def _build_provider_actions(self, picker_id: str, providers: list, *, include_back: bool = False) -> Dict[str, Any]:
|
|
@@ -5409,7 +5507,10 @@ class InlineAdapter(BasePlatformAdapter):
|
|
|
5409
5507
|
try:
|
|
5410
5508
|
from hermes_cli.models_catalog_static import group_providers
|
|
5411
5509
|
grouped = group_providers(list(by_slug.keys()))
|
|
5412
|
-
except Exception:
|
|
5510
|
+
except Exception as exc:
|
|
5511
|
+
_hermes_seam_unavailable(
|
|
5512
|
+
"hermes_cli.models_catalog_static.group_providers", "the model picker lists providers ungrouped", exc,
|
|
5513
|
+
)
|
|
5413
5514
|
grouped = [{"kind": "single", "slug": slug} for slug in by_slug.keys()]
|
|
5414
5515
|
for row in grouped:
|
|
5415
5516
|
if row.get("kind") == "group":
|
|
@@ -5784,7 +5885,9 @@ def _inline_catalog_part_summary(part: Dict[str, Any]) -> str:
|
|
|
5784
5885
|
def _inline_version_text(last_sync: Optional[Dict[str, Any]] = None) -> str:
|
|
5785
5886
|
version = _installed_inline_plugin_version()
|
|
5786
5887
|
try:
|
|
5787
|
-
from
|
|
5888
|
+
from importlib.metadata import version as _distribution_version
|
|
5889
|
+
|
|
5890
|
+
hermes_version = _distribution_version("hermes-agent")
|
|
5788
5891
|
except Exception:
|
|
5789
5892
|
hermes_version = None
|
|
5790
5893
|
|
|
@@ -5854,7 +5957,9 @@ def register(ctx) -> None:
|
|
|
5854
5957
|
from . import cli as _cli
|
|
5855
5958
|
from . import tools as _tools
|
|
5856
5959
|
|
|
5857
|
-
|
|
5960
|
+
display_fields = _display_registration_fields()
|
|
5961
|
+
if not display_fields:
|
|
5962
|
+
_install_inline_display_defaults()
|
|
5858
5963
|
ctx.register_platform(
|
|
5859
5964
|
name="inline",
|
|
5860
5965
|
label="Inline",
|
|
@@ -5877,6 +5982,7 @@ def register(ctx) -> None:
|
|
|
5877
5982
|
allow_update_command=True,
|
|
5878
5983
|
platform_hint=_tools.INLINE_PLATFORM_GUIDANCE,
|
|
5879
5984
|
**_target_registration_hooks(),
|
|
5985
|
+
**display_fields,
|
|
5880
5986
|
)
|
|
5881
5987
|
register_command = getattr(ctx, "register_command", None)
|
|
5882
5988
|
if callable(register_command):
|
package/plugin/inline/cli.py
CHANGED
|
@@ -30,7 +30,7 @@ _MACHINE_SETUP_PROTOCOL_VERSION = 1
|
|
|
30
30
|
# Hermes' own tolerance: macOS readings of one process's start time drift about a second
|
|
31
31
|
# after boot-clock adjustments, and 200 is 2 s on every platform's scale.
|
|
32
32
|
_START_TIME_DRIFT_TOLERANCE = 200
|
|
33
|
-
_PROBE_USER_AGENT = "inline-hermes-agent-adapter/1.0.
|
|
33
|
+
_PROBE_USER_AGENT = "inline-hermes-agent-adapter/1.0.1"
|
|
34
34
|
_ENV_REFERENCE_RE = re.compile(r"^\$\{([A-Za-z_][A-Za-z0-9_]*)\}$")
|
|
35
35
|
|
|
36
36
|
|
|
@@ -43,6 +43,24 @@ def _open_bot_api_probe(request: urllib.request.Request):
|
|
|
43
43
|
return urllib.request.build_opener(_RejectRedirects()).open(request, timeout=30)
|
|
44
44
|
|
|
45
45
|
|
|
46
|
+
def _setup_ui():
|
|
47
|
+
"""Wizard prompts and output through the helpers Hermes documents for ``setup_fn``:
|
|
48
|
+
prompts from ``hermes_cli.setup`` and output from ``hermes_cli.cli_output``."""
|
|
49
|
+
from types import SimpleNamespace
|
|
50
|
+
|
|
51
|
+
from hermes_cli import cli_output
|
|
52
|
+
from hermes_cli.setup import prompt, prompt_yes_no
|
|
53
|
+
|
|
54
|
+
return SimpleNamespace(
|
|
55
|
+
print_header=cli_output.print_header,
|
|
56
|
+
print_info=cli_output.print_info,
|
|
57
|
+
print_success=cli_output.print_success,
|
|
58
|
+
print_warning=cli_output.print_warning,
|
|
59
|
+
prompt=prompt,
|
|
60
|
+
prompt_yes_no=prompt_yes_no,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
46
64
|
def gateway_setup() -> None:
|
|
47
65
|
"""Interactively create or connect an Inline bot.
|
|
48
66
|
|
|
@@ -50,8 +68,9 @@ def gateway_setup() -> None:
|
|
|
50
68
|
persistence inside Hermes' own config helper so profiles and file
|
|
51
69
|
permissions behave exactly like the built-in Telegram setup.
|
|
52
70
|
"""
|
|
53
|
-
from hermes_cli import
|
|
54
|
-
|
|
71
|
+
from hermes_cli import config as hermes_gateway
|
|
72
|
+
|
|
73
|
+
hermes_setup = _setup_ui()
|
|
55
74
|
|
|
56
75
|
hermes_setup.print_header("Inline")
|
|
57
76
|
existing = (
|
|
@@ -403,7 +422,7 @@ def _machine_setup(args) -> int:
|
|
|
403
422
|
token = token.strip()
|
|
404
423
|
if not token:
|
|
405
424
|
raise SystemExit("Inline bot token from stdin is empty")
|
|
406
|
-
from hermes_cli import
|
|
425
|
+
from hermes_cli import config as hermes_gateway
|
|
407
426
|
|
|
408
427
|
hermes_gateway.save_env_value("INLINE_TOKEN", token)
|
|
409
428
|
allowed = _apply_access(
|
|
@@ -431,7 +450,7 @@ def _machine_setup(args) -> int:
|
|
|
431
450
|
|
|
432
451
|
|
|
433
452
|
def _status(args) -> int:
|
|
434
|
-
from hermes_cli import
|
|
453
|
+
from hermes_cli import config as hermes_gateway
|
|
435
454
|
|
|
436
455
|
raw_config = _read_inline_config(hermes_gateway)
|
|
437
456
|
token = (
|