(options: SelfhostBootOptions)
| 27 | } |
| 28 | |
| 29 | export const bootSelfhost = async (options: SelfhostBootOptions): Promise<BootedProcesses> => { |
| 30 | const dataDir = options.dataDir ?? resolve(selfhostDir, ".e2e-data"); |
| 31 | if (options.fresh ?? true) rmSync(dataDir, { recursive: true, force: true }); |
| 32 | |
| 33 | const procs = bootProcesses( |
| 34 | [ |
| 35 | { |
| 36 | cmd: "bunx", |
| 37 | args: [ |
| 38 | "--bun", |
| 39 | "vite", |
| 40 | "dev", |
| 41 | "--port", |
| 42 | String(options.port), |
| 43 | "--strictPort", |
| 44 | ...(options.host ? ["--host", options.host] : []), |
| 45 | ], |
| 46 | cwd: selfhostDir, |
| 47 | env: { |
| 48 | EXECUTOR_DATA_DIR: dataDir, |
| 49 | BETTER_AUTH_SECRET: "executor-selfhost-e2e-secret-0123456789", |
| 50 | EXECUTOR_BOOTSTRAP_ADMIN_EMAIL: options.admin.email, |
| 51 | EXECUTOR_BOOTSTRAP_ADMIN_PASSWORD: options.admin.password, |
| 52 | EXECUTOR_WEB_BASE_URL: options.webBaseUrl, |
| 53 | // The harness boots loopback MCP/OAuth test servers and points the |
| 54 | // instance at them; the hosted SSRF guard would otherwise block |
| 55 | // outbound probes/dials to localhost. Hermetic test instance only. |
| 56 | EXECUTOR_ALLOW_LOCAL_NETWORK: "true", |
| 57 | ...(options.sandboxTimeoutMs !== undefined |
| 58 | ? { EXECUTOR_SANDBOX_TIMEOUT_MS: String(options.sandboxTimeoutMs) } |
| 59 | : {}), |
| 60 | }, |
| 61 | logFile: options.logFile, |
| 62 | }, |
| 63 | ], |
| 64 | { label: "selfhost" }, |
| 65 | ); |
| 66 | |
| 67 | try { |
| 68 | // Probe via `localhost`, not 127.0.0.1 — without --host, vite binds the |
| 69 | // resolver's first answer for localhost (::1 on macOS), so the IPv4 |
| 70 | // loopback literal never answers. |
| 71 | await waitForBoot(procs, (signal) => |
| 72 | waitForHttp(`http://localhost:${options.port}`, { signal }), |
| 73 | ); |
| 74 | } catch (error) { |
| 75 | await procs.teardown(); |
| 76 | throw error; |
| 77 | } |
| 78 | return procs; |
| 79 | }; |
no test coverage detected