Commit graph

23 commits

Author SHA1 Message Date
Bryan Helmkamp
b5f200d701
chore(web): namespace static images under /images and skip in HTTP logs
Move favicon, logo, logotype, and PNG icons from /public/ root to
/public/images/ so the HTTP log middleware can drop them by path
prefix. Extends the existing /assets/ skip in http_log_middleware to
cover /images/ as well, removing favicon/logo entries from the server
log without filtering by extension (which would risk muting future
extension-suffixed API routes).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 08:57:14 -04:00
Bryan Helmkamp
90b6db1e52
feat(web): polish runs board and install nav
Hide the Steer action on board cards outside demo mode so the action
list reflects what the operator can actually do. Hide the lifecycle
status pill on cards in the Initializing column since the column header
already conveys the state. Shorten the install wizard top nav label
"Object store" to "Storage".
2026-04-27 16:02:54 -07:00
Bryan Helmkamp
2460ffc37a
feat(install): add sandbox provider step to web install wizard
Operators choose Docker (default, zero-config) or Daytona (validated
via Daytona SDK) during browser install. Selection is captured in
settings.toml under [run.sandbox] -- explicitly even for Docker, so the
choice is locked in. Daytona keys land in the vault as DAYTONA_API_KEY
(Environment secret). Step always runs after object_store and before
the LLM step.

Server adds POST /install/sandbox/test (validates Daytona key via
client.list) and PUT /install/sandbox; both reuse the install-token
auth and InstallSecret redaction patterns established by object-store.
A resolve_install_sandbox_state helper preserves a saved Daytona key
when the operator revisits the step without re-entering it. The
in-memory api_key is dropped from PendingInstall after finish, matching
the manual_credentials cleanup for S3 access keys.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 14:59:17 -07:00
Bryan Helmkamp
c28b040c6f
fix(install): reject wildcard public URLs
Normalize bind-address wildcards before presenting install URLs, reject wildcard public origins at CLI and server install boundaries, and surface recovery guidance in the installer and doctor output.
2026-04-25 18:58:00 -04:00
Bryan Helmkamp
533785cd4c fix(install): expose local object store root 2026-04-24 12:38:04 -04:00
Bryan Helmkamp
b975a802dc fix(install): show AWS access key ID as plain input
The AWS access key ID is not a secret — swap its PasswordInput for a
regular text input so operators can read and edit it directly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 12:29:27 -04:00
Bryan Helmkamp
7d791c531b fix(install): use single-line install token input
Refresh the embedded SPA assets so the Rust bundle serves the updated install screen.
2026-04-24 12:20:17 -04:00
Bryan Helmkamp
ef78228c06
refactor(install): flatten resolver, dedupe fetch wrappers, share step-submit helper
- Derive strum::IntoStaticStr on InstallObjectStoreProvider/CredentialMode and use it in as_session_value instead of a hand-written match.
- Split resolve_install_object_store_state: extract resolve_s3_manual_credentials and fold the redundant outer "missing credentials" guard into its (None, None) arm.
- Replace the per-endpoint installFetch boilerplate with installRequest / installJsonRequest<T> so each install-api wrapper is a single call.
- Extract runStepSubmit inside InstallApp; the LLM, server, object-store, and GitHub step handlers now share the setSubmitting / try / refresh-session / navigate / finally scaffolding.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 20:21:13 -04:00
Bryan Helmkamp
e184213330
refactor(install): collapse install UI pickers and dedupe server strings
- Derive install stepper's current step from INSTALL_STEPS instead of a hand-maintained pathname if-chain.
- Replace four near-identical picker components with one generic CardPicker plus per-flow option arrays.
- Extract repeated object-store validation error strings into constants and a small helper.
- Run the S3 artifacts/ and slatedb/ prefix probes concurrently via tokio::try_join!.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 19:43:49 -04:00
Bryan Helmkamp
4bf0c40319
feat(install): add object-store step to web install 2026-04-22 19:31:09 -04:00
Bryan Helmkamp
7ed57ba863
fix(install): show loading until session resolves, not just during fetch
GithubAppDoneScreen was firing `<Navigate to="/install/github">` on the
first render after GitHub's manifest callback because the render path
used `sessionState.status === "loading"` as its loading gate. Between
initial mount (sessionState defaults to "idle") and the session-fetch
useEffect flipping it to "loading", the main layout rendered once with
`session === null`. Done screen saw `github === undefined`, treated the
session as misconfigured, and bounced the user back to the already-done
"Connect GitHub" form — a redirect loop after a successful GitHub App
install. Broaden the gate to `!session` so every transient state with a
token-but-no-session shows the loading screen, not a half-rendered step.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 14:40:21 -04:00
Bryan Helmkamp
328bddea33
fix(install): pass GitHub App manifest state as form field, not URL query
GitHub's App Manifest endpoint rejects `redirect_url` values that carry a
query string with "invalid redirect_uri", leaving the web wizard stuck:
the 10-minute pending-setup guard then blocked every retry for ten
minutes. Move the CSRF state out of `redirect_url` and into a hidden
`state` form field on the auto-submit — GitHub preserves it on the
callback, matching the CLI's working Manifest flow. Drop the retry
conflict so a fresh POST to /install/github/app/manifest always replaces
the pending entry and mints a new state token; stale callbacks are
already rejected by the existing state-match check on the redirect
handler.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 14:21:33 -04:00
Bryan Helmkamp
9b5bc8ede4
style(run-stages): copy affordance, collapsed long output, sticky header
- extract a shared CopyButton into components/ui.tsx and drop the
  install wizard's local duplicate
