feat(web): serve workspace files from their own origin

A sandbox header bought isolation by taking the document's origin away, and
measuring that cost decided against it: the reported artifact throws
SecurityError on load, and because an uncaught exception aborts the rest of
its <script>, every listener declared after that line — theme toggle, mobile
menu, model tabs — never binds. Two of the four artifacts in the reporting
user's workspace were dead pages under it, and they still looked right.

A second listener on the API's host, answering /f and nothing else, is the
same boundary without the amputation: cross-origin to /api (refused by the
Origin fence and by CORS), same-origin with itself (localStorage, cookies and
fetch all work). Its port is published into the index page; the browser half
reads it to address previews, and its absence — the keyless fixture lane — is
what makes a file row fall back to the Host opener instead of a dead tab.

fileUrl moves from IWorkspaces to ConnectionHandle: the transport owns both
the listener that serves the bytes and the port that addresses it.
This commit is contained in:
ZiyaZhang
2026-08-01 02:17:25 -07:00
parent 1082518520
commit 59bfe77fb8
37 changed files with 469 additions and 197 deletions

View File

@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/client/ui-conversation/README.md
README.md: ba55f0704500034b7afb37258064fe0801aaee91
README.zh.md: 4212908b355a81dfd5af8645ce5d4284a4555622
README.md: 8c2075d615eccad1bbc7f5de1255ea4add69fab8
README.zh.md: 634721b4248da75cbd4e81528340936a31ece28d