Skip to content

fix(console): stop blank /chat by rewriting OpenWork asset URLs - #188

Open
Travis-Gilbert wants to merge 1 commit into
mainfrom
fix/chat-openwork-asset-prefix
Open

fix(console): stop blank /chat by rewriting OpenWork asset URLs#188
Travis-Gilbert wants to merge 1 commit into
mainfrom
fix/chat-openwork-asset-prefix

Conversation

@Travis-Gilbert

@Travis-Gilbert Travis-Gilbert commented Aug 4, 2026

Copy link
Copy Markdown
Owner

Summary

  • After login, console sends users to /chat, which middleware proxies to OpenWork.
  • OpenWork HTML still referenced root-absolute /assets/... and favicons; those hit the console origin and 404, so the page stayed blank.
  • Rewrite HTML href/src (and Location redirects) under /chat/ so the existing proxy serves them.

Test plan

  • npm test -- --run src/lib/chat-openwork-proxy.test.ts in apps/console
  • After deploy: curl -s https://v2.theoremharness.com/chat | rg 'src="/chat/assets/' shows prefixed assets
  • Login → /chat loads OpenWork UI (no blank blink / no /assets 404s in Network)

Summary by CodeRabbit

  • Bug Fixes
    • Improved chat proxy handling for HTML assets and CSS references.
    • Fixed redirects so paths are correctly prefixed without duplicating existing prefixes.
    • Preserved malformed or already-compatible locations safely.
    • Ensured rewritten HTML responses report accurate content length.

Root-absolute Vite assets 404 on the console origin after the /chat
proxy strips the prefix, which left a blank page after login.
Copilot AI lite review requested due to automatic review settings August 4, 2026 22:40
@ecc-tools

ecc-tools Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

ECC bundle files are already tracked in this repository. Skipping generation of another bundle PR.

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The change adds shared helpers for rewriting OpenWork HTML assets and redirect locations under /chat. Middleware now uses these helpers, removes stale HTML Content-Length, and tests cover the rewriting behavior.

Changes

OpenWork chat proxy

Layer / File(s) Summary
Chat proxy rewrite helpers and tests
apps/console/src/lib/chat-openwork-proxy.ts, apps/console/src/lib/chat-openwork-proxy.test.ts
HTML asset references and CSS URLs receive the /chat prefix. The register implementation attribute is added when absent. Root-relative and absolute redirect locations are rewritten without duplicating existing prefixes.
Middleware integration
apps/console/src/middleware.ts
The middleware uses the shared HTML and location helpers. It removes the upstream Content-Length header after HTML rewriting.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant OpenWork
  participant ChatMiddleware
  participant Browser
  OpenWork->>ChatMiddleware: HTML response or Location header
  ChatMiddleware->>ChatMiddleware: Rewrite assets or redirect path with /chat
  ChatMiddleware->>Browser: Rewritten response
Loading

Possibly related PRs

  • Travis-Gilbert/CommonPlace#165: Both changes modify the OpenWork /chat proxy middleware, while this change extracts and tests the rewriting behavior.

Suggested reviewers: copilot

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the console fix for blank /chat pages by rewriting OpenWork asset URLs.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/chat-openwork-asset-prefix

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Not ready to approve

The new rewrite logic has edge-case bugs around already-prefixed /chat and absolute-URL Location handling that can produce incorrect paths (e.g. /chat/chat) and should be fixed before approval.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Pull request overview

