



























@@ -1,10 +1,14 @@
11import "../infra/fs-safe-defaults.js";
2-import fs from "node:fs/promises";
32import {
43acquireFileLock as acquireFsSafeFileLock,
54drainFileLockManagerForTest,
65resetFileLockManagerForTest,
76} from "@openclaw/fs-safe/file-lock";
7+import {
8+readLockFileOwnerPayload,
9+removeReportedStaleLockIfStillStale,
10+shouldRemoveDeadOwnerOrExpiredLock,
11+} from "../infra/stale-lock-file.js";
812import { isPidAlive } from "../shared/pid-alive.js";
9131014export type FileLockOptions = {
@@ -18,11 +22,6 @@ export type FileLockOptions = {
1822stale: number;
1923};
202421-type LockFilePayload = {
22-pid?: number;
23-createdAt?: string;
24-};
25-2625export type FileLockHandle = {
2726lockPath: string;
2827release: () => Promise<void>;
@@ -43,28 +42,13 @@ export type FileLockStaleError = Error & {
43424443const FILE_LOCK_MANAGER_KEY = "openclaw.plugin-sdk.file-lock";
454446-type LockFileSnapshot = {
47-raw: string;
48-payload: Record<string, unknown> | null;
49-};
50-51-function readLockPayload(value: Record<string, unknown> | null): LockFilePayload | null {
52-if (!value) {
53-return null;
54-}
55-return {
56-pid: typeof value.pid === "number" ? value.pid : undefined,
57-createdAt: typeof value.createdAt === "string" ? value.createdAt : undefined,
58-};
59-}
60-6145async function shouldReclaimPluginLock(params: {
6246lockPath: string;
6347payload: Record<string, unknown> | null;
6448staleMs: number;
6549nowMs: number;
6650}): Promise<boolean> {
67-const payload = readLockPayload(params.payload);
51+const payload = readLockFileOwnerPayload(params.payload);
6852if (payload?.pid && !isPidAlive(payload.pid)) {
6953return true;
7054}
@@ -79,81 +63,6 @@ function isFileLockError(error: unknown, code: string): boolean {
7963return (error as { code?: unknown } | null)?.code === code;
8064}
816582-async function readLockFileSnapshot(lockPath: string): Promise<LockFileSnapshot | null> {
83-let raw: string;
84-try {
85-raw = await fs.readFile(lockPath, "utf8");
86-} catch (err) {
87-if ((err as NodeJS.ErrnoException).code === "ENOENT") {
88-return null;
89-}
90-throw err;
91-}
92-93-try {
94-const parsed = JSON.parse(raw) as unknown;
95-return {
96- raw,
97-payload:
98-parsed && typeof parsed === "object" && !Array.isArray(parsed)
99- ? (parsed as Record<string, unknown>)
100- : null,
101-};
102-} catch {
103-return { raw, payload: null };
104-}
105-}
106-107-function shouldRemoveReportedStalePluginLock(params: {
108-payload: Record<string, unknown> | null;
109-staleMs: number;
110-nowMs: number;
111-}): boolean {
112-const payload = readLockPayload(params.payload);
113-if (payload?.pid) {
114-return !isPidAlive(payload.pid);
115-}
116-if (payload?.createdAt) {
117-const createdAt = Date.parse(payload.createdAt);
118-return !Number.isFinite(createdAt) || params.nowMs - createdAt > params.staleMs;
119-}
120-return true;
121-}
122-123-async function removeReportedStaleLockIfStillStale(params: {
124-lockPath: string;
125-staleMs: number;
126-}): Promise<boolean> {
127-const snapshot = await readLockFileSnapshot(params.lockPath);
128-if (!snapshot) {
129-return true;
130-}
131-if (
132-!shouldRemoveReportedStalePluginLock({
133-payload: snapshot.payload,
134-staleMs: params.staleMs,
135-nowMs: Date.now(),
136-})
137-) {
138-return false;
139-}
140-141-const current = await readLockFileSnapshot(params.lockPath);
142-if (!current) {
143-return true;
144-}
145-if (current.raw !== snapshot.raw) {
146-return false;
147-}
148-149-try {
150-await fs.unlink(params.lockPath);
151-return true;
152-} catch (err) {
153-return (err as NodeJS.ErrnoException).code === "ENOENT";
154-}
155-}
156-15766function normalizeLockError(err: unknown): never {
15867if ((err as { code?: unknown }).code === FILE_LOCK_TIMEOUT_ERROR_CODE) {
15968throw Object.assign(new Error((err as Error).message), {
@@ -201,7 +110,11 @@ export async function acquireFileLock(
201110lockPath &&
202111(await removeReportedStaleLockIfStillStale({
203112 lockPath,
204-staleMs: options.stale,
113+shouldRemove: (snapshot) =>
114+shouldRemoveDeadOwnerOrExpiredLock({
115+payload: snapshot.payload,
116+staleMs: options.stale,
117+}),
205118}))
206119) {
207120continue;
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。