fix(core,tooling): keep the harness's own ports out of the Agent's environment and off the dev server (#100)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: J.Li <jl2388@cam.ac.uk>
This commit is contained in:
@@ -23,11 +23,11 @@ DTO types are imported type-only from `@prismshadow/penguin-server/api`; no serv
|
||||
Prereqs: Node >= 24, pnpm; run `pnpm install` at the repo root first (core must be built — the root `dev:*` scripts handle that).
|
||||
|
||||
```bash
|
||||
pnpm dev:server # backend at 127.0.0.1:7364
|
||||
pnpm dev:server # backend at 127.0.0.1:7368 (dev port, not the installed server's 7364)
|
||||
pnpm dev:web # Vite dev server at 127.0.0.1:7365; /api proxied (SSE passes through)
|
||||
```
|
||||
|
||||
The proxy target defaults to `http://127.0.0.1:7364` (`PENGUIN_API_PROXY` overrides). Auth is a same-origin HttpOnly cookie, so the proxy keeps everything same-origin.
|
||||
The proxy target defaults to `http://127.0.0.1:7368` — the development backend, kept off the installed server's 7364 so the two can run at once (`PORT` moves both, `PENGUIN_API_PROXY` overrides the target outright). Auth is a same-origin HttpOnly cookie, so the proxy keeps everything same-origin.
|
||||
|
||||
```bash
|
||||
pnpm --filter @prismshadow/penguin-web typecheck
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Dev-server `/api` proxy target resolution (vite.config.ts). The interesting case is an
|
||||
* empty `PORT=`: shells export it, `.env` files carry it, and resolving it to a bare
|
||||
* `http://127.0.0.1:` sends every API call to port 80 without ever failing — a wrong backend
|
||||
* is far harder to notice than a broken one, so the empty case is pinned here.
|
||||
*/
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { apiProxyTarget } from "../vite.config.js";
|
||||
|
||||
describe("apiProxyTarget", () => {
|
||||
it("defaults to the development backend, not the installed server's 7364", () => {
|
||||
expect(apiProxyTarget({})).toBe("http://127.0.0.1:7368");
|
||||
});
|
||||
|
||||
it("treats an empty PORT as unset rather than as port 80", () => {
|
||||
expect(apiProxyTarget({ PORT: "" })).toBe("http://127.0.0.1:7368");
|
||||
});
|
||||
|
||||
it("follows PORT so moving the backend moves the proxy with it", () => {
|
||||
expect(apiProxyTarget({ PORT: "9999" })).toBe("http://127.0.0.1:9999");
|
||||
});
|
||||
|
||||
it("PENGUIN_API_PROXY replaces the whole target, PORT and all", () => {
|
||||
expect(apiProxyTarget({ PORT: "9999", PENGUIN_API_PROXY: "http://10.0.0.2:8080" })).toBe(
|
||||
"http://10.0.0.2:8080",
|
||||
);
|
||||
});
|
||||
|
||||
it("an empty PENGUIN_API_PROXY falls back instead of proxying to nowhere", () => {
|
||||
expect(apiProxyTarget({ PENGUIN_API_PROXY: "" })).toBe("http://127.0.0.1:7368");
|
||||
});
|
||||
});
|
||||
@@ -1,9 +1,11 @@
|
||||
/**
|
||||
* Vite config: React SPA + Tailwind CSS 4.
|
||||
*
|
||||
* Dev server listens on 7365; `/api` is proxied to the local Web server (defaults to 127.0.0.1:7364,
|
||||
* overridable via PENGUIN_API_PROXY). SSE (text/event-stream) passes through http-proxy transparently, no
|
||||
* special config needed.
|
||||
* Dev server listens on 7365; `/api` is proxied to the **development** backend (127.0.0.1:7368 --
|
||||
* `pnpm dev:server`, deliberately not the installed server's 7364, which is routinely running at the
|
||||
* same time). Honors PORT so overriding the backend port moves the proxy with it, and
|
||||
* PENGUIN_API_PROXY overrides the whole target. SSE (text/event-stream) passes through http-proxy
|
||||
* transparently, no special config needed.
|
||||
* The vitest config is kept separate in vitest.config.ts (its embedded vite 5 types conflict with this
|
||||
* package's vite 7 plugin types, hence the separate file to avoid the clash).
|
||||
*/
|
||||
@@ -11,15 +13,32 @@ import react from "@vitejs/plugin-react";
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
import { defineConfig } from "vite";
|
||||
|
||||
/**
|
||||
* Resolves the `/api` proxy target: PENGUIN_API_PROXY replaces it outright, otherwise the
|
||||
* development backend on PORT — the same variable `pnpm dev:server` binds — defaulting to 7368.
|
||||
*
|
||||
* Empty counts as unset, as everywhere else PORT is read in this repo (server/src/config.ts,
|
||||
* cli/src/commands/serve.ts, scripts/run-with-env.mjs). Here `??` would be actively harmful: an
|
||||
* exported-but-empty `PORT=` yields `http://127.0.0.1:` — port 80 — and every /api call would be
|
||||
* answered by whatever happens to listen there, silently and without an error, which is the exact
|
||||
* wrong-backend failure this default exists to prevent.
|
||||
*
|
||||
* Takes the environment as an argument so the resolution can be unit-tested (a vite config's
|
||||
* `server.proxy` cannot be exercised without starting a dev server).
|
||||
*/
|
||||
export function apiProxyTarget(env: Record<string, string | undefined> = process.env): string {
|
||||
return env.PENGUIN_API_PROXY || `http://127.0.0.1:${env.PORT || "7368"}`;
|
||||
}
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [react(), tailwindcss()],
|
||||
server: {
|
||||
// Fixed PenguinHarness dev port (stands alone — only the main server default is
|
||||
// shared, as DEFAULT_SERVER_PORT in core; vite configs cannot import core TS).
|
||||
// Fixed PenguinHarness dev port (stands alone — vite configs cannot import core TS,
|
||||
// so the numbers are literals here; the allocation table lives in core's internal/ports.ts).
|
||||
port: 7365,
|
||||
proxy: {
|
||||
"/api": {
|
||||
target: process.env.PENGUIN_API_PROXY ?? "http://127.0.0.1:7364",
|
||||
target: apiProxyTarget(),
|
||||
changeOrigin: false,
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user