Skip to content

Adopt clients, edit MCP and scan client configs in the app instead of core - #180

Merged
fylorn merged 2 commits into
devfrom
feat/adopt-in-app
Sep 24, 2026
Merged

fylorn merged 2 commits into
devfrom
feat/adopt-in-app

Conversation

@fylorn

@fylorn fylorn commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Phase P3 of the remote-core plan: client adoption, MCP editing, the static scan and the file watcher move from core into the desktop app. They change files on this machine, so they belong here whichever core the app talks to.

What moved

  • tw-adopt and tw-scan now live in src-tauri/crates/ (copied from core v0.46.0; history not kept). src-tauri/Cargo.toml is now a workspace (default-members includes the crates, so CI's cargo clippy/test --manifest-path src-tauri/Cargo.toml covers them). The unused tw-config dependency is gone. The core crates (tw-api, tw-types, tw-yaml, tw-guard, tw-watch) are pinned once in [workspace.dependencies], all to the same tag.
  • New Tauri commands (the webview passes ids only, never paths or free text):
    • clients: list_clients, plan_adopt, adopt_client, plan_restore, restore_client, diagnose_client, prepare_client_key (returns the key name only; copying goes through copy_key), plus the existing copy_client_endpoint, reveal_client_config, restore_all
    • MCP / scan: mcp_targets, plan_mcp, apply_mcp, scan_clients (user-level only; the unused project-directory input is gone)
    • keys: gateway_base, delete_key (refuses a key that an adopted client still has in its config), rotate_key (rotates in core, then writes the new key into the adopted client on this machine)
  • What core is still asked: the gateway port (Overview.listen.port), the keys (Keys), and a client's dedicated key (ClientKey). The "wait for a real request" signal still comes from core's event stream (keys' last_seen_ms and request events).
  • The file watcher runs in the app (scan::spawn_watcher). It emits the Tauri event local-event (clients_changed / scan_alert), and scan alerts go into the notice bus (notices::rules::scan_alert). The first scan is a baseline, as before. useCoreEvent listens to both channels and ignores core's same-named events, which the pinned core still sends.
  • Uninstall's restore-all no longer asks core, so it works even when core is down.
  • The menu bar's copy-address action and the keys page get the gateway address from the same function as the clients page. Before, both read it from /clients.

Types

src-tauri/src/wire.rs holds the types for these commands and generates src/generated/lite-api.ts (checked by tests/ts_bindings.rs, regenerate with UPDATE_TS=1). If a name also exists in tw-api.ts, the generator imports it instead of declaring it again, and fails when the two shapes differ. After core drops those types, the same generator declares them here. Renamed where the shape changed: ScanResponse → ScanReport (no projects), KeyRotated → KeyRotation.

Message codes

The moved code keeps its codes (adopt.*, control.client_unknown, control.no_keys, control.key_used_by_client) because codes are the translation contract. The app now keeps its own manifest, src-tauri/msg-codes.txt. It is generated from msg!/code! in src-tauri/src and src-tauri/crates by the same scanner core uses: UPDATE_MSG_CODES=1 cargo test --manifest-path src-tauri/Cargo.toml --test msg_codes. The translation checks in tests/msg_codes.rs now run against core's tw_api::MSG_CODES plus this file. lite.* codes made by the frontend are still exempt. No core.zh.json changes were needed.

Webview surface

The whitelist (call.rs ALLOWED and control.ts WEBVIEW_ENDPOINTS) drops Scan, Clients, PlanAdopt, Adopt, PlanRestore, Restore, Why, McpTargets, McpPlan, McpApply. DeleteKey, RotateKey and ClientKey are now reachable only through the Rust commands above, and the test that lists what the webview cannot reach includes them.

Compatibility

This works against the currently pinned core v0.46.0: its endpoints stay unused, its watcher still runs, and its ScanAlert/ClientsChanged are ignored. While core still syncs rotated keys itself, rotate_key passes on core's report instead of writing a second time. That branch goes away with the fields once core drops them. The core PR that deletes the endpoints (ThinkWatchProject/ThinkWatch-Core#186) can merge after this one. I built this branch against it with a [patch]; when lite bumps to that core, only three spots need changes: the transitional sync handling in rotate_key, two cases in src/types.ts, and the control.key_bind_failed translation.

For remote mode (P5)

  • clients::gateway_host() returns the host clients should point at. It is 127.0.0.1 now; in remote mode it becomes the server's address.
  • clients::gateway_base(control, host) and ops::Gateway { base, keys } take the gateway and keys as inputs, so they can come from a remote core.
  • clients::retarget_adopted(control, host) repoints every adopted client at another core's gateway with that core's dedicated keys. It backs the switch-confirm checkbox.

Checks

  • cargo fmt --check, cargo clippy --all-targets -D warnings, cargo test (workspace, including control_plane against a v0.46.0 twcore), pnpm typecheck, pnpm test
  • Ported core's endpoint tests to unit tests of the local ops: plan writes nothing and masks keys, key naming (claude-code-2), no_keys, adopt → restore round trip, the adopted-owner check, repoint after rotation, MCP copy/remove/refusal, the watcher reporting only new findings and never touching a file.
  • Checked in the isolated preview harness: the clients page (list, adopt plan → confirm, manual setup "create and copy"), the keys page (owner labels via list_clients), and the MCP page (scan, targets, copy plan → apply). A local-event scan alert shows the MCP badge; the same event from core is ignored.

🤖 Generated with Claude Code

fylorn and others added 2 commits September 24, 2026 23:23
… core

Client adoption, MCP editing, the static scan and the file watcher change
files on this machine, so they now run in the desktop app. The tw-adopt and
tw-scan crates move here from core (src-tauri/crates/, one workspace with the
app; the unused tw-config dependency is dropped).

- New Tauri commands replace the control-plane endpoints: list_clients,
  plan_adopt, adopt_client, plan_restore, restore_client, diagnose_client,
  prepare_client_key, mcp_targets, plan_mcp, apply_mcp, scan_clients,
  gateway_base, plus delete_key and rotate_key (the adopted-client check and
  the key sync need this machine's files). restore_all no longer asks core.
- Core is still asked for two things only: the gateway port (Overview) and
  keys (Keys, ClientKey). The gateway host is a single function so remote
  mode can swap it; retarget_adopted() repoints every adopted client.
- The watcher runs in the app: `local-event` (clients_changed / scan_alert)
  to the webview, scan alerts into the notice bus; the first scan is a
  baseline. Core's own ClientsChanged/ScanAlert are ignored.
- Types for these commands live in src-tauri/src/wire.rs and generate
  src/generated/lite-api.ts; names identical to tw-api.ts are imported.
- Message codes the app emits itself are listed in src-tauri/msg-codes.txt
  (generated from source); translations are checked against it plus core's.
- The webview endpoint whitelist drops the moved endpoints, and DeleteKey,
  RotateKey and ClientKey now go only through Rust commands.

Works against the pinned core v0.46.0 (its endpoints just go unused).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e clients list

The menu bar's copy-address action still read gateway_base from the /clients
endpoint, which core is dropping. It now uses the same function as the
clients and keys pages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant