Commit Graph

25 Commits

Author SHA1 Message Date
ethernet
53e6f001c7 refactor(pm): consolidate runtime ownership and updater completion
Run historical updater completion in a fresh interpreter so cached imports
cannot revive retired dependency installers. Share Git and ZIP completion,
carry receipt and recovery state, and preserve child exit status.

Route plugin admission, binary acquisition, desktop launch and build paths
through PM. Replace redundant helpers and tests with real worker, package,
publication and launch checks. Keep the shipped compatibility surface fixed.

Targeted Python and desktop checks pass. Native update journeys and fresh
production image qualification remain pending. This is a checkpoint before
those acceptance runs.
2026-09-12 16:30:35 -04:00
Teknium
7a33369e81 simplify(compat): interrupt — drop _ThreadAwareEventProxy/_interrupt_event legacy alias, repoint 2 test files
No runtime consumer read the proxy (terminal_tool/environments call is_interrupted()/set_interrupt()
directly); its only users were tests patching tools.interrupt._interrupt_event, which had no effect on
the code under test. tools/terminal_tool.py's own re-export of the name is owned by another worker.
2026-09-03 14:00:59 -07:00
Teknium
c93ace77c2 simplify(compat): config/runtime_provider/plugins/commands/secrets_cli/kanban — drop 96 re-exports (incl. PEP 562 facades) + 3 aliases (get_pre_tool_call_directive/_block_message, get_telegram_handler_factories), repoint 56 callers + 50 test files 2026-09-03 14:00:17 -07:00
Teknium
e83816a4d1 review-fix(comments): restore lost #NNNN rationale comments across non-test source (mechanical sweep, condensed, code unchanged)
For each issue anchor present in BASE 63279301bc non-test .py and absent on HEAD, the BASE comment/docstring block was re-attached at the HEAD location of the code it explained (matched by the distinctive code line / enclosing def). Sentences already covered by an existing HEAD comment were deduped; the issue number always survives. Insert-only: no code lines changed.
2026-09-03 09:44:26 -07:00
Teknium
bcddf6a084 refactor(hermes_cli): setup cluster — drop statement-separating blanks inside function bodies (AST-neutral) 2026-09-02 22:25:24 -07:00
Teknium
17d20ae1e5 refactor(hermes_cli): setup cluster — compact send arg table, quick-setup label builders, first-time mode table by name 2026-09-02 22:21:47 -07:00
Teknium
f622145b84 refactor(hermes_cli): setup cluster — hug lone closing brackets 2026-09-02 21:53:35 -07:00
Teknium
f2b25bebe7 refactor(hermes_cli): setup cluster — print_header(gap=), yes/no answer table, bws _step helper, sync row table 2026-09-02 21:33:24 -07:00
Teknium
71e0cb0301 refactor(hermes_cli): setup cluster — join wrapped prose literals, drop blanks after local imports 2026-09-02 21:20:04 -07:00
Teknium
2e555504bf refactor(hermes_cli): setup cluster — compact docstrings/comments, fold help-line loops into _info 2026-09-02 21:12:33 -07:00
Teknium
21835bac7a refactor(hermes_cli): setup cluster — pack import/signature lists, tighten wizard picker lambdas 2026-09-02 20:49:41 -07:00
Teknium
756507554b refactor(hermes_cli): setup cluster pass 1 — unify env-var prompt/save, nous-flow, migration-step, bws setup phase helpers 2026-09-02 20:36:04 -07:00
Teknium
3371814034 refactor(hclib): auth — Nous/Copilot/portal auth, credential lifecycle, keepalive simplified 2026-09-02 14:42:18 -07:00
Halldrix
3f9150e5c4 fix(secrets_cli): defer bitwarden backend import to first attribute access
Address blocking review on #86782 (trevorgordon981, 2026-08-15): the
previous lazy closures in main.py only deferred the module-level
"import secrets_cli" statement, but were themselves invoked at parse
time — so the chain main -> secrets_cli -> bitwarden -> cryptography
still ran eagerly on every command, including `hermes update --check`.
The closure indirection was dead laziness.

Move the laziness to where the crypto payload actually lives:

1. secrets_cli.py: drop the module-top "from agent.secret_sources
   import bitwarden as bw" import. Each cmd_* handler now resolves the
   backend via a local _load_bw() helper, which imports
   agent.secret_sources.bitwarden on first use. register_cli() no
   longer touches crypto at all — it only wires argparse structure.

