openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
167 lines (166 loc) • 6.61 kB
JavaScript
import { i as isPathInside } from "./path-BlG8lhgR.js";
import { f as resolveHomeDir, h as shortenHomeInString } from "./utils-CCC-BEJH.js";
import "./path-guards-CBe_wA_B.js";
import { n as listAgentIds, o as resolveAgentWorkspaceDir } from "./agent-scope-config-CgCYpZfK.js";
import { n as resolveDefaultAgentWorkspaceDir } from "./workspace-default-B3AjhIK3.js";
import { S as resolveWorkspaceAttestationPaths, w as shouldRemoveWorkspaceAttestation } from "./workspace-B0bmoo98.js";
import path from "node:path";
import fs from "node:fs/promises";
//#region src/commands/cleanup-utils.ts
function collectWorkspaceDirs(cfg) {
const dirs = /* @__PURE__ */ new Set();
if (!cfg) {
dirs.add(resolveDefaultAgentWorkspaceDir());
return [...dirs];
}
for (const agentId of listAgentIds(cfg)) dirs.add(resolveAgentWorkspaceDir(cfg, agentId));
return [...dirs];
}
/** Determine which config, credential, and workspace paths cleanup should consider. */
function buildCleanupPlan(params) {
return {
configInsideState: isPathWithin(params.configPath, params.stateDir),
oauthInsideState: isPathWithin(params.oauthDir, params.stateDir),
workspaceDirs: collectWorkspaceDirs(params.cfg)
};
}
/** Return true when `child` resolves inside `parent`. */
function isPathWithin(child, parent) {
return isPathInside(parent, child);
}
function isUnsafeRemovalTarget(target) {
if (!target.trim()) return true;
const resolved = path.resolve(target);
if (resolved === path.parse(resolved).root) return true;
const home = resolveHomeDir();
if (home && resolved === path.resolve(home)) return true;
if (isPathWithin(path.resolve(process.cwd()), resolved)) return true;
return false;
}
/** Remove one path after rejecting empty/root/home targets and honoring dry-run mode. */
async function removePath(target, runtime, opts) {
if (!target?.trim()) return {
ok: false,
skipped: true
};
const resolved = path.resolve(target);
const displayLabel = shortenHomeInString(opts?.label ?? resolved);
if (isUnsafeRemovalTarget(resolved)) {
runtime.error(`Refusing to remove unsafe path: ${displayLabel}`);
return { ok: false };
}
if (opts?.dryRun) {
runtime.log(`[dry-run] remove ${displayLabel}`);
return {
ok: true,
skipped: true
};
}
try {
await fs.rm(resolved, {
recursive: true,
force: true
});
runtime.log(`Removed ${displayLabel}`);
return { ok: true };
} catch (err) {
runtime.error(`Failed to remove ${displayLabel}: ${String(err)}`);
return { ok: false };
}
}
/** Remove workspace attestation files associated with cleanup-target workspaces. */
async function removeWorkspaceAttestationPaths(workspaceDirs, runtime, opts) {
for (const workspaceDir of workspaceDirs) for (const [index, attestationPath] of resolveWorkspaceAttestationPaths(workspaceDir).entries()) if (await shouldRemoveWorkspaceAttestation(attestationPath, { trustUnknown: index === 0 })) await removePath(attestationPath, runtime, opts);
}
async function existingPaths(paths) {
const existing = [];
for (const target of paths) {
if (!target?.trim()) continue;
const resolved = path.resolve(target);
try {
await fs.lstat(resolved);
existing.push(resolved);
} catch {}
}
return existing;
}
function shouldPreservePath(target, preservePaths) {
return preservePaths.some((preservePath) => isPathWithin(target, preservePath));
}
function pathContainsPreservedPath(target, preservePaths) {
return preservePaths.some((preservePath) => isPathWithin(preservePath, target));
}
async function removePathPreserving(target, preservePaths, runtime, opts) {
if (!target?.trim()) return {
ok: false,
skipped: true
};
const resolved = path.resolve(target);
const displayLabel = shortenHomeInString(opts?.label ?? resolved);
if (isUnsafeRemovalTarget(resolved)) {
runtime.error(`Refusing to remove unsafe path: ${displayLabel}`);
return { ok: false };
}
if (shouldPreservePath(resolved, preservePaths)) return {
ok: true,
skipped: true
};
if (!pathContainsPreservedPath(resolved, preservePaths)) return removePath(resolved, runtime, opts);
if (opts?.dryRun) {
const preserved = preservePaths.filter((preservePath) => isPathWithin(preservePath, resolved)).map((preservePath) => shortenHomeInString(preservePath)).join(", ");
runtime.log(`[dry-run] remove ${displayLabel} preserving ${preserved}`);
return {
ok: true,
skipped: true
};
}
try {
if (!(await fs.lstat(resolved)).isDirectory()) return removePath(resolved, runtime, opts);
const entries = await fs.readdir(resolved);
for (const entry of entries) await removePathPreserving(path.join(resolved, entry), preservePaths, runtime);
runtime.log(`Removed contents of ${displayLabel}`);
return { ok: true };
} catch (err) {
runtime.error(`Failed to remove ${displayLabel}: ${String(err)}`);
return { ok: false };
}
}
/** Remove state plus config/OAuth paths, preserving selected paths nested inside state. */
async function removeStateAndLinkedPaths(cleanup, runtime, opts) {
const stateDir = path.resolve(cleanup.stateDir);
const preservePaths = (opts?.dryRun ? (opts.preservePaths ?? []).map((target) => path.resolve(target)) : await existingPaths(opts?.preservePaths ?? [])).filter((target) => isPathWithin(target, stateDir));
if (preservePaths.length > 0) await removePathPreserving(stateDir, preservePaths, runtime, {
dryRun: opts?.dryRun,
label: cleanup.stateDir
});
else await removePath(cleanup.stateDir, runtime, {
dryRun: opts?.dryRun,
label: cleanup.stateDir
});
if (!cleanup.configInsideState) await removePath(cleanup.configPath, runtime, {
dryRun: opts?.dryRun,
label: cleanup.configPath
});
if (!cleanup.oauthInsideState) await removePath(cleanup.oauthDir, runtime, {
dryRun: opts?.dryRun,
label: cleanup.oauthDir
});
}
/** Remove all workspace directories selected by the cleanup plan. */
async function removeWorkspaceDirs(workspaceDirs, runtime, opts) {
for (const workspace of workspaceDirs) await removePath(workspace, runtime, {
dryRun: opts?.dryRun,
label: workspace
});
}
/** List per-agent session directories beneath a state directory. */
async function listAgentSessionDirs(stateDir) {
const root = path.join(stateDir, "agents");
try {
return (await fs.readdir(root, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => path.join(root, entry.name, "sessions"));
} catch {
return [];
}
}
//#endregion
export { removeStateAndLinkedPaths as a, removePath as i, isPathWithin as n, removeWorkspaceAttestationPaths as o, listAgentSessionDirs as r, removeWorkspaceDirs as s, buildCleanupPlan as t };