feat(hooks): 添加 Hooks 配置管理功能

- 新增 Server Hooks API 路由 (CRUD + 测试执行)
- 新增 HooksPanel 组件用于管理所有钩子类型
- 新增 HookEditor 组件用于编辑单个钩子规则
- 支持 file_edited/file_created/file_deleted/session_completed 四种钩子
- 集成到 web 和 desktop 应用
This commit is contained in:
2025-12-12 21:02:06 +08:00
parent 622bd869f9
commit 9365e07df1
12 changed files with 1990 additions and 5 deletions
+132
View File
@@ -20,6 +20,10 @@ import type {
MCPServerStatus,
MCPToolInfo,
MCPConfig,
HookConfig,
FileHookConfig,
ShellCommandConfig,
HookTestResult,
} from './types.js';
// Re-export types
@@ -45,6 +49,11 @@ export type {
MCPToolInfo,
MCPConfig,
MCPServerConfigInfo,
// Hooks types
HookConfig,
FileHookConfig,
ShellCommandConfig,
HookTestResult,
} from './types.js';
// API Configuration
@@ -344,3 +353,126 @@ export async function getMCPConfig(): Promise<{
}> {
return request('GET', '/mcp/config');
}
// ============ Hooks API ============
/**
* 获取完整钩子配置
*/
export async function getHooksConfig(): Promise<{
success: boolean;
data: HookConfig;
error?: string;
}> {
return request('GET', '/hooks/config');
}
/**
* 更新完整钩子配置
*/
export async function updateHooksConfig(config: HookConfig): Promise<{
success: boolean;
data: HookConfig;
error?: string;
}> {
return request('PUT', '/hooks/config', config);
}
/**
* 获取 file_edited 钩子配置
*/
export async function getFileEditedHooks(): Promise<{
success: boolean;
data: FileHookConfig;
error?: string;
}> {
return request('GET', '/hooks/file-edited');
}
/**
* 更新 file_edited 钩子配置
*/
export async function updateFileEditedHooks(hooks: FileHookConfig): Promise<{
success: boolean;
data: FileHookConfig;
error?: string;
}> {
return request('PUT', '/hooks/file-edited', hooks);
}
/**
* 获取 file_created 钩子配置
*/
export async function getFileCreatedHooks(): Promise<{
success: boolean;
data: FileHookConfig;
error?: string;
}> {
return request('GET', '/hooks/file-created');
}
/**
* 更新 file_created 钩子配置
*/
export async function updateFileCreatedHooks(hooks: FileHookConfig): Promise<{
success: boolean;
data: FileHookConfig;
error?: string;
}> {
return request('PUT', '/hooks/file-created', hooks);
}
/**
* 获取 file_deleted 钩子配置
*/
export async function getFileDeletedHooks(): Promise<{
success: boolean;
data: FileHookConfig;
error?: string;
}> {
return request('GET', '/hooks/file-deleted');
}
/**
* 更新 file_deleted 钩子配置
*/
export async function updateFileDeletedHooks(hooks: FileHookConfig): Promise<{
success: boolean;
data: FileHookConfig;
error?: string;
}> {
return request('PUT', '/hooks/file-deleted', hooks);
}
/**
* 获取 session_completed 钩子配置
*/
export async function getSessionCompletedHooks(): Promise<{
success: boolean;
data: ShellCommandConfig[];
error?: string;
}> {
return request('GET', '/hooks/session-completed');
}
/**
* 更新 session_completed 钩子配置
*/
export async function updateSessionCompletedHooks(hooks: ShellCommandConfig[]): Promise<{
success: boolean;
data: ShellCommandConfig[];
error?: string;
}> {
return request('PUT', '/hooks/session-completed', hooks);
}
/**
* 测试执行钩子命令
*/
export async function testHookCommand(command: ShellCommandConfig): Promise<{
success: boolean;
data?: HookTestResult;
error?: string;
}> {
return request('POST', '/hooks/test', command);
}
+45
View File
@@ -236,3 +236,48 @@ export interface MCPServerConfigInfo {
enabled?: boolean;
timeout?: number;
}
// ============ Hooks 相关 ============
/** Shell 命令配置 */
export interface ShellCommandConfig {
/** 命令数组,如 ['npm', 'run', 'lint'] */
command: string[];
/** 环境变量 */
environment?: Record<string, string>;
/** 超时时间(毫秒) */
timeout?: number;
/** 工作目录 */
cwd?: string;
}
/** 文件钩子配置 (glob pattern -> commands) */
export interface FileHookConfig {
[pattern: string]: ShellCommandConfig[];
}
/** 完整钩子配置 */
export interface HookConfig {
/** 文件编辑后触发 */
file_edited?: FileHookConfig;
/** 文件创建后触发 */
file_created?: FileHookConfig;
/** 文件删除后触发 */
file_deleted?: FileHookConfig;
/** 会话结束时触发 */
session_completed?: ShellCommandConfig[];
}
/** 钩子命令测试结果 */
export interface HookTestResult {
/** 是否成功 */
success: boolean;
/** 标准输出 */
stdout: string;
/** 标准错误输出 */
stderr: string;
/** 退出码 */
exitCode: number;
/** 执行时间(毫秒) */
duration: number;
}