From c271b5fbf4ac117597252f6115aafea387088bcd Mon Sep 17 00:00:00 2001 From: Carlos Villela Date: Mon, 8 Jun 2026 12:00:02 -0700 Subject: [PATCH] fix(tunnel): keep service pid directory stable Signed-off-by: Carlos Villela --- src/lib/tunnel/services-sandbox.test.ts | 8 ++------ src/lib/tunnel/services.ts | 5 ++--- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/lib/tunnel/services-sandbox.test.ts b/src/lib/tunnel/services-sandbox.test.ts index 0e9962b3105..99fb3719432 100644 --- a/src/lib/tunnel/services-sandbox.test.ts +++ b/src/lib/tunnel/services-sandbox.test.ts @@ -361,16 +361,14 @@ describe("stopAll with sandbox channels", () => { logSpy.mockRestore(); }); - it("uses the effective env-selected sandbox for host-side pid cleanup", () => { + it("uses the effective env-selected sandbox for sandbox cleanup with explicit host pidDir", () => { const logSpy = vi.spyOn(console, "log").mockImplementation(() => {}); const savedNemoclaw = process.env.NEMOCLAW_SANDBOX; const savedNemoclawName = process.env.NEMOCLAW_SANDBOX_NAME; const savedSandbox = process.env.SANDBOX_NAME; - const savedTmpDir = process.env.TMPDIR; const pidRoot = mkdtempSync(join(tmpdir(), "nemoclaw-services-pid-root-")); const effectivePidDir = join(pidRoot, "nemoclaw-services-name-sandbox"); const lowerPriorityPidDir = join(pidRoot, "nemoclaw-services-other-sandbox"); - process.env.TMPDIR = pidRoot; rmSync(effectivePidDir, { recursive: true, force: true }); rmSync(lowerPriorityPidDir, { recursive: true, force: true }); mkdirSync(effectivePidDir, { recursive: true, mode: 0o700 }); @@ -382,7 +380,7 @@ describe("stopAll with sandbox channels", () => { delete process.env.SANDBOX_NAME; try { - stopAll(); + stopAll({ pidDir: effectivePidDir }); expect(spawnSyncSpy).toHaveBeenCalledWith( "/usr/local/bin/openshell", @@ -398,8 +396,6 @@ describe("stopAll with sandbox channels", () => { else delete process.env.NEMOCLAW_SANDBOX_NAME; if (savedSandbox !== undefined) process.env.SANDBOX_NAME = savedSandbox; else delete process.env.SANDBOX_NAME; - if (savedTmpDir !== undefined) process.env.TMPDIR = savedTmpDir; - else delete process.env.TMPDIR; rmSync(pidRoot, { recursive: true, force: true }); logSpy.mockRestore(); } diff --git a/src/lib/tunnel/services.ts b/src/lib/tunnel/services.ts index 8f36bf57a35..dbee2081c98 100644 --- a/src/lib/tunnel/services.ts +++ b/src/lib/tunnel/services.ts @@ -14,7 +14,6 @@ import { unlinkSync, writeFileSync, } from "node:fs"; -import { tmpdir } from "node:os"; import { basename, join } from "node:path"; import { dockerSpawnSync } from "../adapters/docker"; import { resolveOpenshell } from "../adapters/openshell/resolve"; @@ -35,7 +34,7 @@ export interface ServiceOptions { dashboardPort?: number; /** Repo root directory — used to locate scripts/. */ repoDir?: string; - /** Override PID directory (default: {os.tmpdir()}/nemoclaw-services-{sandbox}). */ + /** Override PID directory (default: /tmp/nemoclaw-services-{sandbox}). */ pidDir?: string; /** Cloudflare named tunnel token. Falls back to CLOUDFLARE_TUNNEL_TOKEN. */ cloudflareTunnelToken?: string; @@ -386,7 +385,7 @@ function resolvePidDir(opts: ServiceOptions): string { const sandbox = validateSandboxName( opts.sandboxName ?? process.env.NEMOCLAW_SANDBOX ?? process.env.SANDBOX_NAME ?? "default", ); - return opts.pidDir ?? join(tmpdir(), `nemoclaw-services-${sandbox}`); + return opts.pidDir ?? `/tmp/nemoclaw-services-${sandbox}`; } export function showStatus(opts: ServiceOptions = {}): void {