refactor: 统一项目名称从OpenFang到ZCLAW
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
重构所有代码和文档中的项目名称,将OpenFang统一更新为ZCLAW。包括: - 配置文件中的项目名称 - 代码注释和文档引用 - 环境变量和路径 - 类型定义和接口名称 - 测试用例和模拟数据 同时优化部分代码结构,移除未使用的模块,并更新相关依赖项。
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
/**
|
||||
* ZCLAW Gateway Client (Browser/Tauri side)
|
||||
*
|
||||
* Core WebSocket client for OpenFang Kernel protocol.
|
||||
* Core WebSocket client for ZCLAW Kernel protocol.
|
||||
* Handles connection management, WebSocket framing, heartbeat,
|
||||
* event dispatch, and chat/stream operations.
|
||||
*
|
||||
@@ -22,7 +22,7 @@ export type {
|
||||
GatewayPong,
|
||||
GatewayFrame,
|
||||
AgentStreamDelta,
|
||||
OpenFangStreamEvent,
|
||||
ZclawStreamEvent,
|
||||
ConnectionState,
|
||||
EventCallback,
|
||||
} from './gateway-types';
|
||||
@@ -51,7 +51,7 @@ import type {
|
||||
GatewayFrame,
|
||||
GatewayResponse,
|
||||
GatewayEvent,
|
||||
OpenFangStreamEvent,
|
||||
ZclawStreamEvent,
|
||||
ConnectionState,
|
||||
EventCallback,
|
||||
AgentStreamDelta,
|
||||
@@ -158,7 +158,7 @@ function createIdempotencyKey(): string {
|
||||
|
||||
export class GatewayClient {
|
||||
private ws: WebSocket | null = null;
|
||||
private openfangWs: WebSocket | null = null; // OpenFang stream WebSocket
|
||||
private zclawWs: WebSocket | null = null; // ZCLAW stream WebSocket
|
||||
private state: ConnectionState = 'disconnected';
|
||||
private requestId = 0;
|
||||
private pendingRequests = new Map<string, {
|
||||
@@ -243,20 +243,20 @@ export class GatewayClient {
|
||||
|
||||
// === Connection ===
|
||||
|
||||
/** Connect using REST API only (for OpenFang mode) */
|
||||
/** Connect using REST API only (for ZCLAW mode) */
|
||||
async connectRest(): Promise<void> {
|
||||
if (this.state === 'connected') {
|
||||
return;
|
||||
}
|
||||
this.setState('connecting');
|
||||
try {
|
||||
// Check if OpenFang API is healthy
|
||||
// Check if ZCLAW API is healthy
|
||||
const health = await this.restGet<{ status: string; version?: string }>('/api/health');
|
||||
if (health.status === 'ok') {
|
||||
this.reconnectAttempts = 0;
|
||||
this.setState('connected');
|
||||
this.startHeartbeat(); // Start heartbeat after successful connection
|
||||
this.log('info', `Connected to OpenFang via REST API${health.version ? ` (v${health.version})` : ''}`);
|
||||
this.log('info', `Connected to ZCLAW via REST API${health.version ? ` (v${health.version})` : ''}`);
|
||||
this.emitEvent('connected', { version: health.version });
|
||||
} else {
|
||||
throw new Error('Health check failed');
|
||||
@@ -264,7 +264,7 @@ export class GatewayClient {
|
||||
} catch (err: unknown) {
|
||||
this.setState('disconnected');
|
||||
const errorMessage = err instanceof Error ? err.message : String(err);
|
||||
throw new Error(`Failed to connect to OpenFang: ${errorMessage}`);
|
||||
throw new Error(`Failed to connect to ZCLAW: ${errorMessage}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,7 +273,7 @@ export class GatewayClient {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// Check if URL is for OpenFang (port 4200 or 50051) - use REST mode
|
||||
// Check if URL is for ZCLAW (port 4200 or 50051) - use REST mode
|
||||
if (this.url.includes(':4200') || this.url.includes(':50051')) {
|
||||
return this.connectRest();
|
||||
}
|
||||
@@ -389,10 +389,10 @@ export class GatewayClient {
|
||||
|
||||
// === High-level API ===
|
||||
|
||||
// Default agent ID for OpenFang (will be set dynamically from /api/agents)
|
||||
// Default agent ID for ZCLAW (will be set dynamically from /api/agents)
|
||||
private defaultAgentId: string = '';
|
||||
|
||||
/** Try to fetch default agent ID from OpenFang /api/agents endpoint */
|
||||
/** Try to fetch default agent ID from ZCLAW /api/agents endpoint */
|
||||
async fetchDefaultAgentId(): Promise<string | null> {
|
||||
try {
|
||||
// Use /api/agents endpoint which returns array of agents
|
||||
@@ -422,7 +422,7 @@ export class GatewayClient {
|
||||
return this.defaultAgentId;
|
||||
}
|
||||
|
||||
/** Send message to agent (OpenFang chat API) */
|
||||
/** Send message to agent (ZCLAW chat API) */
|
||||
async chat(message: string, opts?: {
|
||||
sessionKey?: string;
|
||||
agentId?: string;
|
||||
@@ -432,24 +432,24 @@ export class GatewayClient {
|
||||
temperature?: number;
|
||||
maxTokens?: number;
|
||||
}): Promise<{ runId: string; sessionId?: string; response?: string }> {
|
||||
// OpenFang uses /api/agents/{agentId}/message endpoint
|
||||
// ZCLAW uses /api/agents/{agentId}/message endpoint
|
||||
let agentId = opts?.agentId || this.defaultAgentId;
|
||||
|
||||
// If no agent ID, try to fetch from OpenFang status
|
||||
// If no agent ID, try to fetch from ZCLAW status
|
||||
if (!agentId) {
|
||||
await this.fetchDefaultAgentId();
|
||||
agentId = this.defaultAgentId;
|
||||
}
|
||||
|
||||
if (!agentId) {
|
||||
throw new Error('No agent available. Please ensure OpenFang has at least one agent.');
|
||||
throw new Error('No agent available. Please ensure ZCLAW has at least one agent.');
|
||||
}
|
||||
|
||||
const result = await this.restPost<{ response?: string; input_tokens?: number; output_tokens?: number }>(`/api/agents/${agentId}/message`, {
|
||||
message,
|
||||
session_id: opts?.sessionKey,
|
||||
});
|
||||
// OpenFang returns { response, input_tokens, output_tokens }
|
||||
// ZCLAW returns { response, input_tokens, output_tokens }
|
||||
return {
|
||||
runId: createIdempotencyKey(),
|
||||
sessionId: opts?.sessionKey,
|
||||
@@ -457,7 +457,7 @@ export class GatewayClient {
|
||||
};
|
||||
}
|
||||
|
||||
/** Send message with streaming response (OpenFang WebSocket) */
|
||||
/** Send message with streaming response (ZCLAW WebSocket) */
|
||||
async chatStream(
|
||||
message: string,
|
||||
callbacks: {
|
||||
@@ -472,20 +472,20 @@ export class GatewayClient {
|
||||
agentId?: string;
|
||||
}
|
||||
): Promise<{ runId: string }> {
|
||||
let agentId = opts?.agentId || this.defaultAgentId;
|
||||
const agentId = opts?.agentId || this.defaultAgentId;
|
||||
const runId = createIdempotencyKey();
|
||||
const sessionId = opts?.sessionKey || `session_${Date.now()}`;
|
||||
|
||||
// If no agent ID, try to fetch from OpenFang status (async, but we'll handle it in connectOpenFangStream)
|
||||
// If no agent ID, try to fetch from ZCLAW status (async, but we'll handle it in connectZclawStream)
|
||||
if (!agentId) {
|
||||
// Try to get default agent asynchronously
|
||||
this.fetchDefaultAgentId().then(() => {
|
||||
const resolvedAgentId = this.defaultAgentId;
|
||||
if (resolvedAgentId) {
|
||||
this.streamCallbacks.set(runId, callbacks);
|
||||
this.connectOpenFangStream(resolvedAgentId, runId, sessionId, message);
|
||||
this.connectZclawStream(resolvedAgentId, runId, sessionId, message);
|
||||
} else {
|
||||
callbacks.onError('No agent available. Please ensure OpenFang has at least one agent.');
|
||||
callbacks.onError('No agent available. Please ensure ZCLAW has at least one agent.');
|
||||
callbacks.onComplete();
|
||||
}
|
||||
}).catch((err) => {
|
||||
@@ -498,22 +498,22 @@ export class GatewayClient {
|
||||
// Store callbacks for this run
|
||||
this.streamCallbacks.set(runId, callbacks);
|
||||
|
||||
// Connect to OpenFang WebSocket if not connected
|
||||
this.connectOpenFangStream(agentId, runId, sessionId, message);
|
||||
// Connect to ZCLAW WebSocket if not connected
|
||||
this.connectZclawStream(agentId, runId, sessionId, message);
|
||||
|
||||
return { runId };
|
||||
}
|
||||
|
||||
/** Connect to OpenFang streaming WebSocket */
|
||||
private connectOpenFangStream(
|
||||
/** Connect to ZCLAW streaming WebSocket */
|
||||
private connectZclawStream(
|
||||
agentId: string,
|
||||
runId: string,
|
||||
sessionId: string,
|
||||
message: string
|
||||
): void {
|
||||
// Close existing connection if any
|
||||
if (this.openfangWs && this.openfangWs.readyState !== WebSocket.CLOSED) {
|
||||
this.openfangWs.close();
|
||||
if (this.zclawWs && this.zclawWs.readyState !== WebSocket.CLOSED) {
|
||||
this.zclawWs.close();
|
||||
}
|
||||
|
||||
// Build WebSocket URL
|
||||
@@ -528,34 +528,34 @@ export class GatewayClient {
|
||||
wsUrl = httpUrl.replace(/^http/, 'ws') + `/api/agents/${agentId}/ws`;
|
||||
}
|
||||
|
||||
this.log('info', `Connecting to OpenFang stream: ${wsUrl}`);
|
||||
this.log('info', `Connecting to ZCLAW stream: ${wsUrl}`);
|
||||
|
||||
try {
|
||||
this.openfangWs = new WebSocket(wsUrl);
|
||||
this.zclawWs = new WebSocket(wsUrl);
|
||||
|
||||
this.openfangWs.onopen = () => {
|
||||
this.log('info', 'OpenFang WebSocket connected');
|
||||
// Send chat message using OpenFang actual protocol
|
||||
this.zclawWs.onopen = () => {
|
||||
this.log('info', 'ZCLAW WebSocket connected');
|
||||
// Send chat message using ZCLAW actual protocol
|
||||
const chatRequest = {
|
||||
type: 'message',
|
||||
content: message,
|
||||
session_id: sessionId,
|
||||
};
|
||||
this.openfangWs?.send(JSON.stringify(chatRequest));
|
||||
this.zclawWs?.send(JSON.stringify(chatRequest));
|
||||
};
|
||||
|
||||
this.openfangWs.onmessage = (event) => {
|
||||
this.zclawWs.onmessage = (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
this.handleOpenFangStreamEvent(runId, data, sessionId);
|
||||
this.handleZclawStreamEvent(runId, data, sessionId);
|
||||
} catch (err: unknown) {
|
||||
const errorMessage = err instanceof Error ? err.message : String(err);
|
||||
this.log('error', `Failed to parse stream event: ${errorMessage}`);
|
||||
}
|
||||
};
|
||||
|
||||
this.openfangWs.onerror = (_event) => {
|
||||
this.log('error', 'OpenFang WebSocket error');
|
||||
this.zclawWs.onerror = (_event) => {
|
||||
this.log('error', 'ZCLAW WebSocket error');
|
||||
const callbacks = this.streamCallbacks.get(runId);
|
||||
if (callbacks) {
|
||||
callbacks.onError('WebSocket connection failed');
|
||||
@@ -563,14 +563,14 @@ export class GatewayClient {
|
||||
}
|
||||
};
|
||||
|
||||
this.openfangWs.onclose = (event) => {
|
||||
this.log('info', `OpenFang WebSocket closed: ${event.code} ${event.reason}`);
|
||||
this.zclawWs.onclose = (event) => {
|
||||
this.log('info', `ZCLAW WebSocket closed: ${event.code} ${event.reason}`);
|
||||
const callbacks = this.streamCallbacks.get(runId);
|
||||
if (callbacks && event.code !== 1000) {
|
||||
callbacks.onError(`Connection closed: ${event.reason || 'unknown'}`);
|
||||
}
|
||||
this.streamCallbacks.delete(runId);
|
||||
this.openfangWs = null;
|
||||
this.zclawWs = null;
|
||||
};
|
||||
} catch (err: unknown) {
|
||||
const errorMessage = err instanceof Error ? err.message : String(err);
|
||||
@@ -583,13 +583,13 @@ export class GatewayClient {
|
||||
}
|
||||
}
|
||||
|
||||
/** Handle OpenFang stream events */
|
||||
private handleOpenFangStreamEvent(runId: string, data: OpenFangStreamEvent, sessionId: string): void {
|
||||
/** Handle ZCLAW stream events */
|
||||
private handleZclawStreamEvent(runId: string, data: ZclawStreamEvent, sessionId: string): void {
|
||||
const callbacks = this.streamCallbacks.get(runId);
|
||||
if (!callbacks) return;
|
||||
|
||||
switch (data.type) {
|
||||
// OpenFang actual event types
|
||||
// ZCLAW actual event types
|
||||
case 'text_delta':
|
||||
// Stream delta content
|
||||
if (data.content) {
|
||||
@@ -602,8 +602,8 @@ export class GatewayClient {
|
||||
if (data.phase === 'done') {
|
||||
callbacks.onComplete();
|
||||
this.streamCallbacks.delete(runId);
|
||||
if (this.openfangWs) {
|
||||
this.openfangWs.close(1000, 'Stream complete');
|
||||
if (this.zclawWs) {
|
||||
this.zclawWs.close(1000, 'Stream complete');
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -617,8 +617,8 @@ export class GatewayClient {
|
||||
// Mark complete if phase done wasn't sent
|
||||
callbacks.onComplete();
|
||||
this.streamCallbacks.delete(runId);
|
||||
if (this.openfangWs) {
|
||||
this.openfangWs.close(1000, 'Stream complete');
|
||||
if (this.zclawWs) {
|
||||
this.zclawWs.close(1000, 'Stream complete');
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -649,14 +649,14 @@ export class GatewayClient {
|
||||
case 'error':
|
||||
callbacks.onError(data.message || data.code || data.content || 'Unknown error');
|
||||
this.streamCallbacks.delete(runId);
|
||||
if (this.openfangWs) {
|
||||
this.openfangWs.close(1011, 'Error');
|
||||
if (this.zclawWs) {
|
||||
this.zclawWs.close(1011, 'Error');
|
||||
}
|
||||
break;
|
||||
|
||||
case 'connected':
|
||||
// Connection established
|
||||
this.log('info', `OpenFang agent connected: ${data.agent_id}`);
|
||||
this.log('info', `ZCLAW agent connected: ${data.agent_id}`);
|
||||
break;
|
||||
|
||||
case 'agents_updated':
|
||||
@@ -687,12 +687,12 @@ export class GatewayClient {
|
||||
callbacks.onError('Stream cancelled');
|
||||
this.streamCallbacks.delete(runId);
|
||||
}
|
||||
if (this.openfangWs && this.openfangWs.readyState === WebSocket.OPEN) {
|
||||
this.openfangWs.close(1000, 'User cancelled');
|
||||
if (this.zclawWs && this.zclawWs.readyState === WebSocket.OPEN) {
|
||||
this.zclawWs.close(1000, 'User cancelled');
|
||||
}
|
||||
}
|
||||
|
||||
// === REST API Helpers (OpenFang) ===
|
||||
// === REST API Helpers (ZCLAW) ===
|
||||
|
||||
public getRestBaseUrl(): string {
|
||||
// In browser dev mode, use Vite proxy (empty string = relative path)
|
||||
|
||||
Reference in New Issue
Block a user