/** * UIExtension 类型定义与注册表 * * 每个 UIExtension 是一个独立的、可增删的扩展点。 * 框架只关心 id 和 version,具体操作由 apply/revert 内部处理。 */ export interface UIExtension { /** 唯一标识(如 'override-script'、'html-script-tag') */ id: string; /** 内容版本,改了 apply 逻辑就递增 */ version: number; /** * 共享文件标识(可选)。 * * 多个扩展修改同一文件时(如 toolcard-render 和 toolcallid-propagate 都改 index-*.js), * 设置相同的 sharedFile 值。reconciler 会把它们作为一组处理: * 任何一个需要变更 → 恢复 backup → 重新 apply 全组中应保留的扩展。 * * 不设置此字段的扩展独立 apply/revert,不受其他扩展影响。 */ sharedFile?: string; /** * 每次启动都调用 apply(可选)。 * * 默认行为:manifest version 匹配时跳过 apply。 * 设为 true 后:即使 version 不变也调用 apply,由 apply 内部判断是否需要操作。 * 适用于内容可能独立于 version 变化的扩展(如 override-script 的产物文件)。 */ alwaysApply?: boolean; /** * 加载扩展。 * 接收 control-ui 目录路径,内部自行决定改什么文件、怎么改。 * 返回 true 表示成功,false 表示失败(不写入 manifest,下次重试)。 */ apply: (controlUiDir: string) => boolean; /** * 卸载扩展。 * 还原 apply 所做的全部修改。 * 注意:共享文件的扩展不应单独调用 revert,由 reconciler 统一处理。 */ revert: (controlUiDir: string) => void; } /** * ClawLink 标记前缀,注入的代码旁加此注释以标识修改来源。 * 格式:/*clawlink:* / */ export const CLAWLINK_MARKER_PREFIX = '/*clawlink:'; /** * 生成标记注释。 */ export function makeMarker(extensionId: string): string { return `/*clawlink:${extensionId}*/`; } /** * 检查内容中是否包含指定扩展点的标记。 */ export function hasMarker(content: string, extensionId: string): boolean { return content.includes(makeMarker(extensionId)); }