Skip to content

feat(tui): chat-first OpenTUI default session - #20

Merged
arcabotai merged 2 commits into
mainfrom
feat/tui-conversation-layout
Jul 25, 2026
Merged

feat(tui): chat-first OpenTUI default session#20
arcabotai merged 2 commits into
mainfrom
feat/tui-conversation-layout

Conversation

@arcabotai

@arcabotai arcabotai commented Jul 25, 2026

Copy link
Copy Markdown
Owner

Summary

Makes the chat-first OpenTUI the default interactive ClawFix experience while keeping the classic interface available through --plain.

  • adds branded splash, scrollable conversation transcript, findings sidebar, boxed composer, approval/diff/privacy modal overlays, help view, and local quick actions
  • wires live ClawFix scan/findings into the OpenTUI shell and falls back cleanly to the plain interface when Bun or the TUI runtime is unavailable
  • handles narrow/medium/wide terminals, paste sanitization, busy-state locking, deterministic local commands, consent-gated remote chat, and one-shot JSON output
  • fixes the approval flow to call the real guarded repair engine with finding ID + one-time token and only reports success when verification passes
  • fixes plain TUI fallback binary startup and frozen OpenTUI config invocation crashes
  • adds responsive frame harness and narrow-terminal regression coverage

Verification

  • cd cli/tui && bunx tsc --noEmit
  • cd cli/tui && bun test (60 pass, 0 fail)
  • npm test (350 pass, 0 fail)
  • frame sweep: 40 frames across 140x40, 120x35, 100x30, 80x24, and 60x20; overflow/content checks passed
  • Linux standalone: build, artifact verification, and smoke render passed (5,048 bytes, exit 0)
  • full static security scan of tracked diff and new files: clean
  • independent reviewer: passed with no security concerns or logic errors
  • Claude Opus PR review: blocker found and fixed; regression coverage added

Still requires real-device macOS verification before release.

Replace the flat TUI dump with a splash, scrollable chat transcript, responsive sidebar, boxed composer, and modal privacy/approval flows. Make interactive startup prefer OpenTUI with a plain-session fallback and preserve --plain for the classic interface. Fix standalone binary startup and renderer config mutation so the compiled artifact actually launches the new UI.

Verification: bunx tsc --noEmit; bun test (59 pass); npm test (350 pass); 40-frame responsive sweep; linux-x64 build/artifact/smoke; static security scan clean; independent review passed.
@arcabotai
arcabotai requested a review from felirami as a code owner July 25, 2026 06:04
@arcabotai
arcabotai merged commit 31cfddc into main Jul 25, 2026
3 checks passed
felirami added a commit that referenced this pull request Jul 25, 2026
… build (#22)

* fix(security): actually verify webhook signatures, and fail closed

Both webhook endpoints accepted forged requests.

/webhooks/resend checked only that the svix-id, svix-timestamp and
svix-signature headers were *present*, never that the signature was valid — and
skipped even that when no secret was configured. The route sends mail from
arca@arcabot.ai to a real inbox, so anyone who knew the URL had an
unauthenticated relay: forge an email.received event, choose the from, subject
and body, and the server mails it. Now verified as Svix specifies — HMAC-SHA256
over `id.timestamp.rawBody` with the base64 secret, constant-time compare, and a
timestamp tolerance so a captured delivery cannot be replayed.

/api/webhook/lemonsqueezy hashed `JSON.stringify(req.body)` — the parsed object
re-serialized, not the bytes Lemon Squeezy signed — so a genuine signature could
not match, and it compared with `!==` rather than a constant-time compare. It
also skipped verification entirely without a secret, accepting any POST as a
payment notification.

Both now fail closed on an unset secret, a missing raw body, or a bad signature.
The raw body is captured in the json parser for /webhook routes, since a
signature can only be checked against the bytes that were sent.

Also in the Resend path: `email_id` came from the webhook body and was
interpolated into a Resend API path called with the full-access key, so it is
now constrained to an opaque id and encoded; and inbound from/to/subject are
escaped before being interpolated into the forwarded HTML.

Verified live against the running server: forged deliveries get 401 with both
secrets unset, a correctly signed Resend delivery gets 200, and a forged
signature against a configured secret gets 401.

* feat(tui): ship a musl build so the TUI runs on Alpine hosts

@opentui/core resolves its native library by a hardcoded package name per
platform with no musl detection, so on Alpine it asked for
@opentui/core-linux-x64 and failed to load — all 23 render tests failed there,
and neither gcompat nor installing the musl package helped, because the loader
asks for the glibc package by name.

The new linux-x64-musl target compiles with bun-linux-x64-musl and stages the
musl library under the asset key the runtime actually asks for. Verified on a
real Alpine 3.21 machine by driving the compiled binary through a PTY: it
renders, accepts typed input, and exits cleanly.

Added to the release matrix. Its runtime smoke is skipped on the glibc runner,
where the binary cannot execute; the build is still verified there.

Site copy updated to match what ships: binaries for glibc and musl hosts, and
the fact that every release binary is driven through a real terminal first. Also
drops the stale "PR #20 is merged on main" line now that it has shipped.

---------

Co-authored-by: Cad from Arca <cad@arcabot.ai>
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