refactor(server): 消除与 Core 的重复类型定义

- 删除 Server 中 60+ 个与 Core 重复的类型定义
- 将动态导入 (await import) 改为静态类型导入 (import type)
- 保留必要的运行时静态导入
- 修复测试文件中的 mock 初始化问题
- 净删除约 960 行重复代码

重构文件:
- routes/checkpoints.ts: 删除 155 行重复类型
- routes/agents.ts: 删除 93 行重复类型
- routes/commands.ts: 删除 83 行重复类型
- routes/mcp.ts: 修复类型窄化
- routes/hooks.ts: 已使用静态导入
- routes/providers.ts: 删除 63 行重复类型
- session/manager.ts: 删除 41 行重复类型
- routes/sessions.ts: 添加类型导入
- permission/handler.ts: 添加类型导入
This commit is contained in:
2025-12-16 20:19:24 +08:00
parent 026429cb2f
commit 1b7d55848d
14 changed files with 283 additions and 1240 deletions
+66 -236
View File
@@ -6,157 +6,26 @@
import { Hono } from 'hono';
import { getConfig } from './config.js';
// Core Checkpoint 模块类型
interface CheckpointModule {
getCheckpointManager: () => CheckpointManager;
initCheckpointManager: (
workDir: string,
config?: Partial<CheckpointConfig>
) => Promise<CheckpointManager>;
RestoreMode: typeof RestoreMode;
}
interface CheckpointManager {
initialize(): Promise<void>;
isEnabled(): boolean;
getConfig(): CheckpointConfig;
listCheckpoints(): Promise<CheckpointMetadata[]>;
getCheckpoint(idOrHash: string): Promise<CheckpointMetadata | null>;
getLatestCheckpoint(): Promise<CheckpointMetadata | null>;
createCheckpoint(options: {
name?: string;
description?: string;
trigger?: CheckpointTrigger;
}): Promise<CheckpointMetadata>;
deleteCheckpoint(id: string): Promise<boolean>;
getDiff(checkpointId: string): Promise<DiffInfo>;
getFileDiff(checkpointId: string, filePath: string): Promise<FileDiff>;
rollback(options: RollbackOptions): Promise<RollbackResult>;
checkSafety(checkpointId: string): Promise<SafetyCheckResult>;
unrevert(): Promise<UnrevertResult>;
canUnrevert(): boolean;
getLastRollback(): RollbackRecord | null;
cleanup(): Promise<number>;
getStats(): Promise<CheckpointStats>;
getSessionCheckpoints(sessionId: string): Promise<CheckpointMetadata[]>;
getMessageCheckpoints(messageId: string): Promise<CheckpointMetadata[]>;
}
interface CheckpointConfig {
enabled: boolean;
autoCheckpoint: {
beforeWrite: boolean;
beforeEdit: boolean;
beforeDelete: boolean;
beforeMove: boolean;
beforeBash: boolean;
};
maxCheckpoints: number;
maxAge: number;
storageDir: string;
}
type CheckpointTrigger =
| 'auto'
| 'manual'
| 'tool:write_file'
| 'tool:edit_file'
| 'tool:delete_file'
| 'tool:move_file'
| 'tool:copy_file'
| 'tool:bash'
| 'task_start'
| 'task_complete'
| 'pre_rollback'
| 'session_start'
| 'session_end';
interface CheckpointMetadata {
id: string;
name?: string;
description?: string;
timestamp: number;
trigger: CheckpointTrigger;
toolCall?: {
tool: string;
params: Record<string, unknown>;
};
commitHash: string;
filesChanged: number;
messageId?: string;
sessionId?: string;
turnIndex?: number;
}
type FileChangeType = 'added' | 'modified' | 'deleted' | 'renamed';
interface FileChange {
path: string;
type: FileChangeType;
oldPath?: string;
insertions?: number;
deletions?: number;
}
interface DiffInfo {
from: string;
to: string;
files: FileChange[];
totalInsertions: number;
totalDeletions: number;
}
interface FileDiff {
path: string;
type: FileChangeType;
oldContent?: string;
newContent?: string;
patch?: string;
}
enum RestoreMode {
AI_CHANGES_ONLY = 'ai_changes_only',
WORKSPACE_ONLY = 'workspace_only',
FULL = 'full',
}
interface RollbackOptions {
target: string;
files?: string[];
dryRun?: boolean;
mode?: RestoreMode;
skipSafetyCheck?: boolean;
}
interface RollbackResult {
success: boolean;
restoredFiles: string[];
errors: Array<{ file: string; error: string }>;
previousCommit?: string;
}
interface SafetyCheckResult {
safe: boolean;
warnings: string[];
errors: string[];
}
interface RollbackRecord {
id: string;
timestamp: number;
targetCheckpoint: string;
previousCommit: string;
restoredFiles: string[];
canUnrevert: boolean;
}
interface UnrevertResult {
success: boolean;
restoredCommit: string;
filesRestored: number;
error?: string;
}
import type {
CheckpointMetadata,
CheckpointConfig,
CheckpointTrigger,
FileChange,
FileChangeType,
DiffInfo,
FileDiff,
RollbackOptions,
RollbackResult,
RollbackRecord,
SafetyCheckResult,
UnrevertResult,
} from '@ai-assistant/core';
import {
CheckpointManager,
getCheckpointManager,
initCheckpointManager,
RestoreMode,
} from '@ai-assistant/core';
interface CheckpointStats {
count: number;
@@ -166,46 +35,25 @@ interface CheckpointStats {
export const checkpointsRouter = new Hono();
// Core 模块缓存
let checkpointModule: CheckpointModule | null = null;
// Manager 初始化状态
let managerInitialized = false;
/**
* 初始化 Checkpoint 模块
*/
async function initCheckpointModule(): Promise<CheckpointModule | null> {
if (checkpointModule && managerInitialized) return checkpointModule;
async function ensureCheckpointManager(): Promise<CheckpointManager | null> {
if (managerInitialized) {
return getCheckpointManager();
}
try {
const corePath = '@ai-assistant/core';
const core = (await import(corePath)) as Record<string, unknown>;
if (
typeof core.getCheckpointManager !== 'function' ||
typeof core.initCheckpointManager !== 'function'
) {
console.warn('[Checkpoints] Core module missing Checkpoint exports');
return null;
}
checkpointModule = {
getCheckpointManager: core.getCheckpointManager as () => CheckpointManager,
initCheckpointManager: core.initCheckpointManager as (
workDir: string,
config?: Partial<CheckpointConfig>
) => Promise<CheckpointManager>,
RestoreMode: core.RestoreMode as typeof RestoreMode,
};
// 初始化 Checkpoint Manager
const config = getConfig();
await checkpointModule.initCheckpointManager(config.workdir);
await initCheckpointManager(config.workdir);
managerInitialized = true;
console.log('[Checkpoints] Checkpoint module initialized');
return checkpointModule;
return getCheckpointManager();
} catch (error) {
console.warn('[Checkpoints] Failed to load Checkpoint module:', error);
console.warn('[Checkpoints] Failed to initialize Checkpoint module:', error);
return null;
}
}
@@ -214,9 +62,9 @@ async function initCheckpointModule(): Promise<CheckpointModule | null> {
* GET /checkpoints - 获取所有检查点列表
*/
checkpointsRouter.get('/', async (c) => {
const module = await initCheckpointModule();
const manager = await ensureCheckpointManager();
if (!module) {
if (!manager) {
return c.json(
{
success: false,
@@ -227,7 +75,6 @@ checkpointsRouter.get('/', async (c) => {
}
try {
const manager = module.getCheckpointManager();
const checkpoints = await manager.listCheckpoints();
return c.json({
@@ -259,9 +106,9 @@ checkpointsRouter.get('/', async (c) => {
* GET /checkpoints/stats - 获取检查点统计信息
*/
checkpointsRouter.get('/stats', async (c) => {
const module = await initCheckpointModule();
const manager = await ensureCheckpointManager();
if (!module) {
if (!manager) {
return c.json(
{
success: false,
@@ -272,7 +119,6 @@ checkpointsRouter.get('/stats', async (c) => {
}
try {
const manager = module.getCheckpointManager();
const stats = await manager.getStats();
return c.json({
@@ -294,9 +140,9 @@ checkpointsRouter.get('/stats', async (c) => {
* GET /checkpoints/latest - 获取最新检查点
*/
checkpointsRouter.get('/latest', async (c) => {
const module = await initCheckpointModule();
const manager = await ensureCheckpointManager();
if (!module) {
if (!manager) {
return c.json(
{
success: false,
@@ -307,7 +153,6 @@ checkpointsRouter.get('/latest', async (c) => {
}
try {
const manager = module.getCheckpointManager();
const checkpoint = await manager.getLatestCheckpoint();
return c.json({
@@ -329,9 +174,9 @@ checkpointsRouter.get('/latest', async (c) => {
* GET /checkpoints/unrevert/status - 检查是否可撤销回滚
*/
checkpointsRouter.get('/unrevert/status', async (c) => {
const module = await initCheckpointModule();
const manager = await ensureCheckpointManager();
if (!module) {
if (!manager) {
return c.json(
{
success: false,
@@ -342,7 +187,6 @@ checkpointsRouter.get('/unrevert/status', async (c) => {
}
try {
const manager = module.getCheckpointManager();
const canUnrevert = manager.canUnrevert();
const lastRollback = manager.getLastRollback();
@@ -376,9 +220,9 @@ checkpointsRouter.get('/unrevert/status', async (c) => {
*/
checkpointsRouter.get('/:id', async (c) => {
const id = c.req.param('id');
const module = await initCheckpointModule();
const manager = await ensureCheckpointManager();
if (!module) {
if (!manager) {
return c.json(
{
success: false,
@@ -389,7 +233,6 @@ checkpointsRouter.get('/:id', async (c) => {
}
try {
const manager = module.getCheckpointManager();
const checkpoint = await manager.getCheckpoint(id);
if (!checkpoint) {
@@ -421,9 +264,9 @@ checkpointsRouter.get('/:id', async (c) => {
* POST /checkpoints - 创建手动检查点
*/
checkpointsRouter.post('/', async (c) => {
const module = await initCheckpointModule();
const manager = await ensureCheckpointManager();
if (!module) {
if (!manager) {
return c.json(
{
success: false,
@@ -435,7 +278,6 @@ checkpointsRouter.post('/', async (c) => {
try {
const body = await c.req.json<{ name?: string; description?: string }>();
const manager = module.getCheckpointManager();
const checkpoint = await manager.createCheckpoint({
name: body.name,
@@ -463,9 +305,9 @@ checkpointsRouter.post('/', async (c) => {
*/
checkpointsRouter.delete('/:id', async (c) => {
const id = c.req.param('id');
const module = await initCheckpointModule();
const manager = await ensureCheckpointManager();
if (!module) {
if (!manager) {
return c.json(
{
success: false,
@@ -476,7 +318,6 @@ checkpointsRouter.delete('/:id', async (c) => {
}
try {
const manager = module.getCheckpointManager();
const deleted = await manager.deleteCheckpoint(id);
if (!deleted) {
@@ -509,9 +350,9 @@ checkpointsRouter.delete('/:id', async (c) => {
*/
checkpointsRouter.get('/:id/diff', async (c) => {
const id = c.req.param('id');
const module = await initCheckpointModule();
const manager = await ensureCheckpointManager();
if (!module) {
if (!manager) {
return c.json(
{
success: false,
@@ -522,7 +363,6 @@ checkpointsRouter.get('/:id/diff', async (c) => {
}
try {
const manager = module.getCheckpointManager();
const diff = await manager.getDiff(id);
return c.json({
@@ -546,9 +386,9 @@ checkpointsRouter.get('/:id/diff', async (c) => {
checkpointsRouter.get('/:id/file-diff', async (c) => {
const id = c.req.param('id');
const filePath = c.req.query('path');
const module = await initCheckpointModule();
const manager = await ensureCheckpointManager();
if (!module) {
if (!manager) {
return c.json(
{
success: false,
@@ -569,7 +409,6 @@ checkpointsRouter.get('/:id/file-diff', async (c) => {
}
try {
const manager = module.getCheckpointManager();
const fileDiff = await manager.getFileDiff(id, filePath);
return c.json({
@@ -592,9 +431,9 @@ checkpointsRouter.get('/:id/file-diff', async (c) => {
*/
checkpointsRouter.post('/:id/restore', async (c) => {
const id = c.req.param('id');
const module = await initCheckpointModule();
const manager = await ensureCheckpointManager();
if (!module) {
if (!manager) {
return c.json(
{
success: false,
@@ -611,20 +450,18 @@ checkpointsRouter.post('/:id/restore', async (c) => {
skipSafetyCheck?: boolean;
}>();
const manager = module.getCheckpointManager();
// 转换 mode 字符串为枚举值
let mode: RestoreMode | undefined;
if (body.mode) {
switch (body.mode) {
case 'ai_changes_only':
mode = module.RestoreMode.AI_CHANGES_ONLY;
mode = RestoreMode.AI_CHANGES_ONLY;
break;
case 'workspace_only':
mode = module.RestoreMode.WORKSPACE_ONLY;
mode = RestoreMode.WORKSPACE_ONLY;
break;
case 'full':
mode = module.RestoreMode.FULL;
mode = RestoreMode.FULL;
break;
}
}
@@ -658,9 +495,9 @@ checkpointsRouter.get('/:id/restore/preview', async (c) => {
const id = c.req.param('id');
const modeParam = c.req.query('mode');
const filesParam = c.req.query('files');
const module = await initCheckpointModule();
const manager = await ensureCheckpointManager();
if (!module) {
if (!manager) {
return c.json(
{
success: false,
@@ -671,20 +508,18 @@ checkpointsRouter.get('/:id/restore/preview', async (c) => {
}
try {
const manager = module.getCheckpointManager();
// 转换 mode 字符串为枚举值
let mode: RestoreMode | undefined;
if (modeParam) {
switch (modeParam) {
case 'ai_changes_only':
mode = module.RestoreMode.AI_CHANGES_ONLY;
mode = RestoreMode.AI_CHANGES_ONLY;
break;
case 'workspace_only':
mode = module.RestoreMode.WORKSPACE_ONLY;
mode = RestoreMode.WORKSPACE_ONLY;
break;
case 'full':
mode = module.RestoreMode.FULL;
mode = RestoreMode.FULL;
break;
}
}
@@ -717,9 +552,9 @@ checkpointsRouter.get('/:id/restore/preview', async (c) => {
* POST /checkpoints/unrevert - 撤销最近一次回滚
*/
checkpointsRouter.post('/unrevert', async (c) => {
const module = await initCheckpointModule();
const manager = await ensureCheckpointManager();
if (!module) {
if (!manager) {
return c.json(
{
success: false,
@@ -730,7 +565,6 @@ checkpointsRouter.post('/unrevert', async (c) => {
}
try {
const manager = module.getCheckpointManager();
const result = await manager.unrevert();
if (!result.success) {
@@ -763,9 +597,9 @@ checkpointsRouter.post('/unrevert', async (c) => {
*/
checkpointsRouter.get('/:id/safety-check', async (c) => {
const id = c.req.param('id');
const module = await initCheckpointModule();
const manager = await ensureCheckpointManager();
if (!module) {
if (!manager) {
return c.json(
{
success: false,
@@ -776,7 +610,6 @@ checkpointsRouter.get('/:id/safety-check', async (c) => {
}
try {
const manager = module.getCheckpointManager();
const result = await manager.checkSafety(id);
return c.json({
@@ -798,9 +631,9 @@ checkpointsRouter.get('/:id/safety-check', async (c) => {
* POST /checkpoints/cleanup - 清理过期检查点
*/
checkpointsRouter.post('/cleanup', async (c) => {
const module = await initCheckpointModule();
const manager = await ensureCheckpointManager();
if (!module) {
if (!manager) {
return c.json(
{
success: false,
@@ -811,7 +644,6 @@ checkpointsRouter.post('/cleanup', async (c) => {
}
try {
const manager = module.getCheckpointManager();
const deleted = await manager.cleanup();
return c.json({
@@ -834,9 +666,9 @@ checkpointsRouter.post('/cleanup', async (c) => {
*/
checkpointsRouter.get('/sessions/:sessionId', async (c) => {
const sessionId = c.req.param('sessionId');
const module = await initCheckpointModule();
const manager = await ensureCheckpointManager();
if (!module) {
if (!manager) {
return c.json(
{
success: false,
@@ -847,7 +679,6 @@ checkpointsRouter.get('/sessions/:sessionId', async (c) => {
}
try {
const manager = module.getCheckpointManager();
const checkpoints = await manager.getSessionCheckpoints(sessionId);
return c.json({
@@ -870,9 +701,9 @@ checkpointsRouter.get('/sessions/:sessionId', async (c) => {
*/
checkpointsRouter.get('/messages/:messageId', async (c) => {
const messageId = c.req.param('messageId');
const module = await initCheckpointModule();
const manager = await ensureCheckpointManager();
if (!module) {
if (!manager) {
return c.json(
{
success: false,
@@ -883,7 +714,6 @@ checkpointsRouter.get('/messages/:messageId', async (c) => {
}
try {
const manager = module.getCheckpointManager();
const checkpoints = await manager.getMessageCheckpoints(messageId);
return c.json({