Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
8183149
fix(sandbox): give each Hermes sandbox its own OpenAI-compatible API …
laitingsheng Aug 7, 2026
0595532
fix(sandbox): resolve the Hermes API port for every host-side consumer
laitingsheng Aug 7, 2026
525b49e
Merge remote-tracking branch 'origin/main' into fix/hermes-api-port-p…
laitingsheng Aug 7, 2026
d1232f9
merge: resolve conflicts with main
github-actions[bot] Aug 7, 2026
fe72ed9
Merge remote-tracking branch 'origin/main' into fix/hermes-api-port-p…
laitingsheng Aug 9, 2026
38d5b86
Merge branch 'main' into fix/hermes-api-port-per-sandbox
cv Aug 9, 2026
cc177e7
fix(sandbox): keep the managed startup profile schema dependency-free
laitingsheng Aug 9, 2026
159b667
fix(sandbox): announce the sandbox API port from an API-kind dashboard
laitingsheng Aug 9, 2026
9120850
fix(sandbox): publish the Hermes API port in the same-uid topology
laitingsheng Aug 9, 2026
055fcc3
docs(sandbox): correct the Hermes API port override consequence
laitingsheng Aug 9, 2026
1b819cc
Merge remote-tracking branch 'origin/fix/hermes-api-port-per-sandbox'…
laitingsheng Aug 9, 2026
e929e2d
fix(sandbox): harden Hermes API port resolution
cv Aug 9, 2026
717a9c8
fix(sandbox): remove unused gateway import
cv Aug 9, 2026
a1ebcd1
test(installer): cover registered Hermes forward restore
cv Aug 9, 2026
d925fc8
docs(sandbox): correct the Hermes API port recreate remedy
laitingsheng Aug 9, 2026
a96a0e1
test(sandbox): cover Hermes port trust boundaries
cv Aug 9, 2026
b7502f7
test(sandbox): group Hermes probe coverage
cv Aug 9, 2026
a400cf2
Merge remote-tracking branch 'origin/fix/hermes-api-port-per-sandbox'…
laitingsheng Aug 9, 2026
bf43b74
Merge remote-tracking branch 'origin/fix/hermes-api-port-per-sandbox'…
laitingsheng Aug 9, 2026
3306685
Merge remote-tracking branch 'origin/main' into fix/hermes-api-port-p…
laitingsheng Aug 9, 2026
5a02138
test(hermes): cover the API port marker reader and resolver
laitingsheng Aug 9, 2026
e869820
test(sandbox): cover Hermes API range exhaustion before forced restore
laitingsheng Aug 9, 2026
ca256de
fix(docs): keep the headless token unset contract intact
laitingsheng Aug 9, 2026
98d4595
test(hermes): prove the marker safety checks and probe failure path
laitingsheng Aug 9, 2026
e46d21f
Merge branch 'main' into fix/hermes-api-port-per-sandbox
cv Aug 9, 2026
279f2f4
merge: resolve conflicts with main
github-actions[bot] Aug 10, 2026
9f3c07e
merge: resolve conflicts with main
github-actions[bot] Aug 10, 2026
7449608
fix(hermes): complete API port readiness gates
apurvvkumaria Aug 11, 2026
005c28c
merge: update #8577 with main
apurvvkumaria Aug 11, 2026
b0ff41f
test(hermes): close API port review gaps
prekshivyas Aug 11, 2026
e839d4a
test(e2e): preserve activation failure logs
apurvvkumaria Aug 11, 2026
3205b85
test(e2e): restrict activation failure artifacts
apurvvkumaria Aug 11, 2026
016ab43
Merge remote-tracking branch 'origin/main' into codex/fix-pr-8577
prekshivyas Aug 11, 2026
fc653dc
Merge remote-tracking branch 'origin/main' into codex/fix-pr-8577
prekshivyas Aug 11, 2026
1bdb0b0
Merge remote-tracking branch 'origin/main' into codex/fix-pr-8577
prekshivyas Aug 11, 2026
bac2935
merge: resolve conflicts with main
github-actions[bot] Aug 12, 2026
a3fc7ae
Merge remote-tracking branch 'origin/main' into fix/hermes-api-port-p…
laitingsheng Aug 12, 2026
490d916
docs(hermes): state the per-sandbox API port conditions
laitingsheng Aug 12, 2026
a5fd737
chore(sandbox): format the Hermes gateway log tail command
laitingsheng Aug 12, 2026
b69119a
Merge remote-tracking branch 'origin/main' into fix/hermes-api-port-p…
laitingsheng Aug 12, 2026
ab43d9d
test(sandbox): extract the gateway launch process helper
laitingsheng Aug 12, 2026
e7febca
fix(hermes): restore the runtime directory trust boundary
laitingsheng Aug 12, 2026
9a68d40
test(e2e): capture container logs through the audited shell probe
laitingsheng Aug 12, 2026
6234714
docs(hermes): state the API port override range
laitingsheng Aug 12, 2026
f85744f
merge: resolve conflicts with main
github-actions[bot] Aug 12, 2026
9675497
docs(e2e): clarify shell probe persistence
apurvvkumaria Aug 12, 2026
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
36 changes: 30 additions & 6 deletions agents/hermes/mcp-config-transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
STRICT_HASH_PATH = "/etc/nemoclaw/hermes.config-hash"
GUARD_PATH = "/usr/local/lib/nemoclaw/hermes-runtime-config-guard.py"
ROOT_LIFECYCLE_MARKER = "/run/nemoclaw/hermes-root-lifecycle"
GATEWAY_PUBLIC_PORT_PATH = "/run/nemoclaw/hermes-api-port"
SERVICE_MANAGER_PATH = b"/usr/local/bin/nemoclaw-start"
RELOAD_TIMEOUT_SECONDS = 300
SERVER_NAME_RE = re.compile(r"^[A-Za-z][A-Za-z0-9_-]{0,63}$")
Expand Down Expand Up @@ -94,7 +95,30 @@
MAX_GATEWAY_PID_RECORD_BYTES = 4096
MCP_RACE_RECOVERY_ATTEMPTS = 3
GATEWAY_INTERNAL_PORT = 18642
GATEWAY_PUBLIC_PORT = 8642


