Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 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 src/lib/onboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const ANSI_RE = /\x1B(?:\[[0-?]*[ -/]*[@-~]|\][^\x07]*(?:\x07|\x1B\\)|[@-_])/g;
const { ROOT, SCRIPTS, redact, run, runCapture, runFile, shellQuote, validateName } = require("./runner");
const { stageOptimizedSandboxBuildContext } = require("./sandbox-build-context");
const { buildSubprocessEnv } = require("./subprocess-env");
const { DASHBOARD_PORT, GATEWAY_PORT, VLLM_PORT, OLLAMA_PORT, OLLAMA_PROXY_PORT } = require("./ports");
const { DASHBOARD_PORT, GATEWAY_PORT, VLLM_PORT, OLLAMA_PORT, OLLAMA_PROXY_PORT, SANDBOX_DASHBOARD_PORT } = require("./ports");
const {
getDefaultOllamaModel,
getBootstrapOllamaModelOptions,
Expand Down Expand Up @@ -5539,6 +5539,13 @@ function ensureDashboardForward(sandboxName, chatUiUrl = `http://127.0.0.1:${CON
const portToStop = getDashboardForwardPort(chatUiUrl);
const forwardTarget = getDashboardForwardTarget(chatUiUrl);
runOpenshell(["forward", "stop", portToStop], { ignoreError: true });
// When using a custom dashboard port, also stop any lingering forward on the
// sandbox-default port that openshell sandbox create may have auto-created.
// Without this, `forward list` shows both the custom and default forwards (#2007).
const defaultPortStr = String(SANDBOX_DASHBOARD_PORT);
if (portToStop !== defaultPortStr) {
runOpenshell(["forward", "stop", defaultPortStr], { ignoreError: true });
}
// Use stdio "ignore" to prevent spawnSync from waiting on inherited pipe fds.
// The --background flag forks a child that inherits stdout/stderr; if those are
// pipes, spawnSync blocks until the background process exits (never).
Expand Down
2 changes: 1 addition & 1 deletion src/lib/ports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const GATEWAY_PORT = parsePort("NEMOCLAW_GATEWAY_PORT", 8080);
* configured via NEMOCLAW_DASHBOARD_PORT at onboard time. This constant represents
* the hardcoded default when no override is set.
*/
const SANDBOX_DASHBOARD_PORT = 18789;
export const SANDBOX_DASHBOARD_PORT = 18789;
/** Dashboard UI port (default SANDBOX_DASHBOARD_PORT, override via NEMOCLAW_DASHBOARD_PORT). This is the host-side port. */
export const DASHBOARD_PORT = parsePort("NEMOCLAW_DASHBOARD_PORT", SANDBOX_DASHBOARD_PORT);
/** vLLM / NIM inference port (default 8000, override via NEMOCLAW_VLLM_PORT). */
Expand Down
23 changes: 23 additions & 0 deletions test/onboard.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -993,6 +993,29 @@ describe("onboard helpers", () => {
);
});

it("cleans up the sandbox-default dashboard forward via SANDBOX_DASHBOARD_PORT (no hardcoded 18789)", () => {
const source = fs.readFileSync(
path.join(import.meta.dirname, "..", "src", "lib", "onboard.ts"),
"utf-8",
);

// The cleanup branch must compare against the imported constant, not a literal.
assert.match(source, /SANDBOX_DASHBOARD_PORT/);
assert.match(
source,
/const defaultPortStr = String\(SANDBOX_DASHBOARD_PORT\);[\s\S]*runOpenshell\(\["forward", "stop", defaultPortStr\]/,
);
// Guard against regressions to the hardcoded literal.
assert.doesNotMatch(source, /const DEFAULT_SANDBOX_PORT = "18789"/);

// The shared port constant must be exported so onboard.ts can import it.
const portsSource = fs.readFileSync(
path.join(import.meta.dirname, "..", "src", "lib", "ports.ts"),
"utf-8",
);
assert.match(portsSource, /export const SANDBOX_DASHBOARD_PORT = 18789/);
});

it("classifies gateway reuse states conservatively", () => {
expect(
getGatewayReuseState(
Expand Down