feat: fix proxy compatibility and establish TUI foundation

This commit is contained in:
proxyctl coordinator
2026-07-17 20:30:52 +08:00
parent 0f4c122eff
commit daeb788eac
34 changed files with 1521 additions and 176 deletions
@@ -0,0 +1,19 @@
# 01 — Build the responsive TUI foundation
**What to build:** Replace the current unbounded terminal document with a responsive, styled application shell that preserves the existing reachable workflows while establishing the layout, theme, redaction, and page seams required by the redesign.
**Blocked by:** None — can start immediately.
**Status:** done
- [x] Bubble Tea remains the runtime, with Bubbles and Lip Gloss available as direct production dependencies.
- [x] Production runs in the terminal alternate screen and restores the previous screen on normal exit.
- [x] The complete TUI Model handles terminal resize messages and renders a usable wide layout at 120×36.
- [x] The same Model renders a usable single-column layout at 80×24.
- [x] Terminals below 60×16 render a concise minimum-size message instead of a corrupted interface.
- [x] Selection and focus survive resize whenever their referenced item still exists.
- [x] Semantic styling supports light, dark, and `NO_COLOR` rendering without making color the only signal.
- [x] All text entering the presentation layer crosses one defensive credential-redaction path.
- [x] Application coordination, responsive layout, semantic theme, pages, and reusable rendering no longer accumulate in one monolithic implementation.
- [x] Existing Target and Proxy Profile workflows remain reachable; no long-lived legacy/new TUI switch is introduced.
- [x] Complete-Model tests cover the three agreed terminal-size classes.
@@ -0,0 +1,20 @@
# 02 — Deliver the Operations Console dashboard
**What to build:** Give users the accepted Operations Console experience: a stable, scannable Target list and concise selected-Target details, side by side on wide terminals and stacked on narrow terminals.
**Blocked by:** 01 — Build the responsive TUI foundation.
**Status:** ready-for-agent
- [ ] The header shows a compact proxyctl identity, the Active Profile, and counts for the current Target states.
- [ ] Available Targets appear in stable product order by default.
- [ ] A contextual action reveals unavailable Targets and their detection evidence without changing product order.
- [ ] Each Target row combines a standard Unicode/ASCII-safe symbol, explicit state text, and semantic styling.
- [ ] The selected Target details prioritize state, evidence, affected scope, permissions, ownership/restoration implications, and restart risk.
- [ ] Wide terminals show Target list and details panes side by side; narrow terminals stack the same regions.
- [ ] Tab changes pane focus, the focused pane is visible without relying only on color, and arrows navigate within the focused pane.
- [ ] Long Target lists and details remain reachable through list/viewport scrolling.
- [ ] The footer shows only actions valid for the current focus and state.
- [ ] Resizing preserves selection by Target identity rather than fragile row position.
- [ ] Complete-Model tests cover every Target state, unavailable visibility, long content, focus, scrolling, and wide/narrow rendering.
- [ ] Production rendering is rewritten from the spec; prototype branch `prototype/tui-operations-console` commit `364f28d` is reference material only and is not copied into production.
@@ -0,0 +1,19 @@
# 03 — Stream progressive startup and refresh
**What to build:** Show the dashboard immediately and update each Target independently as detection completes, both at startup and when the user refreshes.
**Blocked by:** 02 — Deliver the Operations Console dashboard.
**Status:** ready-for-agent
- [ ] Interactive application construction exposes Target identity and descriptions before availability and inspection complete.
- [ ] Startup renders every known Target in a structured Checking state without waiting for aggregate detection.
- [ ] Availability and inspection results stream to the TUI one Target at a time.
- [ ] Refresh uses the same progressive behavior rather than a separate aggregate-only path.
- [ ] Checking rows display a non-color-only progress cue and update independently on success or failure.
- [ ] Independent completion order does not reorder Targets or move selection to a different identity.
- [ ] A Target detection failure is represented truthfully without preventing unrelated Target results from appearing.
- [ ] The progressive Application Interface contains presentation-independent phases and results rather than Bubble Tea types or strings that callers must parse.
- [ ] Existing non-interactive maintenance behavior remains compatible.
- [ ] Application tests cover independent completion order, mixed success/failure, and final snapshots.
- [ ] Complete-Model tests prove the first useful frame precedes detection completion and each subsequent result updates only the relevant row.
@@ -0,0 +1,21 @@
# 04 — Unify operation coordination and status feedback
**What to build:** Give every interactive operation one coherent model for progress, serialization, confirmations, current status, partial failure, and safe exit while keeping the dashboard browsable.
**Blocked by:** 03 — Stream progressive startup and refresh.
**Status:** ready-for-agent
- [ ] Detection, Target mutation, and Active Profile transition use one presentation-independent progressive operation contract where their semantics overlap.
- [ ] System mutations remain serialized and a conflicting action exposes an actionable disabled reason instead of silently ignoring input.
- [ ] During a mutation, users can still select Targets, scroll details, and open existing read-only Help.
- [ ] The affected Target row displays progress while the status area describes the current step.
- [ ] Successful status is replaceable by later activity.
- [ ] Current errors remain visible until acknowledged, superseded by explicit resolution, or replaced by a directly related result.
- [ ] Current error details can be expanded without introducing an activity-history page.
- [ ] Partial Active Profile transitions retain successful Target updates, preserve the no-cross-Target-rollback rule, and focus the first failed Target.
- [ ] Confirmation state, progress, and final result are structured; the TUI does not parse user-facing strings to infer operation state.
- [ ] `q` on the dashboard and Ctrl+C on any page request exit; during a mutation the UI visibly reports deferred exit and exits only after safe completion.
- [ ] No new work can begin after deferred exit is requested.
- [ ] The concrete Application remains usable by existing synchronous callers while obsolete interactive fallback paths are removed after migration.
- [ ] Application and complete-Model tests cover browsing while busy, blocked mutations, persistent errors, partial failure, confirmation, and deferred exit.
@@ -0,0 +1,20 @@
# 05 — Add contextual Target actions and Drift Resolution
**What to build:** Replace the ambiguous Target toggle with actions that reflect the selected Target's real state, including explicit Drift Resolution choices.
**Blocked by:** 04 — Unify operation coordination and status feedback.
**Status:** ready-for-agent
- [ ] A Disabled Target offers Enable.
- [ ] An Enabled Target offers Disable.
- [ ] An External Target offers Apply Profile and explains that proxyctl will begin managing the configuration while preserving restorable state.
- [ ] A Drifted Target offers Resolve rather than silently choosing a direction.
- [ ] Drift Resolution offers Reapply Active Profile and Restore Original as separate intents.
- [ ] Reapply uses the Active Profile and returns truthful Enabled, Drifted, or failure state.
- [ ] Restore Original reuses existing ownership safeguards and refuses safely when restoration evidence is insufficient.
- [ ] No Adapter ownership, restoration, or cross-Target rollback semantics are weakened.
- [ ] Docker restart confirmation remains required for relevant actions and cancellation is the default focused choice.
- [ ] Routine reversible actions do not gain new confirmation dialogs.
- [ ] Action availability and disabled reasons are structured in the Application snapshot rather than inferred from rendered labels.
- [ ] Application and complete-Model tests cover all Target states, both Drift Resolution branches, missing Active Profile, ownership loss, confirmation, and cancellation without mutation.
@@ -0,0 +1,19 @@
# 06 — Add the searchable Active Profile picker
**What to build:** Let users search for and activate a Proxy Profile directly from the dashboard without mixing routine switching with Profile management.
**Blocked by:** 02 — Deliver the Operations Console dashboard; 04 — Unify operation coordination and status feedback.
**Status:** ready-for-agent
- [ ] A dashboard action opens a keyboard-only modal Profile picker.
- [ ] The Active Profile is pinned first and all remaining Profiles are sorted by name.
- [ ] The picker supports filtering and scrolling across more Profiles than fit in the terminal.
- [ ] Enter activates the selected Profile and Escape closes the picker without mutation.
- [ ] Activation uses the unified progressive operation contract and displays per-Target progress on the dashboard.
- [ ] Docker restart confirmation appears when required, with cancellation as the safe default.
- [ ] Partial failure retains successful Target updates, closes or preserves the picker predictably, and focuses the first failed Target.
- [ ] Search text and selection survive resize when the selected Profile still exists.
- [ ] Empty and no-match states provide clear next actions without conflating switching with creation.
- [ ] Profile names and all operation text are defensively redacted before rendering.
- [ ] Complete-Model tests cover ordering, search, keyboard selection, resize, activation, confirmation, cancellation, partial failure, and empty states.
@@ -0,0 +1,19 @@
# 07 — Build the dedicated Profile management page
**What to build:** Provide a scrollable keyboard-first page for creating, editing, reviewing, and deleting Proxy Profiles while keeping activation in the dashboard picker.
**Blocked by:** 01 — Build the responsive TUI foundation; 04 — Unify operation coordination and status feedback.
**Status:** ready-for-agent
- [ ] Profile management is a distinct page from the dashboard activation picker.
- [ ] The Profile list scrolls and keeps selection visible with more entries than fit on screen.
- [ ] The selected Profile's mode, obscured URLs, and Bypass values are reviewable without exposing credentials.
- [ ] Create, edit, and delete entry points are available through contextual keyboard actions.
- [ ] Deletion uses a reusable confirmation dialog with cancellation focused by default.
- [ ] Deleting the final Proxy Profile clearly explains that existing managed configuration remains and becomes Drifted.
- [ ] Escape and `q` return to the previous page; Ctrl+C follows the global exit contract.
- [ ] Operations disabled by in-flight work expose a reason rather than silently ignoring input.
- [ ] Selection and viewport survive resize and CRUD results whenever the referenced Profile still exists.
- [ ] Empty-state copy provides a direct creation path and does not incorrectly claim no Active Profile when non-active Profiles exist.
- [ ] Complete-Model tests cover long lists, details, create/edit entry, deletion confirmation, final deletion, cancellation, resize, and navigation consistency.
@@ -0,0 +1,19 @@
# 08 — Deliver the mature Profile editor
**What to build:** Replace append-only Profile editing with efficient terminal text inputs, precise validation, and a live Bypass interpretation that users can trust before saving.
**Blocked by:** 07 — Build the dedicated Profile management page.
**Status:** ready-for-agent
- [ ] Name and URL fields support insertion, cursor movement, Home, End, Delete, Backspace, paste, and horizontal scrolling.
- [ ] Focus is explicit and remains understandable without color.
- [ ] Single and Advanced mode transitions preserve values according to existing Proxy Profile semantics.
- [ ] Field-specific validation appears beside the relevant field and updates when input changes.
- [ ] Save uses the same canonical Profile validation semantics as the live feedback, preventing contradictory results.
- [ ] Bypass accepts efficient delimited input and displays a live normalized, deduplicated preview.
- [ ] Invalid Bypass entries are identified individually without hiding valid entries.
- [ ] Proxy credentials remain obscured during entry, scrolling, validation, review, errors, and resize.
- [ ] Save returns to management with the saved Profile selected; cancel leaves persistence unchanged.
- [ ] Long URLs and Bypass content remain usable at 80×24.
- [ ] Complete-Model tests cover editing operations, paste, horizontal scrolling, mode transitions, validation lifecycle, Bypass preview, save, cancel, resize, and adversarial secret-bearing input.
@@ -0,0 +1,19 @@
# 09 — Attach Protocol Probe to the Profile field
**What to build:** Make Protocol Probe an explicit, user-initiated URL-field action whose progress and result remain attached to the input that produced them.
**Blocked by:** 08 — Deliver the mature Profile editor.
**Status:** ready-for-agent
- [ ] The applicable URL field visibly offers Protocol Probe and documents its keyboard shortcut.
- [ ] Probe never runs when the form opens, when focus changes, or when a Profile is saved.
- [ ] Progress, endpoint-safe status, and detected capabilities render with the initiating field rather than as unrelated global status.
- [ ] HTTP-only detection populates the established HTTP representation.
- [ ] SOCKS5-only detection preserves the established `socks5h` semantics.
- [ ] Dual HTTP/SOCKS5 detection creates the established compatible Advanced Profile values.
- [ ] Editing the relevant URL, changing mode, saving, or cancelling invalidates an in-flight result.
- [ ] Repeated invocation while the same probe is active does not start duplicate work.
- [ ] Userinfo and credentials never appear in dial evidence, status, errors, or rendered frames.
- [ ] Failure and unrecognized-protocol results leave user input editable and do not prevent saving a manually chosen value.
- [ ] Complete-Model tests cover all capabilities, failure, explicit invocation, no hidden network work, duplicate suppression, resize, and every stale-result path.
@@ -0,0 +1,19 @@
# 10 — Unify Help, dialogs, and navigation
**What to build:** Give every page a consistent keyboard navigation and modal model, with scrollable contextual Help that can be consulted without losing current work.
**Blocked by:** 04 — Unify operation coordination and status feedback; 07 — Build the dedicated Profile management page.
**Status:** ready-for-agent
- [ ] Help opens as a scrollable contextual dialog over the current page rather than an unbounded static document.
- [ ] Help describes actions valid for the current page, focus, Target state, and operation state.
- [ ] Escape closes the top dialog or returns one page level.
- [ ] `q` returns from child pages and exits only from the dashboard.
- [ ] Ctrl+C requests exit from every page and follows deferred-exit safety during mutations.
- [ ] Closing a dialog restores the prior page, focus, selection, and viewport when still valid.
- [ ] Dialog stacking and confirmation precedence are deterministic; an input cannot accidentally act on content behind a dialog.
- [ ] Dialogs resize within the agreed terminal bounds and preserve meaningful focus.
- [ ] Help can be opened and scrolled during a mutation while conflicting actions remain disabled.
- [ ] Footer hints agree with actual accepted keys, including cancel and back behavior.
- [ ] Complete-Model tests cover Help from every page, long Help, nested dialog precedence, resize, focus restoration, `q`, Escape, Ctrl+C, and in-flight operation behavior.
@@ -0,0 +1,20 @@
# 11 — Harden accessibility, privacy, and rendering
**What to build:** Verify and finish the redesigned TUI across supported terminal sizes, color modes, hostile text, and process lifecycle so that visual polish does not compromise meaning, privacy, or reliability.
**Blocked by:** 03 — Stream progressive startup and refresh; 04 — Unify operation coordination and status feedback; 05 — Add contextual Target actions and Drift Resolution; 06 — Add the searchable Active Profile picker; 07 — Build the dedicated Profile management page; 08 — Deliver the mature Profile editor; 09 — Attach Protocol Probe to the Profile field; 10 — Unify Help, dialogs, and navigation.
**Status:** ready-for-agent
- [ ] Fixed-size render coverage includes 120×36, 80×24, and 59×15 across the completed workflows.
- [ ] Light, dark, and `NO_COLOR` renders retain readable state, focus, progress, warnings, disabled actions, and errors.
- [ ] Standard Unicode symbols have a safe rendering strategy and no Nerd Font dependency.
- [ ] Long Target names, Profile names, evidence, details, URLs, Bypass values, errors, and confirmation copy remain bounded and reachable.
- [ ] Secrets injected through valid and malformed Profile fields, evidence, notices, errors, progress, command output, and confirmations never appear in any rendered frame.
- [ ] Resize stress preserves valid page, dialog, focus, selection, viewport, in-flight operation, and deferred-exit state.
- [ ] Alternate-screen process tests verify normal exit and safe deferred exit restore the prior terminal screen.
- [ ] Startup, refresh, Target action, Active Profile switching, management, editor, Probe, Help, and Drift Resolution each have complete-Model behavioral coverage.
- [ ] Tests assert semantic behavior rather than private component structure or raw styling calls.
- [ ] Existing Application and Adapter suites remain green without duplicating Target configuration tests in the TUI.
- [ ] Documentation describes the redesigned keyboard model, responsive size behavior, Profile switching, contextual Target actions, and privacy boundaries.
- [ ] Full repository and hermetic verification pass without modifying real Shell, APT, Docker, GNOME, or systemd state.