- sticky stage header at the top of the turn stream so users always
  know which stage they're reading as they scroll
- copy-to-clipboard button on System, Assistant, and Command blocks;
  revealed on hover/focus
- stdout/stderr longer than 20 lines collapse to the last 20 with a
  "Show N earlier lines" expander
- bump the [10px] labels in tool-use.tsx to [11px] for readability

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 17:27:25 -04:00
Bryan Helmkamp
54867d29ed
feat(web): unify empty/error/loading states in one primitive
Adds apps/fabro-web/app/components/state.tsx exposing EmptyState,
ErrorState, and LoadingState on a shared StatePanel chrome so every
"the content isn't ready" surface looks like the same app.

Swaps in place of bare <p> tags and ad-hoc bordered divs:
- run-detail: "Run not found" is now an ErrorState
- run-stages: "No stages yet" is an EmptyState
- run-overview: empty-graph panel is an EmptyState
- run-billing: empty-billing panel is an EmptyState
- runs: filtered-empty ("no matching runs") now renders an EmptyState
  (the branded landing empty is preserved as RunsLandingEmpty)
- install-app: session-loading StatusPanel replaced by LoadingState

No change to the root ErrorBoundary — full-page crashes stay there.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 17:22:57 -04:00
Bryan Helmkamp
b448a4e246
fix(theme): contrast on primary buttons and muted text in light mode
- introduce --color-on-primary (navy-950 in dark, white in light)
  so text on bg-teal-500 reads clearly regardless of mode; swap
  hardcoded text-navy-950 occurrences on teal fills for text-on-primary