def _gateway_public_port() -> int:
"""Resolve the per-sandbox port the socat relay exposes the API on.

NemoClaw allocates this port per sandbox so two Hermes sandboxes can serve
inference on one host. The value reaches the entrypoint through the
supervisor environment, which this one-shot exec does not inherit, so the
entrypoint publishes it as a root-owned read-only marker. Reading anything
the sandbox user can write would let the agent redirect the relay probe.
A sandbox whose image predates the marker has none and keeps the original
port, which the sandbox user can bind.
"""
try:
raw = Path(GATEWAY_PUBLIC_PORT_PATH).read_text(encoding="utf-8").strip()
except OSError:
return 8642
if not raw.isdigit():
return 8642
port = int(raw)
return port if 1024 <= port <= 65535 else 8642


GATEWAY_PUBLIC_PORT = _gateway_public_port()
TRUSTED_HERMES_GATEWAY_LAUNCHERS = {
b"/usr/local/bin/hermes.real",
b"/usr/local/lib/nemoclaw/hermes",
Expand Down Expand Up @@ -981,12 +1005,12 @@ def probe_timeout() -> float:
if internal_timeout <= 0 or not _gateway_health_endpoint_ready(
GATEWAY_INTERNAL_PORT, internal_timeout
):
return False, "waiting-for-internal-health-on-18642"
return False, "waiting-for-internal-health"
public_timeout = probe_timeout()
if public_timeout <= 0 or not _gateway_health_endpoint_ready(
GATEWAY_PUBLIC_PORT, public_timeout
):
return False, "waiting-for-public-relay-health-on-8642"
return False, "waiting-for-public-relay-health"
return True, "waiting-for-stable-replacement-identity"


Expand All @@ -1012,8 +1036,8 @@ def reload_gateway() -> bool:
re_kick_sent = False
phase_order = {
"waiting-for-replacement-identity": 0,
"waiting-for-internal-health-on-18642": 1,
"waiting-for-public-relay-health-on-8642": 2,
"waiting-for-internal-health": 1,
"waiting-for-public-relay-health": 2,
"waiting-for-stable-replacement-identity": 3,
}
last_safe_phase = "waiting-for-replacement-identity"
Expand Down Expand Up @@ -1053,7 +1077,7 @@ def reload_gateway() -> bool:
# The managed supervisor owns the public socat relay. Once the
# replacement gateway is internally healthy, another gateway
# signal cannot repair that relay and only creates crash churn.
and observed_phase != "waiting-for-public-relay-health-on-8642"
and observed_phase != "waiting-for-public-relay-health"
and current is not None
and _gateway_has_managed_parent(current[0])
and _gateway_identity() == current
Expand Down
24 changes: 22 additions & 2 deletions agents/hermes/plugin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1032,6 +1032,25 @@ def _load_hermes_config():
return None


def _hermes_api_port():
"""Read the per-sandbox port the OpenAI-compatible API is exposed on.

NemoClaw allocates this port per sandbox so two Hermes sandboxes can serve
inference on one host, and the entrypoint publishes it as a root-owned
read-only marker. A sandbox whose image predates the marker has none and
keeps the original port, which the sandbox user can bind.
"""
try:
with open("/run/nemoclaw/hermes-api-port") as f:
raw = f.read().strip()
except OSError:
return 8642
if not raw.isdigit():
return 8642
port = int(raw)
return port if 1024 <= port <= 65535 else 8642
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated


def _get_sandbox_info():
"""Gather sandbox status information."""
hermes_cfg = _load_hermes_config()
Expand All @@ -1052,10 +1071,11 @@ def _get_sandbox_info():
provider = nemoclaw_cfg.get("provider", provider)

# Check gateway health
api_port = _hermes_api_port()
gateway_ok = False
try:
result = subprocess.run(
["curl", "-sf", "http://localhost:8642/health"],
["curl", "-sf", f"http://localhost:{api_port}/health"],
capture_output=True,
text=True,
timeout=5,
Expand All @@ -1072,7 +1092,7 @@ def _get_sandbox_info():
"provider": provider,
"base_url": base_url,
"gateway": "running" if gateway_ok else "stopped",
"port": 8642,
"port": api_port,
}


Expand Down
36 changes: 33 additions & 3 deletions agents/hermes/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,30 @@ else
fi
fi

if [ "$_dashboard_port" -eq 8642 ]; then
echo "[SECURITY] Invalid Hermes dashboard port 8642 - reserved for the Hermes OpenAI-compatible API" >&2
# The API port is a per-sandbox host resource: the host forwards the same
# number it is exposed on here, so two sandboxes on one host need two values.
# NemoClaw allocates the port and passes it in; the default keeps a sandbox
# whose create environment carries no value on the original port.
_api_port_raw="${NEMOCLAW_HERMES_API_PORT:-}"
if [ -z "$_api_port_raw" ]; then
PUBLIC_PORT=8642
else
PUBLIC_PORT="$(printf '%s' "$_api_port_raw" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
_api_port_valid=1
case "$PUBLIC_PORT" in
*[!0-9]* | '') _api_port_valid=0 ;;
esac
if [ "$_api_port_valid" -eq 1 ] && { [ "$PUBLIC_PORT" -lt 1024 ] || [ "$PUBLIC_PORT" -gt 65535 ]; }; then
_api_port_valid=0
fi
if [ "$_api_port_valid" -ne 1 ]; then
echo "[SECURITY] Invalid NEMOCLAW_HERMES_API_PORT='${NEMOCLAW_HERMES_API_PORT}' - must be an integer between 1024 and 65535" >&2
exit 1
fi
fi

