openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
148 lines (147 loc) • 5.88 kB
JavaScript
import { d as asPositiveSafeInteger } from "./number-coercion-CLj0HTDM.js";
import { c as isRecord } from "./record-coerce-DItp3I4t.js";
import { i as normalizeBoundedOptionalString } from "./string-coerce-CIXf7egm.js";
import "./string-coerce-runtime-GQa0ehRA.js";
import { t as createDirtyDirectoryWatch } from "./session-catalog-tree-watch-CQi4yGOk.js";
import { i as childDirectories, o as desktopSessionsDir, p as setBoundedCache, u as readJsonFile } from "./session-catalog-scan-4wxzSKEc.js";
import { t as readClaudeDesktopCustomGroups } from "./claude-desktop-groups-2_aZ-It-.js";
import path from "node:path";
import fs from "node:fs/promises";
//#region extensions/anthropic/session-catalog-desktop.ts
const MAX_STRING_LENGTH = 4096;
const MAX_SESSION_PULL_REQUESTS = 20;
const CLAUDE_DESKTOP_SCAN_TTL_MS = 6e4;
function pullRequestState(value) {
if (typeof value !== "string") return;
const state = value.trim().toLowerCase();
return state === "open" || state === "draft" || state === "merged" || state === "closed" ? state : void 0;
}
function desktopPullRequestSummary(metadata) {
const visibleByNumber = /* @__PURE__ */ new Map();
const dismissed = /* @__PURE__ */ new Set();
if (Array.isArray(metadata.prs)) for (const value of metadata.prs) {
if (!isRecord(value)) continue;
const entry = value;
const number = asPositiveSafeInteger(entry.prNumber);
if (!number) continue;
if (entry.dismissed === true) {
dismissed.add(number);
visibleByNumber.delete(number);
continue;
}
if (!dismissed.has(number) && !visibleByNumber.has(number)) visibleByNumber.set(number, pullRequestState(entry.state));
}
const currentNumber = asPositiveSafeInteger(metadata.prNumber);
let currentState = currentNumber ? visibleByNumber.get(currentNumber) : void 0;
if (currentNumber && !dismissed.has(currentNumber)) {
currentState = pullRequestState(metadata.prState) ?? currentState;
visibleByNumber.delete(currentNumber);
visibleByNumber.set(currentNumber, currentState);
}
const visible = [...visibleByNumber].map(([number, state]) => ({
number,
state
}));
if (visible.length === 0) return;
const state = currentState ?? visible.at(-1)?.state;
if (!state) return;
return {
numbers: visible.slice(-20).map((entry) => entry.number),
state
};
}
function parsePullRequestSummary(value) {
if (value === void 0) return;
if (!isRecord(value) || !Array.isArray(value.numbers)) throw new Error("Claude node returned an invalid pull request summary");
const numbers = value.numbers.flatMap((candidate) => {
const number = asPositiveSafeInteger(candidate);
return number === void 0 ? [] : [number];
});
const state = pullRequestState(value.state);
if (numbers.length === 0 || numbers.length !== value.numbers.length || numbers.length > MAX_SESSION_PULL_REQUESTS || new Set(numbers).size !== numbers.length || !state) throw new Error("Claude node returned an invalid pull request summary");
return {
numbers,
state
};
}
async function readDesktopMetadata(homeDir, forceRefresh) {
const active = /* @__PURE__ */ new Map();
const archived = /* @__PURE__ */ new Set();
const customGroups = await readClaudeDesktopCustomGroups(homeDir, forceRefresh);
for (const accountDir of await childDirectories(desktopSessionsDir(homeDir))) for (const workspaceDir of await childDirectories(accountDir)) {
let entries;
try {
entries = await fs.readdir(workspaceDir);
} catch {
continue;
}
for (const name of entries) {
if (!name.startsWith("local_") || !name.endsWith(".json")) continue;
const raw = await readJsonFile(path.join(workspaceDir, name));
if (!isRecord(raw)) continue;
const metadata = raw;
const cliSessionId = normalizeBoundedOptionalString(metadata.cliSessionId, 256);
if (!cliSessionId) continue;
if (metadata.isArchived === true) {
archived.add(cliSessionId);
active.delete(cliSessionId);
continue;
}
if (!archived.has(cliSessionId)) {
const localSessionId = normalizeBoundedOptionalString(metadata.sessionId, 256);
const customGroup = localSessionId ? customGroups.get(localSessionId) : void 0;
active.set(cliSessionId, customGroup ? {
...metadata,
customGroup
} : metadata);
}
}
}
return {
available: true,
active,
archived,
customGroups
};
}
const desktopOverlays = /* @__PURE__ */ new Map();
const emptyDesktopOverlay = {
available: false,
active: /* @__PURE__ */ new Map(),
archived: /* @__PURE__ */ new Set(),
customGroups: /* @__PURE__ */ new Map()
};
async function readDesktopOverlay(homeDir, forceRefresh) {
const entry = desktopOverlays.get(homeDir);
if (entry?.refreshing) {
if (!forceRefresh) return entry.overlay;
await entry.overlay;
return readDesktopOverlay(homeDir, forceRefresh);
}
const dirty = entry?.watch?.takeDirty();
if (!forceRefresh && entry && entry.refreshedAt + CLAUDE_DESKTOP_SCAN_TTL_MS > Date.now() && !(dirty instanceof Set && dirty.size > 0)) {
setBoundedCache(desktopOverlays, homeDir, entry, 8, (evicted) => evicted.watch?.close());
return entry.overlay;
}
const watch = entry?.watch ?? createDirtyDirectoryWatch(desktopSessionsDir(homeDir));
const current = {
watch,
refreshedAt: Date.now(),
refreshing: true,
overlay: Promise.resolve(emptyDesktopOverlay)
};
current.overlay = (async () => {
if (!(await fs.stat(desktopSessionsDir(homeDir)).catch(() => void 0))?.isDirectory()) {
watch.close();
current.watch = void 0;
return emptyDesktopOverlay;
}
return readDesktopMetadata(homeDir, forceRefresh);
})().finally(() => {
current.refreshing = false;
});
setBoundedCache(desktopOverlays, homeDir, current, 8, (evicted) => evicted.watch?.close());
return current.overlay;
}
//#endregion
export { readDesktopOverlay as a, parsePullRequestSummary as i, desktopPullRequestSummary as n, emptyDesktopOverlay as r, MAX_STRING_LENGTH as t };