Skip to content
Closed
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
27 changes: 24 additions & 3 deletions bin/lib/onboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -2140,6 +2140,23 @@ async function setupPolicies(sandboxName) {
const CONTROL_UI_PORT = 18789;
const CONTROL_UI_CHAT_PATH = "/chat?session=main";

/**
* Extract gateway auth token from sandbox connect output.
* The output may contain shell noise; looks for a line matching TOKEN:<value>.
* Exported for unit tests.
*/
function parseTokenFromOutput(output) {
if (!output || typeof output !== "string") return null;
for (const line of output.split("\n")) {
const trimmed = line.trim();
if (trimmed.startsWith("TOKEN:") && trimmed.length > 6) {
const token = trimmed.slice(6).trim();
if (token) return token;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
return null;
}

function findOpenclawJsonPath(dir) {
if (!fs.existsSync(dir)) return null;
const entries = fs.readdirSync(dir, { withFileTypes: true });
Expand Down Expand Up @@ -2178,8 +2195,8 @@ function fetchGatewayAuthTokenFromSandbox(sandboxName) {
} finally {
try {
fs.rmSync(tmpDir, { recursive: true, force: true });
} catch {
// ignore cleanup errors
} catch (e) {
console.error(` Failed to remove temp dir ${tmpDir}: ${e.message}`);
}
}
}
Expand Down Expand Up @@ -2214,10 +2231,13 @@ function printDashboard(sandboxName, model, provider, nimContainer = null) {
else if (provider === "ollama-local") providerLabel = "Local Ollama";

const token = fetchGatewayAuthTokenFromSandbox(sandboxName);
const dashboardUrl = token
? `http://localhost:${CONTROL_UI_PORT}/#token=${encodeURIComponent(token)}`
: `http://localhost:${CONTROL_UI_PORT}/`;

console.log("");
console.log(` ${"─".repeat(50)}`);
// console.log(` Dashboard http://localhost:18789/`);
console.log(` Dashboard ${dashboardUrl}`);
console.log(` Sandbox ${sandboxName} (Landlock + seccomp + netns)`);
console.log(` Model ${model} (${providerLabel})`);
console.log(` NIM ${nimLabel}`);
Expand Down Expand Up @@ -2282,6 +2302,7 @@ module.exports = {
hasStaleGateway,
isSandboxReady,
onboard,
parseTokenFromOutput,
pruneStaleSandboxEntry,
runCaptureOpenshell,
setupInference,
Expand Down
28 changes: 28 additions & 0 deletions test/onboard.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
getInstalledOpenshellVersion,
getSandboxInferenceConfig,
getStableGatewayImageRef,
parseTokenFromOutput,
patchStagedDockerfile,
writeSandboxConfigSyncFile,
} from "../bin/lib/onboard";
Expand Down Expand Up @@ -782,4 +783,31 @@ const { setupInference } = require(${onboardPath});
assert.equal(commands.length, 3);
});

describe("parseTokenFromOutput", () => {
it("extracts token from a TOKEN: prefixed line", () => {
const out = "shell noise\nTOKEN:test-token-unit-fixture\nexit\n";
expect(parseTokenFromOutput(out)).toBe("test-token-unit-fixture");
});

it("returns the first TOKEN: match when multiple lines match", () => {
const out = "TOKEN:first\nTOKEN:second\n";
expect(parseTokenFromOutput(out)).toBe("first");
});

it("trims whitespace around the TOKEN: line", () => {
const out = " TOKEN:abc123 \n";
expect(parseTokenFromOutput(out)).toBe("abc123");
});

it("returns null when no TOKEN: prefix is present", () => {
expect(parseTokenFromOutput("no token here")).toBe(null);
expect(parseTokenFromOutput("some output\nexit\n")).toBe(null);
});

it("returns null for empty or non-string input", () => {
expect(parseTokenFromOutput("")).toBe(null);
expect(parseTokenFromOutput(null)).toBe(null);
expect(parseTokenFromOutput(undefined)).toBe(null);
});
});
});