- darken --color-fg-muted in light mode from slate-400 (#94a3b8) to
  slate-500 (#64748b); slate-400 failed AA on the tinted page
- deepen page tint to #eef2f7 and strengthen line/line-strong so
  white cards have real edges, not invisible hairlines

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 17:12:01 -04:00
Bryan Helmkamp
2ff71d74bb
refactor(web): align auth surfaces with the install wizard
Extract the wizard's INPUT_CLASS, PRIMARY_BUTTON_CLASS,
SECONDARY_BUTTON_CLASS, and ErrorMessage into
apps/fabro-web/app/components/ui.tsx so auth-login, setup, and the
install wizard share one source of truth.

- auth-login: raise the heading to text-2xl, swap white-on-teal for
  navy-on-teal, replace the bordered dev-token input with the outline
  pattern, use the ErrorMessage pill for invalid tokens, associate the
  input with a label, and shrink the GitHub mark to size-4 per the
  icons guideline
- setup: replace the nested bg-overlay cards with a numbered <ol>
  matching the wizard's welcome layout, raise the heading, switch the
  primary button to navy-on-teal
- install-app: re-import the shared primitives instead of holding
  local duplicates

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 16:43:41 -04:00
Bryan Helmkamp
54ddaa2cee
feat(install): redesign web wizard and scope dev token to PAT installs
Redesign the install wizard for clarity:
- swap the sidebar layout for a centered column and a horizontal stepper
- make completed/current stepper entries clickable links
- reorder steps so Server URL precedes LLMs
- use env-var placeholders (ANTHROPIC_API_KEY, etc.) with
  per-provider "Where do I get this?" disclosures
- replace the readonly "Validated username" input with a success pill
- drop the GitHub App name field (GitHub confirms the name anyway)
- re-label the GitHub App option and split review rows by strategy
- add a copy action to the Server URL on the review screen

Scope the dev token to PAT installs:
- only generate the dev token, write its files, and set FABRO_DEV_TOKEN
  inside the GithubInstallState::Token arm
- mark dev_token optional on InstallFinishResponse in the OpenAPI spec
- hide the Development token card on /install/finishing when absent
- add app_install_finish_omits_dev_token_and_does_not_write_it test

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 16:11:02 -04:00
Bryan Helmkamp
20e4ef32ae
refactor(install): collapse overlapping React state into discriminated unions
Two wins here. First, the `session`/`loadingSession`/`sessionError`
triple is replaced with a single `SessionState` discriminated union, so
the component can switch on `.status` instead of juggling three
correlated flags. Second, the seven flat `useState` calls for the
GitHub step are grouped into `githubStrategy` + `tokenForm` + `appForm`,
with `appForm.owner` typed as the generated `InstallGithubAppOwner`
tagged object. Invalid states like "token flow but org slug set" simply
stop existing.

\`buildInstallGithubAppOwner\` is deleted (unused) — form handlers build
the tagged object in place, which is small enough to stay readable.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 13:59:09 -04:00
Bryan Helmkamp
acb6b3f9d6
refactor(install): tag GithubAppOwner with discriminated object shape
The install GitHub App manifest shape encoded owner as `"personal"` or
`"org:<slug>"` - a magic string parsed in install-app.tsx, built by
install-api.ts, and reparsed server-side. Replace with a tagged object
`{ kind: "personal" } | { kind: "org", slug }` in the OpenAPI spec, the
progenitor-generated Rust types, and the frontend.

Server-side, the internal `GitHubAppOwner` enum keeps its semantic
shape but gains a `TryFrom<GithubAppOwnerInput>` conversion and emits
the tagged JSON via `as_session_value`.

Frontend drops `buildGithubOwnerValue` in favor of
`buildInstallGithubAppOwner`, and the ready-screen renders the owner
through a small helper instead of string concatenation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 13:48:31 -04:00
Bryan Helmkamp
3f21644d80
fix(install): cover follow-up edge cases
Harden the remaining install flow regressions and add the missing
coverage for startup dispatch, finish-time shutdown behavior, and
partial-state persistence after vault failures.
2026-04-19 13:32:46 -04:00
Bryan Helmkamp
75f8ed845b
fix(install): harden web wizard against review findings
Tighten the browser-based install flow after correctness and adversarial
review, without changing the external wizard shape.

- Persist the actual bind in server.listen, not the canonical URL
- Reject concurrent /install/finish and rapid GitHub App retries
- Keep the prior GitHub Token strategy until App callback succeeds
- Recover from poisoned install locks instead of propagating panics
- Rollback both settings and vault on failed persistence
- Redirect GitHub callback errors back into the wizard UI
- Validate LLM keys via /models probe instead of a billed generate()
- Reject canonical URLs with trailing slash, path, query, or fragment
- Accept any valid install-token source, not just the first present one
- Redact the install token in structured logs
- Assert install-mode SPA marker injection at startup
- Warn on suspected concurrent operators via UA + X-Forwarded-For
- Add component-level test for the GitHub callback error banner

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 12:43:00 -04:00
Bryan Helmkamp
9dd792c8b8
fix(install): exclude openai-compatible from v1 setup
Restrict the browser install flow to Anthropic, OpenAI, and Gemini,
remove the unused install-time base URL surface, and reject
openai_compatible with a stable 422 response.

Also fix the finishing health poller so it only redirects after the
server comes back healthy outside install mode instead of jumping early
on transient restart failures.
2026-04-19 11:43:38 -04:00
Bryan Helmkamp
ecdfdd82d8
feat(install): add browser-based setup flow
Implement the web-first install experience across the server, CLI, API spec,
web app, and packaged SPA assets.

This also removes test-side process env mutation by pushing env-dependent
decision points behind explicit helpers and test wiring.
2026-04-19 11:20:58 -04:00