if [ "$_dashboard_port" -eq "$PUBLIC_PORT" ]; then
echo "[SECURITY] Invalid Hermes dashboard port ${PUBLIC_PORT} - reserved for the Hermes OpenAI-compatible API" >&2
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
exit 1
fi

Expand All @@ -181,7 +203,6 @@ else
CHAT_UI_URL="${CHAT_UI_URL:-http://127.0.0.1:${_dashboard_port}}"
fi

PUBLIC_PORT=8642
# Hermes binds the API server to 127.0.0.1. Run it on an internal port and
# use socat to expose the OpenAI-compatible API on PUBLIC_PORT.
INTERNAL_PORT=18642
Expand Down Expand Up @@ -3290,6 +3311,15 @@ printf '%s\n' 'root-separated' >/run/nemoclaw/hermes-root-lifecycle
chown root:root /run/nemoclaw/hermes-root-lifecycle
chmod 0444 /run/nemoclaw/hermes-root-lifecycle

# SECURITY: publish the resolved API port as a root-owned read-only marker.
# The port reaches this entrypoint through the supervisor's environment, which
# a later one-shot `openshell sandbox exec` does not inherit. Helpers that probe
# the public relay read this marker instead. It must stay unwritable by the
# sandbox user, or the agent could redirect those probes at another port.
printf '%s\n' "$PUBLIC_PORT" >/run/nemoclaw/hermes-api-port
chown root:root /run/nemoclaw/hermes-api-port
chmod 0444 /run/nemoclaw/hermes-api-port

