feat(hermes): expose built-in dashboard#4811
Conversation
Signed-off-by: Shannon Sands <shannon.sands.1979@gmail.com>
Signed-off-by: Shannon Sands <shannon.sands.1979@gmail.com>
|
Ready to act? Review this PR in Change Stack to turn feedback into patch suggestions you can inspect and refine. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Enterprise Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe PR makes the Hermes browser dashboard the primary surfaced endpoint (port 18789): it adds manifest dashboard auth/health fields, ensures web assets at image-build time, refactors start.sh for dashboard port/forward/socat lifecycle, splits dashboard vs gateway health probes, updates onboarding/CLI URL/token flows, and aligns tests/docs. ChangesHermes dashboard transition
Sequence Diagram(s)sequenceDiagram
participant OnboardFinalization
participant buildChain
participant verifyDeployment
participant SandboxGateway
participant HostDashboardProbe
OnboardFinalization->>buildChain: pass dashboardHealthEndpoint, gatewayPort, gatewayHealthEndpoint
buildChain->>verifyDeployment: return expanded delivery chain
verifyDeployment->>SandboxGateway: curl gatewayPort + gatewayHealthEndpoint
verifyDeployment->>HostDashboardProbe: probe dashboard public port + dashboardHealthEndpoint
sequenceDiagram
participant User
participant start_sh
participant dashboard_proc
participant socat_forwarder
User->>start_sh: set CHAT_UI_URL / NEMOCLAW_DASHBOARD_PORT
start_sh->>start_sh: derive DASHBOARD_PUBLIC_PORT / INTERNAL_PORT
start_sh->>dashboard_proc: start Hermes dashboard bind INTERNAL_PORT
start_sh->>socat_forwarder: start_socat_forwarder PUBLIC→INTERNAL (capture PID)
start_sh->>start_sh: print Dashboard URL and API URL
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Suggested labels
Suggested reviewers
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 docstrings
🧪 Generate unit tests (beta)
Comment |
|
🌿 Preview your docs: https://nvidia-preview-pr-4811.docs.buildwithfern.com/nemoclaw |
| *socat*"TCP-LISTEN:${dashboard_public}"*"TCP:127.0.0.1:${dashboard_internal}"*) | ||
| echo "[gateway] Removing orphaned dashboard socat forwarder for ${dashboard_public}->${dashboard_internal} (pid ${pid})" >&2 | ||
| *socat*"TCP-LISTEN:${dashboard_public_port}"*"TCP:127.0.0.1:${dashboard_internal_port}"*) | ||
| [ -n "$dashboard_public_port" ] && [ -n "$dashboard_internal_port" ] || continue |
E2E Advisor RecommendationRequired E2E: Dispatch hint: Auto-dispatched E2E: Full advisor summaryE2E Recommendation AdvisorBase: Required E2E
Optional E2E
New E2E recommendations
Dispatch hint
|
E2E Scenario Advisor RecommendationRequired scenario E2E: Dispatch required scenario E2E:
Full scenario advisor summaryE2E Scenario AdvisorBase: Required scenario E2E
Optional scenario E2E
Relevant changed files
|
PR Review AdvisorFindings: 1 needs attention, 12 worth checking, 0 nice ideas Review findings🛠️ Needs attention
🔎 Worth checking
🌱 Nice ideas
Consider writing more tests for
Since last review detailsCurrent findings:
This is an automated advisory review. A human maintainer must make the final merge decision. |
Selective E2E Results — ❌ Some jobs failedRun: 27001798205
|
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (2)
docs/get-started/quickstart-hermes.mdx (1)
101-105: ⚡ Quick winUse active voice in the dashboard asset sentence.
Line 104 is passive (“are built into”). Please rewrite it in active voice to match the docs style requirements.
As per coding guidelines, "Active voice required. Flag passive constructions."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/get-started/quickstart-hermes.mdx` around lines 101 - 105, Rewrite the passive sentence about dashboard assets into active voice: change "The Hermes dashboard assets are built into the sandbox image, so the dashboard starts without running `npm` as the sandbox user under `/opt/hermes`." to an active construction such as "NemoClaw builds the Hermes dashboard assets into the sandbox image, so the dashboard starts without running `npm` as the sandbox user under `/opt/hermes`." Ensure the subject (e.g., NemoClaw) performs the action to match the docs' active-voice guideline and keep the rest of the sentence unchanged.src/lib/verify-deployment.test.ts (1)
58-59: ⚡ Quick winReduce order-coupling in probe assertions.
Line 58 and Line 59 assume probe call order. This can fail on harmless internal reordering. Prefer existence-based checks.
Suggested diff
- expect(sandboxScripts[0]).toContain("http://127.0.0.1:8642/health"); - expect(hostProbes[0]).toEqual({ port: 18789, path: "/api/status" }); + expect( + sandboxScripts.some((script) => script.includes("http://127.0.0.1:8642/health")), + ).toBe(true); + expect(hostProbes).toContainEqual({ port: 18789, path: "/api/status" });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/lib/verify-deployment.test.ts` around lines 58 - 59, The assertions currently rely on probe call order (sandboxScripts[0] and hostProbes[0]) which is fragile; change them to existence-based checks by asserting the sandboxScripts array contains the health URL (e.g., use a contains/arrayContaining style check against "http://127.0.0.1:8642/health") and assert hostProbes contains the probe object (use a contains/containEqual/arrayContaining check for { port: 18789, path: "/api/status" }) so the test no longer depends on element ordering.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/reference/commands.mdx`:
- Around line 656-659: The docs currently contradict about Hermes and the
dashboard-url: update the Hermes-specific section to match the earlier statement
that agent dashboards managing their own session (e.g., "Hermes Agent") will
print a plain dashboard URL; specifically edit the Hermes section text that
currently says "`dashboard-url` is not applicable to Hermes" to instead state
that `dashboard-url` is applicable and returns the plain dashboard URL for
Hermes Agent (mentioning "Hermes Agent" and the `dashboard-url` command so
readers can locate the paragraph), or alternatively change the earlier general
sentence to say Hermes is not applicable—ensure both occurrences use the same
behavior and wording.
In `@src/lib/dashboard/contract.ts`:
- Around line 69-81: In normalizeEndpointPath change the URL-handling branch so
that when new URL(raw).pathname is obtained you return that pathname even if
it's "/" (i.e. remove the special-case that rejects "/"); specifically update
the condition inside normalizeEndpointPath (the branch that calls new
URL(raw).pathname) to return path when it exists (or is defined) rather than
only when path !== "/", ensuring URL-like inputs such as "http://127.0.0.1/"
produce "/" instead of "/http://127.0.0.1/".
In `@src/lib/onboard/dashboard.ts`:
- Around line 243-246: The call to runCaptureOpenshell is normalizing a null
ownership-failure signal into an empty string, hiding failures; change the
wrapper passed to bestEffortForwardStopForSandbox so that if
deps.runCaptureOpenshell(...) returns null you throw an error (preserving the
failure), otherwise return the string — i.e., in the callback used by
bestEffortForwardStopForSandbox (the one now doing (args, opts) =>
(deps.runCaptureOpenshell(args, opts) ?? "") as string) detect a null result and
throw a descriptive Error instead of converting to "", so ownership lookup
failures still trigger the stop-path behavior.
In `@test/sandbox-provisioning.test.ts`:
- Around line 1001-1003: The npm stub inside the runLoggedDockerShell invocation
uses $hermes_web_dist which is unbound under set -u; change the stub to guard
against unset by using parameter expansion and a non-empty check, e.g. replace
the mkdir line with something like: if [ -n "${hermes_web_dist:-}" ] && [
"${1:-}" = "run" ] && [ "${2:-}" = "build" ]; then mkdir -p "$hermes_web_dist";
fi so the code in the runLoggedDockerShell call (the npm() { ... } stub) never
references an unbound variable.
---
Nitpick comments:
In `@docs/get-started/quickstart-hermes.mdx`:
- Around line 101-105: Rewrite the passive sentence about dashboard assets into
active voice: change "The Hermes dashboard assets are built into the sandbox
image, so the dashboard starts without running `npm` as the sandbox user under
`/opt/hermes`." to an active construction such as "NemoClaw builds the Hermes
dashboard assets into the sandbox image, so the dashboard starts without running
`npm` as the sandbox user under `/opt/hermes`." Ensure the subject (e.g.,
NemoClaw) performs the action to match the docs' active-voice guideline and keep
the rest of the sentence unchanged.
In `@src/lib/verify-deployment.test.ts`:
- Around line 58-59: The assertions currently rely on probe call order
(sandboxScripts[0] and hostProbes[0]) which is fragile; change them to
existence-based checks by asserting the sandboxScripts array contains the health
URL (e.g., use a contains/arrayContaining style check against
"http://127.0.0.1:8642/health") and assert hostProbes contains the probe object
(use a contains/containEqual/arrayContaining check for { port: 18789, path:
"/api/status" }) so the test no longer depends on element ordering.
🪄 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: Enterprise
Run ID: 2f41ecca-f3a6-4460-9622-55cc0c38898b
📒 Files selected for processing (25)
agents/hermes/Dockerfileagents/hermes/Dockerfile.baseagents/hermes/manifest.yamlagents/hermes/start.shdocs/get-started/quickstart-hermes.mdxdocs/reference/commands.mdxsrc/commands/sandbox/dashboard-url.tssrc/lib/agent/base-image.test.tssrc/lib/agent/defs.test.tssrc/lib/agent/defs.tssrc/lib/agent/onboard.test.tssrc/lib/agent/onboard.tssrc/lib/agent/runtime.test.tssrc/lib/dashboard-url-command.test.tssrc/lib/dashboard-url-command.tssrc/lib/dashboard/contract.test.tssrc/lib/dashboard/contract.tssrc/lib/onboard.tssrc/lib/onboard/dashboard.tssrc/lib/verify-deployment.test.tssrc/lib/verify-deployment.tstest/hermes-start.test.tstest/onboard-dashboard.test.tstest/onboard.test.tstest/sandbox-provisioning.test.ts
|
Addressed the review-advisor feedback in f01d1dc:
Validation after changes:
I also re-ran the one |
Selective E2E Results — ❌ Some jobs failedRun: 27002940784
|
Signed-off-by: Carlos Villela <cvillela@nvidia.com>
Selective E2E Results — ❌ Some jobs failedRun: 27041711625
|
Signed-off-by: Carlos Villela <cvillela@nvidia.com>
Selective E2E Results — ❌ Some jobs failedRun: 27041868556
|
Selective E2E Results — ✅ All requested jobs passedRun: 27041994567
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/nightly-e2e.yaml:
- Line 2540: The github-script step uses template-expanded `${{
toJSON(inputs.jobs) }}` inside the JS body causing injection warnings; change to
pass inputs.jobs via the step's env (e.g., set env.REQUESTED_JOBS: ${{
toJSON(inputs.jobs) }}) and update the script to read process.env.REQUESTED_JOBS
when computing requestedJobsRaw (and similarly update the separate "Post E2E
results to PR" github-script step to use
env.REQUESTED_JOBS/process.env.REQUESTED_JOBS instead of `${{
toJSON(inputs.jobs) }}`) so both places (the step creating requestedJobsRaw and
the "Post E2E results to PR" step) avoid template expansion in the script body.
🪄 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: Enterprise
Run ID: 67095f95-947f-4419-9d0b-98dd4826a053
📒 Files selected for processing (22)
.github/workflows/nightly-e2e.yamlagents/hermes/Dockerfileagents/hermes/Dockerfile.baseagents/hermes/start.shdocs/get-started/quickstart-hermes.mdxdocs/reference/commands-nemohermes.mdxdocs/reference/commands.mdxsrc/lib/agent/defs.test.tssrc/lib/agent/defs.tssrc/lib/dashboard-url-command.test.tssrc/lib/dashboard-url-command.tssrc/lib/dashboard/contract.test.tssrc/lib/dashboard/contract.tssrc/lib/onboard.tssrc/lib/onboard/agent-dashboard-forward.tssrc/lib/onboard/dashboard-forward-control.tssrc/lib/onboard/dashboard.tssrc/lib/verify-deployment-agent.test.tstest/e2e/test-hermes-e2e.shtest/hermes-start.test.tstest/onboard.test.tstest/sandbox-provisioning.test.ts
✅ Files skipped from review due to trivial changes (1)
- docs/reference/commands.mdx
🚧 Files skipped from review as they are similar to previous changes (7)
- test/sandbox-provisioning.test.ts
- agents/hermes/Dockerfile.base
- test/onboard.test.ts
- src/lib/onboard.ts
- docs/get-started/quickstart-hermes.mdx
- src/lib/agent/defs.ts
- src/lib/dashboard/contract.ts
There was a problem hiding this comment.
Caution
Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/nightly-e2e.yaml:
- Line 2540: The github-script step uses template-expanded `${{
toJSON(inputs.jobs) }}` inside the JS body causing injection warnings; change to
pass inputs.jobs via the step's env (e.g., set env.REQUESTED_JOBS: ${{
toJSON(inputs.jobs) }}) and update the script to read process.env.REQUESTED_JOBS
when computing requestedJobsRaw (and similarly update the separate "Post E2E
results to PR" github-script step to use
env.REQUESTED_JOBS/process.env.REQUESTED_JOBS instead of `${{
toJSON(inputs.jobs) }}`) so both places (the step creating requestedJobsRaw and
the "Post E2E results to PR" step) avoid template expansion in the script body.
🪄 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: Enterprise
Run ID: 67095f95-947f-4419-9d0b-98dd4826a053
📒 Files selected for processing (22)
.github/workflows/nightly-e2e.yamlagents/hermes/Dockerfileagents/hermes/Dockerfile.baseagents/hermes/start.shdocs/get-started/quickstart-hermes.mdxdocs/reference/commands-nemohermes.mdxdocs/reference/commands.mdxsrc/lib/agent/defs.test.tssrc/lib/agent/defs.tssrc/lib/dashboard-url-command.test.tssrc/lib/dashboard-url-command.tssrc/lib/dashboard/contract.test.tssrc/lib/dashboard/contract.tssrc/lib/onboard.tssrc/lib/onboard/agent-dashboard-forward.tssrc/lib/onboard/dashboard-forward-control.tssrc/lib/onboard/dashboard.tssrc/lib/verify-deployment-agent.test.tstest/e2e/test-hermes-e2e.shtest/hermes-start.test.tstest/onboard.test.tstest/sandbox-provisioning.test.ts
✅ Files skipped from review due to trivial changes (1)
- docs/reference/commands.mdx
🚧 Files skipped from review as they are similar to previous changes (7)
- test/sandbox-provisioning.test.ts
- agents/hermes/Dockerfile.base
- test/onboard.test.ts
- src/lib/onboard.ts
- docs/get-started/quickstart-hermes.mdx
- src/lib/agent/defs.ts
- src/lib/dashboard/contract.ts
🛑 Comments failed to post (1)
.github/workflows/nightly-e2e.yaml (1)
2540-2540:
⚠️ Potential issue | 🟡 Minor | ⚡ Quick win🧩 Analysis chain
🏁 Script executed:
#!/bin/bash set -euo pipefail if command -v zizmor >/dev/null 2>&1; then zizmor .github/workflows/nightly-e2e.yaml | rg -n "template-injection|2540" -C2 || true else echo "zizmor is not installed in this environment; run your CI static-analysis job to confirm the warning is cleared." fiRepository: NVIDIA/NemoClaw
Length of output: 2710
🏁 Script executed:
#!/bin/bash set -euo pipefail # Show the exact JS snippet around workflow line 2540 to ensure the proposed change targets the right code. nl -ba .github/workflows/nightly-e2e.yaml | sed -n '2525,2555p'Repository: NVIDIA/NemoClaw
Length of output: 102
🏁 Script executed:
#!/bin/bash set -euo pipefail awk 'NR>=2525 && NR<=2555 {printf "%d:%s\n", NR, $0}' .github/workflows/nightly-e2e.yamlRepository: NVIDIA/NemoClaw
Length of output: 1807
🏁 Script executed:
#!/bin/bash set -euo pipefail rg -n "workflow_dispatch|inputs:|jobs:" .github/workflows/nightly-e2e.yaml | head -n 200Repository: NVIDIA/NemoClaw
Length of output: 9436
🏁 Script executed:
#!/bin/bash set -euo pipefail # 1) Inspect workflow_dispatch inputs definition for `jobs` awk 'NR>=100 && NR<=160 {print NR ":" $0}' .github/workflows/nightly-e2e.yaml # 2) Inspect the entire `scorecard` github-script step around the flagged line awk 'NR>=2485 && NR<=2565 {print NR ":" $0}' .github/workflows/nightly-e2e.yamlRepository: NVIDIA/NemoClaw
Length of output: 7716
🏁 Script executed:
#!/bin/bash set -euo pipefail FILE=".github/workflows/nightly-e2e.yaml" TMP="$(mktemp)" cp "$FILE" "$TMP" python3 - <<'PY' import re, pathlib, sys, os path = os.environ["TMP"] p = pathlib.Path(path) s = p.read_text() # Insert env block for REQUESTED_JOBS between uses and with: in the scorecard step # Match the specific step header region we saw in the repo. s2 = s s2 = s2.replace( " uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0\n with:\n", " uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0\n env:\n REQUESTED_JOBS: ${{ inputs.jobs }}\n with:\n" ) # Replace the template expansion inside the JS body s2 = s2.replace( "const requestedJobsRaw = isDispatch ? (${{ toJSON(inputs.jobs) }} || '').trim() : '';\n", "const requestedJobsRaw = isDispatch ? (process.env.REQUESTED_JOBS || '').trim() : '';\n" ) if s2 == s: raise SystemExit("No changes applied; patterns didn't match.") p.write_text(s2) print("temp workflow written:", path) PY echo "---- zizmor results (temp patch) ----" if command -v zizmor >/dev/null 2>&1; then zizmor "$TMP" | rg -n "template-injection|2540" -C2 || true else echo "zizmor not installed" fi echo "---- sanity check: remaining toJSON(inputs.jobs) ----" rg -n "toJSON\(inputs\.jobs\)" "$TMP" || trueRepository: NVIDIA/NemoClaw
Length of output: 195
🏁 Script executed:
#!/bin/bash set -euo pipefail FILE=".github/workflows/nightly-e2e.yaml" TMP="$(mktemp)" cp "$FILE" "$TMP" python3 - <<PY import pathlib p = pathlib.Path("$TMP") s = p.read_text() old = " uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0\n with:\n" new = " uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0\n env:\n REQUESTED_JOBS: \${{ inputs.jobs }}\n with:\n" if old not in s: raise SystemExit("Pattern for scorecard step header not found; aborting.") s = s.replace(old, new) needle = "const requestedJobsRaw = isDispatch ? (\${{ toJSON(inputs.jobs) }} || '').trim() : '';\n" repl = "const requestedJobsRaw = isDispatch ? (process.env.REQUESTED_JOBS || '').trim() : '';\n" if needle not in s: raise SystemExit("Target JS line not found; aborting.") s = s.replace(needle, repl) p.write_text(s) print("temp workflow written to:", p) PY echo "---- zizmor results (temp patch) ----" if command -v zizmor >/dev/null 2>&1; then zizmor "$TMP" | rg -n "template-injection|2540" -C2 || true else echo "zizmor not installed" fi echo "---- remaining toJSON(inputs.jobs) ----" rg -n "toJSON\\(inputs\\.jobs\\)" "$TMP" || trueRepository: NVIDIA/NemoClaw
Length of output: 385
🏁 Script executed:
#!/bin/bash set -euo pipefail FILE=".github/workflows/nightly-e2e.yaml" TMP="$(mktemp --suffix=.yaml)" cp "$FILE" "$TMP" python3 - <<PY import pathlib p = pathlib.Path("$TMP") s = p.read_text() old = " uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0\n with:\n" new = " uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0\n env:\n REQUESTED_JOBS: \${{ inputs.jobs }}\n with:\n" if old not in s: raise SystemExit("Pattern for scorecard step header not found; aborting.") s = s.replace(old, new) needle = "const requestedJobsRaw = isDispatch ? (\${{ toJSON(inputs.jobs) }} || '').trim() : '';\n" repl = "const requestedJobsRaw = isDispatch ? (process.env.REQUESTED_JOBS || '').trim() : '';\n" if needle not in s: raise SystemExit("Target JS line not found; aborting.") s = s.replace(needle, repl) p.write_text(s) print("temp workflow written to:", p) PY echo "---- zizmor results (temp patch) ----" if command -v zizmor >/dev/null 2>&1; then zizmor "$TMP" | rg -n "template-injection" -C2 || true else echo "zizmor not installed" fi echo "---- remaining toJSON(inputs.jobs) in patched workflow ----" rg -n "toJSON\\(inputs\\.jobs\\)" "$TMP" || true echo "---- scorecard requestedJobsRaw line in patched workflow ----" rg -n "requestedJobsRaw = isDispatch" "$TMP" || trueRepository: NVIDIA/NemoClaw
Length of output: 2364
🏁 Script executed:
#!/bin/bash set -euo pipefail FILE=".github/workflows/nightly-e2e.yaml" TMP="$(mktemp --suffix=.yaml)" cp "$FILE" "$TMP" python3 - <<PY import pathlib p = pathlib.Path("$TMP") s = p.read_text() old = " uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0\n with:\n" new = " uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0\n env:\n REQUESTED_JOBS: \${{ inputs.jobs }}\n with:\n" if old not in s: raise SystemExit("header pattern not found") s = s.replace(old, new) needle = "const requestedJobsRaw = isDispatch ? (\${{ toJSON(inputs.jobs) }} || '').trim() : '';\n" repl = "const requestedJobsRaw = isDispatch ? (process.env.REQUESTED_JOBS || '').trim() : '';\n" if needle not in s: raise SystemExit("JS needle not found") s = s.replace(needle, repl) p.write_text(s) print(str(p)) PY TMP_PATH="$(cat <(python3 - <<'PY' import pathlib,glob # read the tmp path from the last line of python? can't PY ) 2>/dev/null || true)" # Since we can't reliably capture from the previous python via bash here, just locate the temp file by suffix: # (In practice, just print lines for the most recent tmp file is enough.) # We'll instead print from "$TMP" using the variable still in scope. echo "Using temp: $TMP" echo "---- lines around the remaining finding near 2540 in patched file ----" awk 'NR>=2528 && NR<=2555 {printf "%d:%s\n", NR, $0}' "$TMP" echo "---- remaining toJSON(inputs.jobs) occurrences ----" rg -n "toJSON\\(inputs\\.jobs\\)" "$TMP" || trueRepository: NVIDIA/NemoClaw
Length of output: 1826
🏁 Script executed:
#!/bin/bash set -euo pipefail awk 'NR>=2310 && NR<=2385 {printf "%d:%s\n", NR, $0}' .github/workflows/nightly-e2e.yamlRepository: NVIDIA/NemoClaw
Length of output: 3618
🏁 Script executed:
#!/bin/bash set -euo pipefail awk 'NR>=2310 && NR<=2385 {printf "%d:%s\n", NR, $0}' .github/workflows/nightly-e2e.yamlRepository: NVIDIA/NemoClaw
Length of output: 3618
Use
envpassthrough forinputs.jobsin github-script to avoid template-expansion injection warnings
actions/github-scriptat line 2540 uses${{ toJSON(inputs.jobs) }}inside the JS body (flagged by static analysis). Switching toenv.REQUESTED_JOBS+process.env.REQUESTED_JOBSavoids the template expansion.
Also update the separateactions/github-scriptstep “Post E2E results to PR” (it still uses${{ toJSON(inputs.jobs) }}around line 2351), otherwise theinputs.jobswarning will remain.Suggested patch (scorecard step)
- name: Generate nightly scorecard id: scorecard uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + REQUESTED_JOBS: ${{ inputs.jobs }} with: script: | @@ - const requestedJobsRaw = isDispatch ? (${{ toJSON(inputs.jobs) }} || '').trim() : ''; + const requestedJobsRaw = isDispatch ? (process.env.REQUESTED_JOBS || '').trim() : '';📝 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.- name: Generate nightly scorecard id: scorecard uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: REQUESTED_JOBS: ${{ inputs.jobs }} with: script: | const requestedJobsRaw = isDispatch ? (process.env.REQUESTED_JOBS || '').trim() : '';🧰 Tools
🪛 zizmor (1.25.2)
[error] 2540-2540: code injection via template expansion (template-injection): may expand into attacker-controllable code
(template-injection)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/nightly-e2e.yaml at line 2540, The github-script step uses template-expanded `${{ toJSON(inputs.jobs) }}` inside the JS body causing injection warnings; change to pass inputs.jobs via the step's env (e.g., set env.REQUESTED_JOBS: ${{ toJSON(inputs.jobs) }}) and update the script to read process.env.REQUESTED_JOBS when computing requestedJobsRaw (and similarly update the separate "Post E2E results to PR" github-script step to use env.REQUESTED_JOBS/process.env.REQUESTED_JOBS instead of `${{ toJSON(inputs.jobs) }}`) so both places (the step creating requestedJobsRaw and the "Post E2E results to PR" step) avoid template expansion in the script body.
|
Updated PR after the merge with main and addressed the remaining feedback. Changes pushed through d3e7c04:
Validation:
|
Selective E2E Results — ❌ Some jobs failedRun: 27042071176
|
|
Update after latest push:
Validation:
Current status: PR checks are green; PR remains review-required. |
Signed-off-by: Carlos Villela <cvillela@nvidia.com>
Selective E2E Results — ✅ All requested jobs passedRun: 27042720120
|
Selective E2E Results — ✅ All requested jobs passedRun: 27042799774
|
## Summary - Adds the `v0.0.60` section to `docs/about/release-notes.mdx` using the dev announcement from discussion #4877. - Fills the source-doc gaps found during release-prep review across inference, policy tiers, command behavior, security boundaries, Hermes dashboard/tooling, runtime context, and troubleshooting. - Refreshes generated agent skills under `.agents/skills/` from the current Fern docs output and upgrades Fern from `5.44.3` to `5.45.0`. ## Source summary - #4037 -> `docs/reference/architecture.mdx`, `docs/about/how-it-works.mdx`, `docs/about/release-notes.mdx`: Documents system-only runtime context that stays out of visible chat. - #4875 -> `docs/reference/architecture.mdx`, `docs/about/how-it-works.mdx`, `docs/about/release-notes.mdx`: Documents try-first sandbox network/filesystem guidance and clearer failure classification. - #4788 -> `docs/security/best-practices.mdx`, `docs/about/release-notes.mdx`: Documents shared OpenClaw device-approval policy for startup and connect. - #4768 -> `docs/reference/network-policies.mdx`, `docs/network-policy/integration-policy-examples.mdx`, `docs/get-started/quickstart.mdx`, `docs/get-started/quickstart-hermes.mdx`, `docs/reference/commands.mdx`: Documents `weather`, `public-reference`, and Hermes managed-tool gateway preset behavior. - #3788 and #4864 -> `docs/reference/network-policies.mdx`, `docs/reference/commands.mdx`: Documents non-interactive policy-tier fail-fast behavior and interactive prompt fallback. - #4756 and #4866 -> `docs/reference/commands.mdx`: Documents env-aware default sandbox resolution for `list`, `status`, and `tunnel` commands. - #4320 -> `docs/reference/commands.mdx`: Documents `$$nemoclaw tunnel status` behavior. - #4328 -> `docs/reference/commands.mdx`: Documents line-scoped policy preset descriptions in `policy-list`. - #4580 and #4748 -> `docs/reference/architecture.mdx`: Documents package-managed OpenShell gateway service and Docker-driver gateway-marker behavior. - #4598 -> `docs/manage-sandboxes/lifecycle.mdx`: Documents concurrent gateway/dashboard cleanup isolation by sandbox name and port. - #4777 -> `docs/reference/troubleshooting.mdx`: Documents Docker GPU patch rollback behavior. - #4610 -> `docs/reference/troubleshooting.mdx`, `docs/reference/commands.mdx`: Keeps mutable OpenClaw config permission guidance aligned and removes skipped experimental wording. - #4868 -> `docs/reference/commands.mdx`: Keeps `.dockerignore` handling for custom `onboard --from <Dockerfile>` contexts in generated skills. - #4870 -> `docs/reference/commands.mdx`, `docs/manage-sandboxes/runtime-controls.mdx`: Documents `NEMOCLAW_MINIMAL_BOOTSTRAP` and generated skill coverage. - #4641 -> `docs/inference/inference-options.mdx`, `docs/reference/troubleshooting.mdx`: Documents local NVIDIA NIM platform-digest pulls and served-model id adoption. - #4810 and #4867 -> `docs/inference/inference-options.mdx`: Documents stable NGC managed-vLLM image lineage and DGX Station DeepSeek V4 Flash coverage. - #4852 -> `docs/inference/use-local-inference.mdx`, `docs/reference/troubleshooting.mdx`: Documents Ollama model fit filtering, 16K context floor, cold-load retry, and failed-model exclusion. - #4847 -> `docs/inference/switch-inference-providers.mdx`: Documents API-family sync, Hermes `api_mode`, and Bedrock Runtime exception. - #4800 -> `docs/inference/tool-calling-reliability.mdx`: Documents Nemotron managed-inference native tool-search fallback. - #4333 -> `docs/inference/switch-inference-providers.mdx`: Documents interactive multimodal input prompting. - #4086 -> `docs/reference/troubleshooting.mdx`: Keeps proxy bypass normalization in generated troubleshooting coverage. - #4811 and #4855 -> `docs/get-started/quickstart-hermes.mdx`: Documents prebuilt Hermes dashboard assets and TUI recovery without runtime rebuilds. - #4854 -> `docs/inference/switch-inference-providers.mdx`, `docs/reference/commands.mdx`: Documents Hermes proxy API-key placeholder preservation during inference switches. - #4248 -> `docs/manage-sandboxes/messaging-channels.mdx`, `.agents/skills/`: Keeps messaging enrollment behavior aligned with manifest-hook implementation. - #4771 -> `docs/security/best-practices.mdx`, `docs/security/credential-storage.mdx`: Documents Hermes placeholder-only secret boundary for sandbox-visible runtime files. - #4787 -> `docs/security/best-practices.mdx`, `docs/about/release-notes.mdx`: Documents expanded memory scanner examples for OpenAI project keys and Slack app-level tokens. - #4848 -> `docs/reference/commands.mdx`: Documents OpenClaw skill install mirroring into the agent home directory. - #4790 -> `docs/about/release-notes.mdx`: Uses the prior release-prep structure and generated `.agents/skills/` refresh as the template for this release. ## Verification - `python3 scripts/docs-to-skills.py docs/ .agents/skills/ --prefix nemoclaw-user --doc-platform fern-mdx` - `python3 scripts/docs-to-skills.py docs/ .agents/skills/ skills/ --prefix nemoclaw-user --doc-platform fern-mdx --dry-run` - `npm run docs` - `git diff --check` - skip-term scan across `docs/`, `.agents/skills/`, and `skills/` - `npm run build:cli` - `npm run typecheck:cli` - Commit and pre-push hook suites, including markdownlint, gitleaks, env-var docs gate, docs-to-skills verification, and skills YAML tests <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **New Features** * DeepSeek-V4-Flash now available as default inference model for DGX Station. * Hermes dashboard improved with dedicated port and OAuth-authenticated tool gateway selection. * Added weather and public-reference policy presets for expanded agent capabilities. * Enhanced Ollama model selection with GPU memory filtering and automatic retry for timeouts. * **Bug Fixes** * Improved policy tier validation to prevent invalid configurations. * Better sandbox cleanup scoping by port to prevent conflicts across deployments. * Added GPU patch failure recovery with automatic rollback. * **Documentation** * Expanded troubleshooting guides for inference, security, and sandbox lifecycle. * Added .dockerignore best practices for custom deployments. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Carlos Villela <cvillela@nvidia.com>
Summary
This PR salvages #4391 by rebasing the Hermes dashboard work onto current
main, resolving conflicts narrowly, and keeping the change focused on exposing Hermes' built-in dashboard while preserving the OpenAI-compatible API on port 8642. It also prebuilds Hermes dashboard assets in the sandbox image sohermes dashboarddoes not runnpmas the sandbox user under/opt/hermes.Related Issue
Salvages #4391. Related: #4764.
Changes
18789while keeping the OpenAI-compatible API forward on port8642.agents/hermes/Dockerfile.baseand the final Hermes image fallback path, then startshermes dashboard --skip-buildat runtime.Type of Change
Verification
npx prek run --all-filespassesnpm testpassesnpm run docsbuilds without warnings (doc changes only)Additional validation run:
npm run build:clipassesnpm run typecheck:clipassesnpm run source-shape:checkpassessrc/lib/onboard.tsnet -1; no newly added JS files)npm testwas attempted, but one existing installer preflight test failed in this local host environment (warns on Podman but still runs onboarding) because the host reports missing NVIDIA CDI specs and sudo is unavailable; this is outside the Hermes dashboard diff.Signed-off-by: Carlos Villela cvillela@nvidia.com
Summary by CodeRabbit
New Features
Bug Fixes / Reliability
Documentation