fix(sf): use fixed server port

This commit is contained in:
Mikael Hugo 2026-05-17 18:55:21 +02:00
parent 425bba7d39
commit 3568972059
4 changed files with 55 additions and 16 deletions

12
package-lock.json generated
View file

@ -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"

View file

@ -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 = [];

View file

@ -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<string, string> | 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<string, string>;
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");

View file

@ -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 = {