From 3568972059f943a5af4f20203b396342e87613b2 Mon Sep 17 00:00:00 2001 From: Mikael Hugo Date: Sun, 17 May 2026 18:55:21 +0200 Subject: [PATCH] fix(sf): use fixed server port --- package-lock.json | 12 +---- .../sf/tests/inline-runtime-gate.test.mjs | 4 -- src/tests/integration/web-mode-cli.test.ts | 51 +++++++++++++++++++ src/web-mode.ts | 4 +- 4 files changed, 55 insertions(+), 16 deletions(-) diff --git a/package-lock.json b/package-lock.json index c3c018d7b..e8574a726 100644 --- a/package-lock.json +++ b/package-lock.json @@ -93,12 +93,9 @@ }, "optionalDependencies": { "@anthropic-ai/claude-agent-sdk": "^0.2.137", - "@singularity-forge/engine-darwin-arm64": ">=2.10.2", - "@singularity-forge/engine-darwin-x64": ">=2.10.2", "@singularity-forge/engine-linux-arm64-gnu": ">=2.10.2", "@singularity-forge/engine-linux-x64-gnu": ">=2.10.2", "@singularity-forge/engine-win32-x64-msvc": ">=2.10.2", - "fsevents": "~2.3.3", "koffi": "^2.16.2", "vectordrive": "^0.1.35" } @@ -6009,12 +6006,6 @@ "resolved": "packages/daemon", "link": true }, - "node_modules/@singularity-forge/engine-darwin-arm64": { - "optional": true - }, - "node_modules/@singularity-forge/engine-darwin-x64": { - "optional": true - }, "node_modules/@singularity-forge/engine-linux-arm64-gnu": { "optional": true }, @@ -10286,6 +10277,7 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -17873,8 +17865,6 @@ "node": ">=26.1.0" }, "optionalDependencies": { - "@singularity-forge/engine-darwin-arm64": ">=2.75.0", - "@singularity-forge/engine-darwin-x64": ">=2.75.0", "@singularity-forge/engine-linux-arm64-gnu": ">=2.75.0", "@singularity-forge/engine-linux-x64-gnu": ">=2.75.0", "@singularity-forge/engine-win32-x64-msvc": ">=2.75.0" diff --git a/src/resources/extensions/sf/tests/inline-runtime-gate.test.mjs b/src/resources/extensions/sf/tests/inline-runtime-gate.test.mjs index f0dcdd149..897f6139a 100644 --- a/src/resources/extensions/sf/tests/inline-runtime-gate.test.mjs +++ b/src/resources/extensions/sf/tests/inline-runtime-gate.test.mjs @@ -13,16 +13,12 @@ import * as Path from "node:path"; import * as Vitest from "vitest"; import * as Db from "../sf-db.js"; import * as Gate from "../uok/inline-runtime-gate.js"; -import * as Bootstrap from "../uok/gate-registry-bootstrap.js"; -import { UokGateRunner } from "../uok/gate-runner.js"; - const { mkdirSync, mkdtempSync, rmSync } = Fs; const { tmpdir } = Os; const { join } = Path; const { afterEach, beforeEach, describe, test } = Vitest; const { closeDatabase, openDatabase, upsertRequirement } = Db; const { inlineRuntimeGate, isInlineEligible } = Gate; -const { bootstrapGateRegistry, BOOTSTRAP_GATES } = Bootstrap; const tmpRoots = []; diff --git a/src/tests/integration/web-mode-cli.test.ts b/src/tests/integration/web-mode-cli.test.ts index a65e5202c..8be245774 100644 --- a/src/tests/integration/web-mode-cli.test.ts +++ b/src/tests/integration/web-mode-cli.test.ts @@ -237,6 +237,57 @@ test("launchWebMode prefers the packaged standalone host and opens the resolved assert.equal(webMode.readPidFile(pidFilePath), 99999); }); +test("launchWebMode defaults to fixed port 4000 when no port is specified", async (_t) => { + const tmp = mkdtempSync(join(tmpdir(), "sf-web-fixed-port-")); + const standaloneRoot = join(tmp, "dist", "web", "standalone"); + const serverPath = join(standaloneRoot, "server.js"); + mkdirSync(standaloneRoot, { recursive: true }); + writeFileSync(serverPath, 'console.log("stub")\n'); + + let openedUrl = ""; + let spawnEnv: Record | undefined; + + afterEach(() => { + rmSync(tmp, { recursive: true, force: true }); + }); + + const status = await webMode.launchWebMode( + { + cwd: "/tmp/current-project", + projectSessionsDir: "/tmp/.sf/sessions/--tmp-current-project--", + agentDir: "/tmp/.sf/agent", + packageRoot: tmp, + }, + { + initResources: () => undefined, + execPath: "/custom/node", + env: {}, + spawn: (_command, _args, options) => { + spawnEnv = options.env as Record; + return { + pid: 99999, + once: () => undefined, + unref: () => undefined, + } as any; + }, + waitForBootReady: async () => undefined, + openBrowser: (url) => { + openedUrl = url; + }, + writePidFile: () => undefined, + registryPath: join(tmp, "web-instances.json"), + }, + ); + + assert.equal(status.ok, true); + if (!status.ok) throw new Error("expected successful web launch status"); + assert.equal(status.port, 4000); + assert.equal(status.url, "http://127.0.0.1:4000"); + assert.match(openedUrl, /^http:\/\/127\.0\.0\.1:4000\/#token=[a-f0-9]{64}$/); + assert.equal(spawnEnv?.PORT, "4000"); + assert.equal(spawnEnv?.SF_WEB_PORT, "4000"); +}); + test("stopWebMode kills process by PID and removes PID file", (_t) => { const tmp = mkdtempSync(join(tmpdir(), "sf-web-stop-")); const pidFilePath = join(tmp, "web-server.pid"); diff --git a/src/web-mode.ts b/src/web-mode.ts index cf79681c2..b8a7700b4 100644 --- a/src/web-mode.ts +++ b/src/web-mode.ts @@ -22,6 +22,7 @@ import { } from "./app-paths.js"; const DEFAULT_HOST = "127.0.0.1"; +const DEFAULT_PORT = 4000; const DEFAULT_PACKAGE_ROOT = resolve(import.meta.dirname, ".."); /** Open a URL in the user's default browser. */ @@ -808,7 +809,8 @@ export async function launchWebMode( } const port = - options.port ?? (await (deps.resolvePort ?? reserveWebPort)(host)); + options.port ?? + (deps.resolvePort ? await deps.resolvePort(host) : DEFAULT_PORT); const authToken = randomBytes(32).toString("hex"); const url = `http://${host}:${port}`; const env = {