fix(cli): stop in-sandbox messaging channels on nemoclaw stop#1977
Conversation
`nemoclaw stop` only killed the host-side `cloudflared` process but left the OpenClaw gateway (and its Telegram/Discord/Slack messaging channels) running inside the sandbox. Bots continued responding to messages after stop. After PR #1081 moved messaging bridges into the sandbox, no stop mechanism was added to replace the old host-side process kill. Add `stopSandboxChannels()` to `services.ts` that execs into the sandbox via `openshell sandbox exec <name> -- pkill -TERM -f "openclaw gateway"`. The gateway's own signal handler (`cleanup()` in nemoclaw-start.sh) forwards SIGTERM to child processes, which stops all messaging channel polling. `stopAll()` now calls `stopSandboxChannels()` before stopping host-side services. When the sandbox name is unavailable or openshell is not installed, it warns and continues — host-side cleanup still proceeds. - [x] `npx vitest run --project cli src/lib/services.test.ts` — 14 existing tests pass - [x] `npx vitest run --project cli src/lib/services-sandbox.test.ts` — 9 new tests pass - [x] `npx vitest run --project cli src/lib/services-command.test.ts` — 4 tests pass - [x] `npm test` — full suite (1635 tests) passes - [x] `npm run typecheck:cli` — clean - [ ] Manual: `nemoclaw stop` → Telegram/Discord bots stop responding Signed-off-by: Brandon Pelfrey <bpelfrey@nvidia.com>
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds an in‑sandbox shutdown for OpenClaw channels via a new exported Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant stopAll as stopAll()
participant Resolver as resolveOpenshell()
participant stopSandbox as stopSandboxChannels()
participant Spawn as spawnSync()
participant Sandbox as Sandbox/OpenClaw
participant Host as Host services (cloudflared)
User->>stopAll: run "nemoclaw stop"
stopAll->>stopAll: derive sandboxName from opts/env
alt sandboxName present
stopAll->>stopSandbox: stopSandboxChannels(sandboxName)
stopSandbox->>Resolver: resolveOpenshell()
Resolver-->>stopSandbox: openshell path or null
alt openshell found
stopSandbox->>Spawn: spawnSync(openshell, ["sandbox","exec","--name", sandboxName, "--","pkill","-TERM","-f","openclaw[- ]gateway"], {timeout:15000})
Spawn->>Sandbox: execute pkill inside sandbox
Sandbox-->>Spawn: exit status (0/1/>1/null)
Spawn-->>stopSandbox: result
stopSandbox->>stopSandbox: interpret status (0/1 success, else warn)
else openshell missing
stopSandbox-->>stopAll: log "openshell not found"
end
else no sandboxName
stopAll-->>stopAll: log "No sandbox name available"
end
stopAll->>Host: stopService(..., "cloudflared")
Host-->>stopAll: stopped
stopAll-->>User: "All services stopped"
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 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.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/lib/services-sandbox.test.ts (1)
182-203: Add a regression test forNEMOCLAW_SANDBOX_NAMEfallback.This suite currently validates
NEMOCLAW_SANDBOXfallback only. Please add a sibling case assertingstopAll({ pidDir })usesprocess.env.NEMOCLAW_SANDBOX_NAMEwhen opts are empty, so the env compatibility path stays protected.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/services-sandbox.test.ts` around lines 182 - 203, Add a sibling test in src/lib/services-sandbox.test.ts that mirrors the existing "reads sandbox name from NEMOCLAW_SANDBOX env when not in opts" case but sets process.env.NEMOCLAW_SANDBOX_NAME = "env-sandbox-name" (saving and restoring the original value), calls stopAll({ pidDir }), and asserts spawnSyncSpy was invoked with an argument array containing "env-sandbox-name" (use expect.arrayContaining and expect.any(Object) like the existing assertion); also mock/restore console.log (logSpy) as in the original test to avoid noisy output.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/lib/services.ts`:
- Around line 288-295: The shutdown logic for determining sandboxName (variable
sandboxName in stopAll) is missing the environment variable
NEMOCLAW_SANDBOX_NAME used elsewhere; update the fallback chain for sandboxName
(currently opts.sandboxName ?? process.env.NEMOCLAW_SANDBOX ??
process.env.SANDBOX_NAME) to also check process.env.NEMOCLAW_SANDBOX_NAME so
stopSandboxChannels(sandboxName) runs when that env var was set; ensure you only
change the fallback order to include process.env.NEMOCLAW_SANDBOX_NAME and keep
existing warnings the same.
---
Nitpick comments:
In `@src/lib/services-sandbox.test.ts`:
- Around line 182-203: Add a sibling test in src/lib/services-sandbox.test.ts
that mirrors the existing "reads sandbox name from NEMOCLAW_SANDBOX env when not
in opts" case but sets process.env.NEMOCLAW_SANDBOX_NAME = "env-sandbox-name"
(saving and restoring the original value), calls stopAll({ pidDir }), and
asserts spawnSyncSpy was invoked with an argument array containing
"env-sandbox-name" (use expect.arrayContaining and expect.any(Object) like the
existing assertion); also mock/restore console.log (logSpy) as in the original
test to avoid noisy output.
🪄 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 Plus
Run ID: a7f17d09-5ef6-4950-af19-a09f81b7fe4b
📒 Files selected for processing (2)
src/lib/services-sandbox.test.tssrc/lib/services.ts
ericksoa
left a comment
There was a problem hiding this comment.
Nice fix — the problem is real and the approach is clean. A few things to address before merge:
Bug: Missing NEMOCLAW_SANDBOX_NAME in env var fallback
The new stopAll fallback chain checks NEMOCLAW_SANDBOX and SANDBOX_NAME, but the rest of the codebase (nemoclaw.ts:1746, onboard.ts:1617, deploy.ts:119,251) consistently sets NEMOCLAW_SANDBOX_NAME. This is likely the most common env var to be set in practice. Could you add it to the chain?
const sandboxName =
opts.sandboxName ??
process.env.NEMOCLAW_SANDBOX ??
process.env.NEMOCLAW_SANDBOX_NAME ??
process.env.SANDBOX_NAME;(CodeRabbit flagged this too — agree with that finding.)
Minor: npx prek run --all-files
The verification checklist shows this unchecked — could you confirm it passes?
Everything else looks good — pkill exit code handling, the 15s timeout, ordering (sandbox stop before host-side cleanup), and the test coverage are all solid. 👍
… NEMOCLAW_SANDBOX_NAME fallback Address review feedback on #1977: 1. Use `--name` flag for `openshell sandbox exec` instead of positional arg. The positional form is not supported by current openshell and caused exit 127. 2. Narrow pkill pattern from `"openclaw gateway"` to `"openclaw gateway run"` so decoy or unrelated sandbox processes (e.g. `openclaw gateway decoy`, `openclaw gateway status`) are not killed. 3. Add `NEMOCLAW_SANDBOX_NAME` to the env var fallback chain in `stopAll()`, between `NEMOCLAW_SANDBOX` and `SANDBOX_NAME`. This is the env var set by `nemoclaw.ts`, `onboard.ts`, and `deploy.ts`. 4. Add three regression tests: `--name` flag assertion, precise pkill pattern assertion, and `NEMOCLAW_SANDBOX_NAME` env var fallback. Signed-off-by: Brandon Pelfrey <bpelfrey@nvidia.com>
|
Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually. Contributors can view more details about this message here. |
…box name Three fixes for the in-sandbox gateway stop failing to kill the real process: 1. pkill pattern: 'openclaw gateway run' → 'openclaw.gateway.run' regex so it matches both 'openclaw-gateway run' (the actual binary) and the space-separated form. 2. resolveDefaultSandboxName now checks NEMOCLAW_SANDBOX_NAME / NEMOCLAW_SANDBOX / SANDBOX_NAME env vars before falling back to the registry default. This ensures explicit env overrides are respected. 3. Log the sandbox name being targeted so mismatches are immediately visible in CLI output.
…inary The gateway re-execs after startup, changing its argv from 'openclaw gateway run' to 'openclaw-gateway' (dropping the 'run' subcommand). The previous pattern 'openclaw.gateway.run' never matched the running process because 'run' was no longer in the cmdline. Use 'openclaw[- ]gateway' which matches both: - the launcher form: 'openclaw gateway run' (space-separated) - the re-exec'd form: 'openclaw-gateway' (hyphenated, no 'run') This is consistent with the proven pgrep/pkill patterns used in test/e2e/test-issue-2478-crash-loop-recovery.sh.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/lib/services.ts`:
- Around line 296-307: In stopAll, compute the effective sandboxName first using
the same precedence as src/lib/services-command.ts (opts.sandboxName ??
process.env.NEMOCLAW_SANDBOX_NAME ?? process.env.NEMOCLAW_SANDBOX ??
process.env.SANDBOX_NAME), then set opts.sandboxName to that value (if not
already) and only after that call resolvePidDir(opts) so pidDir targets the same
sandbox; keep the stopSandboxChannels(sandboxName) call as-is to stop the
correct channels.
🪄 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: ed3cdccd-2a16-4bac-b4b5-2330f3d05aa7
📒 Files selected for processing (4)
src/lib/services-command.test.tssrc/lib/services-command.tssrc/lib/services-sandbox.test.tssrc/lib/services.ts
✅ Files skipped from review due to trivial changes (1)
- src/lib/services-command.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- src/lib/services-sandbox.test.ts
stopAll() read NEMOCLAW_SANDBOX / NEMOCLAW_SANDBOX_NAME / SANDBOX_NAME env vars and passed them directly to stopSandboxChannels without SAFE_NAME_RE validation. Malformed names (spaces, path traversal) could flow into 'openshell sandbox exec --name ...' causing inconsistent or mis-targeted shutdown behavior. Apply the same SAFE_NAME_RE check used by resolvePidDir/ resolveDefaultSandboxName so invalid names are rejected with a warning instead of being passed to openshell. Add regression tests for spaces and path-traversal in env var names.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
src/lib/services.ts (1)
297-317:⚠️ Potential issue | 🟠 MajorResolve effective sandbox before PID-dir resolution to avoid target drift.
At Line 297,
pidDiris resolved before sandbox selection/validation, and at Line 303-305 env precedence differs fromresolveDefaultSandboxName(NEMOCLAW_SANDBOX_NAMEshould win). This can make host cleanup and in-sandbox shutdown hit different sandboxes.🔧 Suggested fix
export function stopAll(opts: ServiceOptions = {}): void { - const pidDir = resolvePidDir(opts); - ensurePidDir(pidDir); - // Stop the in-sandbox OpenClaw gateway (and its messaging channels). const rawSandboxName = opts.sandboxName ?? - process.env.NEMOCLAW_SANDBOX ?? process.env.NEMOCLAW_SANDBOX_NAME ?? + process.env.NEMOCLAW_SANDBOX ?? process.env.SANDBOX_NAME; const sandboxName = rawSandboxName && SAFE_NAME_RE.test(rawSandboxName) && !rawSandboxName.includes("..") ? rawSandboxName : undefined; + const pidDir = resolvePidDir({ ...opts, sandboxName }); + ensurePidDir(pidDir); if (sandboxName) { stopSandboxChannels(sandboxName);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/services.ts` around lines 297 - 317, The pidDir is being resolved before determining the effective sandbox which can cause host cleanup and in-sandbox shutdown to target different sandboxes; move the sandbox resolution/validation (the rawSandboxName -> sandboxName logic that uses SAFE_NAME_RE and the '..' check and calls stopSandboxChannels) to run before resolvePidDir/ensurePidDir (i.e., determine sandboxName first), and adjust the environment-variable precedence to match resolveDefaultSandboxName so NEMOCLAW_SANDBOX_NAME wins over NEMOCLAW_SANDBOX and SANDBOX_NAME; keep using stopSandboxChannels(sandboxName) when valid and preserve the same warning behavior when invalid or missing.
🧹 Nitpick comments (1)
src/lib/services-sandbox.test.ts (1)
234-262: Add a dual-env precedence test to lock intended sandbox resolution.Current tests cover each env var in isolation, but not the case where both are set. Adding that case will catch regressions in precedence handling.
🧪 Suggested test addition
+ it("prefers NEMOCLAW_SANDBOX_NAME over NEMOCLAW_SANDBOX when both are set", () => { + const logSpy = vi.spyOn(console, "log").mockImplementation(() => {}); + const savedNemoclaw = process.env.NEMOCLAW_SANDBOX; + const savedNemoclawName = process.env.NEMOCLAW_SANDBOX_NAME; + process.env.NEMOCLAW_SANDBOX = "legacy-sandbox"; + process.env.NEMOCLAW_SANDBOX_NAME = "preferred-sandbox"; + + try { + stopAll({ pidDir }); + } finally { + if (savedNemoclaw !== undefined) process.env.NEMOCLAW_SANDBOX = savedNemoclaw; + else delete process.env.NEMOCLAW_SANDBOX; + if (savedNemoclawName !== undefined) process.env.NEMOCLAW_SANDBOX_NAME = savedNemoclawName; + else delete process.env.NEMOCLAW_SANDBOX_NAME; + } + + expect(spawnSyncSpy).toHaveBeenCalledWith( + "/usr/local/bin/openshell", + expect.arrayContaining(["preferred-sandbox"]), + expect.any(Object), + ); + logSpy.mockRestore(); + });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/services-sandbox.test.ts` around lines 234 - 262, Add a test that sets both NEMOCLAW_SANDBOX and NEMOCLAW_SANDBOX_NAME, calls stopAll({ pidDir }), and asserts the resolved sandbox follows the intended precedence (e.g., NEMOCLAW_SANDBOX should override NEMOCLAW_SANDBOX_NAME). Use the same pattern as existing tests: spy console.log, set savedEnv backups, set both env vars (e.g., process.env.NEMOCLAW_SANDBOX="direct-sandbox" and process.env.NEMOCLAW_SANDBOX_NAME="named-sandbox"), call stopAll, then expect spawnSyncSpy toHaveBeenCalledWith("/usr/local/bin/openshell", expect.arrayContaining(["direct-sandbox"]), expect.any(Object)), and restore env and log spies; reference stopAll, NEMOCLAW_SANDBOX, NEMOCLAW_SANDBOX_NAME, and spawnSyncSpy.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/lib/services-sandbox.test.ts`:
- Line 8: The import symbol realSpawnSync is declared but unused; remove the
unused import from the top of the test module or, if you intend to keep it
intentionally unused, rename it to _realSpawnSync to follow the unused-variable
naming guideline—update the import statement that currently imports
realSpawnSync from "node:child_process" accordingly and run tests/lint to
confirm no unused-import errors remain.
---
Duplicate comments:
In `@src/lib/services.ts`:
- Around line 297-317: The pidDir is being resolved before determining the
effective sandbox which can cause host cleanup and in-sandbox shutdown to target
different sandboxes; move the sandbox resolution/validation (the rawSandboxName
-> sandboxName logic that uses SAFE_NAME_RE and the '..' check and calls
stopSandboxChannels) to run before resolvePidDir/ensurePidDir (i.e., determine
sandboxName first), and adjust the environment-variable precedence to match
resolveDefaultSandboxName so NEMOCLAW_SANDBOX_NAME wins over NEMOCLAW_SANDBOX
and SANDBOX_NAME; keep using stopSandboxChannels(sandboxName) when valid and
preserve the same warning behavior when invalid or missing.
---
Nitpick comments:
In `@src/lib/services-sandbox.test.ts`:
- Around line 234-262: Add a test that sets both NEMOCLAW_SANDBOX and
NEMOCLAW_SANDBOX_NAME, calls stopAll({ pidDir }), and asserts the resolved
sandbox follows the intended precedence (e.g., NEMOCLAW_SANDBOX should override
NEMOCLAW_SANDBOX_NAME). Use the same pattern as existing tests: spy console.log,
set savedEnv backups, set both env vars (e.g.,
process.env.NEMOCLAW_SANDBOX="direct-sandbox" and
process.env.NEMOCLAW_SANDBOX_NAME="named-sandbox"), call stopAll, then expect
spawnSyncSpy toHaveBeenCalledWith("/usr/local/bin/openshell",
expect.arrayContaining(["direct-sandbox"]), expect.any(Object)), and restore env
and log spies; reference stopAll, NEMOCLAW_SANDBOX, NEMOCLAW_SANDBOX_NAME, and
spawnSyncSpy.
🪄 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: 8b7cc99b-f05b-40e5-acdc-7adeae1b1687
📒 Files selected for processing (2)
src/lib/services-sandbox.test.tssrc/lib/services.ts
…ndboxName stopAll() used NEMOCLAW_SANDBOX ?? NEMOCLAW_SANDBOX_NAME ?? SANDBOX_NAME but resolveDefaultSandboxName() in services-command.ts uses NEMOCLAW_SANDBOX_NAME ?? NEMOCLAW_SANDBOX ?? SANDBOX_NAME. Align to NEMOCLAW_SANDBOX_NAME-first so both paths target the same sandbox when multiple env vars are set. Add regression test asserting NEMOCLAW_SANDBOX_NAME wins over NEMOCLAW_SANDBOX.
There was a problem hiding this comment.
♻️ Duplicate comments (1)
src/lib/services.ts (1)
296-322:⚠️ Potential issue | 🟠 Major | 🏗️ Heavy liftResolve
pidDirfrom the same sandbox you stop.
stopAll()still callsresolvePidDir(opts)before it derives the effectivesandboxName, so host-side cleanup can target a different PID directory than the in-sandbox shutdown whenNEMOCLAW_SANDBOX_NAMEis set. This is the same mismatch previously flagged and it remains open.Suggested fix
export function stopAll(opts: ServiceOptions = {}): void { - const pidDir = resolvePidDir(opts); - ensurePidDir(pidDir); - // Stop the in-sandbox OpenClaw gateway (and its messaging channels). const rawSandboxName = opts.sandboxName ?? process.env.NEMOCLAW_SANDBOX_NAME ?? process.env.NEMOCLAW_SANDBOX ?? process.env.SANDBOX_NAME; const sandboxName = rawSandboxName && SAFE_NAME_RE.test(rawSandboxName) && !rawSandboxName.includes("..") ? rawSandboxName : undefined; + const pidDir = resolvePidDir({ ...opts, sandboxName }); + ensurePidDir(pidDir); if (sandboxName) { stopSandboxChannels(sandboxName); } else if (rawSandboxName) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/services.ts` around lines 296 - 322, stopAll currently calls resolvePidDir(opts) before computing the effective sandboxName (rawSandboxName -> sandboxName), which can cause host-side cleanup to use the wrong PID directory; fix by deriving sandboxName first (using rawSandboxName, SAFE_NAME_RE and the same environment precedence logic) and then call resolvePidDir passing the resolved sandboxName (or adjust resolvePidDir signature to accept opts.sandboxName) so stopService(pidDir, "cloudflared") uses the PID directory for the same sandbox you just stopped; update references in stopAll (rawSandboxName, sandboxName, resolvePidDir, stopService) accordingly and remove the earlier precomputed pidDir.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@src/lib/services.ts`:
- Around line 296-322: stopAll currently calls resolvePidDir(opts) before
computing the effective sandboxName (rawSandboxName -> sandboxName), which can
cause host-side cleanup to use the wrong PID directory; fix by deriving
sandboxName first (using rawSandboxName, SAFE_NAME_RE and the same environment
precedence logic) and then call resolvePidDir passing the resolved sandboxName
(or adjust resolvePidDir signature to accept opts.sandboxName) so
stopService(pidDir, "cloudflared") uses the PID directory for the same sandbox
you just stopped; update references in stopAll (rawSandboxName, sandboxName,
resolvePidDir, stopService) accordingly and remove the earlier precomputed
pidDir.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 75bb081d-031f-4a71-894b-1303a9639de1
📒 Files selected for processing (2)
src/lib/services-sandbox.test.tssrc/lib/services.ts
✅ Files skipped from review due to trivial changes (1)
- src/lib/services-sandbox.test.ts
Signed-off-by: Brandon Pelfrey <bpelfrey@nvidia.com>
Signed-off-by: Brandon Pelfrey <bpelfrey@nvidia.com>
Automated PR review summaryReviewed PR #1977: fix(cli): stop in-sandbox messaging channels on nemoclaw stop Recommendation
Installation and setup findings
What was validated
Failing tests and unresolved impact
Passing tests and why they matteredPassing test 1:
|
…ng-2 # Conflicts: # src/lib/services.ts
More recent review from Carlos, concerns addressed.
## Summary Daily release-prep documentation refresh for merged PRs from the past 24 hours. This updates user-facing docs for Telegram mention-only mode, in-sandbox messaging shutdown, Hermes onboarding/runtime behavior, and compatible-endpoint smoke validation, then bumps the docs metadata to 0.0.33 after tag v0.0.32. ## Related Issue None. ## Changes - #2417 / c7e49ad: Document `TELEGRAM_REQUIRE_MENTION` for Telegram group-chat replies in `docs/manage-sandboxes/messaging-channels.md` and `docs/reference/commands.md`. - #1977 / 69403e0: Update `nemoclaw tunnel stop` and deprecated `nemoclaw stop` docs to explain that NemoClaw also attempts to stop the in-sandbox OpenClaw gateway and messaging polling. - #2781 / b83ffe2, #2859 / 4df8be6, and #2846 / 0968dfd: Refresh the Hermes quickstart for the default `my-hermes` sandbox name, cross-agent same-name guard, agent type visibility in `nemoclaw list`, Brave prompt omission, and supported prebaked Hermes integrations. - #2849 / fd240ff: Document the Telegram plus OpenAI-compatible endpoint `inference.local` smoke check in inference options and troubleshooting. - Bump `docs/versions1.json` and `docs/project.json` from 0.0.32 to 0.0.33 for daily release preparation. ## Type of Change - [ ] Code change (feature, bug fix, or refactor) - [ ] Code change with doc updates - [ ] Doc only (prose changes, no code sample modifications) - [x] Doc only (includes code sample changes) ## Verification - [ ] `npx prek run --all-files` passes - [ ] `npm test` passes - [ ] Tests added or updated for new or changed behavior - [x] No secrets, API keys, or credentials committed - [x] Docs updated for user-facing behavior changes - [x] `make docs` builds without warnings (doc changes only) - [x] Doc pages follow the [style guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md) (doc changes only) - [ ] New doc pages include SPDX header and frontmatter (new pages only) Additional checks run: - `python3 scripts/docs-to-skills.py docs/ .agents/skills/ --prefix nemoclaw-user --dry-run` - `git diff --check` - `make docs` passed with the existing local version-switcher read message. - Full `npx prek run --all-files` and `npm test` were skipped for this doc-only automation run. Commit and pre-push hooks otherwise passed docs, lint, secret, and conversion checks until the local `Test (skills YAML)` hook failed because `vitest/config` is not installed in this fresh worktree. --- Signed-off-by: Miyoung Choi <miyoungc@nvidia.com> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Updated Hermes quickstart: default sandbox name is "hermes"; guidance to use distinct sandbox names, note same-name reuse is prevented, Hermes wizard does not request Brave Web Search, and sandbox listings now show agent type. * Clarified provider onboarding: bounded in-sandbox smoke check runs when Telegram messaging is enabled. * Expanded Telegram docs: added TELEGRAM_REQUIRE_MENTION (DMs still governed by TELEGRAM_ALLOWED_IDS), onboarding examples, stop-messaging/tunnel behavior, and troubleshooting. * Promoted docs to version 0.0.33. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Miyoung Choi <miyoungc@nvidia.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Summary
nemoclaw stoponly killed the host-sidecloudflaredprocess but left the OpenClaw gateway (and its Telegram/Discord/Slack messaging channels) running inside the sandbox. After PR #1081 moved bridges into the sandbox, no stop mechanism was added. This PR addsstopSandboxChannels()which execs into the sandbox viaopenshell sandbox execto SIGTERM the gateway process, stopping all messaging channel polling.Related Issue
Fixes #1825
Changes
stopSandboxChannels()tosrc/lib/services.ts— resolvesopenshell, execspkill -TERM -f "openclaw gateway"inside the sandbox, and handles all pkill exit codes (0 = stopped, 1 = already stopped, >1 = unreachable)stopAll()to callstopSandboxChannels()before stopping host-side services; reads sandbox name from opts,NEMOCLAW_SANDBOX, orSANDBOX_NAMEenv vars; warns when no sandbox name is availablespawnSyncfromnode:child_processandresolveOpenshellfrom./resolve-openshellsrc/lib/services-sandbox.test.tswith 9 tests covering: successful gateway stop, pkill exit 1 (no match), unreachable sandbox, null status (timeout), openshell not found, stopAll with sandbox name, stopAll without sandbox name, cloudflared cleanup on exec failure, and env var fallbackType of Change
Verification
npx prek run --all-filespassesnpm testpassesmake docsbuilds without warnings (doc changes only)AI Disclosure
Signed-off-by: Brandon Pelfrey bpelfrey@nvidia.com
Summary by CodeRabbit
New Features
Tests