openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
187 lines (186 loc) • 6.81 kB
JavaScript
import { t as hasErrnoCode } from "./errno-CkbDOfLk.js";
import { c as resolveAgentConfig, o as listAgentIds } from "./agent-scope-config-DcbEhP0R.js";
import { n as isManagedGitHubProfileId } from "./github-identity-profile-id-BJzGq1wi.js";
import "./agent-scope-DbtJyKUL.js";
import { _ as resolveManagedGitHubProfileRoot, h as resolveManagedGitHubAgentKey, w as listGitHubOAuthRecords } from "./github-tool-identity-CsNyDjEx.js";
import path from "node:path";
import fs from "node:fs/promises";
//#region src/agents/github-tool-profile-cleanup.ts
const MAX_CLEANUP_WARNINGS = 20;
const STAGING_PROFILE_PREFIX = ".github-profile.staging-";
const MANAGED_AGENT_KEY_PATTERN = /^[a-f0-9]{64}$/u;
async function cleanupProfileRoot(params) {
let rootStat;
try {
rootStat = await fs.lstat(params.root);
} catch (error) {
if (hasErrnoCode(error, "ENOENT")) return 0;
throw error;
}
if (!rootStat.isDirectory() || rootStat.isSymbolicLink()) {
params.warnings.push(`refused non-directory managed GitHub profile root: ${params.root}`);
return 0;
}
const root = path.resolve(params.root);
if (await fs.realpath(root) !== root) {
params.warnings.push(`refused symlinked managed GitHub profile root: ${params.root}`);
return 0;
}
let removed = 0;
for (const entry of await fs.readdir(root, { withFileTypes: true })) {
const candidate = path.join(root, entry.name);
const isProfile = isManagedGitHubProfileId(entry.name);
const isStaging = entry.name.startsWith(STAGING_PROFILE_PREFIX);
if (!isProfile && !isStaging) {
params.warnings.push(`ignored unexpected managed GitHub profile entry: ${candidate}`);
continue;
}
let stat;
try {
stat = await fs.lstat(candidate);
} catch (error) {
if (hasErrnoCode(error, "ENOENT")) continue;
throw error;
}
if (!stat.isDirectory() || stat.isSymbolicLink()) {
params.warnings.push(`refused unsafe managed GitHub profile cleanup candidate: ${candidate}`);
continue;
}
if (isProfile && params.preservedProfileIds.has(entry.name)) continue;
const resolved = await fs.realpath(candidate);
if (path.dirname(resolved) !== root || path.basename(resolved) !== entry.name) {
params.warnings.push(`refused escaped managed GitHub profile cleanup candidate: ${candidate}`);
continue;
}
await fs.rm(candidate, { recursive: true });
removed += 1;
}
return removed;
}
async function validateDirectDirectory(params) {
let stat;
try {
stat = await fs.lstat(params.candidate);
} catch (error) {
if (hasErrnoCode(error, "ENOENT")) return false;
throw error;
}
if (!stat.isDirectory() || stat.isSymbolicLink()) {
params.warnings.push(`refused unsafe ${params.label}: ${params.candidate}`);
return false;
}
const resolved = await fs.realpath(params.candidate);
if (path.dirname(resolved) !== params.parent || path.basename(resolved) !== path.basename(params.candidate)) {
params.warnings.push(`refused escaped ${params.label}: ${params.candidate}`);
return false;
}
return true;
}
async function removeOrphanAgentRoot(params) {
if (!await validateDirectDirectory({
candidate: params.root,
parent: params.registryRoot,
warnings: params.warnings,
label: "managed GitHub agent profile root"
})) return 0;
for (const entry of await fs.readdir(params.root, { withFileTypes: true })) {
if (!isManagedGitHubProfileId(entry.name) && !entry.name.startsWith(STAGING_PROFILE_PREFIX)) {
params.warnings.push(`ignored unexpected managed GitHub agent profile entry: ${path.join(params.root, entry.name)}`);
return 0;
}
if (!await validateDirectDirectory({
candidate: path.join(params.root, entry.name),
parent: params.root,
warnings: params.warnings,
label: "managed GitHub agent profile cleanup candidate"
})) return 0;
}
await fs.rm(params.root, { recursive: true });
return 1;
}
async function cleanupAgentProfileRegistry(params) {
let rootStat;
try {
rootStat = await fs.lstat(params.root);
} catch (error) {
if (hasErrnoCode(error, "ENOENT")) return 0;
throw error;
}
if (!rootStat.isDirectory() || rootStat.isSymbolicLink()) {
params.warnings.push(`refused non-directory managed GitHub agent registry: ${params.root}`);
return 0;
}
const registryRoot = path.resolve(params.root);
if (await fs.realpath(registryRoot) !== registryRoot) {
params.warnings.push(`refused symlinked managed GitHub agent registry: ${params.root}`);
return 0;
}
let removed = 0;
for (const entry of await fs.readdir(registryRoot, { withFileTypes: true })) {
const candidate = path.join(registryRoot, entry.name);
if (!MANAGED_AGENT_KEY_PATTERN.test(entry.name)) {
params.warnings.push(`ignored unexpected managed GitHub agent entry: ${candidate}`);
continue;
}
if (params.preservedProfiles.has(entry.name)) {
removed += await cleanupProfileRoot({
root: candidate,
preservedProfileIds: params.preservedProfiles.get(entry.name) ?? /* @__PURE__ */ new Set(),
warnings: params.warnings
});
continue;
}
removed += await removeOrphanAgentRoot({
root: candidate,
registryRoot,
warnings: params.warnings
});
}
return removed;
}
/** Retires only generations unreferenced by the immutable startup config snapshot. */
async function cleanupRetiredManagedGitHubProfiles(params) {
const warnings = [];
const systemRoot = resolveManagedGitHubProfileRoot({
agentId: "system",
scope: "system",
env: params.env
});
const systemProfiles = new Set(params.config.tools?.github?.profileId ? [params.config.tools.github.profileId] : []);
const agentProfiles = new Map(listAgentIds(params.config).map((agentId) => {
const profileId = resolveAgentConfig(params.config, agentId)?.tools?.github?.profileId;
return [resolveManagedGitHubAgentKey(agentId), new Set(profileId ? [profileId] : [])];
}));
for (const { record } of listGitHubOAuthRecords()) {
if (!record) continue;
if (record.scope === "system") {
systemProfiles.add(record.profileId);
continue;
}
const agentKey = resolveManagedGitHubAgentKey(record.agentId);
const profiles = agentProfiles.get(agentKey) ?? /* @__PURE__ */ new Set();
profiles.add(record.profileId);
agentProfiles.set(agentKey, profiles);
}
let removed = await cleanupProfileRoot({
root: systemRoot,
preservedProfileIds: systemProfiles,
warnings
});
removed += await cleanupAgentProfileRegistry({
root: path.join(path.dirname(systemRoot), "agents"),
preservedProfiles: agentProfiles,
warnings
});
if (warnings.length <= MAX_CLEANUP_WARNINGS) return {
removed,
warnings
};
const omitted = warnings.length - MAX_CLEANUP_WARNINGS;
return {
removed,
warnings: [...warnings.slice(0, MAX_CLEANUP_WARNINGS), `omitted ${omitted} additional managed GitHub profile cleanup warnings`]
};
}
//#endregion
export { cleanupRetiredManagedGitHubProfiles };