flowviant 0.13.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,7 +4,7 @@ import { readFileSync } from 'node:fs';
4
4
  import { join } from 'node:path';
5
5
  import { homedir } from 'node:os';
6
6
 
7
- export const VERSION = '0.13.0';
7
+ export const VERSION = '0.15.0';
8
8
 
9
9
  // Credential stored by `flowviant login` (device auth) — the no-token,
10
10
  // no-env-var path. An explicit --fleet flag or FLOWVIANT_FLEET env still wins.
package/bin/lib/live.mjs CHANGED
@@ -663,6 +663,8 @@ export async function runLiveWorker({
663
663
  kind,
664
664
  cmd: entry.cmd,
665
665
  port: entry.port,
666
+ env: entry.env, // optional: extra env from .flowviant/preview.json
667
+ hostHeader: entry.hostHeader, // optional: override/disable the Host rewrite
666
668
  log: (m) => info(`${label} ${c.dim(m)}`),
667
669
  });
668
670
  if (preview) {
@@ -11,9 +11,17 @@
11
11
  * cloudflared / no inferable config → no live preview, and review falls back to
12
12
  * the captured evidence the agent attached (never a hard failure).
13
13
  *
14
- * Config shape:
15
- * { "ui": { "cmd": "<start dev server>", "port": 5173 },
16
- * "api": { "cmd": "<start api>", "port": 8787 } }
14
+ * Config shape (the escape hatch for any setup the defaults don't handle — the
15
+ * repo declares its own recipe, so the daemon never needs per-framework code):
16
+ * { "ui": {
17
+ * "cmd": "<start dev server>", // required
18
+ * "port": 5173, // required
19
+ * "env": { "FOO": "bar" }, // optional — extra env for the dev server
20
+ * "hostHeader": "localhost" // optional — Host sent to the origin;
21
+ * // false disables the rewrite (for apps
22
+ * // that need their real public Host)
23
+ * },
24
+ * "api": { "cmd": "<start api>", "port": 8787 } }
17
25
  */
18
26
 
19
27
  import { spawn, execFileSync } from 'node:child_process';
@@ -214,16 +222,37 @@ const BIND_RE = /https?:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0):(\d+)/i;
214
222
  * theirs is taken, and tunneling to the guess then 502s. We fall back to the
215
223
  * configured port only if the server never announces one.
216
224
  */
217
- export async function startPreview({ worktree, kind, cmd, port, log, timeoutMs = 180_000 }) {
225
+ export async function startPreview({
226
+ worktree,
227
+ kind,
228
+ cmd,
229
+ port,
230
+ env: extraEnv,
231
+ hostHeader,
232
+ log,
233
+ timeoutMs = 180_000,
234
+ }) {
218
235
  const cf = await ensureCloudflared(log);
219
236
  if (!cf) return null; // fall back to captured evidence
237
+ // Host the origin sees. Default 'localhost' (what a local browser sends) so
238
+ // dev servers that validate Host — Vite server.allowedHosts, webpack, Next's
239
+ // allowedDevOrigins — accept the tunnel. `hostHeader: false` in preview.json
240
+ // disables the rewrite for apps that route on their real public Host.
241
+ const hostRewrite =
242
+ hostHeader === false ? null : typeof hostHeader === 'string' && hostHeader ? hostHeader : 'localhost';
220
243
  return new Promise((resolve) => {
221
244
  // We SIGKILL the dev server's whole group on teardown, which skips a tool's
222
245
  // graceful cleanup — some dev servers (e.g. vinext) leave a singleton
223
246
  // dev-lock behind and then REFUSE to start next time. Disable known locks so
224
247
  // a reused/uncleaned worktree still previews. Harmless to tools that ignore
225
- // these vars; BROWSER=none stops any auto-open.
226
- const env = { ...process.env, VINEXT_NO_DEV_LOCK: '1', BROWSER: 'none' };
248
+ // these vars; BROWSER=none stops any auto-open. A repo's preview.json `env`
249
+ // is layered last, so it can override any of these.
250
+ const env = {
251
+ ...process.env,
252
+ VINEXT_NO_DEV_LOCK: '1',
253
+ BROWSER: 'none',
254
+ ...(extraEnv && typeof extraEnv === 'object' ? extraEnv : {}),
255
+ };
227
256
  // detached so each gets its own process group — `bun run dev` via a shell
228
257
  // spawns a grandchild dev server that would otherwise SURVIVE a kill of the
229
258
  // shell. We kill the whole group instead. stdout/stderr piped so we can read
@@ -274,10 +303,12 @@ export async function startPreview({ worktree, kind, cmd, port, log, timeoutMs =
274
303
  tunnelStarted = true;
275
304
  clearTimeout(bindTimer);
276
305
  log?.(`preview: dev server on :${p} — opening the tunnel…`);
277
- tunnel = spawn(cf, ['tunnel', '--url', `http://localhost:${p}`], {
278
- detached: true,
279
- stdio: ['ignore', 'pipe', 'pipe'],
280
- });
306
+ // --http-host-header: send the origin the Host it expects (default
307
+ // localhost — see hostRewrite above). Passes Vite/webpack/Next host checks
308
+ // with zero repo config; skipped when preview.json sets hostHeader:false.
309
+ const args = ['tunnel', '--url', `http://localhost:${p}`];
310
+ if (hostRewrite) args.push('--http-host-header', hostRewrite);
311
+ tunnel = spawn(cf, args, { detached: true, stdio: ['ignore', 'pipe', 'pipe'] });
281
312
  const onTunnel = (d) => {
282
313
  const m = TUNNEL_RE.exec(d.toString());
283
314
  if (m) finish({ url: m[0], kind, stop });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flowviant",
3
- "version": "0.13.0",
3
+ "version": "0.15.0",
4
4
  "description": "Run your own Claude Code as headless build agents for Flowviant — on your own credentials. Claims dispatched work, opens PRs, captures review evidence, and routes questions back to you.",
5
5
  "type": "module",
6
6
  "bin": {