2. _BWS_VERSION is duplicated in secrets_cli as a plain string so the
   "install" subparser help text renders without importing the
   backend. agent.secret_sources.bitwarden._BWS_VERSION stays the
   source of truth; bump both together when pinning a new bws release.

3. Module-level PEP 562 __getattr__ resolves "secrets_cli.bw" lazily.
   Existing upstream tests (test_secrets_bitwarden_non_tty.py) that
   monkeypatch "hermes_cli.secrets_cli.bw.find_bws" keep working —
   monkeypatch resolves the string one level deep, triggering
   __getattr__, which imports the real bitwarden module and lets the
   patch land on the same cached module object the handlers import.

4. main.py: revert the closure indirection back to a direct parse-time
   _secrets_cli.register_cli() call — safe now that register_cli is
   crypto-free by construction. The argparse wiring is again visible
   at the call site (matching checkpoints.py / curator.py convention),
   which addresses the original parse-time-vs-post-parse contract
   concern from the previous review round.

Adds a decisive main()-level regression test requested by review:
test_main_update_check_crypto_absent_in_sys_modules spawns main() in a
subprocess with argv=['hermes', 'update', '--check'], patches
hermes_cli.main._cmd_update_check to short-circuit before any network,
and asserts cryptography.hazmat.bindings._rust stays out of
sys.modules both at dispatch time and after main() returns. This is
the exact invariant the Windows self-lock depends on; the previous
import-only tests could not observe the failure because parser
construction runs inside main().

Verification:
- scripts/run_tests.sh tests/test_lazy_secrets_import.py
  tests/test_lazy_secrets_dispatch.py
  tests/hermes_cli/test_secrets_bitwarden_non_tty.py
  -> 13/13 passed (includes the new decisive test + the 2 upstream
     tests that broke under the earlier _LazyBitwarden proxy).
- Sabotage run: same suite against the pre-fix main.py + secrets_cli.py
  fails the new decisive test with "cryptography._rust loaded by main()
  before update dispatch" — confirming the test guards the bug.
- Manual trace: at _cmd_update_check dispatch time, sys.modules
  contains hermes_cli.secrets_cli (parse-time structure only) but NOT
  agent.secret_sources.bitwarden and NOT cryptography._rust.

Refs: #86781
Refs: #83569
2026-08-15 01:55:22 -07:00
Halldrix
230e5ff048 fix(main): revert secrets_cli to upstream, wrap registration in lazy closures
secrets_cli.py: revert to upstream eager import (tests rely on  as
module attribute for monkeypatch; the previous _LazyBitwarden proxy
broke 2 existing tests because agent.secret_sources.bitwarden lacks
BwsClient in the upstream codebase).

main.py: wrap secrets_cli/onepassword_secrets_cli imports in
_register_bitwarden/_register_onepassword closures.  The parser tree is
created at parse-time (register_cli attaches subparsers), but the
module import itself defers to first use — argparse only calls the
closure when it encounters the subcommand, so importing main() no
longer loads bitwarden/cryptography eagerly.

env_loader.py: keep the known-source-names gate (any dict with a
known source name and enabled: true).

Verification: 12/12 tests pass (3 sys.modules + 7 E2E subprocess +
2 upstream test_secrets_bitwarden_non_tty).
2026-08-15 01:55:22 -07:00
Halldrix
5a76c8a978 fix(update): lazy-import secrets backends + defer registry import — break Windows self-lock loop
Address review feedback from trevorgordon981 on PR #86782:

1. **Pre-register parsers at parse-time, lazy-import backends only**
   - secrets_cli.register_cli() and onepassword_secrets_cli.register_cli()
     now run eager at parser-build time (no deferral past parse_args)
   - Only the agent.secret_sources.bitwarden/onepassword imports are lazy
     (inside each cmd_* handler via _load_bitwarden()/_load_onepassword())
   - This eliminates the 'invalid choice' and infinite-recursion risks

2. **Known-source-names gate for env_loader registry**
   - Only keys in {bitwarden, onepassword, op, 1password, bw} trigger the
     registry import; a generic dict entry no longer forces crypto load
   - Prevents unrelated config dicts from paying crypto cost

