From 579fd4a50ee655eae40009ecabef368fee0e8b53 Mon Sep 17 00:00:00 2001 From: Tommaso Casaburi Date: Mon, 30 Mar 2026 17:20:38 +0700 Subject: [PATCH] fix(dev server): route default start through portless --- AGENTS.md | 4 ++-- README.md | 8 ++++--- docs/agent-playbooks/known-surprises.md | 10 ++++++++ package.json | 9 ++++--- scripts/AGENTS.md | 2 +- scripts/agent-init.sh | 2 +- scripts/start-dev.js | 31 +++++++++++++++++++++++++ vite.config.js | 2 +- yarn.lock | 15 ++++++++++++ 9 files changed, 72 insertions(+), 11 deletions(-) create mode 100644 scripts/start-dev.js diff --git a/AGENTS.md b/AGENTS.md index 2862a316..34977bc7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -163,14 +163,14 @@ src/ ## Local Development URL -The default web dev server is `http://localhost:3000`. Browser automation, local smoke/bootstrap helpers, and Electron dev flows should target that URL unless the parent agent gives a different one. +This project uses [Portless](https://github.com/vercel-labs/portless) for the normal web dev flow. The default web dev server is `http://seedit.localhost:1355`. Browser automation and local smoke/bootstrap helpers should target that URL unless the caller explicitly bypasses Portless with `PORTLESS=0`. ## Common Commands ```bash corepack enable corepack yarn install -yarn start # http://localhost:3000 +yarn start # http://seedit.localhost:1355 yarn build yarn lint yarn type-check diff --git a/README.md b/README.md index 757cc66b..9a683e00 100644 --- a/README.md +++ b/README.md @@ -43,13 +43,15 @@ The default list of communities, used on s/all on Seedit, is bitsocial's [defaul 1. `yarn install` to install Seedit dependencies 2. `yarn start` to run the web client +The default web dev server runs at `http://seedit.localhost:1355` via [Portless](https://port1355.dev/), so it can share the same proxy as other Bitsocial projects without colliding on raw Vite ports. To bypass Portless and use plain Vite directly, run `PORTLESS=0 yarn start`. + ### Scripts: -- Web client: `yarn start` +- Web client: `yarn start` (`http://seedit.localhost:1355`) - Electron client (must start web client first): `yarn electron` - Electron client and don't delete data: `yarn electron:no-delete-data` -- Web client and electron client: `yarn electron:start` -- Web client and electron client and don't delete data: `yarn electron:start:no-delete-data` +- Web client and electron client: `yarn electron:start` (forces `PORTLESS=0` and uses `http://localhost:3000`) +- Web client and electron client and don't delete data: `yarn electron:start:no-delete-data` (forces `PORTLESS=0` and uses `http://localhost:3000`) ### Build: diff --git a/docs/agent-playbooks/known-surprises.md b/docs/agent-playbooks/known-surprises.md index ace70cf5..9227b72e 100644 --- a/docs/agent-playbooks/known-surprises.md +++ b/docs/agent-playbooks/known-surprises.md @@ -57,3 +57,13 @@ If uncertain, ask the developer before adding an entry. - **Impact:** The desktop app may open but fail to load comments, communities, or other RPC-backed data. - **Mitigation:** Before Electron packaging or release verification, rebuild `better-sqlite3` for the target Electron version, for example with `npx electron-rebuild -f -o better-sqlite3`, then verify the rebuilt native module under the Electron runtime. - **Status:** confirmed + +### Portless is now the canonical web dev URL + +- **Date:** 2026-03-30 +- **Observed by:** Codex +- **Context:** Normal `yarn start` runs alongside other local Bitsocial projects +- **What was surprising:** The repo historically assumed `http://localhost:3000`, but the normal web dev flow now runs through Portless at `http://seedit.localhost:1355` so multiple Bitsocial apps can coexist without raw-port collisions. +- **Impact:** Agents can point browser automation, health checks, or local smoke scripts at the wrong URL and conclude the app is down when it is healthy. +- **Mitigation:** Use `http://seedit.localhost:1355` for standard web dev and agent smoke flows. Only rely on `http://localhost:3000` when a script intentionally forces `PORTLESS=0`, such as the combined Electron dev commands. +- **Status:** confirmed diff --git a/package.json b/package.json index 9bbb7827..a26965ac 100755 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "zustand": "4.4.3" }, "scripts": { - "start": "vite", + "start": "node scripts/start-dev.js", "build": "cross-env NODE_ENV=production PUBLIC_URL=./ GENERATE_SOURCEMAP=false vite build", "build:preload": "cross-env NODE_ENV=production vite build --config electron/vite.preload.config.js", "build-netlify": "cross-env NODE_OPTIONS=\"--max_old_space_size=4096\" NODE_ENV=production PUBLIC_URL=./ GENERATE_SOURCEMAP=true VITE_COMMIT_REF=$COMMIT_REF CI='' vite build", @@ -65,8 +65,8 @@ "analyze-bundle": "cross-env NODE_ENV=production PUBLIC_URL=./ GENERATE_SOURCEMAP=true vite build && npx source-map-explorer 'build/assets/*.js'", "electron": "yarn build:preload && yarn electron:before && electron .", "electron:no-delete-data": "yarn build:preload && electron .", - "electron:start": "concurrently \"cross-env BROWSER=none yarn start\" \"wait-on http://localhost:3000 && yarn electron\"", - "electron:start:no-delete-data": "concurrently \"cross-env BROWSER=none yarn start\" \"wait-on http://localhost:3000 && yarn electron:no-delete-data\"", + "electron:start": "concurrently \"cross-env BROWSER=none PORTLESS=0 yarn start\" \"wait-on http://localhost:3000 && yarn electron\"", + "electron:start:no-delete-data": "concurrently \"cross-env BROWSER=none PORTLESS=0 yarn start\" \"wait-on http://localhost:3000 && yarn electron:no-delete-data\"", "electron:package": "yarn build && yarn build:preload && electron-forge package", "electron:build": "yarn build && yarn build:preload && electron-forge make", "electron:build:linux": "yarn build && yarn build:preload && electron-forge make --platform=linux", @@ -141,6 +141,9 @@ "vitest": "4.1.0", "wait-on": "9.0.4" }, + "optionalDependencies": { + "portless": "0.7.0" + }, "resolutions": { "js-yaml": "4.1.1", "baseline-browser-mapping": "2.9.11", diff --git a/scripts/AGENTS.md b/scripts/AGENTS.md index bdd19524..93dab74f 100644 --- a/scripts/AGENTS.md +++ b/scripts/AGENTS.md @@ -4,7 +4,7 @@ These rules apply to `scripts/**`. Follow the repo-root `AGENTS.md` first, then - Keep scripts non-interactive and idempotent. Print the command, URL, branch, or path being acted on so failures are diagnosable. - Use repo-relative paths and environment variables instead of user-specific absolute paths. -- For dev-server helpers, default to `http://localhost:3000` unless the caller overrides `AGENT_APP_URL`. +- For dev-server helpers, default to `http://seedit.localhost:1355` and respect the existing `PORTLESS=0` fallback instead of hard-coding alternate ports. If a flow intentionally bypasses Portless, override `AGENT_APP_URL` explicitly. - Keep shell helpers thin. When logic becomes stateful or cross-platform, prefer a Node script. - Git and worktree helpers must validate input and default to safe operations. - If a helper deletes local branches automatically, document the exact eligibility checks and keep the behavior conservative. diff --git a/scripts/agent-init.sh b/scripts/agent-init.sh index 64761fcd..814771a6 100644 --- a/scripts/agent-init.sh +++ b/scripts/agent-init.sh @@ -8,7 +8,7 @@ if [ "$#" -ne 0 ]; then fi wait_timeout="${AGENT_INIT_TIMEOUT_SECONDS:-60}" -app_url="${AGENT_APP_URL:-http://localhost:3000}" +app_url="${AGENT_APP_URL:-http://seedit.localhost:1355}" repo_root="$(git rev-parse --show-toplevel)" log_dir="$repo_root/.playwright-cli" diff --git a/scripts/start-dev.js b/scripts/start-dev.js new file mode 100644 index 00000000..48e3673b --- /dev/null +++ b/scripts/start-dev.js @@ -0,0 +1,31 @@ +import { existsSync } from 'node:fs'; +import { join } from 'node:path'; +import { spawn } from 'node:child_process'; + +const isWindows = process.platform === 'win32'; +const usePortless = process.env.PORTLESS !== '0' && !isWindows; +const binDir = join(process.cwd(), 'node_modules', '.bin'); +const executableSuffix = isWindows ? '.cmd' : ''; +const portlessBin = join(binDir, `portless${executableSuffix}`); +const viteBin = join(binDir, `vite${executableSuffix}`); + +const command = usePortless && existsSync(portlessBin) ? portlessBin : viteBin; +const args = command === portlessBin ? ['seedit', 'vite'] : []; + +if (command !== portlessBin && process.env.PORTLESS !== '0') { + console.warn('portless unavailable on this platform, using vite directly on http://localhost:3000'); +} + +const child = spawn(command, args, { + stdio: 'inherit', + env: process.env, +}); + +child.on('exit', (code, signal) => { + if (signal) { + process.kill(process.pid, signal); + return; + } + + process.exit(code ?? 0); +}); diff --git a/vite.config.js b/vite.config.js index 94071a40..dad0603d 100644 --- a/vite.config.js +++ b/vite.config.js @@ -162,7 +162,7 @@ export default defineConfig({ }, server: { port: 3000, - open: true, + open: process.env.PORT ? 'http://seedit.localhost:1355/' : true, watch: { usePolling: true, }, diff --git a/yarn.lock b/yarn.lock index ab74793f..50a1bd21 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17154,6 +17154,17 @@ __metadata: languageName: node linkType: hard +"portless@npm:0.7.0": + version: 0.7.0 + resolution: "portless@npm:0.7.0" + dependencies: + chalk: "npm:^5.3.0" + bin: + portless: dist/cli.js + conditions: (os=darwin | os=linux | os=win32) + languageName: node + linkType: hard + "possible-typed-array-names@npm:^1.0.0": version: 1.1.0 resolution: "possible-typed-array-names@npm:1.1.0" @@ -18664,6 +18675,7 @@ __metadata: node-fetch: "npm:2" oxfmt: "npm:0.24.0" oxlint: "npm:1.39.0" + portless: "npm:0.7.0" progress: "npm:2.0.3" react: "npm:19.1.2" react-ace: "npm:14.0.1" @@ -18689,6 +18701,9 @@ __metadata: vitest: "npm:4.1.0" wait-on: "npm:9.0.4" zustand: "npm:4.4.3" + dependenciesMeta: + portless: + optional: true languageName: unknown linkType: soft