feat: package skeleton#2
Merged
Merged
Conversation
Bootstrap hermes-nodes-plugin as a pip-installable package that auto-loads into Hermes via the hermes_agent.plugins entry-point group. What: - pyproject.toml: setuptools build, project metadata, dev extras (pytest, pytest-asyncio), hermes_agent.plugins entry point, setuptools package discovery, pytest config. - hermes_nodes_plugin/__init__.py: stub register(ctx) that no-ops; __version__ = '0.1.0'. - tests/test_smoke.py: 4 tests — package import, register contract with mock ctx, idempotency, and entry-point discovery via importlib.metadata with a regression guard against the module:function form (Hermes's loader expects ep.load() to return a module, not a function). - tests/__init__.py, .gitignore: standard Python project layout. Design decision: the entry point targets the module (hermes_nodes_plugin), not the module:function form (hermes_nodes_plugin:register). The module:function form causes ep.load() to return the function object, which breaks Hermes's _load_plugin since it does getattr(module, 'register') expecting a module. This is the documented pattern in Hermes's plugin dev guide. Refs: plan §Task 2.1, REQUIREMENTS.md §FR-3. Acceptance: pip install -e . works, register is importable, 4/4 smoke tests pass, ruff clean, no security concerns in static scan. Signed-off-by: Blasius Patrick <blasius.patrick@gmail.com>
blaspat
added a commit
that referenced
this pull request
Jun 12, 2026
- #2: Defer expanduser() via get_store_path() helper - #3: Remove --name fallback, make required - #5: Add max_length=32 to ts field - #6: Add logger.warning on auth failure - #7: Add test_pair_suggestion_has_name - #8: --name in all Go pair test calls Signed-off-by: Blasius Patrick <blasius.patrick@gmail.com>
blaspat
added a commit
that referenced
this pull request
Jun 12, 2026
* fix: add ts field to hello and auth Pydantic models The Go client sends a ts timestamp on every envelope via MarshalJSON (envelope + payload flattened to top level). Both _HelloMessage and _AuthMessage had extra="forbid" but neither declared ts, so every hello/auth message from the Go client was rejected as containing an unknown field → close 4003 (message out of order). Add ts: str | None = None to both models so the field is accepted without relaxing the extra="forbid" guard against truly unknown fields. Signed-off-by: Blasius Patrick <blasius.patrick@gmail.com> * fix: include /ws/nodes path in pair output and README The pair command prints a copy-pasteable command, but the example URL didn't include the /ws/nodes path. Updated: - cli.py: <host:port> → <host:port>/ws/nodes - README.md example: wss://vps.yourdomain.com:6969 → ...:6969/ws/nodes Signed-off-by: Blasius Patrick <blasius.patrick@gmail.com> * fix: include --name flag in hermes-node pair suggestion The suggested laptop command didn't include --name <name>, so the operator would pair without knowing to pass the server-registered name. The Go client then defaults the name to 'config.toml' (from filepath.Base(configPath)), which doesn't match, causing authentication-failed on the first connect. Also update the README example to match. Signed-off-by: Blasius Patrick <blasius.patrick@gmail.com> * fix: apply Quinn review findings #2, #3, #5, #6, #7, #8 - #2: Defer expanduser() via get_store_path() helper - #3: Remove --name fallback, make required - #5: Add max_length=32 to ts field - #6: Add logger.warning on auth failure - #7: Add test_pair_suggestion_has_name - #8: --name in all Go pair test calls Signed-off-by: Blasius Patrick <blasius.patrick@gmail.com> --------- Signed-off-by: Blasius Patrick <blasius.patrick@gmail.com>
blaspat
added a commit
that referenced
this pull request
Jun 15, 2026
* fix: add ts field to hello and auth Pydantic models The Go client sends a ts timestamp on every envelope via MarshalJSON (envelope + payload flattened to top level). Both _HelloMessage and _AuthMessage had extra="forbid" but neither declared ts, so every hello/auth message from the Go client was rejected as containing an unknown field → close 4003 (message out of order). Add ts: str | None = None to both models so the field is accepted without relaxing the extra="forbid" guard against truly unknown fields. Signed-off-by: Blasius Patrick <blasius.patrick@gmail.com> * fix: include /ws/nodes path in pair output and README The pair command prints a copy-pasteable command, but the example URL didn't include the /ws/nodes path. Updated: - cli.py: <host:port> → <host:port>/ws/nodes - README.md example: wss://vps.yourdomain.com:6969 → ...:6969/ws/nodes Signed-off-by: Blasius Patrick <blasius.patrick@gmail.com> * fix: include --name flag in hermes-node pair suggestion The suggested laptop command didn't include --name <name>, so the operator would pair without knowing to pass the server-registered name. The Go client then defaults the name to 'config.toml' (from filepath.Base(configPath)), which doesn't match, causing authentication-failed on the first connect. Also update the README example to match. Signed-off-by: Blasius Patrick <blasius.patrick@gmail.com> * fix: apply Quinn review findings #2, #3, #5, #6, #7, #8 - #2: Defer expanduser() via get_store_path() helper - #3: Remove --name fallback, make required - #5: Add max_length=32 to ts field - #6: Add logger.warning on auth failure - #7: Add test_pair_suggestion_has_name - #8: --name in all Go pair test calls Signed-off-by: Blasius Patrick <blasius.patrick@gmail.com> * fix: accept session_id kwarg in lifecycle hook wrappers Hermes passes session_id as a keyword argument to hook callbacks. The wrappers were missing the parameter, causing: TypeError: _on_session_start_lazy() got an unexpected keyword argument 'session_id' Fix: add session_id: str = to both _on_session_start_lazy and _on_session_end_lazy. The parameter is accepted but unused (the lifecycle functions themselves don't need it). Signed-off-by: Blasius Patrick <blasius.patrick@gmail.com> --------- Signed-off-by: Blasius Patrick <blasius.patrick@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Bootstrap
hermes-nodes-pluginas a pip-installable package that auto-loads into Hermes via thehermes_agent.pluginsentry-point group.What's in this PR
pyproject.toml: setuptools build, project metadata, dev extras (pytest, pytest-asyncio),hermes_agent.pluginsentry point, setuptools package discovery, pytest confighermes_nodes_plugin/__init__.py: stubregister(ctx)that no-ops;__version__ = 0.1.0tests/test_smoke.py: 4 tests covering import, register contract, idempotency, and entry-point discoverytests/__init__.py,.gitignore: standard Python project layoutDesign decision (please review)
The plan said entry-point target should be
hermes_nodes_plugin:register(module:functionform). I diverged tohermes_nodes_plugin(module only) after simulating Hermes's loader:ep.load()on amodule:functiontarget returns the function, not the module. Thengetattr(function_obj, "register", None)is alwaysNone, so Hermes silently logs "no register() function" and the plugin never loads. The module-only form is what Hermes's own dev guide documents.I added a regression-guard test that fails if anyone reverts to the
module:functionform. If you specifically want themodule:functionform, the fix has to happen in Hermes's loader (out of scope for this repo).Verification
pip install -e .succeeds in a fresh venv (3.11.15)pytest -v— 4/4 passruff check .— cleanRefs
Test plan for reviewer