diff --git a/package.json b/package.json index 643b68a4a..698e30039 100644 --- a/package.json +++ b/package.json @@ -19,10 +19,10 @@ "debug:supertokens": "export DEBUG=com.supertokens &&bun run dev:backend", "debug:web": "http-server build/web/", "dev:backend -verbose": "export DEBUG=* &&bun run dev:backend", - "dev:backend": "bun run dev:ports && cd packages/backend && bun --watch src/app.ts", + "dev:backend": "bun run dev:ports backend && cd packages/backend && bun --watch src/app.ts", "dev:ports": "bun packages/scripts/src/commands/dev-ports.ts", "dev:update": "git checkout main && git pull &&bun install", - "dev:web": "bun run dev:ports && cd packages/web &&bun run dev.ts", + "dev:web": "bun run dev:ports web && cd packages/web &&bun run dev.ts", "lint": "bun packages/scripts/src/testing/check-semantic-colors.ts && biome check .", "lint:fix": "biome check --write .", "format": "biome format --write .", @@ -58,7 +58,7 @@ "@types/node": "^22.13.10", "@types/node-fetch": "^2.6.13", "axe-core": "^4.12.1", - "typescript": "^7.0.2" + "typescript": "7.0.2" }, "resolutions": { "glob": "^13.0.6", diff --git a/packages/scripts/src/commands/dev-ports.ts b/packages/scripts/src/commands/dev-ports.ts index 48ce493d5..3780774a1 100644 --- a/packages/scripts/src/commands/dev-ports.ts +++ b/packages/scripts/src/commands/dev-ports.ts @@ -29,6 +29,10 @@ export interface DevPorts { backend: number; } +// Which dev server is launching, so the port-in-use warning only covers the +// service that's actually about to bind. Port reassignment stays pair-based. +export type Scope = "web" | "backend"; + export function readPorts(yamlText: string): DevPorts | null { try { const config = parse(yamlText) as { @@ -154,7 +158,47 @@ async function findNextPorts(claimed: DevPorts[]): Promise { return null; } -async function main(): Promise { +// Best-effort: PIDs listening on the given TCP port, or [] if none (or if +// lsof isn't available). Sibling-config detection can't see a stale process +// still bound to this worktree's own port, so we probe the live socket only +// to print a friendlier warning than the raw EADDRINUSE stack trace the dev +// server would otherwise throw. +function portHolders(port: number): string[] { + try { + const out = execSync(`lsof -nP -iTCP:${port} -sTCP:LISTEN -t`, { + encoding: "utf8", + stdio: ["ignore", "pipe", "ignore"], + }); + return out + .split("\n") + .map((line) => line.trim()) + .filter(Boolean); + } catch { + return []; // no holder (lsof exits non-zero) or lsof unavailable + } +} + +// `scope` limits the check to the service actually being launched, so +// `dev:backend` doesn't warn about a web server legitimately running on 9080 +// (and vice versa). Omitted → check both. +function warnIfPortsHeld(ports: DevPorts, scope?: Scope): void { + for (const [label, port] of [ + ["web", ports.web], + ["backend", ports.backend], + ] as const) { + if (scope && label !== scope) continue; + const pids = portHolders(port); + if (pids.length > 0) { + console.log( + `[dev-ports] warning: ${label} port ${port} is already in use by ` + + `PID ${pids.join(", ")}. The dev server will fail to bind. ` + + `Free it with: kill ${pids.join(" ")}`, + ); + } + } +} + +async function main(scope?: Scope): Promise { const root = process.cwd(); const configPath = path.join(root, "compass.yaml"); @@ -170,6 +214,7 @@ async function main(): Promise { const claimed = readSiblingPorts(root); if (!isPortsClaimed(current, claimed)) { + warnIfPortsHeld(current, scope); return; } @@ -183,6 +228,7 @@ async function main(): Promise { console.log( "[dev-ports] compass.yaml uses custom URLs — manage ports manually", ); + warnIfPortsHeld(current, scope); return; } @@ -194,7 +240,10 @@ async function main(): Promise { } if (require.main === module) { - main().catch((err) => { + const arg = process.argv[2]; + const scope: Scope | undefined = + arg === "web" || arg === "backend" ? arg : undefined; + main(scope).catch((err) => { console.log(err); process.exit(1); }); diff --git a/packages/web/src/components/CompassProvider/CompassProvider.tsx b/packages/web/src/components/CompassProvider/CompassProvider.tsx index e87dac68a..17fec86ae 100644 --- a/packages/web/src/components/CompassProvider/CompassProvider.tsx +++ b/packages/web/src/components/CompassProvider/CompassProvider.tsx @@ -80,6 +80,8 @@ export const CompassOptionalProviders = ({ children }: PropsWithChildren) => { apiKey={ENV_WEB.POSTHOG_KEY as string} options={{ api_host: ENV_WEB.POSTHOG_HOST!, + // Assumes the US cloud; self-hosters on another instance would differ. + ui_host: "https://us.posthog.com", capture_exceptions: { capture_unhandled_errors: true, capture_unhandled_rejections: true, diff --git a/packages/web/src/components/ReleaseNotesPrompt/ReleaseNotesPrompt.tsx b/packages/web/src/components/ReleaseNotesPrompt/ReleaseNotesPrompt.tsx index 44826d6ee..82622a8c2 100644 --- a/packages/web/src/components/ReleaseNotesPrompt/ReleaseNotesPrompt.tsx +++ b/packages/web/src/components/ReleaseNotesPrompt/ReleaseNotesPrompt.tsx @@ -87,8 +87,7 @@ export function ReleaseNotesPrompt() { Want the latest Compass news?

- Get monthly release notes with new features, improvements, and - helpful tips. Unsubscribe anytime. + Get monthly product email. Unsubscribe anytime.

@@ -97,14 +96,14 @@ export function ReleaseNotesPrompt() { onClick={decline} className="c-button c-button-secondary rounded-full px-5" > - Nah, I don't want updates + Nah, I don't want more emails
@@ -115,7 +114,7 @@ export function ReleaseNotesPrompt() {

{state === "confirmed" - ? "Monthly notes headed your way." + ? "You'll get the next release notes in your inbox" : "No problem, you can signup using the cmd palette if you change your mind."}