This PR fixes the post-login blank /chat experience in the console by rewriting OpenWork’s HTML asset URLs (and redirects) so root-absolute references like /assets/... resolve through the existing /chat/* reverse-proxy instead of 404ing on the console origin.

Changes:

  • Update apps/console middleware /chat fetch-proxy to rewrite OpenWork HTML and Location headers.
  • Introduce shared rewrite helpers (rewriteOpenworkChatHtml, rewriteOpenworkLocation) for consistent proxy behavior.
  • Add Vitest coverage for the rewrite helpers.
File summaries
File Description
apps/console/src/middleware.ts Uses the new rewrite helpers to fix HTML asset paths under /chat and to prefix redirect locations.
apps/console/src/lib/chat-openwork-proxy.ts Adds HTML attribute/CSS url(...) and redirect Location rewriting utilities for the /chat proxy.
apps/console/src/lib/chat-openwork-proxy.test.ts Adds unit tests validating the rewrite behavior for HTML assets and redirects.
Review details

Suppressed comments (2)

apps/console/src/lib/chat-openwork-proxy.ts:25

  • Same issue as the attribute rewrite: the url(/...) rewrite only exempts /chat/…, not /chat (or /chat?…). This can produce /chat/chat in CSS/inline styles.
  out = out.replace(
    /\b(url)\((["']?)\/(?!\/|chat\/)/g,
    '$1($2/chat/',
  );

apps/console/src/lib/chat-openwork-proxy.test.ts:34

  • The Location rewrite has logic for absolute URLs, but the tests only cover relative redirects. Add an assertion for an absolute URL whose pathname is not already under /chat (e.g. /chatty) to prevent regressions in the absolute-URL branch.
  it('prefixes absolute redirects', () => {
    expect(rewriteOpenworkLocation('/')).toBe('/chat/');
    expect(rewriteOpenworkLocation('/settings')).toBe('/chat/settings');
    expect(rewriteOpenworkLocation('/chat/x')).toBe('/chat/x');
  });
  • Files reviewed: 3/3 changed files
  • Comments generated: 3
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Comment on lines +22 to +26
it('does not double-prefix paths already under /chat', () => {
const html = `<html data-register-impl="openwork.chat"><script src="/chat/assets/x.js"></script></html>`;
expect(rewriteOpenworkChatHtml(html)).toContain('src="/chat/assets/x.js"');
expect(rewriteOpenworkChatHtml(html)).not.toContain('src="/chat/chat/');
});
Comment on lines +16 to +19
out = out.replace(
/\b(href|src|poster)=(["'])\/(?!\/|chat\/)/g,
'$1=$2/chat/',
);
Comment on lines +35 to +38
const url = new URL(location);
if (!url.pathname.startsWith('/chat')) {
url.pathname = `/chat${url.pathname === '/' ? '/' : url.pathname}`;
}

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/console/src/lib/chat-openwork-proxy.ts`:
- Around line 35-39: Update the URL helper around the existing pathname-prefix
logic to accept the workspace origin and convert same-origin full HTTP(S) URLs
into relative `/chat${pathname}${search}${hash}` redirects. Parse pathname
independently before checking the prefix, preserving `/chat?tab=x` unchanged and
avoiding matches such as `/chatty`; leave third-party and non-HTTP URLs
untouched. Add coverage for same-origin workspace redirects, external OAuth
URLs, query/fragment preservation, and the `/chat?tab=x` case.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3d678a63-d3bc-45c8-9482-07e47499c684

📥 Commits

Reviewing files that changed from the base of the PR and between 318cbf6 and 9ff5fa0.

📒 Files selected for processing (3)
  • apps/console/src/lib/chat-openwork-proxy.test.ts
  • apps/console/src/lib/chat-openwork-proxy.ts
  • apps/console/src/middleware.ts

Comment on lines +35 to +39
const url = new URL(location);
if (!url.pathname.startsWith('/chat')) {
url.pathname = `/chat${url.pathname === '/' ? '/' : url.pathname}`;
}
return url.toString();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Keep workspace redirects on the console proxy.

A full upstream redirect such as https://workspace.example/settings becomes https://workspace.example/chat/settings. At apps/console/src/middleware.ts, Lines 68-70 forward that URL to the browser. The browser leaves the console origin instead of requesting /chat/settings, so the console-scoped cookie cannot support the redirected request.

Pass the workspace origin to this helper. For a full URL with that origin, return /chat${pathname}${search}${hash}. Preserve third-party and non-HTTP URLs. Also parse the pathname before testing the prefix so /chat?tab=x is not double-prefixed and /chatty is not treated as /chat.

Add coverage for a full workspace URL, an external OAuth URL, query and fragment preservation, and /chat?tab=x.

Proposed fix
-export function rewriteOpenworkLocation(location: string | null): string | null {
+export function rewriteOpenworkLocation(
+  location: string | null,
+  workspace: string,
+): string | null {
   if (!location) return location;
+  if (location.startsWith('//')) return location;
   if (location.startsWith('/chat/') || location === '/chat') return location;
   if (location.startsWith('/')) return `/chat${location === '/' ? '/' : location}`;
   try {
     const url = new URL(location);
-    if (!url.pathname.startsWith('/chat')) {
-      url.pathname = `/chat${url.pathname === '/' ? '/' : url.pathname}`;
-    }
-    return url.toString();
+    if (url.origin !== new URL(workspace).origin) return location;
+    if (url.pathname === '/chat' || url.pathname.startsWith('/chat/')) {
+      return `${url.pathname}${url.search}${url.hash}`;
+    }
+    return `/chat${url.pathname === '/' ? '/' : url.pathname}${url.search}${url.hash}`;
   } catch {
     return location;
   }
 }
- rewriteOpenworkLocation(location)
+ rewriteOpenworkLocation(location, WORKSPACE)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/console/src/lib/chat-openwork-proxy.ts` around lines 35 - 39, Update the
URL helper around the existing pathname-prefix logic to accept the workspace
origin and convert same-origin full HTTP(S) URLs into relative
`/chat${pathname}${search}${hash}` redirects. Parse pathname independently
before checking the prefix, preserving `/chat?tab=x` unchanged and avoiding
matches such as `/chatty`; leave third-party and non-HTTP URLs untouched. Add
coverage for same-origin workspace redirects, external OAuth URLs,
query/fragment preservation, and the `/chat?tab=x` case.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9ff5fa0864

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

/<html([^>]*)>/i,
'<html$1 data-register-impl="openwork.chat">',
);
const stamped = rewriteOpenworkChatHtml(html);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep OpenWork routing under the /chat mount

When this serves the rewritten OpenWork HTML at /chat, only the asset URLs are moved under the proxy prefix. I checked the proxied app: it mounts a plain BrowserRouter with no basename in apps/chat/src/index.react.tsx lines 40 and 48, and its wildcard route redirects unknown paths to /session in apps/chat/src/react-app/shell/app-root.tsx lines 289 to 290. As soon as the now-loadable bundle runs on /chat, it matches the wildcard and navigates the browser to console-root /session, outside this middleware, so /chat still will not stay on the OpenWork UI. Configure a /chat basename or otherwise rewrite the route base along with the assets.

Useful? React with 👍 / 👎.

Comment on lines +35 to +39
const url = new URL(location);
if (!url.pathname.startsWith('/chat')) {
url.pathname = `/chat${url.pathname === '/' ? '/' : url.pathname}`;
}
return url.toString();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Return proxied absolute redirects to the console origin

When the workspace responds with an absolute Location, this code keeps that upstream origin and only changes the path. In production CONSOLE_WORKSPACE_URL can be a private/internal Railway host, so a redirect like http://commonplace-workspace.railway.internal/settings would be sent to the browser as http://commonplace-workspace.railway.internal/chat/settings, bypassing the console origin and failing for users; external absolute redirects are also mutated. For proxied redirects, same-upstream absolute locations should become console-relative /chat..., while other origins should be left alone.

Useful? React with 👍 / 👎.

Comment on lines +16 to +18
out = out.replace(
/\b(href|src|poster)=(["'])\/(?!\/|chat\/)/g,
'$1=$2/chat/',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Route OpenWork API calls through the chat proxy

This rewrite only fixes static HTML attributes, but the proxied OpenWork bundle still derives its server base from window.location.origin: resolveOpenworkConnection() falls back to the bare origin in apps/chat/src/react-app/shell/openwork-connection.ts lines 92 to 95, and createOpenworkServerClient() then calls root paths such as /workspaces in apps/chat/src/app/lib/openwork-server.ts line 1339. Because this middleware only proxies /chat/*, those runtime requests hit the Next console origin instead of the workspace and the loaded UI cannot list workspaces or sessions. Inject or rewrite the OpenWork base to /chat (or proxy the required root API paths) along with the asset URLs.

Useful? React with 👍 / 👎.

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.

2 participants