feat(cli): add gateway-token command to retrieve auth token#944
feat(cli): add gateway-token command to retrieve auth token#944kagura-agent wants to merge 3 commits intoNVIDIA:mainfrom
Conversation
📝 WalkthroughWalkthroughAdds two sandbox-scoped CLI actions— Changes
Sequence Diagram(s)sequenceDiagram
participant CLI as CLI (nemoclaw)
participant Sandbox as Sandbox
participant Onboard as onboard helpers
participant PortFwd as PortForward
participant Opener as OS Browser Opener
CLI->>Sandbox: ensure sandbox is live
CLI->>PortFwd: start/ensure port-forward 18789 (background)
PortFwd-->>CLI: port-forward running
CLI->>PortFwd: waitForPort(18789)
PortFwd-->>CLI: port ready
CLI->>Onboard: fetchGatewayAuthTokenFromSandbox(sandbox)
Onboard-->>CLI: gateway auth token (or null)
CLI->>Onboard: buildControlUiUrls(token)
Onboard-->>CLI: list of Control UI URLs
CLI->>Opener: spawn platform opener with selected URL
Opener-->>CLI: spawn success / error
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
bin/nemoclaw.js (1)
339-392: Consider extracting shared token retrieval logic to reduce duplication.This function duplicates most of the logic from
getGatewayToken()inonboard.js(lines 2174-2193). The key difference is the candidate path discovery strategy:
- onboard.js dynamically discovers subdirectories via
fs.readdirSync(...).filter(e => e.isDirectory())- This implementation uses a hardcoded path
sandbox/.openclaw/openclaw.jsonplus afindfallbackConsider extracting a shared helper (e.g., in
lib/runner.jsor a newlib/token.js) that both functions can use. This would ensure consistent behavior ifopenshell sandbox downloadchanges its output structure.That said, the
findfallback provides reasonable resilience, and the implementation is correct.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@bin/nemoclaw.js` around lines 339 - 392, Extract the duplicated logic that downloads/locates/parses openclaw.json and returns the gateway token into a shared helper (e.g., create getSandboxGatewayToken in lib/token.js or lib/runner.js) and have sandboxGatewayToken and getGatewayToken (from onboard.js) call it; the helper should encapsulate: creating the temp dir, invoking openshell sandbox download, checking candidate paths (openclaw.json and sandbox/.openclaw/openclaw.json), falling back to a recursive find, parsing JSON, returning cfg.gateway.auth.token (or throwing a clear error), and performing cleanup of the temp dir so callers just handle printing/logging and process.exit semantics.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@bin/nemoclaw.js`:
- Around line 339-392: Extract the duplicated logic that
downloads/locates/parses openclaw.json and returns the gateway token into a
shared helper (e.g., create getSandboxGatewayToken in lib/token.js or
lib/runner.js) and have sandboxGatewayToken and getGatewayToken (from
onboard.js) call it; the helper should encapsulate: creating the temp dir,
invoking openshell sandbox download, checking candidate paths (openclaw.json and
sandbox/.openclaw/openclaw.json), falling back to a recursive find, parsing
JSON, returning cfg.gateway.auth.token (or throwing a clear error), and
performing cleanup of the temp dir so callers just handle printing/logging and
process.exit semantics.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: a3dd1f23-9f80-465c-a03e-6a0f87c5fe44
📒 Files selected for processing (1)
bin/nemoclaw.js
|
@kagura-agent thanks! can you resolve the conflicts with |
|
@kagura-agent We need to minimize showing the token unless required. Two requests (I will add to the issue):
|
Add 'nemoclaw <name> gateway-token' command that prints the gateway auth token for a sandbox, replacing the multi-step workaround with openshell sandbox download + jq. The command: - Downloads openclaw.json from the sandbox via openshell - Extracts gateway.auth.token from the config - Prints the token to stdout (pipe-friendly) - Cleans up temp files in all cases - Provides clear error messages when sandbox is inaccessible or token is not configured Usage: nemoclaw my-assistant gateway-token The token can be used directly in scripts: TOKEN=$(nemoclaw my-assistant gateway-token) openclaw acp --url wss://localhost:18789 --token $TOKEN Closes NVIDIA#938 Signed-off-by: Kagura <kagura-agent@users.noreply.github.com>
e7818b7 to
f2ad690
Compare
…h (PR NVIDIA#944 feedback) - Export fetchGatewayAuthTokenFromSandbox and buildControlUiUrls from onboard.js - Refactor sandboxGatewayToken() to use shared fetchGatewayAuthTokenFromSandbox() - Add security warning to gateway-token output (stderr before token on stdout) - Add new 'open' command that opens browser with tokenized URL - Ensures port forward is active before opening - Uses platform-appropriate opener (xdg-open/open/start) - Minimizes token exposure compared to manual copy-paste - Update help text to document both commands Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
@brandonpelfrey @cv Thank you for the thoughtful review. @cv — Rebased on @brandonpelfrey — Your point about minimizing token exposure is well taken. The token showing up in shell history, screencasts, or logs is a real risk that I hadn't given enough weight to in the original implementation. I've restructured the PR around that principle:
I also exported Let me know if anything needs adjustment. |
…h (PR NVIDIA#944 feedback) - Export fetchGatewayAuthTokenFromSandbox and buildControlUiUrls from onboard.js - Refactor sandboxGatewayToken() to use shared fetchGatewayAuthTokenFromSandbox() - Add security warning to gateway-token output (stderr before token on stdout) - Add new 'open' command that opens browser with tokenized URL - Ensures port forward is active before opening - Uses platform-appropriate opener (xdg-open/open/start) - Minimizes token exposure compared to manual copy-paste - Update help text to document both commands Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
f2ad690 to
b926994
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@bin/nemoclaw.js`:
- Around line 667-683: The code calls runOpenshell(["forward", "start",
"--background", "18789", sandboxName], { ignoreError: true }) but does not
verify that the forwarded port is accepting connections before opening the UI;
add a short retry loop after that call to probe 127.0.0.1:18789 (e.g., TCP
connect or HTTP HEAD/GET) with a small delay and a capped number of attempts
(e.g., 5–10 attempts, 200–500ms backoff), and only proceed to call
buildControlUiUrls/fetchGatewayAuthTokenFromSandbox and spawn the browser once
the probe succeeds; if the probe never succeeds within the timeout, log a clear
error and exit (similar to the fetchGatewayAuthTokenFromSandbox error handling)
so opening the Control UI is deterministic.
- Around line 654-664: The sandboxGatewayToken path skips the live-sandbox
reconciliation that connect/open perform and thus can falsely fail when the
gateway is recoverable; modify sandboxGatewayToken to invoke the same
live-sandbox reconciliation routine used by connect/open (e.g., call the
reconcile/recovery function that performs stale-registry cleanup and gateway
recovery) before calling fetchGatewayAuthTokenFromSandbox(sandboxName), retry
fetching the token after a successful reconciliation, and ensure you distinguish
transport/recovery failures from a legitimately missing token in the error
handling and messages (use the same recovery error handling logic as
connect/open).
- Around line 685-696: The browser-launch block should handle Windows builtins,
async spawn errors, and avoid exposing tokens: when determining the opener (see
variable opener) use a Windows-specific invocation (either spawn "cmd.exe" with
["/c","start","", url] or call spawn with { shell: true } on win32), attach an
'error' event handler to the child process returned by
require("child_process").spawn(...) to catch asynchronous launch failures and
report them via console.error, and in the fallback error message print the
precomputed safeUrl (the variable safeUrl that strips the fragment) instead of
the full url to avoid leaking tokens; update the child.unref() usage to still
run after attaching the handler.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 7f2ea934-df08-46a3-96f8-7266985b0dca
📒 Files selected for processing (2)
bin/lib/onboard.jsbin/nemoclaw.js
🚧 Files skipped from review as they are similar to previous changes (1)
- bin/lib/onboard.js
bin/nemoclaw.js
Outdated
| function sandboxGatewayToken(sandboxName) { | ||
| const token = fetchGatewayAuthTokenFromSandbox(sandboxName); | ||
| if (!token) { | ||
| console.error(` ${_RD}Error${R}: Could not retrieve gateway auth token from sandbox '${sandboxName}'.`); | ||
| console.error(` Make sure the sandbox is running and accessible.`); | ||
| process.exit(1); | ||
| } | ||
| // Print warning to stderr before token to stdout | ||
| console.error(` ${YW}⚠️ Treat this token like a password. Do not share it or include it in logs/screencasts.${R}`); | ||
| console.log(token); | ||
| } |
There was a problem hiding this comment.
Run the same live-sandbox reconciliation before printing a token.
Line 655 goes straight to fetchGatewayAuthTokenFromSandbox(), so gateway-token skips the stale-registry cleanup and gateway recovery that connect and open already use. Because that helper returns null for both transport failures and missing token, this command can fail after a gateway restart even when the sandbox is still recoverable.
Suggested fix
-function sandboxGatewayToken(sandboxName) {
+async function sandboxGatewayToken(sandboxName) {
+ await ensureLiveSandboxOrExit(sandboxName);
const token = fetchGatewayAuthTokenFromSandbox(sandboxName);
if (!token) {
console.error(` ${_RD}Error${R}: Could not retrieve gateway auth token from sandbox '${sandboxName}'.`);
console.error(` Make sure the sandbox is running and accessible.`);
process.exit(1);
@@
- case "gateway-token": sandboxGatewayToken(cmd); break;
+ case "gateway-token": await sandboxGatewayToken(cmd); break;Also applies to: 860-860
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@bin/nemoclaw.js` around lines 654 - 664, The sandboxGatewayToken path skips
the live-sandbox reconciliation that connect/open perform and thus can falsely
fail when the gateway is recoverable; modify sandboxGatewayToken to invoke the
same live-sandbox reconciliation routine used by connect/open (e.g., call the
reconcile/recovery function that performs stale-registry cleanup and gateway
recovery) before calling fetchGatewayAuthTokenFromSandbox(sandboxName), retry
fetching the token after a successful reconciliation, and ensure you distinguish
transport/recovery failures from a legitimately missing token in the error
handling and messages (use the same recovery error handling logic as
connect/open).
| // Open URL in default browser (platform-appropriate) | ||
| const opener = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open"; | ||
| try { | ||
| const child = require("child_process").spawn(opener, [url], { | ||
| stdio: "ignore", | ||
| detached: true, | ||
| }); | ||
| child.unref(); | ||
| } catch (_e) { | ||
| console.error(` ${_RD}Error${R}: Could not open browser. Please open this URL manually:`); | ||
| console.error(` ${url}`); | ||
| process.exit(1); |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
In Node.js child_process, does spawn("start", [url])work on Windows, or isstartonly acmd.exebuiltin that requirescmd /c start "" orshell: true? Also, are command-launch failures such as ENOENT reported via the ChildProcess error event rather than thrown synchronously?
💡 Result:
No, spawn("start", [url]) does not work on Windows because start is a cmd.exe builtin command, not a standalone executable. There is no start.exe in the PATH, so spawn (which does not use a shell by default) fails with ENOENT. To use start, you must either spawn cmd.exe explicitly with spawn("cmd.exe", ["/c", "start", "", url]) (the empty quoted string "" is needed as a window title placeholder before the URL if it has spaces), use spawn("start", [url], {shell: true}), or use exec which always spawns a shell. Command-launch failures like ENOENT are reported asynchronously via the ChildProcess error event, not thrown synchronously. The spawn function returns a ChildProcess instance immediately (even on failure to launch), and errors such as inability to find the executable are emitted on the 'error' event. Unhandled errors will crash the process with an uncaught exception, but they are not synchronous throws.
Citations:
- 1: https://nodejs.org/api/child_process.html
- 2: https://nodejs.org/docs/latest/api/child_process.html
- 3: https://stackoverflow.com/questions/60386867/node-spawn-child-process-not-working-in-windows
- 4: https://stackoverflow.com/questions/18334181/spawn-on-node-js-windows-server-2012
- 5: https://stackoverflow.com/questions/27518718/node-js-executing-command-line-opening-a-file
- 6: https://stackoverflow.com/questions/37459717/error-spawn-enoent-on-windows
- 7: https://stackoverflow.com/questions/27688804/how-do-i-debug-error-spawn-enoent-on-node-js
🏁 Script executed:
# First, let's find and read the bin/nemoclaw.js file
fd nemoclaw.js --type fRepository: NVIDIA/NemoClaw
Length of output: 74
🏁 Script executed:
# Read lines 685-696 and some surrounding context to understand the function
sed -n '670,710p' bin/nemoclaw.js | cat -nRepository: NVIDIA/NemoClaw
Length of output: 1777
🏁 Script executed:
# Search for where this function is defined and what the 'url' parameter contains
rg -B 20 'const opener = process.platform' bin/nemoclaw.jsRepository: NVIDIA/NemoClaw
Length of output: 1023
Use shell: true and cmd.exe on Windows to open URLs, add error handling for spawn failures, and print the safe URL in the fallback error message.
The code has three related issues:
-
Windows incompatibility:
spawn("start", [url])fails with ENOENT becausestartis acmd.exebuiltin, not a standalone executable. This requires eitherspawn("cmd.exe", ["/c", "start", "", url])or{shell: true}to work. -
Error handling doesn't work:
spawn()errors are emitted asynchronously on the child processerrorevent, not thrown synchronously. Thetry/catchwon't catch spawn failures, so Windows errors silently crash the process (unless an error listener is attached). Add.on('error', ...)to handle launch failures. -
Token exposure in fallback: Line 695 prints the full
urlincluding the#token=...fragment in the error message. This contradicts the deliberate secret-minimization pattern on lines 677–678 (safeUrl = url.replace(/#.*$/, "")). PrintsafeUrlinstead.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@bin/nemoclaw.js` around lines 685 - 696, The browser-launch block should
handle Windows builtins, async spawn errors, and avoid exposing tokens: when
determining the opener (see variable opener) use a Windows-specific invocation
(either spawn "cmd.exe" with ["/c","start","", url] or call spawn with { shell:
true } on win32), attach an 'error' event handler to the child process returned
by require("child_process").spawn(...) to catch asynchronous launch failures and
report them via console.error, and in the fallback error message print the
precomputed safeUrl (the variable safeUrl that strips the fragment) instead of
the full url to avoid leaking tokens; update the child.unref() usage to still
run after attaching the handler.
…t, fix Windows start - gateway-token: add ensureLiveSandboxOrExit() before fetching token (consistent with connect/open) - open: wait for port 18789 to accept connections before launching browser (TCP probe with 10s timeout) - open: fix Windows compatibility — 'start' is a cmd built-in, needs shell: true and empty title arg
|
Addressed all three review points from CodeRabbit:
|
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
bin/nemoclaw.js (2)
709-710:⚠️ Potential issue | 🟠 MajorHandle browser-launch failures on the child process, and keep the fallback URL redacted.
spawn()launch failures here are reported on the returned child’serrorevent, so the currenttry/catchwill miss missingxdg-open/open/shell failures. If the fallback ever does run, it logsurlinstead ofsafeUrl, which defeats the secret-minimization goal of this command. Attachchild.on("error", ...)and keep the manual-open message fragment-free.🛠️ Suggested fix
- try { - const child = require("child_process").spawn(opener, isWin ? ["", url] : [url], { - stdio: "ignore", - detached: true, - shell: isWin, - }); - child.unref(); - } catch (_e) { - console.error(` ${_RD}Error${R}: Could not open browser. Please open this URL manually:`); - console.error(` ${url}`); - process.exit(1); - } + const child = require("child_process").spawn(opener, isWin ? ["", url] : [url], { + stdio: "ignore", + detached: true, + shell: isWin, + }); + child.on("error", () => { + console.error(` ${_RD}Error${R}: Could not open browser. Please open this URL manually:`); + console.error(` ${safeUrl}`); + console.error(` If you need the auth token for manual login, run: nemoclaw ${sandboxName} gateway-token`); + process.exit(1); + }); + child.unref();In Node.js child_process.spawn, are launch failures like ENOENT delivered via the ChildProcess "error" event rather than thrown synchronously, and is `start` on Windows only usable through cmd/shell handling?Also applies to: 716-725
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@bin/nemoclaw.js` around lines 709 - 710, The code currently spawns a browser child and only relies on try/catch, which misses async spawn failures; attach an error handler to the returned ChildProcess (child.on("error", err => { processLogger.error(...); })) to detect ENOENT etc., and in that handler print the manual-open fallback using safeUrl (not url) so secrets remain redacted; update the spawn logic around the variable used to store the ChildProcess (the `child` returned from spawn) and ensure the existing fallback console message uses `safeUrl` and is only emitted after handling both child errors and normal exit conditions.
696-696:⚠️ Potential issue | 🟠 MajorCatch
waitForPort()timeouts and exit cleanly.If this probe times out, the rejection currently escapes the command instead of producing a normal CLI error. Wrap the
awaitintry/catchand print the same style of actionable message you use for the other sandbox failure paths.🛠️ Suggested fix
- await waitForPort(18789, 10000); + try { + await waitForPort(18789, 10000); + } catch { + console.error(` ${_RD}Error${R}: Control UI port 18789 did not become reachable for sandbox '${sandboxName}'.`); + process.exit(1); + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@bin/nemoclaw.js` at line 696, The await waitForPort(18789, 10000); call can reject on timeout; wrap it in a try/catch inside the same function that runs the sandbox startup, catch the rejection from waitForPort and call the same CLI failure handler used for other sandbox failures (print a clear actionable error message and exit with a non-zero code), e.g., mirror the style used elsewhere in this module for sandbox errors so the command exits cleanly instead of letting the rejection escape.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@bin/nemoclaw.js`:
- Around line 694-695: Before starting the OpenShell forward on port 18789, stop
any existing forward to avoid reusing a stale listener; call
runOpenshell(["forward","stop","18789", sandboxName], { ignoreError: true }) (or
equivalent) immediately before the existing
runOpenshell(["forward","start","--background","18789", sandboxName], ...) so
that runOpenshell("forward stop") is executed (ignoring errors) then
runOpenshell("forward start") to ensure the new forward is the one that binds to
127.0.0.1:18789 and prevents leaking the `#token` fragment to an unrelated
listener.
---
Duplicate comments:
In `@bin/nemoclaw.js`:
- Around line 709-710: The code currently spawns a browser child and only relies
on try/catch, which misses async spawn failures; attach an error handler to the
returned ChildProcess (child.on("error", err => { processLogger.error(...); }))
to detect ENOENT etc., and in that handler print the manual-open fallback using
safeUrl (not url) so secrets remain redacted; update the spawn logic around the
variable used to store the ChildProcess (the `child` returned from spawn) and
ensure the existing fallback console message uses `safeUrl` and is only emitted
after handling both child errors and normal exit conditions.
- Line 696: The await waitForPort(18789, 10000); call can reject on timeout;
wrap it in a try/catch inside the same function that runs the sandbox startup,
catch the rejection from waitForPort and call the same CLI failure handler used
for other sandbox failures (print a clear actionable error message and exit with
a non-zero code), e.g., mirror the style used elsewhere in this module for
sandbox errors so the command exits cleanly instead of letting the rejection
escape.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 0790694c-5097-47d8-944c-9fdd56317f9d
📒 Files selected for processing (1)
bin/nemoclaw.js
| // Start port forward and wait for it to be ready | ||
| runOpenshell(["forward", "start", "--background", "18789", sandboxName], { ignoreError: true }); |
There was a problem hiding this comment.
Stop any existing Control UI forward before reusing port 18789.
forward start --background 18789 ... is best-effort, and waitForPort() only proves that something is listening on that port. If a stale OpenShell forward or another local service already owns 127.0.0.1:18789, this command can open the wrong page and expose the #token=... fragment to that page’s JavaScript. bin/lib/onboard.js:1578-1582 already does a forward stop first for this exact case.
🛠️ Suggested fix
async function sandboxOpen(sandboxName) {
await ensureLiveSandboxOrExit(sandboxName);
// Start port forward and wait for it to be ready
+ runOpenshell(["forward", "stop", "18789"], { ignoreError: true });
runOpenshell(["forward", "start", "--background", "18789", sandboxName], { ignoreError: true });
await waitForPort(18789, 10000);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Start port forward and wait for it to be ready | |
| runOpenshell(["forward", "start", "--background", "18789", sandboxName], { ignoreError: true }); | |
| async function sandboxOpen(sandboxName) { | |
| await ensureLiveSandboxOrExit(sandboxName); | |
| // Start port forward and wait for it to be ready | |
| runOpenshell(["forward", "stop", "18789"], { ignoreError: true }); | |
| runOpenshell(["forward", "start", "--background", "18789", sandboxName], { ignoreError: true }); | |
| await waitForPort(18789, 10000); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@bin/nemoclaw.js` around lines 694 - 695, Before starting the OpenShell
forward on port 18789, stop any existing forward to avoid reusing a stale
listener; call runOpenshell(["forward","stop","18789", sandboxName], {
ignoreError: true }) (or equivalent) immediately before the existing
runOpenshell(["forward","start","--background","18789", sandboxName], ...) so
that runOpenshell("forward stop") is executed (ignoring errors) then
runOpenshell("forward start") to ensure the new forward is the one that binds to
127.0.0.1:18789 and prevents leaking the `#token` fragment to an unrelated
listener.
Summary
Adds
nemoclaw <name> gateway-tokencommand that prints the gateway auth token for a sandbox, replacing the multi-step workaround documented in #938.Before (workaround)
After
Changes
bin/nemoclaw.js: AddedsandboxGatewayToken()function andgateway-tokenactionopenclaw.jsonfrom sandbox viaopenshell sandbox downloadto a temp directorygateway.auth.tokenand prints to stdout (pipe-friendly)Usage
Design Notes
openshell sandbox download+ temp directory + cleanup pattern used byfetchGatewayAuthTokenFromSandbox()in onboard.js$()nemoclaw helpunder Sandbox ManagementCloses #938
Signed-off-by: Kagura kagura-agent@users.noreply.github.com
Summary by CodeRabbit
New Features
gateway-tokenCLI action to fetch and print a sandbox auth token (token to stdout; warnings to stderr; exits on failure).openCLI action to verify a sandbox, ensure local port forwarding, obtain an auth token, derive a Control UI URL (non-token URL logged) and open it in the default browser.Other