Make dev workspaces serve a working browser IDE end to end

The workspace feature shipped its routes, agent tools and docs, but the
editor was never reachable: the project page had no entry point, the ppy
image had no code-server binary, no certificate was ever requested for a
tunnel, and both nginx and the proxy dropped what the editor needs.

- Add a VS Code button to the project action row and a Workspace item to
  the overflow menu, gated by can_open_workspace plus a running instance
  (viewer_can_workspace and workspace_editor_url on ProjectDetailOut).
- Install a pinned code-server in ppy.Dockerfile before USER pravda and
  assert it in the build smoke test, so an image that cannot run the
  editor no longer builds green.
- Run the editor with --auth password and a per workspace 8 character
  pronounceable secret, minted once at the ensure_editor_password choke
  point and injected as PASSWORD. Keep it off WorkspaceViewOut, which the
  admin listing shares.
- Publish the editor tunnel when a workspace is created and issue its
  certificate from a new WorkspaceService phase against the molohttp admin
  API, then notify the owner with the live URL and the password. Only
  pending rows are retried, so a broken host cannot burn the ACME failure
  rate limit. Renewal stays molohttp's job.
- Forward the original Host on proxied requests and the client cookie on
  proxied websockets, so code-server scopes its session cookie to the
  public hostname and authenticates the workbench socket.
- Recreate a container stuck in the created state instead of retrying
  docker start forever against an image it can no longer run.
- Return a JSON string from WorkspaceController.dispatch; raw dicts landed
  in a tool message and aborted the turn at the model endpoint.
- Let the nginx catch-all carry websocket upgrades, keeping upstream
  keepalive, so tunnelled apps and the editor both connect.
This commit is contained in:
2026-08-07 13:46:40 +02:00
parent 21f6ae0615
commit 192df12b1d
25 changed files with 716 additions and 26 deletions
+16 -2
View File
@@ -74,6 +74,19 @@ RUN pip install \
RUN pip install playwright && playwright install --with-deps chromium
FROM deps AS runtime
ARG CODE_SERVER_VERSION=4.131.0
RUN set -eu; \
case "$(dpkg --print-architecture)" in \
amd64) arch=amd64 ;; \
arm64) arch=arm64 ;; \
*) echo "unsupported architecture: $(dpkg --print-architecture)"; exit 1 ;; \
esac; \
curl -fsSL -o /tmp/code-server.tar.gz \
"https://github.com/coder/code-server/releases/download/v${CODE_SERVER_VERSION}/code-server-${CODE_SERVER_VERSION}-linux-${arch}.tar.gz"; \
mkdir -p /usr/local/lib/code-server; \
tar -xzf /tmp/code-server.tar.gz -C /usr/local/lib/code-server --strip-components=1; \
rm -f /tmp/code-server.tar.gz; \
ln -sf /usr/local/lib/code-server/bin/code-server /usr/local/bin/code-server
COPY sudo /usr/local/bin/sudo
COPY aptroot /usr/local/bin/aptroot
COPY pagent /usr/bin/pagent.py
@@ -117,13 +130,14 @@ RUN printf '%s\n' \
RUN set -eu; \
for tool in "python --version" "rustc --version" "cargo --version" \
"nim --version" "nimble --version" "swift --version"; do \
"nim --version" "nimble --version" "swift --version" \
"code-server --version"; do \
$tool > /tmp/toolcheck 2>&1 || { echo "TOOLCHAIN FAILED: $tool"; cat /tmp/toolcheck; exit 1; }; \
head -1 /tmp/toolcheck; \
done; \
rm -f /tmp/toolcheck; \
for b in /usr/local/bin/sudo /usr/local/bin/aptroot /usr/bin/pagent.py \
/usr/bin/botje.py /usr/bin/d.py /usr/bin/dpc; do \
/usr/bin/botje.py /usr/bin/d.py /usr/bin/dpc /usr/local/bin/code-server; do \
[ -x "$b" ] || { echo "missing or not executable: $b"; exit 1; }; \
done; \
[ -f /home/pravda/.vimrc ] || { echo "missing /home/pravda/.vimrc"; exit 1; }