




















1+import { spawn } from "node:child_process";
12import fsSync from "node:fs";
23import fs from "node:fs/promises";
34import os from "node:os";
45import path from "node:path";
56import { MAX_TIMER_TIMEOUT_MS } from "@openclaw/normalization-core/number-coercion";
67import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
8+import { SessionWriteLockStaleError } from "./session-write-lock-error.js";
79810const FAKE_STARTTIME = 12345;
911let testing: typeof import("./session-write-lock.js").testing;
@@ -119,12 +121,13 @@ async function withSymlinkedSessionPaths(
119121120122async function expectActiveInProcessLockIsNotReclaimed(params?: {
121123legacyStarttime?: unknown;
124+createdAt?: string;
122125}): Promise<void> {
123126await withTempSessionLockFile(async ({ sessionFile, lockPath }) => {
124127const lock = await acquireSessionWriteLock({ sessionFile, timeoutMs: 500 });
125128const lockPayload = {
126129pid: process.pid,
127-createdAt: new Date().toISOString(),
130+createdAt: params?.createdAt ?? new Date().toISOString(),
128131 ...(params && "legacyStarttime" in params ? { starttime: params.legacyStarttime } : {}),
129132};
130133await fs.writeFile(lockPath, JSON.stringify(lockPayload), "utf8");
@@ -410,6 +413,46 @@ describe("acquireSessionWriteLock", () => {
410413});
411414});
412415416+it("does not report or remove active in-process locks that pass staleMs", async () => {
417+await expectActiveInProcessLockIsNotReclaimed({
418+createdAt: new Date(Date.now() - 120_000).toISOString(),
419+});
420+});
421+422+it("reports live OpenClaw-owned stale locks without removing them", async () => {
423+await withTempSessionLockFile(async ({ sessionFile, lockPath }) => {
424+const owner = spawn(process.execPath, ["-e", "setInterval(() => {}, 1000)", "openclaw"], {
425+stdio: "ignore",
426+});
427+if (!owner.pid) {
428+throw new Error("missing lock owner pid");
429+}
430+await fs.writeFile(
431+lockPath,
432+JSON.stringify({
433+pid: owner.pid,
434+createdAt: new Date(Date.now() - 120_000).toISOString(),
435+}),
436+"utf8",
437+);
438+439+try {
440+await expect(
441+acquireSessionWriteLock({ sessionFile, timeoutMs: 500, staleMs: 10 }),
442+).rejects.toMatchObject({
443+name: "SessionWriteLockStaleError",
444+staleReasons: ["too-old"],
445+});
446+await expect(
447+acquireSessionWriteLock({ sessionFile, timeoutMs: 500, staleMs: 10 }),
448+).rejects.toBeInstanceOf(SessionWriteLockStaleError);
449+await expect(fs.access(lockPath)).resolves.toBeUndefined();
450+} finally {
451+owner.kill("SIGTERM");
452+}
453+});
454+});
455+413456it("watchdog releases stale in-process locks", async () => {
414457const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-lock-"));
415458const stderrSpy = vi.spyOn(process.stderr, "write").mockImplementation(() => true);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。