diff --git a/src/infra/local-tui-processes.test.ts b/src/infra/local-tui-processes.test.ts index b8dcdf78e9b7..fcccb27b9239 100644 --- a/src/infra/local-tui-processes.test.ts +++ b/src/infra/local-tui-processes.test.ts @@ -2,11 +2,18 @@ import { describe, expect, it, vi } from "vitest"; import { listLocalTuiProcesses, quiesceLocalTuiProcessesBeforeUpdate, + resolveLocalTuiUpdateLockPath, terminateLocalTuiProcesses, waitForLocalTuiUpdate, } from "./local-tui-processes.js"; describe("local TUI processes", () => { + it("scopes the update gate to the invoking user's home", () => { + expect(resolveLocalTuiUpdateLockPath("/home/alice")).not.toBe( + resolveLocalTuiUpdateLockPath("/home/bob"), + ); + }); + it("lists only verified local TUI processes from ps output", () => { const spawnSync = vi.fn().mockReturnValue({ status: 0, diff --git a/src/infra/local-tui-processes.ts b/src/infra/local-tui-processes.ts index d3682b5ee367..39ab585bc2df 100644 --- a/src/infra/local-tui-processes.ts +++ b/src/infra/local-tui-processes.ts @@ -1,4 +1,5 @@ import { spawnSync, type SpawnSyncOptionsWithStringEncoding } from "node:child_process"; +import { createHash } from "node:crypto"; import os from "node:os"; import path from "node:path"; import { parseCmdScriptCommandLine } from "../daemon/cmd-argv.js"; @@ -29,7 +30,12 @@ type PsResult = { const LOCAL_TUI_SUBCOMMANDS = new Set(["chat", "terminal", "tui"]); const LOCAL_TUI_PROCESS_PROBE_TIMEOUT_MS = 1_000; -const LOCAL_TUI_UPDATE_LOCK_PATH = path.join(os.tmpdir(), "openclaw-local-tui-update"); +export function resolveLocalTuiUpdateLockPath(userHome = os.homedir()): string { + const ownerScope = createHash("sha256").update(userHome).digest("hex").slice(0, 16); + return path.join(os.tmpdir(), `openclaw-local-tui-update-${ownerScope}`); +} + +const LOCAL_TUI_UPDATE_LOCK_PATH = resolveLocalTuiUpdateLockPath(); const LOCAL_TUI_UPDATE_LOCK_OPTIONS = { stale: 30_000, retries: { retries: 100, factor: 1, minTimeout: 50, maxTimeout: 250 },