Fit/merge forked#196
Closed
handy-sun wants to merge 119 commits into
Closed
Conversation
…ypes - Add Hermes to AppType enum with additive mode support - Wire Hermes through McpApps, SkillApps, CommonConfigSnippets, PromptRoot - Add hermes field to VisibleApps (default: false) - Add hermes_config_dir and current_provider_hermes to AppSettings - Add get_hermes_override_dir() to settings module - Add Hermes branch to sync_policy::should_sync_live() - Add Hermes prompt file path (~/.hermes/AGENTS.md) - Fix SkillApps initializers in skills DAO - Add Hermes color theme (LightYellow/BrightYellow)
…CLI match arms - Add Hermes branches to all match statements across 12 files - services/provider: mod.rs, common_config.rs, live.rs, usage.rs - services: prompt.rs, mcp.rs, config.rs - cli/tui: helpers.rs - deeplink, proxy, store - LiveSnapshot enum adds Hermes variant with config field - Additive-mode apps use unreachable!() for switch/delete paths - Config read/write stubs marked TODO for Tier 2 hermes_config
…d service match arms - cli/commands: config_common, mcp, provider_input, provider_inspect - cli/tui: data, form (provider_json, provider_state, provider_state_loading, provider_templates), runtime_actions, theme - services: skill, stream_check (provider_extract, service) - All additive-mode paths use empty blocks or TODO stubs - LiveSnapshot::Hermes variant added with config field - Hermes skills dir: ~/.hermes/skills - Hermes accent color: DRACULA_YELLOW
Remove fmt job from rust-ci.yml; add .githooks/pre-commit that runs rustfmt --check on staged .rs files. Set core.hooksPath to .githooks.
Port hermes_config.rs from desktop (config read/write, provider CRUD, MCP section, memory files) Add Hermes MCP format conversion (stdio/HTTP <-> YAML) with merge-on-write Wire provider services: refresh_from_live, read_app_config, live_write, validation Wire usage extraction: api_key, base_url for Hermes providers Wire stream check: model, base_url, auth extraction from Hermes config Wire MCP services: sync_server, remove_server, import_from_hermes
…terals test files create VisibleApps and SkillApps struct literals that now require the hermes field after Tier 2 added it to the definitions; also add Hermes variant to test-local AppType enums
- replace fmt-only check with clippy (ubuntu/macos/windows) + test (ubuntu/macos/windows) - add Linux system deps for GTK/WebKit/Soup - add frontend dist placeholder for Tauri build - keep failover-e2e job with matching deps - trigger on feat/** branches for early CI feedback
- config: save/restore settings for config_dir tests (fixes pollution from preceding env_overrides_settings) - hermes_config: preserve old test_home_override in with_test_home, use global lock_test_home_and_settings() mutex - cli/tui/app/tests: #[cfg(unix)] on symlink-based tests (Windows fix) - codex_oauth: new lock_codex_oauth_test() mutex, flavor=current_thread for affected tests (replaces unreliable serial_test) - streaming failover: flavor=current_thread + global mutex - Add FIXME comments documenting env::set_var root cause (35 call sites)
- Add src-tauri/flake.nix with devShell providing cargo-zigbuild, zig, cmake, rustup, and cargo-watch - Auto-installs rustup stable toolchain and cross-compilation targets (x86_64/aarch64-linux-gnu, aarch64-apple-darwin on macOS) in shellHook - rust-toolchain.toml: channel 1.91.1 -> stable (aligned with flake shell) - No container runtime needed: zig serves as C cross-compiler for rusqlite bundled SQLite and rquickjs QuickJS Usage: cd src-tauri && nix develop cargo zigbuild --target x86_64-unknown-linux-gnu --release cargo zigbuild --target aarch64-unknown-linux-gnu --release
ConfigDirEnvGuard and TempHome now hold the test-home mutex internally and call set_test_home_override, matching the TestHomeEnvGuard pattern from services/proxy.rs. The 3 known-flaky tests (env::set_var races across ~35 concurrent tests) are marked #[ignore] with instructions to run single-threaded. 2 integration tests in import_export_sync.rs still fail (pre-existing, root cause unrelated to this change).
import_openclaw_providers_from_live stores to state.db, not state.config. Both integration tests incorrectly asserted against state.config, which was never updated by the import. Switch to state.db.get_provider_by_id and get_provider_ids to match the actual storage backend.
… CLI commands - provider_state_loading: reuse openclaw form (same additive-mode fields) - provider_templates: add PROVIDER_TEMPLATE_DEFS_HERMES with Custom preset - provider_templates: implement preset application (identical to OpenClaw) - provider_state: add Hermes provider fields (apiKey, baseUrl, user-agent, models) - provider_state: exclude Hermes from common config (additive mode) - provider_json: implement full JSON builder (identical to OpenClaw) - data: add API URL extraction (baseUrl) - provider_inspect: implement model fetch with bearer auth - provider_input: implement display config (apiKey, baseUrl, models count) - stream_check: remove TODO (codex_stream fallback is correct)
Schema and migration (v9->v10) already had the column; DAO layer was hardcoding hermes: false. Now reads/writes enabled_hermes from DB in get_all_installed_skills, get_installed_skill, save_skill, and update_skill_apps.
Six locations had hardcoded app arrays missing Hermes: - pickers.rs: MCP, visible apps, and skills app picker overlays - store.rs: DB-to-config export and config-to-DB persist loops - app_config.rs: prompt auto-import loop - runtime_actions/mcp.rs: MCP sync toggle loop - services/skill.rs: supported_skill_apps and skill removal loop
- app_type_for_picker_index: add index 5 => Hermes - handle_visible_apps_picker_key: .min(4) -> .min(5) for 6 apps - handle_mcp_apps_picker_key: .min(3) -> .min(4) for 5 apps - handle_skills_apps_picker_key: .min(3) -> .min(4) for 5 apps
Extract MCP_PICKER_APPS, VISIBLE_PICKER_APPS, SKILLS_PICKER_APPS consts; handlers use .len()-1 and array indexing instead of magic numbers. Add Copy derive to AppType.
src-tauri/rust-toolchain.toml pins channel to stable (to stay aligned with the nix devShell). dtolnay/rust-toolchain installs the matrix target onto env.RUST_VERSION (1.91.1), but cargo invoked under src-tauri/ auto-switches to the stable toolchain which does not inherit that target. On macos-14 arm64 runners the darwin-x64 cross build then fails with 'can't find crate for core'. Add an explicit 'rustup target add' step running in src-tauri/ so the target lands on the stable toolchain that cargo will actually use. Guarded by use_cross != true to skip cross-docker builds.
- Switch from implicit host-target cargo build to explicit --target - Add 3 targets: linux-x64-musl, linux-arm64-musl, macos-x64 - Use cross for musl/ARM64 targets (same as release) - Add rustup target add step for rust-toolchain.toml mismatch - Update artifact paths and cache keys per target
The function was defined but never called — prompt editing is fully implemented via submit_prompt_edit / edit_prompt.
v5.5.0: Hermes Agent, prompt create/rename, failover controls, Nix flake, Anthropic header stripping, live provider import v5.5.1: minisign keypair replacement, CI cross-target fix
MCP table header, data rows, summary bar, and column widths all rendered only 4 apps (Claude/Codex/Gemini/OpenCode). Add Hermes as the 5th column with active/inactive markers and summary count.
Bump the cc-switch-tui crate version from 0.1.1 to 0.1.2 and keep Cargo.lock aligned so the tagged release workflow can validate GITHUB_REF_NAME against Cargo metadata. Refresh README badges and the fork-specific changelog with the OpenClaw MCP, local environment version display, skills visual selection, and agent import fixes shipped since v0.1.1. Update the GitHub Release body product description to include Hermes alongside the other supported assistants. Verified with: cargo fmt --check; cargo metadata --no-deps --format-version 1; cargo test --locked; git diff --check. crates.io returned 404 for cc-switch-tui/0.1.2 before tagging.
Implement a Codex-only provider catalog flow that keeps TUI-managed custom providers mirrored into the live ~/.codex/config.toml [model_providers.*] table. Add import support for the Codex Providers page so pressing i reads all recognizable providers from the current live config, merges them by stable catalog key first, falls back to an exact name merge when the key is missing, and creates new saved providers for the remaining entries. Keep current-provider semantics separate from catalog import. Importing live providers updates the saved provider set only and does not implicitly switch the active Codex provider. Preserve existing compatibility guarantees around Codex common-config handling by making live catalog sync tolerant of broken legacy snapshots. Invalid saved provider TOML is skipped with a warning instead of aborting unrelated Codex operations such as switching providers or updating common snippets. Also add TUI copy and regression coverage for the new Codex-only import affordance, stable-alias deduplication during import, and catalog synchronization back into live config.
- primary_codex_model_provider_id_with_table(): find the single provider key that owns a [model_providers.<key>] table - rewrite_codex_config_model_provider_key(): rename a provider table key, rewrite profile references, and update root model_provider
When multiple custom providers share the 'custom' key in [model_providers], the catalog sync would overwrite entries. Now: - compact_codex_key_suffix / unique_codex_provider_key_for_conflict: generate unique keys from provider id/name - rewrite_provider_codex_model_provider_key: rewrite a Provider's stored key + settings config - repair_conflicting_custom_codex_provider_keys: detect and fix duplicate 'custom' keys before sync - collect_codex_providers_for_live_sync: run repair then collect - Both sync entry points now acquire write lock and run repair before catalog write
Set up two providers both using key 'custom', switch to the second, and verify both get unique keys (codex_provider / uuid-derived) in the stored data and live config.toml.
…tches
When the user temporarily disables an MCP server (or anything else) by
commenting out a whole subtable in `~/.codex/config.toml`, switching
providers used to silently drop those comment lines. From the user's
perspective, sections they had intentionally turned off would either
disappear or — worse — be perceived as reopened on the next switch.
Root cause: `write_codex_live` previously replaced the entire
config.toml from the stored snapshot, and the recently-added merge
helper rebuilt the document *from the snapshot* and then cloned the
live `[mcp_servers]` Item back in. In toml_edit's model, comment-only
lines that trail the last `[mcp_servers.x]` subtable (e.g. a disabled
`# [mcp_servers.disabled]` block) are attached to the **document-level
trailing decor**, not to any subtable's decor — so cloning the Item
left them behind.
Fix: invert the merge strategy. Edit the **live** document in place
and only overlay entries the snapshot explicitly provides:
- `[mcp_servers]` is never overwritten by the snapshot. The user's
live content, including any commented-out subtables and loose
comments around them, is preserved verbatim.
- Live entries the snapshot does not cover are removed, so the
previous provider's `[projects]` / `[model_providers.OLD]` don't
leak into the new live config (provider isolation).
- Root-level preference keys (`approval_mode`,
`disable_response_storage`, `model_reasoning_effort`,
`check_for_update_on_startup`) follow `preserve_user_preferences`:
- true (provider switch with applyCommonConfig honored): live
wins when present, falls back to snapshot otherwise so initial
writes still seed merged common-snippet defaults.
- false (common-snippet clear, or `applyCommonConfig=false` on
the target provider): snapshot drives; live keys absent from
the snapshot are removed so old snippet residue doesn't bleed.
In `write_codex_live`, `preserve_live_preferences` is now ANDed with
the effective `apply_common_config`. A provider that explicitly opts
out of the common snippet must also wipe any snippet preferences left
in the live config by a previous provider — otherwise the toggle has
no observable effect on existing keys.
Adds tests in `codex_config.rs` covering:
- commented-out `[mcp_servers.x]` blocks and inline comments survive
a provider switch verbatim (the regression that motivated this fix);
- preferences seeded from snapshot when live is empty;
- `preserve=false` drops live preference keys missing from snapshot;
- existing user-preference and mcp preservation behavior.
Dev-only: adds `indoc = "2"` to dev-dependencies for readable TOML
fixtures in the new tests.
…ches Add a test that covers comment patterns the user is likely to keep in ~/.codex/config.toml at the root level: - a header comment attached as prefix decor to an existing key (`# pinned by me — do not change ...` above `model_provider`), - a commented-out root key (`# disable_response_storage = true` between two live keys), - a trailing footnote at EOF. These all survive a provider switch because the new in-place merge overwrites only the value side of existing live keys via toml_edit's IndexMut, leaving the key's prefix decor intact; trailing decor is document-level and untouched by entry replacement. Updates the merge helper's docstring to spell out the exact set of comment shapes that are preserved, so future readers don't need to re-derive the toml_edit decor rules. No behavior change.
…coped blacklist The merge helper previously had a hardcoded PREFERENCE_KEYS list naming the four preference keys it knew about. Everything not on that list was treated as provider-scoped: if the snapshot didn't include the key, it was wiped from the live config on a provider switch. That works for the keys we currently track, but it bakes a maintenance burden into the file: every time Codex adds a new root-level preference (e.g. `sandbox_mode`, `verbose_logging`, …) we'd have to remember to extend PREFERENCE_KEYS, otherwise the user's live setting would be silently discarded on the next switch. Flip the polarity: introduce a small PROVIDER_SCOPED_KEYS blacklist naming the keys that **must** hard-sync with the active provider's snapshot — `model_provider`, `model`, `model_providers`, `projects`. Anything not in this list is user-owned and follows the existing `preserve_user_preferences` rule (live wins on switch when present, seeded from snapshot otherwise; cleared on snippet-driven writes). Why this is the right default: - A new Codex root preference is the much more common future change than a new provider-scoped table — and the cost of the wrong default is asymmetric: misclassifying a preference as provider-scoped loses user data on switch, while misclassifying a provider-scoped key as user-owned at worst leaks one inert value between providers (and a later writer of the same key overwrites it). User data > residue. - The blacklist is short and grounded in observable behavior — these are the only keys whose value must change on every provider switch — so it's far easier to keep correct than an open-ended whitelist of "all the preferences we know about today." No behavior change for the currently-tracked four preference keys; all existing merge tests pass as-is. Adds `merge_keeps_unknown_root_keys_from_live_on_switch` as a forward-compatibility regression guard simulating a future unknown preference (`sandbox_mode`, `verbose_logging`).
Detect when Codex config.toml points at a provider that differs from cc-switch's stored current provider and surface that choice in the TUI. Backfill the actual live provider snapshot before switching, write normal provider switches with the selected model_provider id, and preserve live user preferences/MCP edits instead of resyncing managed MCP over them. Add startup, service, command, and TUI coverage for stale current-provider and live-edit preservation cases.
fix(codex): preserve live config and resolve current drift
Provider switching in the TUI was blocked whenever automatic failover was enabled for the app. That made a stale DB flag behave like an active failover lock even when the local proxy was stopped and no traffic was being routed through cc-switch. Limit the provider switch guard to the actual runtime condition: automatic failover must be enabled and the local proxy must be routing the current app. When the proxy is inactive, users can switch providers normally even if the failover preference remains enabled. Also align the provider list marker with the same active-proxy condition so an inactive proxy shows the current provider marker instead of queue membership. Tests cover inactive-proxy provider switching and provider-list rendering while preserving the active-proxy failover guard.
Absorbed upstream commits: - 5c6d373 Fix broken internal documentation links (SaladDay#167) - d36070b (tui)refine footer shortcuts - 371f422 (prompt)stabilize prompt list order Recorded skipped upstream commits: - 64cbca7 (docs) update RightCode rebate to 5% - d3c240c feat: add CODEX_HOME support (SaladDay#179)
Absorbed upstream commit: - 73b7c3c fix(webdav): avoid upload readback checks Behavior changes: - Remove the WebDAV check_connection probe write/read/delete round trip. - Stop gating uploads on a post-PUT manifest GET readback. - Keep manifest HEAD lookup as best-effort metadata only. - Avoid cleanup of legacy V1 remote data after a plain upload. Recorded but not absorbed in this high-risk pass: - 8330715 Improve failover proxy UX: touches failover policy, proxy lifecycle, DB schema/DAO, provider routing, and TUI UX; conflicts with local failover guard behavior. - 6ff4f88, 8afd907, d3810be, 3fa2723 prompt series: needs a separate prompt-service/form migration because current prompt service and TUI form structure diverge. - 65c4dc7..d160b16 provider common config series: conflicts with local Hermes/OpenClaw provider common-config behavior and live-write boundaries. - a1dd240 usage query configuration: large multi-service/TUI/provider-form feature, not suitable for the WebDAV fix batch. Verification: - cargo fmt --manifest-path src-tauri/Cargo.toml --check - git diff --check --cached - cargo test --manifest-path src-tauri/Cargo.toml --test webdav_sync_service
Hermes provider switching writes the selected provider to the live custom_providers list before applying top-level model defaults. The write path sanitizes UI and DeepLink payloads from baseUrl/apiKey to Hermes' base_url/api_key schema, but apply_switch_defaults was still reading the original unsanitized settings_config and only looking for snake_case keys. That meant switching providers could update model.provider and model.default while leaving model.base_url and model.api_key from the previously active provider. The runtime could then continue routing through the old endpoint or auth token even though the selected provider name and model changed. Update apply_switch_defaults to accept both snake_case and camelCase credential keys, replace top-level model credentials from the selected provider, and clear stale credentials when the selected provider does not provide them. Model tuning fields such as context_length and max_tokens continue to survive provider switches. Add focused Hermes config tests for camelCase credentials and stale credential clearing, and strengthen the ProviderService switch regression to exercise the real UI/DeepLink camelCase payload shape while asserting the live Hermes provider is persisted in canonical snake_case. Verification: cargo fmt --manifest-path src-tauri/Cargo.toml --check; git diff --check; cargo test apply_switch_defaults --manifest-path src-tauri/Cargo.toml; cargo test hermes_switch_updates_live_model_provider_and_default --manifest-path src-tauri/Cargo.toml; cargo test hermes_config::tests --lib --manifest-path src-tauri/Cargo.toml
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.
Sorry, I accidentally clicked the wrong one,i donnot want to merge