release: promote beta to main (runtime CVE refresh + auth/reconnect/e2e fixes) - #321
Conversation
β¦oxes Field incident (customer box, Jul 6): the background mascot phrase regen picked llama3.2:3b, timed out at 60s, and because a failed generation never updates the cache, the still-stale cache retried the full model load on every mascot fetch β 2.6GB RAM + 145% CPU every ~90s until the box hit 92% RAM + 100% swap. - 12h failure backoff persisted in KV: a timeout/bad-output/low-memory attempt blocks background retries; explicit Settings regen bypasses it - tiny models only (<=2B): drop 3B+ from the preferred list and stop falling back to 'first installed model' (could be a user-pulled 7B+) - RAM headroom guard: skip generation below 3GB MemAvailable - keep_alive: 0 so Ollama unloads the model right after the call instead of holding gigabytes for the default 5 minutes Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Bumps [actions/cache](https://github.com/actions/cache) from 4 to 6. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](actions/cache@v4...v6) --- updated-dependencies: - dependency-name: actions/cache dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 7. - [Release notes](https://github.com/actions/checkout/releases) - [Commits](actions/checkout@v4...v7) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps [docker/setup-qemu-action](https://github.com/docker/setup-qemu-action) from 3 to 4. - [Release notes](https://github.com/docker/setup-qemu-action/releases) - [Commits](docker/setup-qemu-action@v3...v4) --- updated-dependencies: - dependency-name: docker/setup-qemu-action dependency-version: '4' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com>
β¦266) ChatGPT-subscription boxes (Codex OAuth, no OpenAI API key) that stored their active model or a fallback as `openai/gpt-5.5` (etc.) before the setup UI routed ChatGPT picks through Codex hit `401 Missing bearer or basic authentication in header` on api.openai.com/v1/responses β often only as a FailoverError days into use, once the OAuth token first refreshes and the failover chain reaches the keyless `openai/*` fallback. The chat-model pick route already rewrites openai/<gpt> -> codex/<gpt>, but only when the user re-picks the model; existing configs never re-pick, so an updated box stays broken until manually re-selected. Migrate primary + fallbacks in gateway-pre-start.sh on gateway start, guarded on "codex OAuth present AND no OpenAI API key" so keyed / dual-auth boxes (where openai/* is a valid route) are left untouched. Mirrors CODEX_SUPPORTED_MODEL_RE / hasOpenAiApiKeyProfile / hasCodexOauthProfile in src/app/setup-api/chat/model/route.ts. Verified against 6 fixtures: openai->codex primary+fallback migrate; keyed box untouched; non-supported (gpt-4o) primary left as-is; already-codex no-op; no-codex-auth untouched; composes with the retired-Sonnet migration. bash -n and py_compile pass. Co-authored-by: Mike (IDRobots) <mike@idrobots.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
β¦ narrow chat) (#267) * fix: chat header pills squeeze + truncate cleanly on narrow panels On a narrow chat the provider / model / thinking selector pills overlapped into an unreadable strip. Two parts: 1. .header-dropdown-trigger gets width:100% so the button fills its flex-shrinking .header-dropdown parent. Previously the button kept its content width and spilled past the shrunk parent, so overflow:hidden on .chat-header-pills clipped / overlapped the pills instead of the labels truncating. Now every pill gives ground evenly and its label ellipsizes (the chevron stays β it's reserved in the 24px right padding). 2. Single row (no wrap) + overflow:hidden, and the chat window clamps to MIN_CHAT_WIDTH (340px) on both resize paths + the rendered width, so the window stops shrinking once the pills reach a readable minimum instead of smashing them. The open menu is portaled to <body> (HeaderDropdown), so clipping the row can't hide it. Verified at 320-420px: even truncation, carets visible, zero overlap. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: open chat from mascot with a macOS-style animation, no corner flash - Stop streaming the frozen mascot's position into mascotX while the chat is open (page.tsx). That nudged mascotX for a frame right after opening, so the popup flashed to the wrong corner before settling. mascotX is now captured once from the tap. - Grow the popup OUT of the mascot: transform-origin pinned to the popup's bottom edge, aligned horizontally with the mascot, and scale 0.82 -> 1 on an easeOutExpo curve (cubic-bezier(0.16,1,0.3,1)) over 0.36s. Smooth, premium, emanates from where you tapped instead of scaling from the popup centre. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: bump SW cache clawbox-v3 -> v4 to invalidate stale assets on the 3.1.10 chat-UI changes Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: keep chat popup header on-screen on short/zoomed viewports The un-dragged popup anchors from the bottom (bottom:170 above the mascot, bottom:65 in tray mode) but its maxHeight budget was a flat 100vh-60px, so on viewports shorter than ~680px (small windows, browser zoom) a 500px-tall popup shoved its whole header β pills, status dot, close button β off the TOP of the screen (rect.y = -76 measured on a 594px viewport). Subtract the bottom anchor from the height budget per mode (+12px top margin) so the header is always visible and the popup just gets shorter instead. Found by driving the real desktop over CDP and sampling the popup rect during open; the same probe confirmed the mascot-open animation runs and there is no left-corner flash. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Mike (IDRobots) <mike@idrobots.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
On pointerdown the mascot sets physicsActive=true, which rendered the crab with transform:undefined -> it reverted to its base left:0 and snapped to the bottom-LEFT corner for the ~100ms a tap is held, before the chat opened. Measured on a real box: crab center x jumped 1241px -> 75px on press and back on release. Keep applying the crab's real translateX (+ hop height) while physics/drag is active instead of clearing the transform; the imperative physics/drag rAF loop still overrides it per-frame, so drag/throw physics are unchanged. Bump SW cache clawbox-v4 -> v5 so existing installs pick up the new bundle. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
β¦hat auto-open - ChatPopup: clawChatBurstIn spring/overshoot/glow entrance animation - Mascot: tap no longer teleports the crab; slides clear of a docked chat panel (rightInset) - page: floating chat popup only opens on mascot tap (ignore persisted ui_chat_open); mascot stays visible beside docked panel Pulled from the todor.local test box where it was verified live (uncommitted there). Part of folding UI polish into the existing v3.1.10.
β¦ cloud providers Every cloud provider now exposes the same Off/Low/Medium/High effort ladder (dropped provider-specific xhigh/max/adaptive/minimal for consistency). ClawBox AI / DeepSeek keep an 'off' default so they stay fast unless the user opts in; other providers default to medium. llama.cpp stays off-only (no reasoning).
β¦nly, no jank) Switching AI provider (codex<->clawbox) restarts the gateway; on the Jetson that pins the CPU. The reload overlay's progress bar was driven by a 200ms setInterval + a width transition (layout every frame), so it visibly stuttered under that load. Replace it with a transform:scaleX CSS keyframe (clawReloadFill) that runs on the compositor thread and stays smooth regardless of main-thread work; reloadProgress is now only the 0->100 completion signal.
β¦ntitled ChatGPT accounts
The codex catalog allowlist regex stripped everything except gpt-5.5/gpt-5.4/
gpt-5.4-mini, so gpt-5.6-sol (and terra/luna) never appeared even for ChatGPT
accounts whose plan includes them. Widen the allowlist to gpt-5.6-{sol,terra,
luna} and add stable labels. These are plan-gated upstream β the live catalog
only returns them for entitled (Plus/Pro/Max) accounts, so boxes on lesser
plans still never see them (no dead buttons).
3.1.10 follow-ups: chat open animation, mascot polish, uniform reasoning picker, smooth provider-switch bar, gpt-5.6-sol in codex picker
Domain migration (71 refs across 12 files):
- clawbox.tech -> clawbox.com
- docs.clawbox.tech -> docs.clawbox.com
Fixes a live deploy bug: GitHub Pages for this repo is already configured
with cname=docs.clawbox.com, but .github/workflows/docs-deploy.yml still
wrote "docs.clawbox.tech" into _site/CNAME and passed it to the deploy
action. The next docs deploy would have reset the custom domain and taken
docs.clawbox.com down. (clawbox.tech and docs.clawbox.tech remain live as
301 redirects via Porkbun, so no inbound links break.)
README β entity clarity and SEO:
- Add an H1 naming the product and category ("ClawBox - the official
OpenClaw AI assistant hardware"); previously the README had no H1.
- Open with an explicit entity sentence: ClawBox is made by ID Robots Ltd.
and is the official hardware for OpenClaw. Grounded-AI answers have been
attributing ClawBox to an unrelated namesake vendor; this states the
maker, the category and the canonical domain in the first 200 characters.
- Add an "official channels" callout listing clawbox.com, docs.clawbox.com,
this repo, Discord and support email, and noting that similarly-named
products from other vendors are unaffiliated and unsupported.
- Clarify the OpenClaw-vs-ClawBox distinction (agent vs appliance) in prose
rather than leaving it implicit.
- State that inference runs locally on-device rather than relaying prompts
to a third-party API.
- Add an FAQ section targeting the questions assistants actually get asked:
who makes it, how it differs from OpenClaw, subscription requirements,
offline operation, where to buy, and self-install on your own Jetson.
- Footer now carries the canonical link, support email and a plain-language
descriptor line.
Uses "ClawBox(TM)", not (R) - the trademark applications are still pending,
so asserting registration would be inaccurate.
β¦-readme Point all references at clawbox.com + strengthen README entity/SEO
Gateway-pre-start.sh runs as a blocking ExecStartPre. Three compounding bugs made it hang or churn the gateway boot path, surfacing as the Discord "Gateway won't start after update" / "Update failed" reports: 1. Version-skew guard used exact-string compare, so an npm republish (2026.7.1 -> 2026.7.1-1) read as a skew and reinstalled codex on every boot. Now compares base MAJOR.MINOR.PATCH; a real API skew still reinstalls, a republish does not. 2. The codex plugin dir was hard-coded to the legacy flat npm layout; on 2026.7.x cores the plugin lives under npm/projects/<hash>/..., so the install check always read "missing" and reinstalled every boot. Now resolves either layout. 3. The reinstall itself was an unbounded npm install on the blocking boot path. Now hard time-boxed (120s) and best-effort: on failure/timeout the gateway still starts. Added TimeoutStartSec=600 as a unit-level backstop. install.sh step_ai_tools_install: Claude Code installer is geo-blocked in some regions and returns an HTML page (HTTP 200) that piped into bash aborted the whole reinstall under set -euo pipefail at [18/23] (nginx 404 brick). Now downloads to a file, verifies it's a script not HTML, and every optional AI-CLI install is non-fatal. Validated on todor: gateway active + listening in 1s, 0 codex reinstalls across restarts.
β¦ an API key (3.1.11) OpenClaw's memory search defaults to OpenAI embeddings, which need an OPENAI_API_KEY the box often doesn't have (ChatGPT-OAuth / DeepSeek users). After updates this surfaced as 'Semantic memory search is still offline ... missing OpenAI provider auth/API-key access', and 'openclaw memory index --force' could not fix it (still no embeddings provider). - install.sh: pull a small local embedding model (qwen3-embedding:0.6b) in step_ollama_install, best-effort (a failed pull never aborts the install; memory falls back to lexical FTS). - gateway-pre-start.sh: when memorySearch has no deliberate provider AND the local model is present in Ollama, point memorySearch at local Ollama. Gated on the model existing so we never leave it fail-closed; only touches an unset/'auto' provider so a deliberate OpenAI/remote setup is preserved. Self-heals existing boxes on upgrade, not just fresh installs. Validated on todor: memory index + search work with zero OpenAI key (exact-keyword recall via local embeddings); self-heal wires the provider on boot when unset and the model is present.
β¦h (3.1.11) Picking gpt-5.6-sol on a device signed in with a ChatGPT/Codex subscription failed with: gpt-5.6-sol is not supported with ChatGPT subscription auth. Use GPT-5.5, GPT-5.4, or GPT-5.4 Mini, or switch OpenAI to API-key mode for Pro/API-only models. That rejection is ours, not OpenAI's. CODEX_SUPPORTED_MODEL_RE was written before GPT-5.6 existed and only matched gpt-5.5/gpt-5.4/-mini, so the setup route 400'd the pick locally before the request ever reached OpenAI. GPT-5.6 is subscription-eligible: OpenClaw's ChatGPT route catalog carries sol/terra/luna, `openai/gpt-5.6-sol` is the documented default for a fresh Codex OAuth setup, and our own CODEX_MODELS catalog in provider-models.ts already lists all three β only this allowlist disagreed. Widen the allowlist to the three GPT-5.6 tiers and refresh the two now-inaccurate error strings. `-pro` tiers stay out; those remain API-key only. GPT-5.6 is a limited preview and per-account access varies, so the pick is now forwarded and any real entitlement error surfaces from upstream rather than being pre-empted locally β matching OpenClaw's documented behaviour of not silently downgrading a GPT-5.6 selection. Covers both entry points: a direct `codex/gpt-5.6-*` pick, and the legacy `openai/gpt-5.6-*` pick that gets routed through Codex when only subscription auth is configured. Tests: 1404 unit tests pass (110 files); tsc clean; lint 0 errors.
β¦erence Both suites were red on this PR, and neither failure came from the 3.1.11 changes β no commit here touches the desktop shell. They broke on beta when #269 made the floating chat popup stop restoring a persisted `ui_chat_open`: // The FLOATING chat popup must never auto-open on load: it should appear // only when the user taps the crab. // (We intentionally ignore a persisted `ui_chat_open` here.) Beta's last green e2e run was c7beb31, before that merge landed, so the two specs still seeding `ui_chat_open: 1` only started failing once a PR ran e2e against post-#269 beta. This is that PR. e2e/first-load-mascot-layout: drop `ui_chat_open` and open the popup through the fresh-install greeting instead (no saved wallpaper/desktop apps), which is the one load-time path that still opens it. Opening it at mount is what the assertions actually need: `frozen` pins the crab straight away. Opening it later with a click passes in isolation but races the mascot's autonomous walk (actions start ~3.5s after mount), which drifts the crab off the popup's `mascotX` anchor and made the run flaky under load. e2e-install/80-chat: hide the mascot and click the taskbar "Chat" button β the same launcher the passing chat-popup specs use β rather than relying on the popup being open already. Both assertions are unchanged; only the way the popup is opened moved. Verified locally: the mascot-layout spec passes standalone and in a full suite run (34 passed).
β¦ button The previous attempt hid the mascot and clicked the taskbar "Chat" button. That works against the mocked desktop but timed out after 60s on a real installed box, so 80-chat still failed. Use the docked side panel instead: `ui_chat_panel_width > 0` is the one preference the desktop still restores on load (src/app/page.tsx), and it opens the chat at mount with the same textbox this test drives. No launcher button, no crab tap, nothing to wait for. Assertions unchanged.
β¦y reads it On a ChatGPT-subscription box every codex model returned: 401 Unauthorized: Missing bearer or basic authentication in header url: https://api.openai.com/v1/responses i.e. codex was unusable β the box fell back to the API-key endpoint with no key. It was not an upstream bug and not an expired token: on the affected device the Codex app-server answered fine when driven directly (`codex exec --model gpt-5.5` β a normal reply, "Logged in using ChatGPT"), while the same model through the gateway 401'd with `profile=-`. Two independent breakages, both ours: 1. Wrong destination. We synthesized ~/.codex/auth.json, but OpenClaw 2026.7.x spawns the app-server with CODEX_HOME=<agentDir>/codex-home β confirmed by reading the live process environment. A credential that exists only in ~/.codex is therefore never seen, so the app-server starts unauthenticated. 2. Wrong source. The synthesis read agents/<id>/agent/auth-profiles.json, but on 2026.7.x the tokens moved into the auth_profile_store table of openclaw-agent.sqlite. On current boxes that file simply does not exist, so the lookup silently found nothing and wrote no credential at all. Fix: resolve the credential from an existing ~/.codex/auth.json when there is one (a real Codex login, so it carries a true id_token rather than the access token we have to fall back to when synthesizing), else synthesize from the profile store β reading auth-profiles.json or, when absent, the sqlite store. Then write it to ~/.codex plus every agent's codex-home. Still write-if-missing per destination, so the app-server keeps ownership of refresh and a newer token is never clobbered. Every step is non-fatal: a missing node:sqlite, table, or locked DB just skips. Verified on the test box: with the agent codex-home credential deleted to reproduce the customer state, a gateway restart self-healed it ("Wrote main agent codex-home/auth.json") and codex/gpt-5.5 and codex/gpt-5.6-sol both answered through the gateway. DeepSeek and the local llamacpp model still answer too. Tests: 1404 unit tests pass; bash and node syntax checked.
fix(gateway): 3.1.11 β updates & boot never brick the box
fix(mascot): stop crab teleporting to the left corner on tap
β¦/actions/checkout-7 chore(deps): bump actions/checkout from 4 to 7
β¦/actions/cache-6 chore(deps): bump actions/cache from 4 to 6
β¦/docker/setup-qemu-action-4 chore(deps): bump docker/setup-qemu-action from 3 to 4
clawbox.com is now the primary domain (clawbox.tech 301-redirects to it). Update all docs-site links β 'Get ClawBox' CTAs, store links, footer, and docs.clawbox.tech self-references β from clawbox.tech to clawbox.com so they point directly at the canonical domain instead of relying on the redirect. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
fix(update): add 'Force full update' recovery action for force-update.sh (#285)
β¦retRef
/code-review: with source validated as an enum and id/provider as non-empty
strings, keys.length===3 already guarantees the keys are exactly
{source,id,provider}; the three keys.includes() calls were dead weight.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fix(gateway): preserve canonical token SecretRefs (#249)
- Redaction now also covers password=/api_key=/bare sk-β¦ provider keys, not just token/secret/credential/Bearer/telegram β the offline page is LAN-visible during setup, so a leaked key/password would be exposed. - install.sh: reset-failed before every gateway restart/start in the setup + legacy-recovery paths, so a tripped start-limit breaker can't leave the gateway offline after an update (systemctl restart is refused until reset-failed once start-limit-hit). - journalctl scoped by _SYSTEMD_INVOCATION_ID alone (dropping -u UNIT, which OR-expands and broke the AND) so the surfaced error is the current failure. - Tighten health-probe timeouts (systemctl 3s->2s, journalctl 5s->2.5s) so the offline page can't block ~8s. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fix(gateway): bound permanent startup failures (#284)
Add a narrow, strictly-validated escape hatch for genuine cross-origin/ custom-origin Control UI deployments: operator-supplied origins in data/control-ui-origins.json (or CLAWBOX_CONTROL_UI_ORIGINS_FILE) are merged into the gateway's generated allowedOrigins and honored by the Next.js proxy's redirect-origin reflection, with exact scheme+host+port matching so a configured hostname can't be reflected across other schemes or ports. Same-origin .local/.ts.net/private access is unaffected and normally needs no entry.
- redirectToSetup: a default-reflectable host (LAN IP / localhost / mDNS) keeps its broad reflection even when an operator also configures an exact origin for it β configuring https://10.42.0.1 no longer breaks plain http://10.42.0.1 on the SoftAP (which would dead-end at clawbox.local). - control-ui-origins normalizeOrigin: reject the lenient WHATWG forms the Python gateway loader rejects β IPv4 shorthand/integer/octal (2130706433, 127.1, 010.0.0.1) and empty userinfo (http://@host) β so the proxy never trusts an origin the gateway will refuse (half-working deployments). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- gateway-proxy-origins: assert exact-match for a non-default configured host, and (regression) that a default host keeps broad reflection when a matching origin is configured. - control-ui-origins: assert empty-userinfo and IPv4-shorthand origins are rejected like the Python gateway loader. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CodeRabbit: rawHost strips the port before isReflectableHost(), so a Host like
'clawbox.local:99999' reaches the reflect path; new URL('http://clawbox.local:99999/setup')
then throws and the request 500s instead of redirecting. Wrap the reflected
redirect in try/catch and fall through to the canonical origin.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
β¦ns-v2 feat(gateway): support configurable trusted control-UI origins (#232)
`OPENROUTER_SLUG_RE` allowed `[a-z0-9._-]` and no colon, so any slug with a `:variant` suffix failed `isValidOpenRouterModelId()`. Every FREE model on OpenRouter is addressed as `<org>/<model>:free`, so the practical effect was that NO free OpenRouter model could be configured on a ClawBox -- the user pasted the correct id and got "invalid model id" back from us, not from OpenRouter. Routing variants (`:online`, `:extended`, `:nitro`, `:thinking`) were broken the same way. Reported in Discord 2026-07-30 for `nvidia/nemotron-3-ultra-550b-a55b:free`, which is the real, current id per https://openrouter.ai/api/v1/models. Allow exactly one non-empty trailing `:variant`. Still rejects the things the pattern was written to catch: empty/whitespace, no slash, leading/trailing slash, doubled slashes -- plus new rejections for a bare trailing colon, a second colon, a colon with no slash, and internal whitespace. Unit suite 810 pass (openrouter-models.test.ts 30). Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Krasimir Kralev <263465593+KrasimirKralev@users.noreply.github.com>
Generalizes the per-caller fixes from #231/#303. A raw fetch("/setup-api/...") sends `Accept: */*`, so the middleware's `accept.includes("application/json")` gate missed it and fell through to the HTML login *redirect* β whose body then made the caller's `.json()` throw on an expired session. ~30 callers across ~15 files carried this latent bug. Fix at the source: return a JSON 401 for the whole /setup-api/* (and /api/*) prefix regardless of the Accept header, so every caller gets a structured 401 it can detect β no client changes needed. Genuine top-level page navigations (no API prefix) still redirect to /login. Deliberate trade-off: the few /setup-api/* routes loaded by direct browser embedding (webapps?app= iframes, apps/icon/[appId] <img>, file downloads) now get a 401 too instead of a login page β which is fine, since a login page inside an <img> or a download stream is useless anyway. Tests: middleware suite updated to assert 401 (not 307) for /setup-api/*, plus a regression test that the 401 body is parseable JSON. 47/47 passing on-device. Closes #304.
β¦) (#317) Collapses the duplicated grace-timer -> probe-loop -> settle engine that was copy-pasted across four places into a single controller hook, `useReconnect({ probe, onReady, graceMs, intervalMs, readyDelayMs, hardTimeoutMs, enabled })`: - WifiHandoffOverlay (cross-origin <img> probe -> redirect to /setup) - CredentialsHandoffOverlay (same-origin HEAD or <img> probe -> continue/redirect) - ReconnectingOverlay (health-endpoint poll -> reload/redirect) - SettingsApp hostname-reboot effect (no-cors ping + hard-redirect fallback) Callers now supply only the reachability probe and the ready action; all the timing, cancellation, and single-firing lives in the hook. Pure refactor β no behavior change intended. The hard-timeout fallback and the once-fire guard preserve the SettingsApp reboot semantics (and make its previously-possible double redirect a single one). Adds unit coverage for the hook lifecycle (grace/probe/ready, disabled, hard-timeout fallback, unmount cancellation). The WiFi handoff path also stays covered by e2e (setup-wifi-handoff.spec.ts). Closes #191.
β¦318) * fix(e2e): warm Turbopack before the suite, re-enable 6 flaky CI specs The e2e job serves the app with `bun run dev` (workers:1), so the FIRST hit to a route pays its full on-demand Turbopack compile. Under GitHub Actions load the cold compile of `/` (the desktop shell) can outlast the 15s expect timeout, which is why six specs failed only on CI while passing on the Jetson β and why chat-popup started timing out on `getByTestId('desktop-root')` after the next 16.2 bump nudged that compile past the threshold. Add a Playwright global-setup that loads `/` and `/setup` once before any test clock starts, paying the compile up front. Re-enable the six specs that were `test.fixme`'d for this exact GH-Actions-only flake: - browser-vnc, desktop-selection, installed-app-settings, mascot-context, terminal-reconnect, clawkeep-interactions Left as-is: clawkeep-flow's two fixmes (a separate ClawKeep-redesign gap, not this flake). Closes #114. * fix(e2e): keep only the warmup-fixable specs enabled The CI run showed the global-setup warmup reliably fixes the cold-compile class (chat-popup's desktop-root render, and terminal-reconnect's WebSocket race) but NOT the five DOM-interaction specs β those still time out at their first click / context-menu on GitHub Actions, a deeper post-mount/hydration lag under `bun run dev` workers:1 that a route warmup doesn't touch. Re-fixme the five interaction-lag specs (browser-vnc, desktop-selection, installed-app-settings, mascot-context, clawkeep-interactions) with an accurate note; keep terminal-reconnect enabled (verified green). The residual stays tracked in #114.
Switch the Playwright webServer from `bun run dev` to a real production build served by the standalone server. The Turbopack dev server was the root of two CI-only e2e failures that never reproduced on-device: - cold on-demand route compile under `workers:1` could outlast the 15s expect timeout (the #114 flake), and - on next 16.2 its HMR websocket crashed hydration under Bun (broke chat-popup + terminal-reconnect on the dep-refresh PR). A pre-built, minified standalone server has no HMR and no on-demand compile, so both classes disappear. Verified on the Jetson: chat-popup + terminal-reconnect + 36 specs pass against the prod build, and e2e JS coverage lands at 48.8% (above the 39% floor β the collector's V8 ranges still attribute fine). SESSION_SECRET is unset for the test server so middleware auth stays inactive and `/` renders the desktop (production sets it via production-server.js; e2e drives the UI, not the auth gate). The WebSocket specs mock `window.WebSocket` in-browser, so the bare standalone server needs no gateway/terminal proxy. Drops the now-obsolete global-setup warmup (a dev-Turbopack workaround). Re-enables terminal-reconnect (passes against prod). The five DOM-interaction specs stay fixme'd: they fail on GitHub Actions AND against the prod build on the Jetson, so they're a distinct per-spec issue, still tracked in #114.
Closes the runtime (shipped-in-product) Dependabot alerts by moving every runtime dependency to a patched version: - next 16.1.6 -> 16.2.11 (28 advisories) - undici 7.24.0 -> 7.29.0 (11 advisories, via overrides β transitive) - ws 8.19.x -> 8.21.3 (2 advisories) - postcss -> 8.5.26 (4 advisories, via overrides β transitive) - nanoid -> 3.3.18 (2 advisories, via overrides β transitive) - sharp -> 0.35.3 (1 advisory, via overrides β transitive) undici/postcss/nanoid/sharp are pinned through an `overrides` block rather than added as direct dependencies β the app does not import them directly, so this keeps the dependency contract clean while still forcing every transitive consumer onto the patched, same-major version (no vulnerable copy left in the tree). Dev/build-only advisories (vitest, vite, esbuild, @babel/core, brace-expansion, flatted, js-yaml, minimatch, picomatch) are intentionally left out β they do not ship in the product, and the vitest 4.0->4.1 bump changes test behaviour, so it needs its own verified pass. Validated on a real Jetson: `bun install` clean, `bun run build` green, `bun run test` 1603/1603 passing.
|
Warning Review limit reached
Next review available in: 46 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review detailsβοΈ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: β Files ignored due to path filters (1)
π Files selected for processing (48)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
π¦ ClawReviewFresh PR washed in with the tide β here's the gist. Promotes the current beta to main, clearing ~48 runtime Dependabot CVEs by bumping next (16.1.6 β 16.2.11), undici, ws, postcss, nanoid, and sharp via direct upgrades plus new bun.lock overrides. Bundles three companion changes: JSON 401 responses for expired /setup-api requests, a shared useReconnect hook refactored out of two reconnect overlays, and the e2e suite rewired to run against a real production standalone build instead of the Turbopack dev server. At a glance
Good to know
β ClawReview π¦, your resident reef crab. Just orientation β CodeRabbit does the line-by-line, humans do the merge. Conventions: docs. |
β¦114) (#320) * test(e2e): fix mascot-tap and desktop-context-menu specs for prod build - mascot: use a real Playwright click, not a synthetic PointerEvent β the handler calls setPointerCapture(pointerId) which throws for dispatched events, aborting the tap. Poll-while-closed since the tap toggles the chat. - desktop-selection: scope the 'Terminal' locator to the new desktop-context-menu testid (an unscoped match hit two elements). * test(e2e): scope desktop-selection Terminal locator, keep it fixme'd The strict-mode 'two Terminal buttons' issue is fixed (scoped to the new desktop-context-menu testid + retry), but launching the terminal still fails against the bare standalone e2e server, which has no /terminal-ws proxy. Left fixme'd with that diagnosis. Tracked in #114. * test(e2e): extract terminal WS mock helper; diagnose desktop-selection - Add e2e/helpers/mock-backends.ts (mockTerminalWebSocket) and use it from terminal-reconnect (DRY; validated still green on the Jetson prod build). - desktop-selection: locator scoping done, WS mock wired, but re-fixme'd with a full diagnosis β the (40,40) right-click doesn't open the desktop context menu on the prod build (hit-testing/layout), which must be solved first. Tracked in #114. * test(e2e): re-enable browser-vnc, clawkeep unpair, installed-app-settings Fix three more #114 specs against the production build: - browser-vnc: the launcher paginates apps; search for 'Browser' instead of relying on page order. - clawkeep-interactions (unpair): the Unpair button sits under the taskbar; dispatch the click to the element so hit-testing doesn't land on the shelf. - installed-app-settings: the freshly-installed icon animates in and never passes the stability gate; dispatch the click to its launch button. desktop-selection: re-fixme'd with a precise finding β its context-menu handler fires but the menu never renders on the standalone production build (every input path), which looks like a real app-side bug, not a test issue. Adds a desktop-surface testid as groundwork. Tracked in #114.
Promotes the current beta to main. Headline: clears the runtime Dependabot alerts flagged on the default branch (main).
Included:
After this lands, the security tab's runtime alert count on main drops to ~0 (only dev-tooling advisories remain β tracked separately).