diff --git a/ecosystem-files/ecosystem.profile.cjs b/ecosystem-files/ecosystem.profile.cjs index f56b3eb7..1592ffb8 100644 --- a/ecosystem-files/ecosystem.profile.cjs +++ b/ecosystem-files/ecosystem.profile.cjs @@ -69,7 +69,7 @@ function defineProfile({ file, include, excluded }) { // `cwd` is pinned because Bun auto-loads .env from the working directory (and the pty sidecar does // `import 'dotenv/config'`). Without it, starting pm2 from anywhere but the repo root silently falls - // back to PORT=5000 with no POSTGRES_URL. + // back to the default PORT with no POSTGRES_URL. // // It also decides where the install is. src/servers/data-path.ts derives OFFICER_ROOT as the PARENT of // the working directory, and data/, capabilities/ and dockers/ hang off that — so a wrong cwd does not diff --git a/src/server.tsx b/src/server.tsx index f0a8829b..aaec895c 100644 --- a/src/server.tsx +++ b/src/server.tsx @@ -23,7 +23,7 @@ import './servers/api/chat/opencode/sidecar-server'; // subscribe to the opencod import type { SidecarRegistration } from './servers/sidecar/registration-protocol'; import { toShellUsername } from './servers/data-path'; -const { PORT = '5000' } = process.env; +const { PORT = '9000' } = process.env; // Build static file routes from public/ const publicRoutes: Record Response> = {}; diff --git a/src/servers/api/chat/agent-panels-routes.ts b/src/servers/api/chat/agent-panels-routes.ts index c96673b2..9d6c2e48 100644 --- a/src/servers/api/chat/agent-panels-routes.ts +++ b/src/servers/api/chat/agent-panels-routes.ts @@ -23,7 +23,7 @@ import { logger } from './logger'; /** A name has to survive being typed into a prompt and into a shell, so keep it boring. */ const NAME_RE = /^[a-z0-9][a-z0-9-]{0,30}[a-z0-9]$|^[a-z0-9]$/; -const API_ORIGIN = `http://127.0.0.1:${process.env.PORT ?? '5000'}`; +const API_ORIGIN = `http://127.0.0.1:${process.env.PORT ?? '9000'}`; export function registerAgentPanelRoutes(router: Hono): void { // GET /chat/agent-panels?dashboardId=… — the address book for one dashboard. diff --git a/src/servers/api/tasks/task-api-env.ts b/src/servers/api/tasks/task-api-env.ts index d8334e93..912a250a 100644 --- a/src/servers/api/tasks/task-api-env.ts +++ b/src/servers/api/tasks/task-api-env.ts @@ -1,6 +1,6 @@ import { sign } from '@@/jwt'; -const { PORT = '5000', PUBLIC_URL } = process.env; +const { PORT = '9000', PUBLIC_URL } = process.env; const PUBLIC_HOST = (() => { try { diff --git a/src/servers/sidecar/caldav/index.ts b/src/servers/sidecar/caldav/index.ts index 2e680e27..f04ef86c 100644 --- a/src/servers/sidecar/caldav/index.ts +++ b/src/servers/sidecar/caldav/index.ts @@ -23,7 +23,7 @@ import { DATA_PATH } from '../../data-path'; // machine-facing interface. Same split officer-email already uses. // ───────────────────────────────────────────────────────────────────────────────────────────────── -const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '5000'}`; +const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '9000'}`; /** Grab an ephemeral free port by briefly binding one and releasing it. */ function getFreePort(): number { diff --git a/src/servers/sidecar/claude/index.ts b/src/servers/sidecar/claude/index.ts index 5379631d..39f861a5 100644 --- a/src/servers/sidecar/claude/index.ts +++ b/src/servers/sidecar/claude/index.ts @@ -3,7 +3,7 @@ import { loadState, flushAndSave, acquireLock, releaseLock, getState } from './s import { startAnthropicProxy, getProxySecret, ensureProxySecret } from './proxy'; import { createSidecarConnector } from '../connect'; -const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '5000'}`; +const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '9000'}`; // ── Startup ── diff --git a/src/servers/sidecar/claude/user-instance.ts b/src/servers/sidecar/claude/user-instance.ts index 8a9f032d..455c8d20 100644 --- a/src/servers/sidecar/claude/user-instance.ts +++ b/src/servers/sidecar/claude/user-instance.ts @@ -61,9 +61,16 @@ async function resolveOwner() { const dbUser = await resolveOwner(); const email = dbUser.email; -// Same officer instance for both, so the fallback port has to agree. It used to default to 5000 for the -// WebSocket and 9010 for the REST base, which would have split them apart if PORT were ever unset. -const OFFICER_PORT = process.env.PORT ?? '9010'; +// Both URLs address the SAME officer instance — this file binds nothing, and the app serves HTTP and +// WebSocket on one listener. So the fallback port has to agree, and twice it did not: 5000 for the +// WebSocket against 9010 for the REST base here, and then 9010 here against 5000 in the app and every +// other sidecar. The second one was the worse half, because chat is the only thing that would have +// broken and nothing else would have looked wrong. +// +// 9000 everywhere now, matching server.tsx and .env.example. 9010 was the old installer's default +// (scripts/setup-old/setup.sh), which is why it was the only one of the three that ever matched a real +// machine. +const OFFICER_PORT = process.env.PORT ?? '9000'; const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${OFFICER_PORT}`; const OFFICER_API_URL = process.env.OFFICER_API_URL ?? `http://127.0.0.1:${OFFICER_PORT}`; const MCP_SERVER_SCRIPT = resolve(import.meta.dir, '../../mcp-tool-server.ts'); diff --git a/src/servers/sidecar/connect.ts b/src/servers/sidecar/connect.ts index 2632c289..da49c237 100644 --- a/src/servers/sidecar/connect.ts +++ b/src/servers/sidecar/connect.ts @@ -5,7 +5,7 @@ type AnyCommand = SidecarCommand; type AnyEvent = SidecarEvent; type SidecarConnectorConfig = { - apiUrl: string; // ws://127.0.0.1:5000/api/sidecar/register + apiUrl: string; // ws://127.0.0.1:9000/api/sidecar/register name: string; capabilities: string[]; onCommand: (cmd: AnyCommand, reply: (msg: AnyEvent) => void) => void; diff --git a/src/servers/sidecar/email/index.ts b/src/servers/sidecar/email/index.ts index d3b529e4..f53323ff 100644 --- a/src/servers/sidecar/email/index.ts +++ b/src/servers/sidecar/email/index.ts @@ -5,7 +5,7 @@ import { broadcastEmailNew } from './routes'; import { startEmailServer } from './http'; import { createSidecarConnector } from '../connect'; -const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '5000'}`; +const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '9000'}`; // The sidecar used to reach BACK into the platform's queue over this socket to get a sync run — // enqueueViaWs / listJobsViaWs and a pending-response map. Syncs run in this process now diff --git a/src/servers/sidecar/gitea/index.ts b/src/servers/sidecar/gitea/index.ts index 5ac8bc3e..713d2286 100644 --- a/src/servers/sidecar/gitea/index.ts +++ b/src/servers/sidecar/gitea/index.ts @@ -42,7 +42,7 @@ import { // sidecar from being a general-purpose SSRF hop into whatever else is on that host. // ───────────────────────────────────────────────────────────────────────────────────────────────── -const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '5000'}`; +const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '9000'}`; // Everything under /api/v1 the UI legitimately needs. // diff --git a/src/servers/sidecar/headscale/index.ts b/src/servers/sidecar/headscale/index.ts index 62f5b370..a99bf8fe 100644 --- a/src/servers/sidecar/headscale/index.ts +++ b/src/servers/sidecar/headscale/index.ts @@ -54,7 +54,7 @@ import { MIN_VERSION_LABEL } from './version'; // — the mistake the Soulseek panels made with 37 raw upstream calls. Every quirk is absorbed here. // ───────────────────────────────────────────────────────────────────────────────────────────────── -const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '5000'}`; +const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '9000'}`; /** Grab an ephemeral free port by briefly binding one and releasing it. */ function getFreePort(): number { diff --git a/src/servers/sidecar/invoiceshelf/index.ts b/src/servers/sidecar/invoiceshelf/index.ts index d5a3d15c..f962efb2 100644 --- a/src/servers/sidecar/invoiceshelf/index.ts +++ b/src/servers/sidecar/invoiceshelf/index.ts @@ -51,7 +51,7 @@ import { getConfig } from './upstream'; // update/*, installation/*, mail config, settings writes, ownership transfer — is deliberately unreachable. // ───────────────────────────────────────────────────────────────────────────────────────────────── -const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '5000'}`; +const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '9000'}`; /** Grab an ephemeral free port by briefly binding one and releasing it. */ function getFreePort(): number { diff --git a/src/servers/sidecar/jellyfin/index.ts b/src/servers/sidecar/jellyfin/index.ts index 3d28c22b..f6c64385 100644 --- a/src/servers/sidecar/jellyfin/index.ts +++ b/src/servers/sidecar/jellyfin/index.ts @@ -43,7 +43,7 @@ import { getConfig, probe } from './upstream'; // general proxy, and why HLS forces it to keep Jellyfin's own paths. // ───────────────────────────────────────────────────────────────────────────────────────────────── -const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '5000'}`; +const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '9000'}`; /** Grab an ephemeral free port by briefly binding one and releasing it. */ function getFreePort(): number { diff --git a/src/servers/sidecar/memos/index.ts b/src/servers/sidecar/memos/index.ts index 2f5f357d..f62fb645 100644 --- a/src/servers/sidecar/memos/index.ts +++ b/src/servers/sidecar/memos/index.ts @@ -22,7 +22,7 @@ import { callMemos, getMemosConfig, invalidateMemosConfig, normalizeBase, probe // SSRF hop into whatever else is on that host. // ───────────────────────────────────────────────────────────────────────────────────────────────── -const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '5000'}`; +const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '9000'}`; // Everything under /api/v1 the UI legitimately needs. Auth routes are excluded on purpose: signin and // signout would mint or destroy sessions on the instance, and this sidecar authenticates with a stored diff --git a/src/servers/sidecar/music/index.ts b/src/servers/sidecar/music/index.ts index 4ed268f3..09eebf59 100644 --- a/src/servers/sidecar/music/index.ts +++ b/src/servers/sidecar/music/index.ts @@ -114,7 +114,7 @@ const asKeys = (v: unknown): string[] | null => // `v` = per-album version stamp; unchanged `v` ⇒ nothing changed ⇒ the phone can skip re-downloading. // ───────────────────────────────────────────────────────────────────────────────────────────────── -const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '5000'}`; +const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '9000'}`; // ── Audio-streaming HTTP server ── diff --git a/src/servers/sidecar/notify/index.ts b/src/servers/sidecar/notify/index.ts index 1086e064..ae06cce3 100644 --- a/src/servers/sidecar/notify/index.ts +++ b/src/servers/sidecar/notify/index.ts @@ -26,7 +26,7 @@ import type { Notification, NotifyType } from './types'; // the visible string is composed here rather than sent by the producer. // ───────────────────────────────────────────────────────────────────────────────────────────────── -const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '5000'}`; +const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '9000'}`; const VALID_TYPES: NotifyType[] = ['job', 'mail', 'agent', 'download', 'test']; diff --git a/src/servers/sidecar/opencode/index.ts b/src/servers/sidecar/opencode/index.ts index aac33861..347154f8 100644 --- a/src/servers/sidecar/opencode/index.ts +++ b/src/servers/sidecar/opencode/index.ts @@ -18,7 +18,7 @@ import { runOpenCodeTurnOnServe, killServeTurn, listRunningServeTurns, stopAllSe // CRUD only, with turns spawned as `opencode run --dir ` subprocesses — that path was deleted on // 2026-08-10 once the serve had streaming, mid-turn injection and interrupt working end to end. -const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '5000'}`; +const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '9000'}`; const OPENCODE_BIN = process.env.OPENCODE_BIN || join(homedir(), '.opencode', 'bin', 'opencode'); const SERVE_CWD = join(DATA_PATH, 'opencode_server'); const HEALTH_TIMEOUT_MS = 20_000; diff --git a/src/servers/sidecar/photos/index.ts b/src/servers/sidecar/photos/index.ts index 03ed4f5b..655220e8 100644 --- a/src/servers/sidecar/photos/index.ts +++ b/src/servers/sidecar/photos/index.ts @@ -46,7 +46,7 @@ import { getConfig } from './upstream'; // ETag included. The administrative half of Immich's API is unreachable — see routes.ts for the list. // ───────────────────────────────────────────────────────────────────────────────────────────────── -const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '5000'}`; +const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '9000'}`; /** Grab an ephemeral free port by briefly binding one and releasing it. */ function getFreePort(): number { diff --git a/src/servers/sidecar/pty/index.mjs b/src/servers/sidecar/pty/index.mjs index 46f99fb1..7f1dd9f1 100644 --- a/src/servers/sidecar/pty/index.mjs +++ b/src/servers/sidecar/pty/index.mjs @@ -19,7 +19,7 @@ import { startServer } from './server.mjs'; import 'dotenv/config'; -const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '5000'}`; +const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '9000'}`; const REGISTER_URL = `${API_URL}/api/sidecar/register`; const RECONNECT_DELAYS = [200, 500, 1000, 2000, 4000, 8000, 15000]; diff --git a/src/servers/sidecar/slskd/index.ts b/src/servers/sidecar/slskd/index.ts index 6b3c1e1c..46646eef 100644 --- a/src/servers/sidecar/slskd/index.ts +++ b/src/servers/sidecar/slskd/index.ts @@ -37,7 +37,7 @@ import { handleOfficerRoute } from './officer'; // (src/servers/sidecar/vault/index.ts) once the client needs real-time updates. SignalR carries its // credential as an `?access_token=` query param on the socket, so the key injection differs from HTTP. -const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '5000'}`; +const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '9000'}`; /** Grab an ephemeral free port by briefly binding one and releasing it. */ function getFreePort(): number { diff --git a/src/servers/sidecar/transmission/index.ts b/src/servers/sidecar/transmission/index.ts index 96b36948..0457fe5d 100644 --- a/src/servers/sidecar/transmission/index.ts +++ b/src/servers/sidecar/transmission/index.ts @@ -45,7 +45,7 @@ import { getTransmissionConfig } from './upstream'; // platform, and which daemon to talk to is per-owner data. // ───────────────────────────────────────────────────────────────────────────────────────────────── -const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '5000'}`; +const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '9000'}`; /** Grab an ephemeral free port by briefly binding one and releasing it. */ function getFreePort(): number { diff --git a/src/servers/sidecar/vault/index.ts b/src/servers/sidecar/vault/index.ts index 7919116e..c80b7e87 100644 --- a/src/servers/sidecar/vault/index.ts +++ b/src/servers/sidecar/vault/index.ts @@ -21,7 +21,7 @@ import { getVaultBase, getVaultWsBase, stripHopByHop, redactPath } from './upstr // The server listens on a random loopback port, reported to the API on connect so it can route here. // ───────────────────────────────────────────────────────────────────────────────────────────────── -const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '5000'}`; +const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '9000'}`; /** Grab an ephemeral free port by briefly binding one and releasing it. */ function getFreePort(): number { diff --git a/src/servers/sidecar/vnc/index.ts b/src/servers/sidecar/vnc/index.ts index 9a7eca10..2cbe5fa1 100644 --- a/src/servers/sidecar/vnc/index.ts +++ b/src/servers/sidecar/vnc/index.ts @@ -3,7 +3,7 @@ import * as vncManager from './vnc-manager'; import { createSidecarConnector } from '../connect'; import { getOwnerHomeDir } from '@@/data-path'; -const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '5000'}`; +const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '9000'}`; // ── Command handlers ── diff --git a/src/servers/sidecar/wallet/index.ts b/src/servers/sidecar/wallet/index.ts index 6581ee5b..304aed85 100644 --- a/src/servers/sidecar/wallet/index.ts +++ b/src/servers/sidecar/wallet/index.ts @@ -81,7 +81,7 @@ import { invalidateAll } from './resolve'; // WALLET_LOCKED from signing paths only; every read above keeps working. // ───────────────────────────────────────────────────────────────────────────────────────────────── -const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '5000'}`; +const API_URL = process.env.API_URL ?? `ws://127.0.0.1:${process.env.PORT ?? '9000'}`; /** Grab an ephemeral free port by briefly binding one and releasing it. */ function getFreePort(): number {