Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 10 additions & 6 deletions src/lib/actions/uninstall/run-plan.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,9 +235,11 @@ describe("uninstall run plan", () => {
const killed: number[] = [];
const exited = new Set<number>();
// Simulate the persisted PID file under ~/.nemoclaw/.
const tmpHome = "/tmp/nemoclaw-uninstall-test-2759-pidfile";
const pidFile = `${tmpHome}/.nemoclaw/ollama-auth-proxy.pid`;
fs.mkdirSync(`${tmpHome}/.nemoclaw`, { recursive: true });
const tmpHome = fs.mkdtempSync(
path.join(os.tmpdir(), "nemoclaw-uninstall-test-2759-pidfile-"),
);
const pidFile = path.join(tmpHome, ".nemoclaw", "ollama-auth-proxy.pid");
fs.mkdirSync(path.join(tmpHome, ".nemoclaw"), { recursive: true });
fs.writeFileSync(pidFile, "44321\n");

try {
Expand Down Expand Up @@ -462,9 +464,11 @@ describe("uninstall run plan", () => {
const logs: string[] = [];
const warnings: string[] = [];
const signals: NodeJS.Signals[] = [];
const tmpHome = "/tmp/nemoclaw-uninstall-test-2759-stuck";
const pidFile = `${tmpHome}/.nemoclaw/ollama-auth-proxy.pid`;
fs.mkdirSync(`${tmpHome}/.nemoclaw`, { recursive: true });
const tmpHome = fs.mkdtempSync(
path.join(os.tmpdir(), "nemoclaw-uninstall-test-2759-stuck-"),
);
const pidFile = path.join(tmpHome, ".nemoclaw", "ollama-auth-proxy.pid");
fs.mkdirSync(path.join(tmpHome, ".nemoclaw"), { recursive: true });
fs.writeFileSync(pidFile, "44322\n");

try {
Expand Down
14 changes: 10 additions & 4 deletions src/lib/tunnel/services-sandbox.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -366,8 +366,13 @@ describe("stopAll with sandbox channels", () => {
const savedNemoclaw = process.env.NEMOCLAW_SANDBOX;
const savedNemoclawName = process.env.NEMOCLAW_SANDBOX_NAME;
const savedSandbox = process.env.SANDBOX_NAME;
const effectivePidDir = "/tmp/nemoclaw-services-name-sandbox";
const lowerPriorityPidDir = "/tmp/nemoclaw-services-other-sandbox";
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 });
mkdirSync(lowerPriorityPidDir, { recursive: true, mode: 0o700 });
writeFileSync(join(effectivePidDir, "cloudflared.pid"), "999999999");
Expand All @@ -393,8 +398,9 @@ 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;
rmSync(effectivePidDir, { recursive: true, force: true });
rmSync(lowerPriorityPidDir, { recursive: true, force: true });
if (savedTmpDir !== undefined) process.env.TMPDIR = savedTmpDir;
else delete process.env.TMPDIR;
rmSync(pidRoot, { recursive: true, force: true });
logSpy.mockRestore();
}
});
Expand Down
5 changes: 3 additions & 2 deletions src/lib/tunnel/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ 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";
Expand All @@ -34,7 +35,7 @@ export interface ServiceOptions {
dashboardPort?: number;
/** Repo root directory — used to locate scripts/. */
repoDir?: string;
/** Override PID directory (default: /tmp/nemoclaw-services-{sandbox}). */
/** Override PID directory (default: {os.tmpdir()}/nemoclaw-services-{sandbox}). */
pidDir?: string;
/** Cloudflare named tunnel token. Falls back to CLOUDFLARE_TUNNEL_TOKEN. */
cloudflareTunnelToken?: string;
Expand Down Expand Up @@ -385,7 +386,7 @@ function resolvePidDir(opts: ServiceOptions): string {
const sandbox = validateSandboxName(
opts.sandboxName ?? process.env.NEMOCLAW_SANDBOX ?? process.env.SANDBOX_NAME ?? "default",
);
return opts.pidDir ?? `/tmp/nemoclaw-services-${sandbox}`;
return opts.pidDir ?? join(tmpdir(), `nemoclaw-services-${sandbox}`);
}

export function showStatus(opts: ServiceOptions = {}): void {
Expand Down
15 changes: 6 additions & 9 deletions test/service-env.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,10 +228,9 @@ describe("service environment", () => {
});

it("proxy-env.sh includes GIT_SSL_CAINFO when set", () => {
const fakeDataDir = join(tmpdir(), `nemoclaw-git-ssl-test-${process.pid}`);
const fakeDataDir = mkdtempSync(join(tmpdir(), "nemoclaw-git-ssl-test-"));
const fakeCaBundle = join(fakeDataDir, "ca-bundle.pem");
execFileSync("mkdir", ["-p", fakeDataDir]);
const tmpFile = join(tmpdir(), `nemoclaw-git-ssl-env-${process.pid}.sh`);
const tmpFile = join(fakeDataDir, "git-ssl-env.sh");
try {
const persistBlock = extractRuntimeShellEnvSnippet();
// Create a fake CA bundle so the -f check passes
Expand Down Expand Up @@ -265,7 +264,7 @@ describe("service environment", () => {
expect(envFile).toContain(fakeCaBundle);
} finally {
try {
execFileSync("rm", ["-rf", fakeDataDir, tmpFile]);
execFileSync("rm", ["-rf", fakeDataDir]);
} catch {
/* ignore */
}
Expand Down Expand Up @@ -1070,9 +1069,8 @@ describe("service environment", () => {
});

it("emit_sandbox_sourced_file prevents symlink-following attack on proxy-env.sh", () => {
const fakeDataDir = join(tmpdir(), `nemoclaw-symlink-test-${process.pid}`);
execFileSync("mkdir", ["-p", fakeDataDir]);
const tmpFile = join(tmpdir(), `nemoclaw-symlink-write-test-${process.pid}.sh`);
const fakeDataDir = mkdtempSync(join(tmpdir(), "nemoclaw-symlink-test-"));
const tmpFile = join(fakeDataDir, "symlink-write-test.sh");
try {
const persistBlock = extractRuntimeShellEnvSnippet();
const sensitiveFile = join(fakeDataDir, "sensitive");
Expand Down Expand Up @@ -1110,8 +1108,7 @@ describe("service environment", () => {
});

it("[simulation] sourcing proxy-env.sh overrides narrow NO_PROXY and no_proxy", () => {
const fakeDataDir = join(tmpdir(), `nemoclaw-bashi-test-${process.pid}`);
execFileSync("mkdir", ["-p", fakeDataDir]);
const fakeDataDir = mkdtempSync(join(tmpdir(), "nemoclaw-bashi-test-"));
try {
const envContent = [
'export HTTP_PROXY="http://10.200.0.1:3128"',
Expand Down
Loading