























@@ -34,6 +34,11 @@ describe("restart-helper", () => {
3434throw error;
3535}
3636});
37+await fs.rmdir(path.dirname(scriptPath)).catch((error: unknown) => {
38+if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
39+throw error;
40+}
41+});
3742}
38433944async function makeTempDir(prefix: string) {
@@ -135,9 +140,63 @@ exit 0
135140expect(content).toContain("systemctl --user restart 'openclaw-gateway.service'");
136141// Script should self-cleanup
137142expect(content).toContain('rm -f "$0"');
143+expect(content).toContain('rmdir "$script_dir" 2>/dev/null || true');
138144await cleanupScript(scriptPath);
139145});
140146147+it("creates restart scripts in a private temp directory with exclusive creation", async () => {
148+Object.defineProperty(process, "platform", { value: "linux" });
149+const timestamp = 1_727_201_234_567;
150+const oldCandidatePath = path.join(os.tmpdir(), `openclaw-restart-${timestamp}.sh`);
151+const victimDir = await makeTempDir("openclaw-restart-helper-victim-");
152+const victimPath = path.join(victimDir, "restart.sh");
153+await fs.rm(oldCandidatePath, { force: true });
154+await fs.writeFile(victimPath, "preexisting script\n", "utf-8");
155+156+let candidateIsSymlink = false;
157+try {
158+await fs.symlink(victimPath, oldCandidatePath);
159+candidateIsSymlink = true;
160+} catch {
161+await fs.writeFile(oldCandidatePath, "preexisting script\n", { flag: "wx" });
162+}
163+164+const dateSpy = vi.spyOn(Date, "now").mockReturnValue(timestamp);
165+const writeFileSpy = vi.spyOn(fs, "writeFile");
166+167+try {
168+const { scriptPath } = await prepareAndReadScript({
169+OPENCLAW_PROFILE: "default",
170+});
171+const scriptDir = path.dirname(scriptPath);
172+const relativeScriptDir = path.relative(os.tmpdir(), scriptDir);
173+174+expect(scriptPath).not.toBe(oldCandidatePath);
175+expect(scriptDir).not.toBe(os.tmpdir());
176+expect(relativeScriptDir).not.toBe("");
177+expect(relativeScriptDir.startsWith("..")).toBe(false);
178+expect(path.isAbsolute(relativeScriptDir)).toBe(false);
179+expect(path.basename(scriptDir)).toMatch(/^openclaw-restart-/);
180+expect(writeFileSpy).toHaveBeenLastCalledWith(
181+scriptPath,
182+expect.any(String),
183+expect.objectContaining({ flag: "wx", mode: 0o755 }),
184+);
185+await expect(fs.readFile(victimPath, "utf-8")).resolves.toBe("preexisting script\n");
186+if (!candidateIsSymlink) {
187+await expect(fs.readFile(oldCandidatePath, "utf-8")).resolves.toBe(
188+"preexisting script\n",
189+);
190+}
191+await cleanupScript(scriptPath);
192+} finally {
193+dateSpy.mockRestore();
194+writeFileSpy.mockRestore();
195+await fs.rm(oldCandidatePath, { force: true });
196+await fs.rm(victimDir, { recursive: true, force: true });
197+}
198+});
199+141200it("uses OPENCLAW_SYSTEMD_UNIT override for systemd scripts", async () => {
142201Object.defineProperty(process, "platform", { value: "linux" });
143202const { scriptPath, content } = await prepareAndReadScript({
@@ -203,6 +262,7 @@ exit 1
203262expect(content).toContain("launchctl bootstrap 'gui/501'");
204263expect(content).toContain("Bootstrap loads RunAtLoad agents");
205264expect(content).toContain('rm -f "$0"');
265+expect(content).toContain('rmdir "$script_dir" 2>/dev/null || true');
206266await cleanupScript(scriptPath);
207267});
208268@@ -379,6 +439,7 @@ exit 0
379439expect(content).toContain("openclaw restart launched startup fallback");
380440expectWindowsRestartWaitOrdering(content);
381441expect(content).toContain('del "%~f0" >nul 2>&1');
442+expect(content).toContain('rmdir "%OPENCLAW_RESTART_SCRIPT_DIR%" >nul 2>&1');
382443await cleanupScript(scriptPath);
383444});
384445此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。