openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
191 lines (190 loc) • 9.58 kB
JavaScript
import { a as readJsonObject, i as isDirectory, o as resolveHomePath, r as exists } from "./helpers-CpgMg4fg.js";
import path from "node:path";
import fs from "node:fs/promises";
import os from "node:os";
import crypto from "node:crypto";
//#region extensions/migrate-claude/source.ts
const CLAUDE_AUTO_MEMORY_MAX_FILES = 2e3;
const CLAUDE_AUTO_MEMORY_MAX_SCAN_ENTRIES = 2e4;
const HOME_ARCHIVE_DIRS = [
"projects",
"cache",
"plans"
];
const PROJECT_ARCHIVE_FILES = [".claude/scheduled_tasks.json"];
function defaultClaudeHome() {
const configuredDir = process.env.CLAUDE_CONFIG_DIR;
return configuredDir ? resolveHomePath(configuredDir) : path.join(os.homedir(), ".claude");
}
function defaultDesktopConfig() {
return path.join(os.homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json");
}
async function addArchivePath(archivePaths, id, candidate, relativePath) {
if (await exists(candidate) || await isDirectory(candidate)) archivePaths.push({
id,
path: candidate,
relativePath
});
}
async function safeReadDir(dir) {
try {
return await fs.readdir(dir, { withFileTypes: true });
} catch (error) {
const code = error && typeof error === "object" && "code" in error ? String(error.code) : void 0;
if (code === "ENOENT" || code === "ENOTDIR") return [];
throw new Error(`Unable to read Claude Code projects directory: ${dir}`, { cause: error });
}
}
async function readMemoryDir(dir) {
try {
return await fs.readdir(dir, { withFileTypes: true });
} catch (error) {
throw new Error(`Unable to read Claude Code auto-memory directory: ${dir}`, { cause: error });
}
}
async function isConfiguredAutoMemoryDirectory(dir) {
try {
return (await fs.stat(dir)).isDirectory();
} catch (error) {
const code = error && typeof error === "object" && "code" in error ? String(error.code) : void 0;
if (code === "ENOENT" || code === "ENOTDIR") return false;
throw new Error(`Unable to access configured Claude Code auto-memory directory: ${dir}`, { cause: error });
}
}
async function probeMarkdownFiles(root) {
const pending = [root];
let visited = 0;
while (pending.length > 0) {
const current = pending.pop();
if (!current) break;
for (const entry of await readMemoryDir(current)) {
visited += 1;
if (visited > 2e4) return "truncated";
if (entry.isFile() && entry.name.toLowerCase().endsWith(".md")) return "found";
if (entry.isDirectory()) pending.push(path.join(current, entry.name));
}
}
return "absent";
}
function autoMemorySourceId(sourcePath) {
return crypto.createHash("sha256").update(path.resolve(sourcePath)).digest("hex").slice(0, 10);
}
async function discoverAutoMemorySources(params) {
const candidates = [];
if (params.homeProjectsDir) for (const entry of await safeReadDir(params.homeProjectsDir)) {
if (!entry.isDirectory()) continue;
candidates.push({
label: entry.name,
path: path.join(params.homeProjectsDir, entry.name, "memory")
});
}
const customDirectory = (await readJsonObject(params.userSettingsPath)).autoMemoryDirectory;
if (typeof customDirectory === "string" && customDirectory.trim()) {
const configuredPath = customDirectory.trim();
if (!path.isAbsolute(configuredPath) && !configuredPath.startsWith("~/")) throw new Error("Claude autoMemoryDirectory must be absolute or start with ~/.");
const customPath = resolveHomePath(configuredPath);
candidates.push({
configured: true,
label: path.basename(customPath) || "custom",
path: customPath
});
}
if (path.basename(params.root) === "memory") candidates.push({
label: path.basename(path.dirname(params.root)) || "project",
path: params.root
});
const seen = /* @__PURE__ */ new Set();
const sources = [];
for (const candidate of candidates) {
if (!(candidate.configured ? await isConfiguredAutoMemoryDirectory(candidate.path) : await isDirectory(candidate.path))) continue;
if (await probeMarkdownFiles(candidate.path) === "absent") continue;
const canonical = await fs.realpath(candidate.path).catch(() => path.resolve(candidate.path));
if (seen.has(canonical)) continue;
seen.add(canonical);
sources.push({
id: autoMemorySourceId(canonical),
label: candidate.label,
path: candidate.path
});
}
return sources.toSorted((left, right) => left.label.localeCompare(right.label));
}
async function discoverClaudeSource(input) {
const explicitInput = Boolean(input?.trim());
const root = resolveHomePath(input?.trim() || defaultClaudeHome());
const rootIsHome = path.basename(root) === ".claude" || !explicitInput && root === defaultClaudeHome();
const inspectGlobal = !explicitInput || rootIsHome;
const homeDir = inspectGlobal ? rootIsHome ? root : defaultClaudeHome() : void 0;
const projectDir = rootIsHome ? void 0 : root;
const archivePaths = [];
const userSettingsPath = homeDir ? path.join(homeDir, "settings.json") : void 0;
const userLocalSettingsPath = homeDir ? path.join(homeDir, "settings.local.json") : void 0;
const userClaudeJsonPath = inspectGlobal ? path.join(os.homedir(), ".claude.json") : void 0;
const userMemoryPath = homeDir ? path.join(homeDir, "CLAUDE.md") : void 0;
const desktopConfigPath = inspectGlobal ? defaultDesktopConfig() : void 0;
const homeProjectsDir = homeDir ? path.join(homeDir, "projects") : void 0;
const userSkillsDir = homeDir ? path.join(homeDir, "skills") : void 0;
const userCommandsDir = homeDir ? path.join(homeDir, "commands") : void 0;
const userAgentsDir = homeDir ? path.join(homeDir, "agents") : void 0;
if (homeDir) for (const dir of HOME_ARCHIVE_DIRS) await addArchivePath(archivePaths, `archive:home:${dir}`, path.join(homeDir, dir), dir);
const source = {
root,
confidence: "low",
autoMemorySources: [],
archivePaths,
...homeDir && await isDirectory(homeDir) ? { homeDir } : {},
...homeProjectsDir && await isDirectory(homeProjectsDir) ? { homeProjectsDir } : {},
...projectDir ? { projectDir } : {},
...userSettingsPath && await exists(userSettingsPath) ? { userSettingsPath } : {},
...userLocalSettingsPath && await exists(userLocalSettingsPath) ? { userLocalSettingsPath } : {},
...userClaudeJsonPath && await exists(userClaudeJsonPath) ? { userClaudeJsonPath } : {},
...userMemoryPath && await exists(userMemoryPath) ? { userMemoryPath } : {},
...userSkillsDir && await isDirectory(userSkillsDir) ? { userSkillsDir } : {},
...userCommandsDir && await isDirectory(userCommandsDir) ? { userCommandsDir } : {},
...userAgentsDir && await isDirectory(userAgentsDir) ? { userAgentsDir } : {},
...desktopConfigPath && await exists(desktopConfigPath) ? { desktopConfigPath } : {}
};
if (projectDir) {
const projectSettingsPath = path.join(projectDir, ".claude", "settings.json");
const projectLocalSettingsPath = path.join(projectDir, ".claude", "settings.local.json");
const projectMcpPath = path.join(projectDir, ".mcp.json");
const projectMemoryPath = path.join(projectDir, "CLAUDE.md");
const projectDotClaudeMemoryPath = path.join(projectDir, ".claude", "CLAUDE.md");
const projectLocalMemoryPath = path.join(projectDir, "CLAUDE.local.md");
const projectRulesDir = path.join(projectDir, ".claude", "rules");
const projectSkillsDir = path.join(projectDir, ".claude", "skills");
const projectCommandsDir = path.join(projectDir, ".claude", "commands");
const projectAgentsDir = path.join(projectDir, ".claude", "agents");
Object.assign(source, {
...await exists(projectSettingsPath) ? { projectSettingsPath } : {},
...await exists(projectLocalSettingsPath) ? { projectLocalSettingsPath } : {},
...await exists(projectMcpPath) ? { projectMcpPath } : {},
...await exists(projectMemoryPath) ? { projectMemoryPath } : {},
...await exists(projectDotClaudeMemoryPath) ? { projectDotClaudeMemoryPath } : {},
...await exists(projectLocalMemoryPath) ? { projectLocalMemoryPath } : {},
...await isDirectory(projectRulesDir) ? { projectRulesDir } : {},
...await isDirectory(projectSkillsDir) ? { projectSkillsDir } : {},
...await isDirectory(projectCommandsDir) ? { projectCommandsDir } : {},
...await isDirectory(projectAgentsDir) ? { projectAgentsDir } : {}
});
for (const file of PROJECT_ARCHIVE_FILES) await addArchivePath(archivePaths, `archive:project:${file}`, path.join(projectDir, file), file);
}
source.autoMemorySources = await discoverAutoMemorySources({
root,
homeProjectsDir: source.homeProjectsDir,
userSettingsPath: source.userSettingsPath
});
const claudeJson = await readJsonObject(source.userClaudeJsonPath);
const hasClaudeJsonState = Boolean(claudeJson.mcpServers || claudeJson.projects);
const desktopConfig = await readJsonObject(source.desktopConfigPath);
const hasDesktopMcp = Boolean(desktopConfig.mcpServers);
const high = Boolean(source.userSettingsPath || source.userMemoryPath || source.projectSettingsPath || source.projectMcpPath || source.projectMemoryPath || source.projectDotClaudeMemoryPath || hasClaudeJsonState || hasDesktopMcp);
const medium = Boolean(source.userSkillsDir || source.projectSkillsDir || source.userCommandsDir || source.projectCommandsDir || source.userAgentsDir || source.projectAgentsDir || source.projectRulesDir || source.projectLocalMemoryPath || source.homeProjectsDir || source.autoMemorySources.length > 0);
source.confidence = high ? "high" : medium ? "medium" : "low";
return source;
}
function hasClaudeSource(source) {
return source.confidence !== "low";
}
//#endregion
export { hasClaudeSource as i, CLAUDE_AUTO_MEMORY_MAX_SCAN_ENTRIES as n, discoverClaudeSource as r, CLAUDE_AUTO_MEMORY_MAX_FILES as t };