import { readFileSync } from 'node:fs'; import { join } from 'node:path'; import { logger } from '../../util/logger.js'; import { findFileByPrefix, resolveOpenClawDistDir } from './locator.js'; import { restoreFile } from './backup.js'; export type RemovedRevertResult = 'none' | 'restored' | 'failed'; export type RemovedUIExtension = { id: string; /** Which shared file group this removed extension belongs to (if any). */ sharedFile?: string; revert(controlUiDir: string): RemovedRevertResult; }; function fileNeedsRemoval(filepath: string, id: string, fingerprints: string[]): boolean { const content = readFileSync(filepath, 'utf-8'); return content.includes(`/*clawlink:${id}`) || fingerprints.some(fp => content.includes(fp)); } function restoreRemovedPatch(filepath: string | null, id: string, fingerprints: string[]): RemovedRevertResult { if (!filepath) return 'none'; if (!fileNeedsRemoval(filepath, id, fingerprints)) return 'none'; const restored = restoreFile(filepath); if (!restored) { logger.warn(`[ui-ext] ${id}: removed extension is present but no backup exists; refusing to continue`); return 'failed'; } logger.info(`[ui-ext] ${id}: removed extension reverted from backup`); return 'restored'; } export const REMOVED_UI_EXTENSIONS: RemovedUIExtension[] = [ { id: 'gateway-clawlink-tool-payload', revert(controlUiDir: string): RemovedRevertResult { const distDir = resolveOpenClawDistDir(controlUiDir); const gatewayPath = distDir ? findFileByPrefix(distDir, 'gateway-cli-') : null; return restoreRemovedPatch(gatewayPath, this.id, [ '__clawlinkToolCallIds', '__clawlinkKeepToolPayload', '__clawlinkEffectiveToolVerbose', ]); }, }, { id: 'agent-event-clawlink-tool-result', revert(controlUiDir: string): RemovedRevertResult { const distDir = resolveOpenClawDistDir(controlUiDir); const agentBundlePath = distDir ? findFileByPrefix(distDir, 'auth-profiles-') : null; return restoreRemovedPatch(agentBundlePath, this.id, [ '__clawlinkIsRemoteToolName', ]); }, }, { id: 'marked-expose', sharedFile: 'index-bundle', revert(controlUiDir: string): RemovedRevertResult { const assetsDir = join(controlUiDir, 'assets'); const bundlePath = findFileByPrefix(assetsDir, 'index-'); return restoreRemovedPatch(bundlePath, this.id, [ 'window.__clawlinkMarked=', '__clawlinkMarked', ]); }, }, ];