openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
96 lines (95 loc) • 2.64 kB
JavaScript
import fs from "node:fs";
import path from "node:path";
//#region extensions/anthropic/session-catalog-tree-watch.ts
const WATCH_RETRY_MS = 5e3;
const WATCH_ARM_MS = 250;
function createDirtyDirectoryWatch(root) {
const recursive = process.platform === "darwin" || process.platform === "win32";
const children = /* @__PURE__ */ new Map();
let rootWatch;
let dirty = /* @__PURE__ */ new Set();
let retryAt = 0;
let attachedAt = 0;
let armed = false;
let closed = false;
const closeWatchers = () => {
rootWatch?.close();
rootWatch = void 0;
for (const watcher of children.values()) watcher.close();
children.clear();
};
const fail = () => {
closeWatchers();
dirty = "all";
retryAt = Date.now() + WATCH_RETRY_MS;
};
const attach = (child) => {
try {
if (!recursive && process.platform !== "linux") {
fail();
return;
}
return fs.watch(child ? path.join(root, child) : root, {
recursive,
persistent: false
}, (event, filename) => {
const name = child ?? filename?.split(/[\\/]/, 1)[0];
if (!name || !child && event === "rename" && filename === path.basename(root)) dirty = "all";
else if (dirty !== "all") dirty.add(name);
if (!child && event === "rename" && name) {
children.get(name)?.close();
children.delete(name);
}
}).on("error", fail);
} catch (error) {
const code = error && typeof error === "object" && "code" in error ? error.code : void 0;
if (child && (code === "ENOENT" || code === "ENOTDIR")) return;
fail();
return;
}
};
const attachRoot = () => {
rootWatch = attach();
attachedAt = performance.now();
armed = false;
dirty = rootWatch ? /* @__PURE__ */ new Set() : "all";
};
attachRoot();
return {
takeDirty() {
if (closed || !rootWatch) {
if (!closed && Date.now() >= retryAt) attachRoot();
return "all";
}
if (!armed) {
armed = performance.now() - attachedAt >= WATCH_ARM_MS;
return "all";
}
const result = dirty;
if (result === "all") {
closeWatchers();
attachRoot();
} else dirty = /* @__PURE__ */ new Set();
return result;
},
observeChildDirectories(names) {
if (recursive || closed || !rootWatch) return;
const wanted = new Set(names);
for (const [name, watcher] of children) if (!wanted.has(name)) {
watcher.close();
children.delete(name);
}
for (const name of wanted) if (!children.has(name)) {
const watcher = attach(name);
if (!rootWatch) break;
if (watcher) children.set(name, watcher);
}
},
close() {
closed = true;
closeWatchers();
}
};
}
//#endregion
export { createDirtyDirectoryWatch as t };