From 018dc3861aad0de8d1d4a737567655d91bac89ea Mon Sep 17 00:00:00 2001 From: Guilhem Lemouel Date: Fri, 24 Apr 2026 13:09:08 +0200 Subject: [PATCH] =?UTF-8?q?refactor(cli):=20wmill=20dev=20=E2=80=94=20clea?= =?UTF-8?q?rer=20mode=20names=20and=20accurate=20startup=20messaging?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rename `startLegacyServer` to `startDirectServer`. "Legacy" implied it was on the way out; the two modes (proxy vs direct WS) actually serve different topologies and both stay. Add comments above each section spelling out who they're for: proxy mode for embedders that require a localhost origin (Claude Code preview), direct mode for standalone browser tabs and the VS Code extension iframe. - Replace the stale "Dev server will automatically point to the last script edited locally" log line. Now print path-aware text: - with --path (or auto-detected): "Watching — edits will live -reload in the dev page" - without: "Open the dev page and pick a flow or script to preview — edits will live-reload" plus a hint about --path Mirror the same in proxy mode after the listen callback. - Drop the redundant "Go to " line when --no-open isn't passed (maybeOpenBrowser already prints "Opened browser at "). - Rename "Server listening on port 3001" to "Dev WebSocket listening on ws://localhost:/ws" so the line's purpose is obvious. Co-Authored-By: Claude Opus 4.7 (1M context) --- cli/src/commands/dev/dev.ts | 54 ++++++++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/cli/src/commands/dev/dev.ts b/cli/src/commands/dev/dev.ts index c1a7df5a4c..46451925a2 100644 --- a/cli/src/commands/dev/dev.ts +++ b/cli/src/commands/dev/dev.ts @@ -630,7 +630,18 @@ export async function dev(opts: GlobalOptions & SyncOptions & DevOpts) { } } - // --- Reverse proxy (when --proxy-port is set) --- + // --- Proxy mode (when --proxy-port is set) --- + // + // Runs a localhost HTTP server that: + // - serves the dev page from `http://localhost:/` (forwarded + // to the remote workspace), so embedders that need a localhost origin + // can render it (e.g. Claude Code's port-detection preview), and + // - upgrades local /ws connections back to this same process for the + // live-reload channel. + // + // The simpler "direct" mode below works for standalone browser tabs and the + // VS Code extension's iframe — only embedders that demand a localhost origin + // need this proxy. async function startProxyServer(proxyPort: number) { const remote = new URL(workspace.remote); @@ -744,15 +755,34 @@ export async function dev(opts: GlobalOptions & SyncOptions & DevOpts) { return new Promise((resolve) => { proxyServer.listen(proxyPort, () => { console.log(`Dev proxy listening on http://localhost:${proxyPort}`); + if (opts.path) { + console.log(`Watching ${opts.path} — edits will live-reload in the dev page`); + } else { + console.log( + "Open the dev page and pick a flow or script to preview — edits will live-reload" + ); + console.log("(pass --path to skip the picker)"); + } maybeOpenBrowser(`http://localhost:${proxyPort}/`); resolve(); }); }); } - // --- Legacy server (direct WebSocket, no proxy) --- + // --- Direct mode (no localhost HTTP proxy) --- + // + // The browser loads the dev page from the remote workspace URL and opens a + // WebSocket directly to this localhost server. Used when: + // - the user runs `wmill dev` and opens a regular browser tab, or + // - the VS Code extension iframe loads the dev page (its iframe URL omits + // `local=true`, so it never opens this WS, but everything else still + // functions through the existing remote workspace connection). + // + // This is the simplest topology: a bare WebSocket server. The reverse-proxy + // mode (above) is only needed when something needs to embed the dev UI on a + // localhost origin (Claude Code's port-detection preview). - async function startLegacyServer() { + async function startDirectServer() { const server = http.createServer((_req, res) => { res.writeHead(200); res.end(); @@ -766,15 +796,21 @@ export async function dev(opts: GlobalOptions & SyncOptions & DevOpts) { (port === PORT ? "" : `&port=${port}`) + (opts.path ? `&path=${opts.path}` : ""); - console.log(`Go to ${url}`); + if (opts.open === false) { + console.log(`Go to ${url}`); + } maybeOpenBrowser(url); - console.log( - "Dev server will automatically point to the last script edited locally" - ); + if (opts.path) { + console.log(`Watching ${opts.path} — edits will live-reload in the dev page`); + } else { + console.log( + "Open the dev page and pick a flow or script to preview — edits will live-reload" + ); + } server.listen(port, () => { - console.log(`Server listening on port ${port}`); + console.log(`Dev WebSocket listening on ws://localhost:${port}/ws`); }); } @@ -787,7 +823,7 @@ export async function dev(opts: GlobalOptions & SyncOptions & DevOpts) { const startServer = opts.proxyPort ? () => startProxyServer(opts.proxyPort!) - : () => startLegacyServer(); + : () => startDirectServer(); await Promise.all([startServer(), watchChanges()]); console.log("Stopped dev mode");