Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@ DATABASE_URL=postgres://openbot:openbot@localhost:5432/openbot
# development. Production refuses to start with this key.
# openssl rand -base64 32
KEY_ENCRYPTION_KEY=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
PORT=3001
# Where the API listens. SERVER_PORT is the name start.sh, Vite, and the ports table already use.
# PORT is the older alias; the process still honours it when SERVER_PORT is unset, so an existing
# .env that only sets PORT keeps working.
SERVER_PORT=3001
# PORT=3001
#
# Where the app is served. start.sh and Vite both read this; the default is 3010.
# APP_PORT=3010
TENANT_PACKAGE_DIR=../examples/fintech
# What this deployment calls itself, when more than one shares an Intelligence project. A copy of a
# deployment made for development uses the same project key, and threads are listed per Bot with
Expand Down
5 changes: 4 additions & 1 deletion app/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import { tanstackRouter } from "@tanstack/router-plugin/vite";
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";

const apiPort =
process.env.SERVER_PORT?.trim() || process.env.PORT?.trim() || "3001";

export default defineConfig({
plugins: [tanstackRouter(), react(), tailwindcss()],
resolve: {
Expand All @@ -18,7 +21,7 @@ export default defineConfig({
// `ws: true` is required for the live screen. Without it Vite answers the upgrade request with
// the app's HTML and the socket fails with an opaque error that looks like a server problem.
"/api": {
target: `http://localhost:${process.env.SERVER_PORT ?? "3001"}`,
target: `http://localhost:${apiPort}`,
ws: true,
},
},
Expand Down
2 changes: 1 addition & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ All four Intelligence values are required together. Missing any of them stops se

| Variable | Default | Meaning |
| -------------------- | ---------------------------------- | ------------------------------------------------------------------- |
| `PORT` | `3001` | API server port. |
| `SERVER_PORT` | `3001` | API server port. `PORT` is the same setting when `SERVER_PORT` is unset. |
| `NODE_ENV` | unset | `production` enables startup refusals for local-only settings. |
| `TENANT_PACKAGE_DIR` | `../examples/fintech` | Tenant package directory, resolved from `server/`. |
| `DEPLOYMENT_ID` | the tenant package's id | Names this deployment inside a shared Intelligence project. |
Expand Down
4 changes: 3 additions & 1 deletion scripts/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ setting() {
}

APP_PORT="$(setting APP_PORT 3010)"
SERVER_PORT="$(setting SERVER_PORT 3001)"
# The documented name first, then PORT, matching the API process. An existing .env that only
# renamed PORT would otherwise have start.sh health-checking 3001 while the server listened elsewhere.
SERVER_PORT="$(setting SERVER_PORT "$(setting PORT 3001)")"
COMPUTER_PORT="$(setting COMPUTER_PORT 4100)"
BOT_PORT="$(setting BOT_PORT 4200)"
LANGGRAPH_PORT="$(setting LANGGRAPH_PORT 4201)"
Expand Down
3 changes: 2 additions & 1 deletion server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
resolveModelApiKey,
} from "./credentials";
import { createDatabase } from "./db/client";
import { resolveListenPort } from "./listen-port";
import { createPluginStore } from "./plugins/store";
import {
createPackageStatusReader,
Expand Down Expand Up @@ -103,7 +104,7 @@ const identifyActor: IdentifyActor = async (request) => {
};

const config = loadConfig();
const port = Number.parseInt(process.env.PORT ?? "3001", 10);
const port = resolveListenPort();
const database = createDatabase(config.databaseUrl);
await initializeDevActorUser(database, config.devNoAuth);
// The vault, built before the agent store because a customer's agent may sit behind a key and that
Expand Down
16 changes: 16 additions & 0 deletions server/src/listen-port.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Where this process should listen.
*
* The ports table, `scripts/start.sh`, and the Vite `/api` proxy all name `SERVER_PORT`. The process
* used to read only `PORT`, which is what `.env.example` used to set, so a clone that followed the
* docs moved the proxy and the health check but not the listener.
*
* `SERVER_PORT` first, then `PORT`, then 3001. Existing files that only set `PORT` keep working.
*/
export function resolveListenPort(
environment: Record<string, string | undefined> = process.env,
): number {
const raw =
environment.SERVER_PORT?.trim() || environment.PORT?.trim() || "3001";
return Number.parseInt(raw, 10);
}
25 changes: 25 additions & 0 deletions server/tests/listen-port.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { describe, expect, test } from "bun:test";
import { resolveListenPort } from "../src/listen-port";

describe("API listen port", () => {
test("defaults to 3001", () => {
expect(resolveListenPort({})).toBe(3001);
});

test("honours PORT, which existing .env files already set", () => {
expect(resolveListenPort({ PORT: "4001" })).toBe(4001);
});

test("honours SERVER_PORT, which is the name the docs and start.sh use", () => {
expect(resolveListenPort({ SERVER_PORT: "4002" })).toBe(4002);
});

test("prefers SERVER_PORT when both are set, so the documented name actually moves the listener", () => {
expect(resolveListenPort({ SERVER_PORT: "4002", PORT: "3001" })).toBe(4002);
});

test("treats blank values as unset", () => {
expect(resolveListenPort({ SERVER_PORT: " ", PORT: "4001" })).toBe(4001);
expect(resolveListenPort({ SERVER_PORT: "", PORT: "" })).toBe(3001);
});
});