3. **End-to-end tests for the real dispatch paths**
   - test_bitwarden_setup_help: runs real CLI subprocess with --help
   - test_bitwarden_status/disable/onepassword_status: run real handlers
   - test_update_check_clean/no_self_lock: run real update --check
   - test_update_check_no_cryptography: sys.modules inspection (backup)

4. **Fix flaky test_turn_lease.py** (unrelated pre-existing failure)

The architecture guarantees:
- parse-time: zero cryptography load (all backends lazy)
- dispatch-time: crypto loads exactly once per secrets command
- update path: completely clean of cryptography._rust mapping

Refs #86781, #86782
2026-08-15 01:55:22 -07:00
teknium1
3d48f893da refactor: single build_subprocess_env() factory for all child-process spawns (profile + secret-scrub single owner) 2026-07-29 10:14:11 -07:00
Stoltemberg
c89481db5e fix: add explicit UTF-8 encoding to all subprocess text=True calls (#53428)
On Windows with Chinese locale (GBK), subprocess.run(text=True) without
explicit encoding causes UnicodeDecodeError crashes. This fix adds
encoding='utf-8', errors='replace' to all subprocess.run() and
subprocess.Popen() calls that use text=True across 76 non-test Python files.

Fixes #53428 (master tracker for Windows GBK locale crash).

Note: credential_pool.py and electron changes excluded per reviewer request —
those will be submitted as separate focused PRs.
2026-07-24 11:45:57 -07:00
izumi0uu
8e089db689 fix(secrets): validate bitwarden status token
Keep the env-presence row, but add a real Bitwarden probe so revoked or malformed tokens no longer look healthy in hermes secrets bitwarden status.

Also document the new status behavior and lock it in with a dedicated regression test.

Refs: NousResearch/hermes-agent#40275
Tested: ./scripts/run_tests.sh tests/hermes_cli/test_bitwarden_status.py tests/test_bitwarden_secrets.py
Tested: .venv/bin/python -m ruff check hermes_cli/secrets_cli.py tests/hermes_cli/test_bitwarden_status.py
2026-07-22 03:20:09 -07:00
Teknium
d3b0e61429 feat(secrets): one-command token rotation + actionable startup errors for all secret sources (#68605)
* feat(secrets): one-command token rotation + actionable startup errors for all secret sources

When a Bitwarden machine-account token expired, users saw a raw Rust
error dump (invalid_client + Location: + backtrace hints) and the only
fix was manually editing .env or re-running the whole setup wizard.

- New `hermes secrets bitwarden token` / `hermes secrets onepassword
  token`: paste a new token (masked prompt or flag), the command probes
  the backend BEFORE persisting — a rejected token changes nothing; a
  good one is written to .env and the fetch caches are cleared.
- New optional SecretSource.remediation(kind, cfg) hook: startup
  warnings now print a '→ Run `hermes secrets <name> token`…' fix-it
  line after any fetch error, for bundled AND plugin sources (generic
  per-ErrorKind defaults in the ABC).
- bws stderr is summarized to its cause line (Location:/backtrace noise
  dropped) and invalid_client/invalid_grant/400 identity rejects are
  now classified AUTH_FAILED (was INTERNAL) with a plain-English
  explanation naming the token env var.
- op whoami probe accepts a candidate token so rotation validates the
  NEW credential, not the ambient one.

Additive hook with defaults — no SECRET_SOURCE_API_VERSION bump.

* docs: fix MDX parse error in secret-source-plugin hook table

Escaped backticks around a <name> placeholder made MDX parse it as an
unclosed JSX tag, breaking the docs-site build.  Use a plain code span
instead.
2026-07-21 06:41:04 -07:00
Teknium
89040e0db3 fix(secrets): fail early with clear error when bitwarden setup runs without TTY (#40571)
Salvaged from #40280; cleaned up, re-verified against main, tests added.

Co-authored-by: liuhao1024 <liuhao1024@users.noreply.github.com>
2026-06-06 18:36:40 -07:00
kshitijk4poor
66827f8947 chore: prune unused imports and duplicate import redefinitions
Remove unused imports (F401) and duplicate/shadowed import
redefinitions (F811) across the codebase using ruff's safe
autofixes. No behavioral changes -- imports only.

- ~1400 safe autofixes applied across 644 files (net -1072 lines)
- __init__.py re-exports preserved (excluded from F401 removal so
  public re-export surfaces stay intact)
- Re-exports that are imported or monkeypatched by tests but look
  unused in their defining module are kept with explicit # noqa:
  F401 (gateway/run.py load_dotenv; run_agent re-exports from
  agent.message_sanitization, agent.context_compressor,
  agent.retry_utils, agent.prompt_builder, agent.process_bootstrap,
  agent.codex_responses_adapter)
- Unsafe F841 (unused-variable) fixes deliberately skipped -- those
  can change behavior when the RHS has side effects
- ruff lints remain disabled in pyproject.toml (only PLW1514 is
  selected); this is a one-time cleanup, not a config change

Verification:
- python -m compileall: clean
- pytest --collect-only: all 27161 tests collect (zero import errors)
- core entry points import clean (run_agent, model_tools, cli,
  toolsets, hermes_state, batch_runner, gateway)
- static scan: every name any test imports directly from an edited
  module still resolves
2026-05-28 22:26:25 -07:00
helix4u
ec4d6f1823 fix(cli): show masked feedback for secret prompts 2026-05-25 01:20:33 -07:00
Teknium
bc3f1f4f34 feat(secrets/bitwarden): EU Cloud + self-hosted server URL support (#31378)
Closes #31370.

bws defaults to the US identity endpoint, so EU Cloud and self-hosted
machine-account tokens fail with [400 Bad Request] {"error":"invalid_client"}
during 'hermes secrets bitwarden setup'. The token is valid — it's just
being checked against the wrong region.

Add a Bitwarden region step to the wizard between the access-token and
project-list steps:

  Step 1  Install bws
  Step 2  Provide access token
  Step 3  Pick region   <-- new (US / EU / self-hosted-custom-URL)
  Step 4  Pick project  (now talks to the right endpoint)
  Step 5  Test fetch

Region is stored in config.yaml as secrets.bitwarden.server_url and
plumbed into every bws subprocess as BWS_SERVER_URL (project list,
secret list, test fetch, and the env_loader startup pull).

Also:
- Non-interactive: 'hermes secrets bitwarden setup --server-url ...'
- Pre-existing BWS_SERVER_URL in the shell is detected and reused
- Cache key includes server_url so EU/US fetches don't collide
- 'hermes secrets bitwarden status' shows the configured region
- 'invalid_client' / '400 Bad Request' from bws now triggers a hint
  pointing at the region setting instead of looking like a bad token
2026-05-24 02:19:57 -07:00
Teknium
552e9c7881 feat(secrets): Bitwarden Secrets Manager integration with lazy bws install (#30035)
* feat(secrets): Bitwarden Secrets Manager integration with lazy bws install

Pull API keys from Bitwarden Secrets Manager at process startup
instead of storing them all in plaintext in ~/.hermes/.env.  One
bootstrap token (BWS_ACCESS_TOKEN) replaces N per-provider keys, and
rotating a credential becomes a single change in the Bitwarden web
app.

Bitwarden defaults to source of truth: secrets pulled from BSM
overwrite any matching env vars on startup so rotations actually
take effect.  Set secrets.bitwarden.override_existing: false in
config.yaml to invert.

The bws binary is auto-downloaded into ~/.hermes/bin/bws on first
use (pinned to v2.0.0, SHA-256 verified against the GitHub release
checksum file).  No apt, brew, or sudo required.

New surfaces:
  hermes secrets bitwarden setup    — interactive wizard
  hermes secrets bitwarden status   — config + binary + token state
  hermes secrets bitwarden sync     — dry-run fetch / --apply exports
  hermes secrets bitwarden disable  — flip enabled: false
  hermes secrets bitwarden install  — just download the binary

Failures (missing binary, bad token, no network) never block Hermes
startup — they emit a one-line warning to stderr and continue with
whatever credentials .env already had.

Docs: website/docs/user-guide/secrets/{index,bitwarden}.md
Tests: tests/test_bitwarden_secrets.py (26 tests, hermetic — bws
       subprocess and HTTP downloads fully mocked)

* chore(infographic): add bitwarden-secrets-manager bento-grid retro-pop-grid

Generated for PR #30035 — Bitwarden Secrets Manager integration.
Style picked via pick_pr_infographic_style.py rotation:
  layout: bento-grid
  style:  retro-pop-grid
  aspect: 1:1 square

Saved at infographic/bitwarden-secrets-manager/infographic.png
2026-05-21 14:10:34 -07:00