UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

187 lines (186 loc) 6.81 kB
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 };