# SECURITY: Protect gateway log from sandbox user tampering
prepare_restricted_log /tmp/gateway.log gateway:gateway 600

Expand Down
8 changes: 4 additions & 4 deletions ci/source-architecture-budget.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"src/lib/cli/nemoclaw-oclif-command.ts": 106,
"src/lib/cli/terminal-style.ts": 44,
"src/lib/core/json-types.ts": 37,
"src/lib/core/ports.ts": 87,
"src/lib/core/ports.ts": 89,
"src/lib/core/shell-quote.ts": 26,
"src/lib/core/url-utils.ts": 28,
"src/lib/core/wait.ts": 35,
Expand All @@ -27,7 +27,7 @@
"src/lib/runner.ts": 88,
"src/lib/security/redact.ts": 51,
"src/lib/state/onboard-session.ts": 36,
"src/lib/state/registry.ts": 97,
"src/lib/state/registry.ts": 99,
"src/lib/state/state-root.ts": 21,
"src/lib/subprocess-env.ts": 24,
"src/lib/validation.ts": 25
Expand All @@ -43,7 +43,7 @@
"src/lib/actions/sandbox/policy-channel.ts": 29,
"src/lib/actions/sandbox/process-recovery.ts": 21,
"src/lib/actions/sandbox/rebuild-pipeline.ts": 28,
"src/lib/actions/sandbox/snapshot.ts": 39,
"src/lib/actions/sandbox/snapshot.ts": 40,
"src/lib/actions/uninstall/run-plan.ts": 26,
"src/lib/inference/onboard-probes.ts": 20,
"src/lib/inference/vllm.ts": 21,
Expand All @@ -55,7 +55,7 @@
},
"allowedCycles": [],
"maxRootFiles": {
"src/lib/onboard": 307,
"src/lib/onboard": 308,
"src/lib/actions": 19,
"src/lib/actions/sandbox": 182,
"src/lib/state": 37,
Expand Down
2 changes: 1 addition & 1 deletion docs/deployment/deploy-to-headless-server.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ Print the Hermes dashboard URL:
$$nemoclaw headless-agent dashboard-url --quiet
```

The Hermes OpenAI-compatible API uses the loopback forward on port `8642`.
The Hermes OpenAI-compatible API uses the loopback forward on the sandbox's API port, `8642` for the first Hermes sandbox on a host.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
For a Hermes sandbox, `gateway-token` is agent-aware and retrieves `API_SERVER_KEY` through the registered `bearer_token` web-auth contract.
Use it as a bearer token, then clear the shell variable:

Expand Down
2 changes: 1 addition & 1 deletion docs/deployment/set-up-mcp-bridge.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ The validated transaction helper runs as a one-shot ordinary `openshell sandbox
It runs as the normal sandbox identity, rejects the legacy root-separated runtime topology, validates the gateway PID and launcher before signaling it, updates the managed compatibility hash, verifies loopback health, and rolls back config and hashes if reload fails.

Within the five-minute reload deadline, if the first signal has not converged after half the budget, the helper may send one additional `SIGUSR1` only after revalidating the gateway identity and managed parent.
Success requires a replacement gateway identity, healthy loopback endpoints on internal port `18642` and public port `8642`, and a stable final identity.
Success requires a replacement gateway identity, healthy loopback endpoints on internal port `18642` and the sandbox's public API port, and a stable final identity.

There is no host listener, persistent control socket, MCP relay, or service for this operation.
The command carries no MCP traffic or raw service credential, and its payload contains only the endpoint definition and OpenShell placeholder.
Expand Down
4 changes: 4 additions & 0 deletions docs/get-started/quickstart-hermes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ Use these details when your first-run path needs more control.

<Accordion title="Use the dashboard and API remotely">
Hermes forwards its dashboard on port `18789` and its OpenAI-compatible API on port `8642`.
Both ports belong to one sandbox.
A second Hermes sandbox on the same host receives the next free port in each range.
Run `openshell forward list` to read the host bind for each of that sandbox's forwards.
For a remote dashboard origin or tunnel, set `CHAT_UI_URL` to the externally reachable dashboard origin before onboarding.

```bash
Expand Down Expand Up @@ -242,6 +245,7 @@ Use these details when your first-run path needs more control.
This verification reports a warning instead of aborting onboarding when the configuration or egress path needs attention.

Hermes exposes its browser dashboard on port `18789` and forwards its OpenAI-compatible API on port `8642` for local clients.
A sandbox that finds either port taken receives the next free one, so the examples below use the first sandbox's ports.
The dashboard assets are built into the sandbox image, so the dashboard starts without running `npm` as the sandbox user under `/opt/hermes`.
Dashboard chat uses the prebuilt `/opt/hermes/ui-tui` bundle.
To recover the dashboard manually, use `hermes dashboard --tui --skip-build` so recovery does not try to rebuild assets under root-owned installation paths.
Expand Down
13 changes: 9 additions & 4 deletions docs/reference/commands.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -700,7 +700,7 @@ Use `--control-ui-port <N>` to choose the host dashboard port for a sandbox.
The value must be an integer from `1024` through `65535`.
This flag takes precedence over `CHAT_UI_URL`, `NEMOCLAW_DASHBOARD_PORT`, the previous registry value, and the default port.

For Hermes sandboxes, do not use port `8642`; NemoClaw reserves it for the Hermes OpenAI-compatible API and rejects it as a dashboard port before sandbox creation.
For Hermes sandboxes, do not use a port from `8642`-`8652`; NemoClaw allocates each sandbox's OpenAI-compatible API port from that range and rejects every port in it as a dashboard port before sandbox creation.

If you enable Slack during onboarding, the wizard collects both the Bot Token (`SLACK_BOT_TOKEN`) and the App-Level Token (`SLACK_APP_TOKEN`).
Socket Mode requires both tokens.
Expand Down Expand Up @@ -1213,6 +1213,8 @@ Host-side validation runs before the sandbox dispatch:

The `agent` wrapper rejects Hermes sandboxes with guidance for the Hermes HTTP API.
Hermes sandboxes expose an OpenAI-compatible API on port `8642` inside the sandbox, so non-interactive use does not need a wrapper command.
A second Hermes sandbox on the same host receives the next free port from `8642` through `8652`.
Run `openshell forward list` to read the host bind for each of that sandbox's forwards.

Forward the port and POST chat completions directly:

Expand Down Expand Up @@ -1913,8 +1915,8 @@ nemohermes my-assistant dashboard-url
nemohermes my-assistant dashboard-url --quiet
```

The Hermes OpenAI-compatible API remains separate on port `8642` and uses `/v1` for OpenAI-compatible clients.
Use `nemohermes my-assistant status` to see both the dashboard and API endpoints.
The Hermes OpenAI-compatible API is separate and serves `/v1` on a per-sandbox port that defaults to `8642`.
Use `openshell forward list` to see the host bind for the dashboard and API forwards.

</AgentOnly>
<AgentOnly variant="deepagents">
Expand Down Expand Up @@ -4160,12 +4162,15 @@ For OpenClaw, `NEMOCLAW_DASHBOARD_PORT` controls the OpenClaw dashboard forward.
<AgentOnly variant="hermes">

For Hermes, `NEMOCLAW_DASHBOARD_PORT` controls the built-in dashboard forward, which defaults to `18789`.
The Hermes OpenAI-compatible API remains separate on port `8642` and uses `/v1` for API clients.
The OpenAI-compatible API is separate and serves `/v1` on a per-sandbox port that defaults to `8642`.
If `8642` is already occupied by another sandbox, onboarding scans `8642` through `8652` and uses the next free API port.
Set `NEMOCLAW_HERMES_API_PORT=<N>` to require a specific port.
Set `NEMOCLAW_HERMES_DASHBOARD_TUI=1` only when you want Hermes' optional in-browser TUI tab.

| Variable | Default | Service |
|----------|---------|---------|
| `NEMOCLAW_DASHBOARD_PORT` | 18789 | Hermes built-in dashboard forward port |
| `NEMOCLAW_HERMES_API_PORT` | 8642 | Hermes OpenAI-compatible API forward port |
| `NEMOCLAW_HERMES_DASHBOARD_TUI` | 0 | Optional Hermes in-browser TUI tab |

</AgentOnly>
Expand Down
9 changes: 7 additions & 2 deletions docs/reference/troubleshooting.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3129,7 +3129,8 @@ After the rebuild, make the intended change through a supported command such as

### Port 8642 in a browser shows a blank page or `Cannot GET /`

`nemohermes onboard` forwards port `8642`, but Hermes serves an OpenAI-compatible API at that port, not a chat dashboard.
`nemohermes onboard` forwards the sandbox's API port, which is `8642` for the first Hermes sandbox on a host.
Hermes serves an OpenAI-compatible API at that port, not a chat dashboard.
A browser visit to `http://127.0.0.1:8642/` (or any non-API path) returns nothing renderable.

Confirm the agent is healthy with the API health endpoint instead:
Expand Down Expand Up @@ -3159,7 +3160,11 @@ openshell forward list # shows the host bind for each forw
curl -sf http://127.0.0.1:8642/health # confirms the relayed endpoint answers
```

If `openshell forward list` does not show port `8642`, run `nemohermes <name> connect --probe-only` (or `nemohermes <name> recover`) to ask the recovery path to re-establish every manifest-declared agent forward port that has gone missing.
If `openshell forward list` does not show the sandbox's API port, run `nemohermes <name> connect --probe-only` (or `nemohermes <name> recover`) to ask the recovery path to re-establish every manifest-declared agent forward port that has gone missing.
Recovery targets each sandbox's own ports.
A second Hermes sandbox on the same host receives the next free API port, so check which sandbox owns each row before assuming a missing `8642` row belongs to the sandbox you are debugging.
A Hermes sandbox onboarded before the API port became per-sandbox carries no allocated port and keeps `8642`.
Run `nemohermes <name> destroy` and onboard the sandbox again when a second Hermes sandbox needs its own API forward, because NemoClaw allocates a port only for a sandbox that has no registry row.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

### `nemohermes` reports `Sandbox 'X' already exists as OpenClaw`

Expand Down
24 changes: 23 additions & 1 deletion scripts/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -462,14 +462,36 @@ resolve_onboarded_agent() {
fi
}

# Read the API port the named sandbox was registered with. Each Hermes sandbox
# allocates its own, so the forward must target this sandbox's port rather than
# the default a sibling sandbox may already hold.
resolve_hermes_api_port() {
local registry_file
registry_file="$(nemoclaw_state_dir)/sandboxes.json"
if [[ -f "$registry_file" ]] && command_exists node; then
node -e '
const fs = require("fs");
try {
const data = JSON.parse(fs.readFileSync(process.argv[1], "utf8"));
const entry = (data.sandboxes || {})[process.argv[2]] || {};
const port = entry.hermesApiPort;
const valid = Number.isInteger(port) && port >= 1024 && port <= 65535;
process.stdout.write(String(valid ? port : 8642));
} catch { process.stdout.write("8642"); }
' "$registry_file" "$1" 2>/dev/null || printf "8642"
else
printf "8642"
fi
}

restore_onboard_forward_after_post_checks() {
local sandbox_name agent_name agent_display port openshell_bin openshell_dir attempt selected_state_dir state_dir pid_file watcher_script watcher_pid
sandbox_name="$(resolve_default_sandbox_name)"
agent_name="$(resolve_onboarded_agent)"
agent_display="$(agent_display_name "$agent_name")"

case "$agent_name" in
hermes) port=8642 ;;
hermes) port="$(resolve_hermes_api_port "$sandbox_name")" ;;
*) return 0 ;;
esac

Expand Down
Loading
Loading