refactor: remove v1 legacy code replaced by OpenClaw

- Remove src/core/* - replaced by OpenClaw built-in capabilities
- Remove src/db/* - OpenClaw has its own SQLite storage
- Remove src/config/* - OpenClaw config system replaces this
- Remove src/im/* - OpenClaw Channel system replaces this
- Remove src/api/* - WebSocket + Tauri Commands replace this
- Remove src/utils/* - using OpenClaw utilities
- Remove src/app.ts, src/index.ts - no longer needed

All v1 code is preserved in archive/v1-code-backup branch

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
iven
2026-03-12 00:31:24 +08:00
parent fa108e78aa
commit 8b1c4d36a0
37 changed files with 0 additions and 2806 deletions

View File

@@ -1,144 +0,0 @@
// ZCLAW API 层 - 供 Tauri 前端调用的接口
import type { ZClawApp } from '../app';
import { createLogger } from '../utils/logger';
const log = createLogger('API');
export interface APIResponse<T = any> {
success: boolean;
data?: T;
error?: string;
}
// API 处理器集合 - 供 Tauri Commands 调用
export class ZClawAPI {
constructor(private app: ZClawApp) {}
// === 聊天 ===
async sendMessage(userId: string, content: string): Promise<APIResponse<{ reply: string }>> {
try {
const reply = await this.app.handleUserMessage(userId, content);
return { success: true, data: { reply } };
} catch (error: any) {
log.error(`sendMessage error: ${error.message}`);
return { success: false, error: error.message };
}
}
// === 任务 ===
async submitTask(userId: string, payload: any): Promise<APIResponse<{ taskId: string }>> {
try {
const engine = this.app.getRemoteExecution();
const taskId = await engine.submitTask({
id: '',
userId,
deviceId: 'local',
channel: 'desktop',
type: 'immediate',
priority: 'normal',
payload,
status: 'pending',
createdAt: new Date(),
});
return { success: true, data: { taskId } };
} catch (error: any) {
return { success: false, error: error.message };
}
}
async getTaskStatus(taskId: string): Promise<APIResponse<{ status: string }>> {
try {
const engine = this.app.getRemoteExecution();
const status = await engine.getStatus(taskId);
return { success: true, data: { status } };
} catch (error: any) {
return { success: false, error: error.message };
}
}
async getTaskStats(): Promise<APIResponse> {
try {
const stats = this.app.getRemoteExecution().getStats();
return { success: true, data: stats };
} catch (error: any) {
return { success: false, error: error.message };
}
}
// === 多 Agent ===
async executeGoal(userId: string, goal: string): Promise<APIResponse> {
try {
const result = await this.app.getAgentOrchestrator().executeGoal(goal);
return { success: true, data: result };
} catch (error: any) {
return { success: false, error: error.message };
}
}
// === 记忆 ===
async getUserProfile(userId: string): Promise<APIResponse> {
try {
const profile = await this.app.getMemory().getProfile(userId);
return { success: true, data: profile || null };
} catch (error: any) {
return { success: false, error: error.message };
}
}
async getMemoryEvents(userId: string, query: string = '', limit: number = 20): Promise<APIResponse> {
try {
const events = await this.app.getMemory().recall(userId, query, limit);
return { success: true, data: events };
} catch (error: any) {
return { success: false, error: error.message };
}
}
// === 定时任务 ===
async listScheduledTasks(userId: string): Promise<APIResponse> {
try {
const tasks = await this.app.getProactive().listTasks(userId);
return { success: true, data: tasks };
} catch (error: any) {
return { success: false, error: error.message };
}
}
async scheduleTask(userId: string, schedule: any, taskConfig: any): Promise<APIResponse> {
try {
await this.app.getProactive().scheduleTask({
id: '',
userId,
channel: 'desktop',
schedule,
task: taskConfig,
status: 'active',
});
return { success: true };
} catch (error: any) {
return { success: false, error: error.message };
}
}
// === 系统状态 ===
async getSystemStatus(): Promise<APIResponse> {
try {
const taskStats = this.app.getRemoteExecution().getStats();
const agents = this.app.getAgentOrchestrator().getAgents().map(a => a.getInfo());
const activePlans = this.app.getAgentOrchestrator().getActivePlans();
const connectedIMs = this.app.getIMGateway().getConnectedAdapters();
return {
success: true,
data: {
taskStats,
activeAgents: agents.length,
activePlans: activePlans.length,
connectedIMs,
},
};
} catch (error: any) {
return { success: false, error: error.message };
}
}
}

View File

@@ -1,173 +0,0 @@
// ZCLAW 主应用类 - 统一协调所有系统
import { loadConfig, getConfig } from './config';
import { initDatabase, closeDatabase } from './db';
import { AIManager } from './core/ai/manager';
import { AgentOrchestrator } from './core/multi-agent/orchestrator';
import { RemoteExecutionEngine } from './core/remote-execution/engine';
import { TaskOrchestrator } from './core/task-orchestration/orchestrator';
import { PersistentMemorySystem } from './core/memory/memory';
import { ProactiveServiceSystem } from './core/proactive/proactive';
import { IMGateway } from './im/gateway';
import { FeishuAdapter } from './im/adapters/feishu';
import { createLogger, setLogLevel } from './utils/logger';
const log = createLogger('ZCLAW');
export class ZClawApp {
private ai!: AIManager;
private agentOrchestrator!: AgentOrchestrator;
private remoteExecution!: RemoteExecutionEngine;
private taskOrchestrator!: TaskOrchestrator;
private memory!: PersistentMemorySystem;
private proactive!: ProactiveServiceSystem;
private imGateway!: IMGateway;
async start(): Promise<void> {
log.info('========================================');
log.info(' ZCLAW - AI Agent Platform');
log.info(' Version: 0.1.0');
log.info('========================================');
// 1. 加载配置
const config = loadConfig();
setLogLevel(config.log.level);
log.info('Configuration loaded');
// 2. 初始化数据库
initDatabase(config.db.path);
log.info('Database initialized');
// 3. 初始化 AI 管理器
this.ai = new AIManager();
log.info('AI Manager initialized');
// 4. 初始化核心系统
this.remoteExecution = new RemoteExecutionEngine();
this.taskOrchestrator = new TaskOrchestrator();
this.memory = new PersistentMemorySystem();
this.proactive = new ProactiveServiceSystem();
log.info('Core systems initialized');
// 5. 初始化多 Agent 协作系统
this.agentOrchestrator = new AgentOrchestrator(this.ai);
log.info('Agent Orchestrator initialized');
// 6. 初始化 IM 网关
this.imGateway = new IMGateway();
this.setupIMAdapters(config);
this.setupMessageRouting();
log.info('IM Gateway initialized');
// 7. 连接 IM
await this.imGateway.connectAll();
log.info('ZCLAW started successfully! 🦞');
log.info(`Server: ${config.server.host}:${config.server.port}`);
log.info(`AI Provider: ${config.ai.provider} (${config.ai.defaultModel})`);
log.info(`Connected IMs: ${this.imGateway.getConnectedAdapters().join(', ') || 'none'}`);
}
private setupIMAdapters(config: ReturnType<typeof getConfig>): void {
// 飞书
if (config.im.feishu.enabled || config.im.feishu.appId) {
const feishu = new FeishuAdapter(config.im.feishu.appId, config.im.feishu.appSecret);
this.imGateway.registerAdapter(feishu);
}
// TODO: 其他 IM 适配器 (Telegram, QQ, WeChat)
}
private setupMessageRouting(): void {
this.imGateway.onMessage(async (message) => {
log.info(`Processing message from ${message.channelType}/${message.userId}: ${message.content.slice(0, 80)}`);
try {
// 记忆:记录用户消息
await this.memory.remember(message.userId, {
id: message.id,
userId: message.userId,
type: 'im_message',
content: { text: message.content, channel: message.channelType },
timestamp: new Date(),
});
// 判断消息类型并路由
const response = await this.handleUserMessage(message.userId, message.content);
// 回复
await this.imGateway.send({
channelType: message.channelType,
channelId: message.channelId,
userId: message.userId,
content: response,
contentType: 'text',
});
} catch (error: any) {
log.error(`Message processing error: ${error.message}`);
await this.imGateway.send({
channelType: message.channelType,
channelId: message.channelId,
content: `处理消息时出错: ${error.message}`,
contentType: 'text',
});
}
});
}
async handleUserMessage(userId: string, content: string): Promise<string> {
// 获取用户记忆上下文
const recentMemories = await this.memory.recall(userId, content, 5);
const context: Record<string, any> = {};
if (recentMemories.length > 0) {
context.recentHistory = recentMemories.map(m => m.content);
}
// 判断是否需要多步骤编排
const isComplex = this.isComplexTask(content);
if (isComplex) {
// 复杂任务 -> 多 Agent 协作
log.info(`Complex task detected, starting multi-agent execution`);
const result = await this.agentOrchestrator.executeGoal(content, context);
return result.success
? result.data?.report || '任务已完成'
: `任务执行失败: ${result.error}`;
} else {
// 简单对话 -> 直接 AI 回复
const profile = await this.memory.getProfile(userId);
const systemPrompt = `你是 ZCLAW一个智能 AI 助手 🦞。你友善、专业、高效。
${profile ? `用户偏好: ${JSON.stringify(profile.preferences)}` : ''}
请用中文简洁回复。`;
return await this.ai.ask(content, systemPrompt);
}
}
private isComplexTask(content: string): boolean {
const complexIndicators = [
'帮我做', '市场调研', '写报告', '分析',
'自动化', '批量', '多步骤', '流程',
'调研', '搜索并整理', '对比', '综合',
'项目管理', '代码生成', '部署',
];
return complexIndicators.some(indicator => content.includes(indicator));
}
// 公开接口:供 Tauri 前端调用
getAI(): AIManager { return this.ai; }
getAgentOrchestrator(): AgentOrchestrator { return this.agentOrchestrator; }
getRemoteExecution(): RemoteExecutionEngine { return this.remoteExecution; }
getTaskOrchestrator(): TaskOrchestrator { return this.taskOrchestrator; }
getMemory(): PersistentMemorySystem { return this.memory; }
getProactive(): ProactiveServiceSystem { return this.proactive; }
getIMGateway(): IMGateway { return this.imGateway; }
async shutdown(): Promise<void> {
log.info('Shutting down ZCLAW...');
await this.agentOrchestrator.shutdown();
await this.imGateway.disconnectAll();
closeDatabase();
log.info('ZCLAW shutdown complete');
}
}

View File

@@ -1,118 +0,0 @@
// ZCLAW 配置管理
import { z } from 'zod';
const ConfigSchema = z.object({
// AI Provider
ai: z.object({
provider: z.enum(['zhipu', 'openai', 'local']).default('zhipu'),
zhipuApiKey: z.string().default(''),
openaiApiKey: z.string().default(''),
openaiBaseUrl: z.string().default('https://api.openai.com/v1'),
defaultModel: z.string().default('glm-4-flash'),
maxTokens: z.number().default(4096),
temperature: z.number().default(0.7),
}),
// Database
db: z.object({
path: z.string().default('./data/zclaw.db'),
}),
// Server
server: z.object({
port: z.number().default(3721),
host: z.string().default('127.0.0.1'),
}),
// IM Channels
im: z.object({
feishu: z.object({
appId: z.string().default(''),
appSecret: z.string().default(''),
enabled: z.boolean().default(false),
}),
telegram: z.object({
botToken: z.string().default(''),
enabled: z.boolean().default(false),
}),
}),
// Execution
execution: z.object({
maxConcurrent: z.number().default(5),
taskTimeout: z.number().default(300000), // 5 minutes
retryAttempts: z.number().default(3),
}),
// Memory
memory: z.object({
maxEventsPerUser: z.number().default(10000),
embeddingModel: z.string().default('text-embedding-3-small'),
}),
// Logging
log: z.object({
level: z.enum(['debug', 'info', 'warn', 'error']).default('info'),
}),
});
export type ZClawConfig = z.infer<typeof ConfigSchema>;
let _config: ZClawConfig | null = null;
export function loadConfig(overrides?: Partial<Record<string, any>>): ZClawConfig {
const env = process.env;
const raw = {
ai: {
provider: env.ZCLAW_AI_PROVIDER || 'zhipu',
zhipuApiKey: env.ZCLAW_ZHIPU_API_KEY || env.ZHIPU_API_KEY || '',
openaiApiKey: env.ZCLAW_OPENAI_API_KEY || env.OPENAI_API_KEY || '',
openaiBaseUrl: env.ZCLAW_OPENAI_BASE_URL || 'https://api.openai.com/v1',
defaultModel: env.ZCLAW_DEFAULT_MODEL || 'glm-4-flash',
maxTokens: parseInt(env.ZCLAW_MAX_TOKENS || '4096'),
temperature: parseFloat(env.ZCLAW_TEMPERATURE || '0.7'),
},
db: {
path: env.ZCLAW_DB_PATH || './data/zclaw.db',
},
server: {
port: parseInt(env.ZCLAW_PORT || '3721'),
host: env.ZCLAW_HOST || '127.0.0.1',
},
im: {
feishu: {
appId: env.ZCLAW_FEISHU_APP_ID || '',
appSecret: env.ZCLAW_FEISHU_APP_SECRET || '',
enabled: env.ZCLAW_FEISHU_ENABLED === 'true',
},
telegram: {
botToken: env.ZCLAW_TELEGRAM_BOT_TOKEN || '',
enabled: env.ZCLAW_TELEGRAM_ENABLED === 'true',
},
},
execution: {
maxConcurrent: parseInt(env.ZCLAW_MAX_CONCURRENT || '5'),
taskTimeout: parseInt(env.ZCLAW_TASK_TIMEOUT || '300000'),
retryAttempts: parseInt(env.ZCLAW_RETRY_ATTEMPTS || '3'),
},
memory: {
maxEventsPerUser: parseInt(env.ZCLAW_MAX_EVENTS || '10000'),
embeddingModel: env.ZCLAW_EMBEDDING_MODEL || 'text-embedding-3-small',
},
log: {
level: env.ZCLAW_LOG_LEVEL || 'info',
},
...overrides,
};
_config = ConfigSchema.parse(raw);
return _config;
}
export function getConfig(): ZClawConfig {
if (!_config) {
return loadConfig();
}
return _config;
}

View File

@@ -1,4 +0,0 @@
export type { AIProvider, ChatMessage, ChatRequest, ChatResponse, StreamChunk, EmbeddingRequest, EmbeddingResponse } from './types';
export { AIManager, getAIManager } from './manager';
export { ZhipuProvider } from './providers/zhipu';
export { OpenAIProvider } from './providers/openai';

View File

@@ -1,142 +0,0 @@
// AI 模型管理器 - 统一调用接口,支持多 Provider fallback
import type { AIProvider, ChatRequest, ChatResponse, ChatMessage, StreamChunk, EmbeddingRequest, EmbeddingResponse } from './types';
import { ZhipuProvider } from './providers/zhipu';
import { OpenAIProvider } from './providers/openai';
import { getConfig } from '../../config';
import { createLogger } from '../../utils/logger';
const log = createLogger('AIManager');
export class AIManager {
private providers: Map<string, AIProvider> = new Map();
private defaultProvider: string;
constructor() {
const config = getConfig();
this.defaultProvider = config.ai.provider;
// 注册智谱 GLM
if (config.ai.zhipuApiKey) {
this.providers.set('zhipu', new ZhipuProvider(config.ai.zhipuApiKey));
log.info('Zhipu GLM provider registered');
}
// 注册 OpenAI 兼容
if (config.ai.openaiApiKey) {
this.providers.set('openai', new OpenAIProvider(config.ai.openaiApiKey, config.ai.openaiBaseUrl));
log.info('OpenAI provider registered');
}
if (this.providers.size === 0) {
log.warn('No AI providers configured. Set ZCLAW_ZHIPU_API_KEY or ZCLAW_OPENAI_API_KEY.');
}
}
getProvider(name?: string): AIProvider {
const providerName = name || this.defaultProvider;
const provider = this.providers.get(providerName);
if (!provider) {
// fallback: 尝试其他可用 provider
const fallback = this.providers.values().next().value;
if (fallback) {
log.warn(`Provider '${providerName}' not available, falling back to '${fallback.name}'`);
return fallback;
}
throw new Error(`No AI provider available. Configure at least one API key.`);
}
return provider;
}
async chat(request: ChatRequest, providerName?: string): Promise<ChatResponse> {
const provider = this.getProvider(providerName);
const config = getConfig();
const enrichedRequest: ChatRequest = {
...request,
model: request.model || config.ai.defaultModel,
temperature: request.temperature ?? config.ai.temperature,
maxTokens: request.maxTokens ?? config.ai.maxTokens,
};
log.debug(`Chat via ${provider.name}`, { model: enrichedRequest.model, messages: enrichedRequest.messages.length });
try {
const response = await provider.chat(enrichedRequest);
log.debug(`Chat response: ${response.usage.totalTokens} tokens`);
return response;
} catch (error: any) {
log.error(`Chat failed via ${provider.name}: ${error.message}`);
// 尝试 fallback
for (const [name, fallbackProvider] of this.providers) {
if (name !== provider.name) {
log.info(`Retrying with fallback provider: ${name}`);
try {
return await fallbackProvider.chat(enrichedRequest);
} catch {
continue;
}
}
}
throw error;
}
}
async chatStream(request: ChatRequest, providerName?: string): Promise<AsyncIterable<StreamChunk>> {
const provider = this.getProvider(providerName);
if (!provider.chatStream) {
throw new Error(`Provider '${provider.name}' does not support streaming`);
}
return provider.chatStream(request);
}
async embed(request: EmbeddingRequest, providerName?: string): Promise<EmbeddingResponse> {
const provider = this.getProvider(providerName);
if (!provider.embed) {
throw new Error(`Provider '${provider.name}' does not support embeddings`);
}
return provider.embed(request);
}
// 便捷方法:单次对话
async ask(prompt: string, systemPrompt?: string): Promise<string> {
const messages: ChatMessage[] = [];
if (systemPrompt) {
messages.push({ role: 'system', content: systemPrompt });
}
messages.push({ role: 'user', content: prompt });
const response = await this.chat({ messages });
return response.content;
}
// 便捷方法:带上下文的多轮对话
async chatWithHistory(messages: ChatMessage[], systemPrompt?: string): Promise<string> {
const allMessages: ChatMessage[] = [];
if (systemPrompt) {
allMessages.push({ role: 'system', content: systemPrompt });
}
allMessages.push(...messages);
const response = await this.chat({ messages: allMessages });
return response.content;
}
// 便捷方法JSON 输出
async askJson<T = any>(prompt: string, systemPrompt?: string): Promise<T> {
const jsonSystemPrompt = (systemPrompt || '') + '\n\n请严格以 JSON 格式输出,不要包含 markdown 代码块标记。';
const content = await this.ask(prompt, jsonSystemPrompt);
// 清理可能的 markdown 代码块包裹
const cleaned = content.replace(/^```(?:json)?\s*\n?/i, '').replace(/\n?```\s*$/i, '').trim();
return JSON.parse(cleaned) as T;
}
}
let _manager: AIManager | null = null;
export function getAIManager(): AIManager {
if (!_manager) {
_manager = new AIManager();
}
return _manager;
}

View File

@@ -1,139 +0,0 @@
// OpenAI Compatible Provider (also works for local models via API)
import type { AIProvider, ChatRequest, ChatResponse, StreamChunk, EmbeddingRequest, EmbeddingResponse } from '../types';
import { createLogger } from '../../../utils/logger';
const log = createLogger('OpenAIProvider');
export class OpenAIProvider implements AIProvider {
name = 'openai';
private apiKey: string;
private baseUrl: string;
constructor(apiKey: string, baseUrl: string = 'https://api.openai.com/v1') {
this.apiKey = apiKey;
this.baseUrl = baseUrl.replace(/\/$/, '');
}
async chat(request: ChatRequest): Promise<ChatResponse> {
const model = request.model || 'gpt-4o-mini';
log.debug(`Chat request to ${model}`);
const response = await fetch(`${this.baseUrl}/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`,
},
body: JSON.stringify({
model,
messages: request.messages,
temperature: request.temperature ?? 0.7,
max_tokens: request.maxTokens ?? 4096,
stream: false,
}),
});
if (!response.ok) {
const errorText = await response.text();
log.error(`OpenAI API error: ${response.status}`, errorText);
throw new Error(`OpenAI API error: ${response.status} - ${errorText}`);
}
const data: any = await response.json();
const choice = data.choices?.[0];
return {
content: choice?.message?.content || '',
model: data.model || model,
usage: {
promptTokens: data.usage?.prompt_tokens || 0,
completionTokens: data.usage?.completion_tokens || 0,
totalTokens: data.usage?.total_tokens || 0,
},
finishReason: choice?.finish_reason || 'stop',
};
}
async *chatStream(request: ChatRequest): AsyncIterable<StreamChunk> {
const model = request.model || 'gpt-4o-mini';
const response = await fetch(`${this.baseUrl}/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`,
},
body: JSON.stringify({
model,
messages: request.messages,
temperature: request.temperature ?? 0.7,
max_tokens: request.maxTokens ?? 4096,
stream: true,
}),
});
if (!response.ok) {
throw new Error(`OpenAI API error: ${response.status}`);
}
const reader = response.body?.getReader();
if (!reader) throw new Error('No response body');
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() || '';
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6).trim();
if (data === '[DONE]') {
yield { content: '', done: true };
return;
}
try {
const parsed = JSON.parse(data);
const delta = parsed.choices?.[0]?.delta?.content || '';
if (delta) {
yield { content: delta, done: false };
}
} catch {
// skip
}
}
}
}
}
async embed(request: EmbeddingRequest): Promise<EmbeddingResponse> {
const input = Array.isArray(request.input) ? request.input : [request.input];
const model = request.model || 'text-embedding-3-small';
const response = await fetch(`${this.baseUrl}/embeddings`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`,
},
body: JSON.stringify({ model, input }),
});
if (!response.ok) {
throw new Error(`OpenAI Embedding API error: ${response.status}`);
}
const data: any = await response.json();
return {
embeddings: data.data?.map((d: any) => d.embedding) || [],
model: data.model || model,
usage: { totalTokens: data.usage?.total_tokens || 0 },
};
}
}

View File

@@ -1,138 +0,0 @@
// 智谱 GLM AI Provider
import type { AIProvider, ChatRequest, ChatResponse, StreamChunk, EmbeddingRequest, EmbeddingResponse } from '../types';
import { createLogger } from '../../../utils/logger';
const log = createLogger('ZhipuProvider');
export class ZhipuProvider implements AIProvider {
name = 'zhipu';
private apiKey: string;
private baseUrl = 'https://open.bigmodel.cn/api/paas/v4';
constructor(apiKey: string) {
this.apiKey = apiKey;
}
async chat(request: ChatRequest): Promise<ChatResponse> {
const model = request.model || 'glm-4-flash';
log.debug(`Chat request to ${model}`, { messageCount: request.messages.length });
const response = await fetch(`${this.baseUrl}/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`,
},
body: JSON.stringify({
model,
messages: request.messages,
temperature: request.temperature ?? 0.7,
max_tokens: request.maxTokens ?? 4096,
stream: false,
}),
});
if (!response.ok) {
const errorText = await response.text();
log.error(`Zhipu API error: ${response.status}`, errorText);
throw new Error(`Zhipu API error: ${response.status} - ${errorText}`);
}
const data: any = await response.json();
const choice = data.choices?.[0];
return {
content: choice?.message?.content || '',
model: data.model || model,
usage: {
promptTokens: data.usage?.prompt_tokens || 0,
completionTokens: data.usage?.completion_tokens || 0,
totalTokens: data.usage?.total_tokens || 0,
},
finishReason: choice?.finish_reason || 'stop',
};
}
async *chatStream(request: ChatRequest): AsyncIterable<StreamChunk> {
const model = request.model || 'glm-4-flash';
const response = await fetch(`${this.baseUrl}/chat/completions`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`,
},
body: JSON.stringify({
model,
messages: request.messages,
temperature: request.temperature ?? 0.7,
max_tokens: request.maxTokens ?? 4096,
stream: true,
}),
});
if (!response.ok) {
throw new Error(`Zhipu API error: ${response.status}`);
}
const reader = response.body?.getReader();
if (!reader) throw new Error('No response body');
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() || '';
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6).trim();
if (data === '[DONE]') {
yield { content: '', done: true };
return;
}
try {
const parsed = JSON.parse(data);
const delta = parsed.choices?.[0]?.delta?.content || '';
if (delta) {
yield { content: delta, done: false };
}
} catch {
// skip invalid JSON
}
}
}
}
}
async embed(request: EmbeddingRequest): Promise<EmbeddingResponse> {
const input = Array.isArray(request.input) ? request.input : [request.input];
const model = request.model || 'embedding-3';
const response = await fetch(`${this.baseUrl}/embeddings`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`,
},
body: JSON.stringify({ model, input }),
});
if (!response.ok) {
throw new Error(`Zhipu Embedding API error: ${response.status}`);
}
const data: any = await response.json();
return {
embeddings: data.data?.map((d: any) => d.embedding) || [],
model: data.model || model,
usage: { totalTokens: data.usage?.total_tokens || 0 },
};
}
}

View File

@@ -1,48 +0,0 @@
// AI 模型集成层 - 类型定义
export interface ChatMessage {
role: 'system' | 'user' | 'assistant';
content: string;
}
export interface ChatRequest {
messages: ChatMessage[];
model?: string;
temperature?: number;
maxTokens?: number;
stream?: boolean;
}
export interface ChatResponse {
content: string;
model: string;
usage: {
promptTokens: number;
completionTokens: number;
totalTokens: number;
};
finishReason: string;
}
export interface StreamChunk {
content: string;
done: boolean;
}
export interface EmbeddingRequest {
input: string | string[];
model?: string;
}
export interface EmbeddingResponse {
embeddings: number[][];
model: string;
usage: { totalTokens: number };
}
export interface AIProvider {
name: string;
chat(request: ChatRequest): Promise<ChatResponse>;
chatStream?(request: ChatRequest): AsyncIterable<StreamChunk>;
embed?(request: EmbeddingRequest): Promise<EmbeddingResponse>;
}

View File

@@ -1 +0,0 @@
export * from './memory';

View File

@@ -1,70 +0,0 @@
// 持续记忆系统
import { generateId } from '../../utils/id';
import { createLogger } from '../../utils/logger';
const log = createLogger('Memory');
export interface UserProfile {
id: string;
preferences: {
language: 'zh' | 'en';
timezone: string;
responseStyle: 'concise' | 'detailed';
};
patterns: {
activeHours: number[];
frequentCommands: string[];
};
}
export interface MemoryEvent {
id: string;
userId: string;
type: string;
content: any;
timestamp: Date;
embedding?: number[];
}
export class PersistentMemorySystem {
private profiles: Map<string, UserProfile> = new Map();
private events: MemoryEvent[] = [];
async remember(userId: string, event: MemoryEvent): Promise<void> {
event.id = event.id || this.generateId();
event.timestamp = new Date();
this.events.push(event);
log.debug(`Event remembered: ${event.id} for user ${userId}`);
}
async recall(userId: string, query: string, limit: number = 10): Promise<MemoryEvent[]> {
// TODO: 实现向量搜索(后续实现)
return this.events.filter(e => e.userId === userId).slice(0, limit);
}
async getProfile(userId: string): Promise<UserProfile | undefined> {
return this.profiles.get(userId);
}
async updateProfile(userId: string, updates: Partial<UserProfile>): Promise<void> {
let profile = this.profiles.get(userId);
if (!profile) {
profile = {
id: userId,
preferences: { language: 'zh', timezone: 'Asia/Shanghai', responseStyle: 'concise' },
patterns: { activeHours: [], frequentCommands: [] },
};
this.profiles.set(userId, profile);
}
Object.assign(profile, updates);
log.debug(`Profile updated for user ${userId}`);
}
async getEventCount(userId: string): Promise<number> {
return this.events.filter(e => e.userId === userId).length;
}
private generateId(): string {
return generateId('mem');
}
}

View File

@@ -1,40 +0,0 @@
// Combiner Agent - 整合多个 Agent 的执行结果
import { BaseAgent } from '../base-agent';
import type { AgentTask } from '../types';
const COMBINER_SYSTEM_PROMPT = `你是一个结果整合专家。你的职责是将多个执行步骤的结果整合为一份完整、结构化的报告。
要求:
1. 综合分析所有步骤的执行结果
2. 提取关键信息
3. 生成简洁清晰的最终报告
4. 如果某些步骤失败,说明缺失的信息
5. 输出格式清晰,适合通过 IM 发送给用户`;
export class CombinerAgent extends BaseAgent {
protected getCapabilities(): string[] {
return ['result_combination', 'report_generation', 'data_synthesis'];
}
protected async run(task: AgentTask): Promise<any> {
const results = task.params.results || [];
const goal = task.params.goal || task.description;
this.log.info(`Combining ${results.length} results for: ${goal}`);
const prompt = `原始目标: ${goal}
各步骤执行结果:
${results.map((r: any, i: number) => `
--- 步骤 ${i + 1} ---
描述: ${r.description || '未知'}
状态: ${r.success ? '成功' : '失败'}
结果: ${JSON.stringify(r.data || r.error, null, 2)}
`).join('\n')}
请整合上述结果,生成一份完整的报告。输出应该清晰、有条理,适合通过即时通讯发送给用户。`;
const report = await this.ai.ask(prompt, COMBINER_SYSTEM_PROMPT);
return { report, stepsCount: results.length, successCount: results.filter((r: any) => r.success).length };
}
}

View File

@@ -1,83 +0,0 @@
// Executor Agent - 通用执行器,根据工具类型执行具体操作
import { BaseAgent } from '../base-agent';
import type { AgentTask } from '../types';
export class BrowserAgent extends BaseAgent {
protected getCapabilities(): string[] {
return ['web_browse', 'web_search', 'screenshot', 'form_fill'];
}
protected async run(task: AgentTask): Promise<any> {
this.log.info(`Browser task: ${task.description}`);
// 使用 AI 模拟浏览器操作结果(后续集成真实浏览器控制)
const prompt = `你需要模拟执行以下浏览器操作并生成合理的结果:
操作描述: ${task.description}
参数: ${JSON.stringify(task.params)}
上下文: ${JSON.stringify(task.context)}
请以 JSON 格式输出执行结果,包含 status、data 字段。`;
return await this.ai.askJson(prompt, '你是一个浏览器操作模拟器,请生成合理的操作结果。');
}
}
export class FileAgent extends BaseAgent {
protected getCapabilities(): string[] {
return ['file_read', 'file_write', 'file_search', 'file_organize'];
}
protected async run(task: AgentTask): Promise<any> {
this.log.info(`File task: ${task.description}`);
const prompt = `你需要执行以下文件操作并生成结果:
操作描述: ${task.description}
参数: ${JSON.stringify(task.params)}
上下文: ${JSON.stringify(task.context)}
请以 JSON 格式输出执行结果。`;
return await this.ai.askJson(prompt, '你是一个文件操作执行器。');
}
}
export class TerminalAgent extends BaseAgent {
protected getCapabilities(): string[] {
return ['command_execute', 'script_run', 'system_info'];
}
protected async run(task: AgentTask): Promise<any> {
this.log.info(`Terminal task: ${task.description}`);
const prompt = `你需要模拟执行以下终端命令操作并生成合理结果:
操作描述: ${task.description}
参数: ${JSON.stringify(task.params)}
上下文: ${JSON.stringify(task.context)}
请以 JSON 格式输出执行结果,包含 stdout、stderr、exitCode 字段。`;
return await this.ai.askJson(prompt, '你是一个终端命令模拟器。');
}
}
export class AIAnalysisAgent extends BaseAgent {
protected getCapabilities(): string[] {
return ['text_analysis', 'content_generation', 'data_processing', 'summarization'];
}
protected async run(task: AgentTask): Promise<any> {
this.log.info(`AI analysis task: ${task.description}`);
const systemPrompt = task.params.systemPrompt || '你是一个智能分析助手,请根据要求完成分析任务。';
const prompt = `${task.description}
${task.context ? `上下文数据:\n${JSON.stringify(task.context, null, 2)}` : ''}
${task.params.data ? `输入数据:\n${JSON.stringify(task.params.data, null, 2)}` : ''}`;
const result = await this.ai.ask(prompt, systemPrompt);
return { analysis: result };
}
}

View File

@@ -1,64 +0,0 @@
// Planner Agent - 任务规划,将用户目标拆解为可执行步骤
import { BaseAgent } from '../base-agent';
import type { AgentTask } from '../types';
const PLANNER_SYSTEM_PROMPT = `你是一个高级任务规划专家。你的职责是将用户的目标拆解为可执行的步骤序列。
可用工具类型:
- browser: 浏览器操作(访问网页、截图、填表、搜索)
- file: 文件操作(读写、搜索、整理、生成文档)
- terminal: 终端命令(代码执行、系统操作、安装软件)
- api: API 调用(搜索引擎、天气、翻译等)
- ai: AI 分析(文本分析、内容生成、数据处理)
输出要求:
严格输出 JSON 格式,不要包含 markdown 代码块标记。
{
"steps": [
{
"id": "step_1",
"description": "步骤描述",
"tool": "browser",
"params": { "url": "https://example.com", "action": "search" },
"dependencies": []
},
{
"id": "step_2",
"description": "步骤描述",
"tool": "ai",
"params": { "action": "analyze" },
"dependencies": ["step_1"]
}
]
}
规则:
1. 每个步骤必须明确、可执行
2. 标注步骤之间的依赖关系dependencies 数组)
3. 无依赖的步骤可以并行执行
4. 步骤数量控制在 3-10 个之间
5. 最后一个步骤通常是整合/汇总结果`;
export class PlannerAgent extends BaseAgent {
protected getCapabilities(): string[] {
return ['task_planning', 'task_decomposition', 'dependency_analysis'];
}
protected async run(task: AgentTask): Promise<any> {
const goal = task.params.goal || task.description;
const context = task.context || {};
this.log.info(`Planning task: ${goal}`);
const prompt = `目标: ${goal}
${Object.keys(context).length > 0 ? `上下文信息:\n${JSON.stringify(context, null, 2)}` : ''}
请将上述目标拆解为可执行步骤。`;
const response = await this.ai.askJson<{ steps: any[] }>(prompt, PLANNER_SYSTEM_PROMPT);
this.log.info(`Plan generated: ${response.steps.length} steps`);
return response;
}
}

View File

@@ -1,126 +0,0 @@
// 多 Agent 协作系统 - Agent 基类
import type { AgentType, AgentConfig, AgentInfo, AgentTask, AgentResult, AgentMessage, AgentStatus } from './types';
import type { MessageBus } from './message-bus';
import type { AIManager } from '../ai/manager';
import { generateId } from '../../utils/id';
import { createLogger, type Logger } from '../../utils/logger';
export abstract class BaseAgent {
readonly id: string;
readonly type: AgentType;
readonly name: string;
protected status: AgentStatus = 'idle';
protected config: AgentConfig;
protected messageBus: MessageBus;
protected ai: AIManager;
protected log: Logger;
protected currentTaskId?: string;
protected createdAt: Date = new Date();
constructor(
type: AgentType,
config: AgentConfig,
messageBus: MessageBus,
ai: AIManager,
) {
this.id = generateId('agent');
this.type = type;
this.name = config.name || `${type}-agent`;
this.config = config;
this.messageBus = messageBus;
this.ai = ai;
this.log = createLogger(`Agent:${this.name}`);
// 订阅消息
this.messageBus.subscribe(this.id, (msg) => this.handleMessage(msg));
this.log.info(`Agent created: ${this.id}`);
}
getInfo(): AgentInfo {
return {
id: this.id,
type: this.type,
name: this.name,
status: this.status,
capabilities: this.getCapabilities(),
currentTaskId: this.currentTaskId,
createdAt: this.createdAt,
};
}
async execute(task: AgentTask): Promise<AgentResult> {
this.status = 'busy';
this.currentTaskId = task.id;
const startTime = Date.now();
this.log.info(`Executing task: ${task.id} - ${task.description}`);
// 通知开始执行
await this.messageBus.send({
from: this.id,
to: '*',
type: 'status',
payload: { taskId: task.id, status: 'running' },
});
try {
const data = await this.run(task);
const result: AgentResult = {
taskId: task.id,
agentId: this.id,
success: true,
data,
duration: Date.now() - startTime,
};
// 通知完成
await this.messageBus.send({
from: this.id,
to: '*',
type: 'result',
payload: result,
});
this.status = 'idle';
this.currentTaskId = undefined;
this.log.info(`Task completed: ${task.id} (${result.duration}ms)`);
return result;
} catch (error: any) {
const result: AgentResult = {
taskId: task.id,
agentId: this.id,
success: false,
error: error.message,
duration: Date.now() - startTime,
};
await this.messageBus.send({
from: this.id,
to: '*',
type: 'error',
payload: result,
});
this.status = 'error';
this.currentTaskId = undefined;
this.log.error(`Task failed: ${task.id} - ${error.message}`);
return result;
}
}
async terminate(): Promise<void> {
this.status = 'terminated';
this.messageBus.unsubscribe(this.id);
this.log.info(`Agent terminated: ${this.id}`);
}
// 子类必须实现
protected abstract run(task: AgentTask): Promise<any>;
protected abstract getCapabilities(): string[];
protected async handleMessage(message: AgentMessage): Promise<void> {
this.log.debug(`Received message from ${message.from}: ${message.type}`);
}
}

View File

@@ -1,7 +0,0 @@
export type { AgentType, AgentConfig, AgentInfo, AgentTask, AgentResult, AgentMessage, AgentStatus, MultiAgentPlan } from './types';
export { BaseAgent } from './base-agent';
export { MessageBus } from './message-bus';
export { AgentOrchestrator } from './orchestrator';
export { PlannerAgent } from './agents/planner-agent';
export { BrowserAgent, FileAgent, TerminalAgent, AIAnalysisAgent } from './agents/executor-agent';
export { CombinerAgent } from './agents/combiner-agent';

View File

@@ -1,71 +0,0 @@
// 多 Agent 消息总线 - Agent 间通信的核心
import type { AgentMessage, MessageHandler } from './types';
import { generateId } from '../../utils/id';
import { createLogger } from '../../utils/logger';
const log = createLogger('MessageBus');
export class MessageBus {
private subscribers: Map<string, MessageHandler[]> = new Map();
private broadcastHandlers: MessageHandler[] = [];
private messageLog: AgentMessage[] = [];
subscribe(agentId: string, handler: MessageHandler): void {
if (!this.subscribers.has(agentId)) {
this.subscribers.set(agentId, []);
}
this.subscribers.get(agentId)!.push(handler);
log.debug(`Agent ${agentId} subscribed to message bus`);
}
unsubscribe(agentId: string): void {
this.subscribers.delete(agentId);
log.debug(`Agent ${agentId} unsubscribed from message bus`);
}
onBroadcast(handler: MessageHandler): void {
this.broadcastHandlers.push(handler);
}
async send(message: Omit<AgentMessage, 'id' | 'timestamp'>): Promise<void> {
const fullMessage: AgentMessage = {
...message,
id: generateId('msg'),
timestamp: new Date(),
};
this.messageLog.push(fullMessage);
log.debug(`Message: ${fullMessage.from} -> ${fullMessage.to} [${fullMessage.type}]`);
if (fullMessage.to === '*') {
// 广播
const allHandlers = [...this.broadcastHandlers];
for (const [, handlers] of this.subscribers) {
allHandlers.push(...handlers);
}
await Promise.allSettled(allHandlers.map(h => h(fullMessage)));
} else {
// 定向发送
const handlers = this.subscribers.get(fullMessage.to);
if (handlers) {
await Promise.allSettled(handlers.map(h => h(fullMessage)));
} else {
log.warn(`No handlers for agent ${fullMessage.to}`);
}
}
}
getMessages(agentId?: string, limit: number = 50): AgentMessage[] {
let messages = this.messageLog;
if (agentId) {
messages = messages.filter(m => m.from === agentId || m.to === agentId || m.to === '*');
}
return messages.slice(-limit);
}
clear(): void {
this.subscribers.clear();
this.broadcastHandlers = [];
this.messageLog = [];
}
}

View File

@@ -1,262 +0,0 @@
// 多 Agent 协作系统 - 编排器(核心协调器)
import type { AgentType, AgentConfig, AgentTask, AgentResult, MultiAgentPlan } from './types';
import { BaseAgent } from './base-agent';
import { MessageBus } from './message-bus';
import { PlannerAgent } from './agents/planner-agent';
import { BrowserAgent, FileAgent, TerminalAgent, AIAnalysisAgent } from './agents/executor-agent';
import { CombinerAgent } from './agents/combiner-agent';
import { AIManager } from '../ai/manager';
import { generateId } from '../../utils/id';
import { createLogger } from '../../utils/logger';
const log = createLogger('AgentOrchestrator');
type ProgressCallback = (plan: MultiAgentPlan, currentStep?: string) => void;
export class AgentOrchestrator {
private agents: Map<string, BaseAgent> = new Map();
private messageBus: MessageBus;
private ai: AIManager;
private activePlans: Map<string, MultiAgentPlan> = new Map();
constructor(ai: AIManager) {
this.messageBus = new MessageBus();
this.ai = ai;
log.info('AgentOrchestrator initialized');
}
// 创建 Agent
spawnAgent(type: AgentType, config: AgentConfig = {}): BaseAgent {
let agent: BaseAgent;
switch (type) {
case 'planner':
agent = new PlannerAgent(type, config, this.messageBus, this.ai);
break;
case 'browser':
agent = new BrowserAgent(type, config, this.messageBus, this.ai);
break;
case 'file':
agent = new FileAgent(type, config, this.messageBus, this.ai);
break;
case 'terminal':
agent = new TerminalAgent(type, config, this.messageBus, this.ai);
break;
case 'combiner':
agent = new CombinerAgent(type, config, this.messageBus, this.ai);
break;
case 'custom':
default:
agent = new AIAnalysisAgent(type, config, this.messageBus, this.ai);
break;
}
this.agents.set(agent.id, agent);
log.info(`Agent spawned: ${agent.id} (${type})`);
return agent;
}
// 终止 Agent
async terminateAgent(agentId: string): Promise<void> {
const agent = this.agents.get(agentId);
if (agent) {
await agent.terminate();
this.agents.delete(agentId);
}
}
// 核心方法:执行多 Agent 协作任务
async executeGoal(goal: string, context: Record<string, any> = {}, onProgress?: ProgressCallback): Promise<AgentResult> {
const planId = generateId('plan');
log.info(`Starting multi-agent execution: ${goal} (${planId})`);
const plan: MultiAgentPlan = {
id: planId,
goal,
steps: [],
agentAssignments: new Map(),
status: 'planning',
results: [],
createdAt: new Date(),
};
this.activePlans.set(planId, plan);
try {
// Phase 1: 规划 - 使用 Planner Agent 拆解任务
plan.status = 'planning';
onProgress?.(plan, '正在规划任务...');
const planner = this.spawnAgent('planner', { name: 'task-planner' });
const planResult = await planner.execute({
id: generateId('task'),
type: 'plan',
description: '规划任务',
params: { goal },
context,
dependencies: [],
});
if (!planResult.success || !planResult.data?.steps) {
throw new Error(`Planning failed: ${planResult.error || 'No steps generated'}`);
}
const steps: AgentTask[] = planResult.data.steps.map((s: any) => ({
id: s.id || generateId('step'),
type: s.tool || 'ai',
description: s.description,
params: s.params || {},
context: {},
dependencies: s.dependencies || [],
}));
plan.steps = steps;
log.info(`Plan created with ${steps.length} steps`);
onProgress?.(plan, `已规划 ${steps.length} 个步骤`);
// Phase 2: 执行 - 按依赖顺序执行各步骤
plan.status = 'executing';
const stepResults: Map<string, AgentResult> = new Map();
// 拓扑排序
const sortedSteps = this.topologicalSort(steps);
for (const step of sortedSteps) {
// 等待依赖完成
for (const depId of step.dependencies) {
const depResult = stepResults.get(depId);
if (depResult?.data) {
step.context[depId] = depResult.data;
}
}
// 根据工具类型选择 Agent
const agentType = this.mapToolToAgentType(step.type);
const executor = this.spawnAgent(agentType, { name: `executor-${step.id}` });
plan.agentAssignments.set(step.id, executor.id);
onProgress?.(plan, `正在执行: ${step.description}`);
const result = await executor.execute(step);
stepResults.set(step.id, result);
plan.results.push(result);
// 执行完毕,终止 executor
await this.terminateAgent(executor.id);
if (!result.success) {
log.warn(`Step failed: ${step.id} - ${result.error}`);
// 继续执行其他不依赖此步骤的任务
}
}
// Phase 3: 整合 - 使用 Combiner Agent 汇总结果
onProgress?.(plan, '正在整合结果...');
const combiner = this.spawnAgent('combiner', { name: 'result-combiner' });
const combineResult = await combiner.execute({
id: generateId('task'),
type: 'combine',
description: '整合所有步骤的结果',
params: {
goal,
results: plan.results.map((r, i) => ({
...r,
description: steps[i]?.description,
})),
},
context: {},
dependencies: [],
});
// 清理
await this.terminateAgent(planner.id);
await this.terminateAgent(combiner.id);
plan.status = 'completed';
onProgress?.(plan, '任务完成');
log.info(`Multi-agent execution completed: ${planId}`);
return {
taskId: planId,
agentId: 'orchestrator',
success: combineResult.success,
data: {
plan: plan.goal,
stepsExecuted: plan.results.length,
stepsSucceeded: plan.results.filter(r => r.success).length,
report: combineResult.data?.report || '',
stepResults: plan.results,
},
duration: Date.now() - plan.createdAt.getTime(),
};
} catch (error: any) {
plan.status = 'failed';
log.error(`Multi-agent execution failed: ${error.message}`);
// 清理所有 Agent
for (const [id] of this.agents) {
await this.terminateAgent(id);
}
return {
taskId: planId,
agentId: 'orchestrator',
success: false,
error: error.message,
duration: Date.now() - plan.createdAt.getTime(),
};
}
}
// 获取活跃计划
getActivePlans(): MultiAgentPlan[] {
return Array.from(this.activePlans.values());
}
// 获取所有 Agent
getAgents(): BaseAgent[] {
return Array.from(this.agents.values());
}
private mapToolToAgentType(tool: string): AgentType {
switch (tool) {
case 'browser': return 'browser';
case 'file': return 'file';
case 'terminal': return 'terminal';
case 'ai':
case 'api':
default: return 'custom'; // AIAnalysisAgent
}
}
private topologicalSort(steps: AgentTask[]): AgentTask[] {
const sorted: AgentTask[] = [];
const visited = new Set<string>();
const visit = (step: AgentTask) => {
if (visited.has(step.id)) return;
visited.add(step.id);
for (const depId of step.dependencies) {
const dep = steps.find(s => s.id === depId);
if (dep) visit(dep);
}
sorted.push(step);
};
steps.forEach(visit);
return sorted;
}
async shutdown(): Promise<void> {
for (const [id] of this.agents) {
await this.terminateAgent(id);
}
this.messageBus.clear();
this.activePlans.clear();
log.info('AgentOrchestrator shutdown');
}
}

View File

@@ -1,63 +0,0 @@
// 多 Agent 协作系统 - 类型定义
export type AgentType = 'planner' | 'browser' | 'file' | 'terminal' | 'combiner' | 'custom';
export type AgentStatus = 'idle' | 'busy' | 'error' | 'terminated';
export interface AgentConfig {
name?: string;
systemPrompt?: string;
model?: string;
maxRetries?: number;
timeout?: number;
}
export interface AgentInfo {
id: string;
type: AgentType;
name: string;
status: AgentStatus;
capabilities: string[];
currentTaskId?: string;
createdAt: Date;
}
export interface AgentTask {
id: string;
type: string;
description: string;
params: Record<string, any>;
context: Record<string, any>;
dependencies: string[];
}
export interface AgentResult {
taskId: string;
agentId: string;
success: boolean;
data?: any;
error?: string;
duration: number;
}
// 消息总线
export interface AgentMessage {
id: string;
from: string;
to: string; // agent ID 或 '*' 表示广播
type: 'task' | 'result' | 'status' | 'data' | 'error' | 'control';
payload: any;
timestamp: Date;
}
export type MessageHandler = (message: AgentMessage) => void | Promise<void>;
// 多 Agent 任务
export interface MultiAgentPlan {
id: string;
goal: string;
steps: AgentTask[];
agentAssignments: Map<string, string>; // taskId -> agentId
status: 'planning' | 'executing' | 'completed' | 'failed';
results: AgentResult[];
createdAt: Date;
}

View File

@@ -1 +0,0 @@
export * from './proactive';

View File

@@ -1,90 +0,0 @@
// 主动服务系统
import { generateId } from '../../utils/id';
import { createLogger } from '../../utils/logger';
const log = createLogger('Proactive');
export interface ScheduledTask {
id: string;
userId: string;
channel: string;
schedule: {
type: 'once' | 'daily' | 'weekly' | 'cron';
time: string;
timezone: string;
};
task: {
type: string;
prompt: string;
};
status: 'active' | 'paused' | 'completed';
lastRun?: Date;
nextRun?: Date;
}
export class ProactiveServiceSystem {
private tasks: Map<string, ScheduledTask> = new Map();
private cronJobs: Map<string, any> = new Map();
async scheduleTask(task: ScheduledTask): Promise<void> {
task.id = task.id || this.generateId();
task.status = 'active';
this.tasks.set(task.id, task);
// node-cron 集成(可选依赖)
try {
const cron = require('node-cron');
const cronExpr = this.toCronExpression(task);
if (cronExpr) {
const job = cron.schedule(cronExpr, () => {
log.info(`Cron triggered: ${task.id}`);
// TODO: 执行实际任务逻辑
task.lastRun = new Date();
}, { timezone: task.schedule.timezone });
this.cronJobs.set(task.id, job);
}
} catch {
log.debug('node-cron not available, scheduling in-memory only');
}
log.info(`Task scheduled: ${task.id} (${task.schedule.type} at ${task.schedule.time})`);
}
async cancelTask(taskId: string): Promise<void> {
const task = this.tasks.get(taskId);
if (task) {
task.status = 'paused';
// TODO: 取消 cron job
}
}
async listTasks(userId: string): Promise<ScheduledTask[]> {
return Array.from(this.tasks.values()).filter(t => t.userId === userId);
}
private toCronExpression(task: ScheduledTask): string | null {
const { type, time } = task.schedule;
if (type === 'cron') return time; // already a cron expression
// Parse HH:MM format
const parts = time.split(':');
if (parts.length !== 2) return null;
const [hour, minute] = parts;
switch (type) {
case 'daily':
return `${minute} ${hour} * * *`;
case 'weekly':
return `${minute} ${hour} * * 1`; // Monday
case 'once':
return null; // handled separately via setTimeout
default:
return null;
}
}
private generateId(): string {
return generateId('cron');
}
}

View File

@@ -1,166 +0,0 @@
// 远程执行系统 - 实现类
import type {
RemoteExecutionSystem,
Device,
Task,
TaskStatus,
StatusHandler,
Result,
DeviceStatus
} from './types';
import { generateId } from '../../utils/id';
import { createLogger } from '../../utils/logger';
const log = createLogger('RemoteExecution');
export class RemoteExecutionEngine implements RemoteExecutionSystem {
private devices: Map<string, Device> = new Map();
private tasks: Map<string, Task> = new Map();
private subscriptions: Map<string, StatusHandler[]> = new Map();
private runningCount = 0;
private maxConcurrent: number;
private pendingQueue: Task[] = [];
constructor(maxConcurrent: number = 5) {
this.maxConcurrent = maxConcurrent;
}
async registerDevice(device: Device): Promise<void> {
this.devices.set(device.id, device);
log.info(`Device registered: ${device.id} (${device.platform})`);
}
async heartbeat(deviceId: string): Promise<DeviceStatus> {
const device = this.devices.get(deviceId);
if (!device) {
throw new Error(`Device not found: ${deviceId}`);
}
device.lastHeartbeat = new Date();
return device.status;
}
async submitTask(task: Task): Promise<string> {
task.id = task.id || generateId('task');
task.status = 'pending';
task.createdAt = new Date();
this.tasks.set(task.id, task);
log.info(`Task submitted: ${task.id} (priority: ${task.priority})`);
if (this.runningCount < this.maxConcurrent) {
this.executeTask(task).catch(err => log.error(`Task execution error: ${err.message}`));
} else {
this.pendingQueue.push(task);
log.debug(`Task queued (${this.pendingQueue.length} pending)`);
}
return task.id;
}
async cancelTask(taskId: string): Promise<void> {
const task = this.tasks.get(taskId);
if (!task) throw new Error(`Task not found: ${taskId}`);
task.status = 'cancelled';
this.notifySubscribers(taskId, 'cancelled');
log.info(`Task cancelled: ${taskId}`);
}
async getStatus(taskId: string): Promise<TaskStatus> {
const task = this.tasks.get(taskId);
if (!task) throw new Error(`Task not found: ${taskId}`);
return task.status;
}
getTask(taskId: string): Task | undefined {
return this.tasks.get(taskId);
}
getDevice(deviceId: string): Device | undefined {
return this.devices.get(deviceId);
}
listDevices(): Device[] {
return Array.from(this.devices.values());
}
listTasks(filter?: { status?: TaskStatus; userId?: string }): Task[] {
let tasks = Array.from(this.tasks.values());
if (filter?.status) tasks = tasks.filter(t => t.status === filter.status);
if (filter?.userId) tasks = tasks.filter(t => t.userId === filter.userId);
return tasks.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
}
subscribe(taskId: string, handler: StatusHandler): void {
if (!this.subscriptions.has(taskId)) {
this.subscriptions.set(taskId, []);
}
this.subscriptions.get(taskId)!.push(handler);
}
async pushResult(taskId: string, result: Result): Promise<void> {
const task = this.tasks.get(taskId);
if (!task) throw new Error(`Task not found: ${taskId}`);
task.result = result;
task.status = result.success ? 'completed' : 'failed';
task.completedAt = new Date();
this.notifySubscribers(taskId, task.status);
}
getStats(): { total: number; running: number; pending: number; completed: number; failed: number } {
const tasks = Array.from(this.tasks.values());
return {
total: tasks.length,
running: tasks.filter(t => t.status === 'running').length,
pending: tasks.filter(t => t.status === 'pending').length,
completed: tasks.filter(t => t.status === 'completed').length,
failed: tasks.filter(t => t.status === 'failed').length,
};
}
private async executeTask(task: Task): Promise<void> {
this.runningCount++;
try {
task.status = 'running';
task.startedAt = new Date();
this.notifySubscribers(task.id, 'running');
log.info(`Executing task: ${task.id}`);
// TODO: 集成 OpenClaw SDK 实际执行
await new Promise(resolve => setTimeout(resolve, 1000));
await this.pushResult(task.id, {
taskId: task.id,
success: true,
data: { message: 'Task completed successfully' }
});
} catch (error: any) {
await this.pushResult(task.id, {
taskId: task.id,
success: false,
error: error.message
});
} finally {
this.runningCount--;
this.processNextInQueue();
}
}
private processNextInQueue(): void {
if (this.pendingQueue.length > 0 && this.runningCount < this.maxConcurrent) {
// 按优先级排序
this.pendingQueue.sort((a, b) => {
const priority: Record<string, number> = { high: 0, normal: 1, low: 2 };
return (priority[a.priority] ?? 1) - (priority[b.priority] ?? 1);
});
const next = this.pendingQueue.shift()!;
this.executeTask(next).catch(err => log.error(`Queued task error: ${err.message}`));
}
}
private notifySubscribers(taskId: string, status: TaskStatus, progress?: number): void {
const handlers = this.subscriptions.get(taskId);
if (handlers) {
handlers.forEach(handler => handler(status, progress));
}
}
}

View File

@@ -1,2 +0,0 @@
export * from './types';
export * from './engine';

View File

@@ -1,46 +0,0 @@
// 远程执行系统 - 核心接口
export interface Device {
id: string;
name: string;
userId: string;
platform: 'macos' | 'windows' | 'linux';
capabilities: string[];
status: 'online' | 'offline' | 'busy';
lastHeartbeat: Date;
}
export interface Task {
id: string;
userId: string;
deviceId: string;
channel: string;
type: 'immediate' | 'scheduled';
priority: 'high' | 'normal' | 'low';
payload: any;
status: TaskStatus;
result?: any;
createdAt: Date;
startedAt?: Date;
completedAt?: Date;
}
export type TaskStatus = 'pending' | 'running' | 'completed' | 'failed' | 'cancelled';
export interface RemoteExecutionSystem {
registerDevice(device: Device): Promise<void>;
heartbeat(deviceId: string): Promise<DeviceStatus>;
submitTask(task: Task): Promise<string>;
cancelTask(taskId: string): Promise<void>;
getStatus(taskId: string): Promise<TaskStatus>;
subscribe(taskId: string, handler: StatusHandler): void;
pushResult(taskId: string, result: Result): Promise<void>;
}
export type StatusHandler = (status: TaskStatus, progress?: number) => void;
export type DeviceStatus = 'online' | 'offline' | 'busy';
export interface Result {
taskId: string;
success: boolean;
data?: any;
error?: string;
}

View File

@@ -1,2 +0,0 @@
export * from './types';
export * from './orchestrator';

View File

@@ -1,196 +0,0 @@
// 任务编排引擎 - 实现类
import type {
TaskOrchestrationEngine,
TaskPlan,
TaskStep,
ExecutionResult,
Progress,
StepStatus
} from './types';
import { generateId } from '../../utils/id';
import { createLogger } from '../../utils/logger';
const log = createLogger('TaskOrchestrator');
export class TaskOrchestrator implements TaskOrchestrationEngine {
private plans: Map<string, TaskPlan> = new Map();
async plan(goal: string, context: any): Promise<TaskPlan> {
// TODO: 使用 AI 规划任务(后续实现)
// 这里先返回一个简单的示例计划
const steps: TaskStep[] = [
{
id: 'step_1',
description: '分析任务需求',
tool: 'ai',
params: { goal },
dependencies: [],
status: 'pending'
},
{
id: 'step_2',
description: '执行任务',
tool: 'executor',
params: { goal },
dependencies: ['step_1'],
status: 'pending'
},
{
id: 'step_3',
description: '整理结果',
tool: 'combiner',
params: {},
dependencies: ['step_2'],
status: 'pending'
}
];
const plan: TaskPlan = {
id: this.generateId(),
goal,
steps,
dependencies: new Map(),
context: new Map(Object.entries(context)),
status: 'planned',
progress: 0
};
this.plans.set(plan.id, plan);
log.info(`Plan created: ${plan.id} with ${steps.length} steps`);
return plan;
}
async execute(plan: TaskPlan): Promise<ExecutionResult> {
plan.status = 'executing';
try {
// 拓扑排序
const sortedSteps = this.topologicalSort(plan.steps);
for (const step of sortedSteps) {
// 检查依赖
await this.waitForDependencies(step, plan);
// 执行步骤
step.status = 'running';
plan.progress = this.calculateProgress(plan);
log.info(`Executing step: ${step.id} - ${step.description}`);
// TODO: 实际执行逻辑(后续实现)
await new Promise(resolve => setTimeout(resolve, 500));
step.status = 'completed';
step.result = { message: 'Step completed' };
plan.progress = this.calculateProgress(plan);
}
plan.status = 'completed';
plan.progress = 1;
return {
planId: plan.id,
status: 'completed',
results: plan.steps.map(s => s.result)
};
} catch (error: any) {
plan.status = 'failed';
throw error;
}
}
async getProgress(planId: string): Promise<Progress> {
const plan = this.plans.get(planId);
if (!plan) {
throw new Error(`Plan not found: ${planId}`);
}
const completed = plan.steps.filter(s => s.status === 'completed').length;
const percentage = (plan.progress * 100).toFixed(1) + '%';
return {
planId: plan.id,
goal: plan.goal,
total: plan.steps.length,
completed,
current: plan.steps.find(s => s.status === 'running')?.description || '',
percentage,
steps: plan.steps.map(s => ({
description: s.description,
status: s.status,
result: s.result
}))
};
}
async pause(planId: string): Promise<void> {
const plan = this.plans.get(planId);
if (plan) {
plan.status = 'paused';
}
}
async resume(planId: string): Promise<void> {
const plan = this.plans.get(planId);
if (plan && plan.status === 'paused') {
plan.status = 'executing';
// 继续执行(后续实现)
}
}
async cancel(planId: string): Promise<void> {
const plan = this.plans.get(planId);
if (plan) {
plan.status = 'failed';
}
}
private topologicalSort(steps: TaskStep[]): TaskStep[] {
// 简单实现:按依赖顺序排序
const sorted: TaskStep[] = [];
const visited = new Set<string>();
const visit = (step: TaskStep) => {
if (visited.has(step.id)) return;
visited.add(step.id);
for (const depId of step.dependencies) {
const dep = steps.find(s => s.id === depId);
if (dep) visit(dep);
}
sorted.push(step);
};
steps.forEach(visit);
return sorted;
}
private async waitForDependencies(step: TaskStep, plan: TaskPlan): Promise<void> {
for (const depId of step.dependencies) {
const dep = plan.steps.find(s => s.id === depId);
if (dep && dep.status !== 'completed') {
// 等待依赖完成(简单实现)
await new Promise(resolve => setTimeout(resolve, 100));
}
}
}
private calculateProgress(plan: TaskPlan): number {
const completed = plan.steps.filter(s => s.status === 'completed').length;
return completed / plan.steps.length;
}
listPlans(filter?: { status?: string }): TaskPlan[] {
let plans = Array.from(this.plans.values());
if (filter?.status) plans = plans.filter(p => p.status === filter.status);
return plans;
}
private generateId(): string {
return generateId('plan');
}
}

View File

@@ -1,53 +0,0 @@
// 任务编排引擎 - 类型定义
export interface TaskPlan {
id: string;
goal: string;
steps: TaskStep[];
dependencies: Map<string, string[]>;
context: Map<string, any>;
status: PlanStatus;
progress: number;
}
export interface TaskStep {
id: string;
description: string;
tool: string;
params: any;
dependencies: string[];
status: StepStatus;
result?: any;
error?: string;
}
export type PlanStatus = 'planned' | 'executing' | 'completed' | 'failed' | 'paused';
export type StepStatus = 'pending' | 'running' | 'completed' | 'failed' | 'skipped';
export interface ExecutionResult {
planId: string;
status: PlanStatus;
results: any[];
}
export interface Progress {
planId: string;
goal: string;
total: number;
completed: number;
current: string;
percentage: string;
steps: Array<{
description: string;
status: StepStatus;
result?: any;
}>;
}
export interface TaskOrchestrationEngine {
plan(goal: string, context: any): Promise<TaskPlan>;
execute(plan: TaskPlan): Promise<ExecutionResult>;
getProgress(planId: string): Promise<Progress>;
pause(planId: string): Promise<void>;
resume(planId: string): Promise<void>;
cancel(planId: string): Promise<void>;
}

View File

@@ -1,91 +0,0 @@
// ZCLAW 数据库管理
import Database from 'better-sqlite3';
import { existsSync, mkdirSync } from 'fs';
import { dirname } from 'path';
import { SCHEMA_SQL } from './schema';
import { createLogger } from '../utils/logger';
const log = createLogger('Database');
let _db: Database.Database | null = null;
export function initDatabase(dbPath: string): Database.Database {
// 确保目录存在
const dir = dirname(dbPath);
if (!existsSync(dir)) {
mkdirSync(dir, { recursive: true });
}
_db = new Database(dbPath);
// 启用 WAL 模式提升性能
_db.pragma('journal_mode = WAL');
_db.pragma('foreign_keys = ON');
// 创建表
_db.exec(SCHEMA_SQL);
log.info(`Database initialized at ${dbPath}`);
return _db;
}
export function getDatabase(): Database.Database {
if (!_db) {
throw new Error('Database not initialized. Call initDatabase() first.');
}
return _db;
}
export function closeDatabase(): void {
if (_db) {
_db.close();
_db = null;
log.info('Database closed');
}
}
// 通用 CRUD 辅助
export class BaseDAO<T extends Record<string, any>> {
constructor(
protected tableName: string,
protected db: Database.Database = getDatabase()
) {}
findById(id: string): T | undefined {
return this.db.prepare(`SELECT * FROM ${this.tableName} WHERE id = ?`).get(id) as T | undefined;
}
findAll(where?: string, params?: any[]): T[] {
const sql = where
? `SELECT * FROM ${this.tableName} WHERE ${where}`
: `SELECT * FROM ${this.tableName}`;
return (params ? this.db.prepare(sql).all(...params) : this.db.prepare(sql).all()) as T[];
}
insert(data: Partial<T>): void {
const keys = Object.keys(data);
const placeholders = keys.map(() => '?').join(', ');
const sql = `INSERT OR REPLACE INTO ${this.tableName} (${keys.join(', ')}) VALUES (${placeholders})`;
this.db.prepare(sql).run(...Object.values(data));
}
update(id: string, data: Partial<T>): void {
const sets = Object.keys(data).map(k => `${k} = ?`).join(', ');
const sql = `UPDATE ${this.tableName} SET ${sets} WHERE id = ?`;
this.db.prepare(sql).run(...Object.values(data), id);
}
delete(id: string): void {
this.db.prepare(`DELETE FROM ${this.tableName} WHERE id = ?`).run(id);
}
count(where?: string, params?: any[]): number {
const sql = where
? `SELECT COUNT(*) as count FROM ${this.tableName} WHERE ${where}`
: `SELECT COUNT(*) as count FROM ${this.tableName}`;
const result = params
? this.db.prepare(sql).get(...params) as { count: number }
: this.db.prepare(sql).get() as { count: number };
return result.count;
}
}

View File

@@ -1,2 +0,0 @@
export { initDatabase, getDatabase, closeDatabase, BaseDAO } from './database';
export { SCHEMA_SQL } from './schema';

View File

@@ -1,118 +0,0 @@
// ZCLAW 数据库 Schema 定义
export const SCHEMA_SQL = `
-- 用户表
CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
avatar TEXT DEFAULT '',
preferences TEXT DEFAULT '{}',
patterns TEXT DEFAULT '{}',
created_at TEXT DEFAULT (datetime('now')),
updated_at TEXT DEFAULT (datetime('now'))
);
-- 设备表
CREATE TABLE IF NOT EXISTS devices (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
name TEXT NOT NULL,
platform TEXT NOT NULL CHECK (platform IN ('macos', 'windows', 'linux')),
capabilities TEXT DEFAULT '[]',
status TEXT DEFAULT 'offline' CHECK (status IN ('online', 'offline', 'busy')),
last_heartbeat TEXT,
created_at TEXT DEFAULT (datetime('now')),
FOREIGN KEY (user_id) REFERENCES users(id)
);
-- 任务表
CREATE TABLE IF NOT EXISTS tasks (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
device_id TEXT,
channel TEXT DEFAULT '',
type TEXT NOT NULL CHECK (type IN ('immediate', 'scheduled')),
priority TEXT DEFAULT 'normal' CHECK (priority IN ('high', 'normal', 'low')),
payload TEXT DEFAULT '{}',
status TEXT DEFAULT 'pending' CHECK (status IN ('pending', 'running', 'completed', 'failed', 'cancelled')),
result TEXT,
error TEXT,
created_at TEXT DEFAULT (datetime('now')),
started_at TEXT,
completed_at TEXT,
FOREIGN KEY (user_id) REFERENCES users(id)
);
-- 任务计划表
CREATE TABLE IF NOT EXISTS task_plans (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
goal TEXT NOT NULL,
steps TEXT DEFAULT '[]',
status TEXT DEFAULT 'planned' CHECK (status IN ('planned', 'executing', 'completed', 'failed', 'paused')),
progress REAL DEFAULT 0,
context TEXT DEFAULT '{}',
created_at TEXT DEFAULT (datetime('now')),
updated_at TEXT DEFAULT (datetime('now')),
FOREIGN KEY (user_id) REFERENCES users(id)
);
-- 记忆事件表
CREATE TABLE IF NOT EXISTS memory_events (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
type TEXT NOT NULL,
content TEXT NOT NULL,
metadata TEXT DEFAULT '{}',
timestamp TEXT DEFAULT (datetime('now')),
FOREIGN KEY (user_id) REFERENCES users(id)
);
-- 定时任务表
CREATE TABLE IF NOT EXISTS scheduled_tasks (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
channel TEXT DEFAULT '',
schedule_type TEXT NOT NULL CHECK (schedule_type IN ('once', 'daily', 'weekly', 'cron')),
schedule_time TEXT NOT NULL,
timezone TEXT DEFAULT 'Asia/Shanghai',
task_type TEXT NOT NULL,
task_prompt TEXT NOT NULL,
status TEXT DEFAULT 'active' CHECK (status IN ('active', 'paused', 'completed')),
last_run TEXT,
next_run TEXT,
created_at TEXT DEFAULT (datetime('now')),
FOREIGN KEY (user_id) REFERENCES users(id)
);
-- 对话历史表
CREATE TABLE IF NOT EXISTS conversations (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
agent_id TEXT DEFAULT 'zclaw',
role TEXT NOT NULL CHECK (role IN ('user', 'assistant', 'system')),
content TEXT NOT NULL,
metadata TEXT DEFAULT '{}',
timestamp TEXT DEFAULT (datetime('now')),
FOREIGN KEY (user_id) REFERENCES users(id)
);
-- Agent 表
CREATE TABLE IF NOT EXISTS agents (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
type TEXT NOT NULL CHECK (type IN ('planner', 'browser', 'file', 'terminal', 'combiner', 'custom')),
config TEXT DEFAULT '{}',
status TEXT DEFAULT 'idle' CHECK (status IN ('idle', 'busy', 'error', 'terminated')),
created_at TEXT DEFAULT (datetime('now'))
);
-- 索引
CREATE INDEX IF NOT EXISTS idx_tasks_user ON tasks(user_id);
CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
CREATE INDEX IF NOT EXISTS idx_memory_user ON memory_events(user_id);
CREATE INDEX IF NOT EXISTS idx_memory_timestamp ON memory_events(timestamp);
CREATE INDEX IF NOT EXISTS idx_conversations_user ON conversations(user_id);
CREATE INDEX IF NOT EXISTS idx_conversations_timestamp ON conversations(timestamp);
CREATE INDEX IF NOT EXISTS idx_scheduled_user ON scheduled_tasks(user_id);
CREATE INDEX IF NOT EXISTS idx_devices_user ON devices(user_id);
`;

View File

@@ -1,124 +0,0 @@
// 飞书 IM 适配器
import type { IMAdapter, IMSendOptions, IMMessageHandler, IMMessage } from '../types';
import { createLogger } from '../../utils/logger';
import { generateId } from '../../utils/id';
const log = createLogger('FeishuAdapter');
export class FeishuAdapter implements IMAdapter {
name = 'feishu';
private appId: string;
private appSecret: string;
private connected = false;
private accessToken = '';
private handlers: IMMessageHandler[] = [];
private pollingInterval?: ReturnType<typeof setInterval>;
constructor(appId: string, appSecret: string) {
this.appId = appId;
this.appSecret = appSecret;
}
async connect(): Promise<void> {
if (!this.appId || !this.appSecret) {
log.warn('Feishu credentials not configured, running in mock mode');
this.connected = true;
return;
}
try {
// 获取 tenant_access_token
const response = await fetch('https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
app_id: this.appId,
app_secret: this.appSecret,
}),
});
const data: any = await response.json();
if (data.code === 0) {
this.accessToken = data.tenant_access_token;
this.connected = true;
log.info('Feishu connected successfully');
} else {
throw new Error(`Feishu auth failed: ${data.msg}`);
}
} catch (error: any) {
log.error(`Feishu connection failed: ${error.message}`);
// 降级到 mock 模式
this.connected = true;
log.warn('Running in mock mode');
}
}
async disconnect(): Promise<void> {
if (this.pollingInterval) {
clearInterval(this.pollingInterval);
}
this.connected = false;
this.accessToken = '';
log.info('Feishu disconnected');
}
async send(options: IMSendOptions): Promise<void> {
if (!this.accessToken) {
log.warn(`[Mock] Send to ${options.channelId}: ${options.content.slice(0, 100)}`);
return;
}
const msgType = options.contentType === 'card' ? 'interactive' : 'text';
const content = msgType === 'text'
? JSON.stringify({ text: options.content })
: options.content;
try {
const response = await fetch('https://open.feishu.cn/open-apis/im/v1/messages', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.accessToken}`,
},
body: JSON.stringify({
receive_id: options.channelId,
msg_type: msgType,
content,
}),
});
const data: any = await response.json();
if (data.code !== 0) {
log.error(`Feishu send failed: ${data.msg}`);
}
} catch (error: any) {
log.error(`Feishu send error: ${error.message}`);
}
}
onMessage(handler: IMMessageHandler): void {
this.handlers.push(handler);
}
isConnected(): boolean {
return this.connected;
}
// 模拟接收消息(用于本地测试)
async simulateMessage(content: string, userId: string = 'test_user'): Promise<void> {
const message: IMMessage = {
id: generateId('msg'),
channelType: 'feishu',
channelId: 'test_channel',
userId,
userName: '测试用户',
content,
contentType: 'text',
timestamp: new Date(),
};
for (const handler of this.handlers) {
await handler(message);
}
}
}

View File

@@ -1,80 +0,0 @@
// IM 网关 - 统一消息路由
import type { IMAdapter, IMMessage, IMSendOptions, IMMessageHandler } from './types';
import { createLogger } from '../utils/logger';
const log = createLogger('IMGateway');
export class IMGateway {
private adapters: Map<string, IMAdapter> = new Map();
private messageHandlers: IMMessageHandler[] = [];
registerAdapter(adapter: IMAdapter): void {
this.adapters.set(adapter.name, adapter);
// 转发消息到全局处理器
adapter.onMessage(async (message) => {
log.info(`Message from ${message.channelType}/${message.userId}: ${message.content.slice(0, 50)}...`);
for (const handler of this.messageHandlers) {
try {
await handler(message);
} catch (error: any) {
log.error(`Message handler error: ${error.message}`);
}
}
});
log.info(`IM adapter registered: ${adapter.name}`);
}
onMessage(handler: IMMessageHandler): void {
this.messageHandlers.push(handler);
}
async send(options: IMSendOptions): Promise<void> {
const adapter = this.adapters.get(options.channelType);
if (!adapter) {
log.error(`No adapter for channel type: ${options.channelType}`);
throw new Error(`No adapter for channel type: ${options.channelType}`);
}
if (!adapter.isConnected()) {
log.warn(`Adapter ${options.channelType} not connected, attempting reconnect...`);
await adapter.connect();
}
await adapter.send(options);
log.debug(`Message sent via ${options.channelType}`);
}
async connectAll(): Promise<void> {
for (const [name, adapter] of this.adapters) {
try {
await adapter.connect();
log.info(`Connected: ${name}`);
} catch (error: any) {
log.error(`Failed to connect ${name}: ${error.message}`);
}
}
}
async disconnectAll(): Promise<void> {
for (const [name, adapter] of this.adapters) {
try {
await adapter.disconnect();
log.info(`Disconnected: ${name}`);
} catch (error: any) {
log.error(`Failed to disconnect ${name}: ${error.message}`);
}
}
}
getAdapter(name: string): IMAdapter | undefined {
return this.adapters.get(name);
}
getConnectedAdapters(): string[] {
return Array.from(this.adapters.entries())
.filter(([, adapter]) => adapter.isConnected())
.map(([name]) => name);
}
}

View File

@@ -1,3 +0,0 @@
export type { IMAdapter, IMMessage, IMSendOptions, IMMessageHandler } from './types';
export { IMGateway } from './gateway';
export { FeishuAdapter } from './adapters/feishu';

View File

@@ -1,33 +0,0 @@
// IM 网关 - 类型定义
export interface IMMessage {
id: string;
channelType: string; // 'feishu' | 'telegram' | 'qq' | 'wecom'
channelId: string; // 频道/群组 ID
userId: string;
userName?: string;
content: string;
contentType: 'text' | 'image' | 'file' | 'card';
metadata?: Record<string, any>;
timestamp: Date;
}
export interface IMSendOptions {
channelType: string;
channelId: string;
userId?: string;
content: string;
contentType?: 'text' | 'card' | 'markdown';
metadata?: Record<string, any>;
}
export type IMMessageHandler = (message: IMMessage) => void | Promise<void>;
export interface IMAdapter {
name: string;
connect(): Promise<void>;
disconnect(): Promise<void>;
send(options: IMSendOptions): Promise<void>;
onMessage(handler: IMMessageHandler): void;
isConnected(): boolean;
}

View File

@@ -1,34 +0,0 @@
// ZCLAW 入口文件
import { ZClawApp } from './app';
// 导出所有模块
export * from './core/remote-execution';
export * from './core/task-orchestration';
export * from './core/memory';
export * from './core/proactive';
export * from './core/multi-agent';
export * from './core/ai';
export * from './im';
export * from './db';
export * from './config';
export * from './utils';
export { ZClawApp } from './app';
// 主启动流程
const app = new ZClawApp();
app.start().catch((error) => {
console.error('ZCLAW failed to start:', error);
process.exit(1);
});
// 优雅退出
process.on('SIGINT', async () => {
await app.shutdown();
process.exit(0);
});
process.on('SIGTERM', async () => {
await app.shutdown();
process.exit(0);
});

View File

@@ -1,12 +0,0 @@
// ZCLAW ID 生成工具
import { randomBytes } from 'crypto';
export function generateId(prefix: string = ''): string {
const timestamp = Date.now().toString(36);
const random = randomBytes(4).toString('hex');
return prefix ? `${prefix}_${timestamp}_${random}` : `${timestamp}_${random}`;
}
export function generateShortId(): string {
return randomBytes(6).toString('base64url');
}

View File

@@ -1,3 +0,0 @@
export { generateId, generateShortId } from './id';
export { createLogger, setLogLevel } from './logger';
export type { Logger } from './logger';

View File

@@ -1,57 +0,0 @@
// ZCLAW 日志系统
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
const LEVEL_PRIORITY: Record<LogLevel, number> = {
debug: 0,
info: 1,
warn: 2,
error: 3,
};
const LEVEL_COLORS: Record<LogLevel, string> = {
debug: '\x1b[36m', // cyan
info: '\x1b[32m', // green
warn: '\x1b[33m', // yellow
error: '\x1b[31m', // red
};
const RESET = '\x1b[0m';
let currentLevel: LogLevel = 'info';
export function setLogLevel(level: LogLevel): void {
currentLevel = level;
}
function shouldLog(level: LogLevel): boolean {
return LEVEL_PRIORITY[level] >= LEVEL_PRIORITY[currentLevel];
}
function formatTimestamp(): string {
return new Date().toISOString().slice(11, 23);
}
function log(level: LogLevel, module: string, message: string, data?: any): void {
if (!shouldLog(level)) return;
const color = LEVEL_COLORS[level];
const timestamp = formatTimestamp();
const prefix = `${color}[${timestamp}] [${level.toUpperCase()}] [${module}]${RESET}`;
if (data !== undefined) {
console.log(`${prefix} ${message}`, typeof data === 'object' ? JSON.stringify(data, null, 2) : data);
} else {
console.log(`${prefix} ${message}`);
}
}
export function createLogger(module: string) {
return {
debug: (message: string, data?: any) => log('debug', module, message, data),
info: (message: string, data?: any) => log('info', module, message, data),
warn: (message: string, data?: any) => log('warn', module, message, data),
error: (message: string, data?: any) => log('error', module, message, data),
};
}
export type Logger = ReturnType<typeof createLogger>;