feat: enhance host API authentication handling and add regression tests
This commit is contained in:
@@ -18,8 +18,6 @@ import { CONFIG_KEYS } from '@runtime/lib/constants';
|
||||
import { normalizeAgentSessionKey } from '@runtime/lib/models';
|
||||
import type { ContentBlock, RawMessage } from '@runtime/shared/chat-model';
|
||||
import type { GatewayEvent, GatewayRpcParams, RuntimeRefreshTopic } from './types';
|
||||
import * as providerHandlers from './handlers/provider';
|
||||
import * as skillHandlers from './handlers/skills';
|
||||
import {
|
||||
createInitialGatewayDiagnostics,
|
||||
type GatewayDiagnosticsSnapshot,
|
||||
@@ -64,6 +62,7 @@ import {
|
||||
} from './reload-policy';
|
||||
import { GatewayStateController, type GatewayRuntimeStatus } from './state';
|
||||
import { connectGatewaySocket, waitForGatewayReady } from './ws-client';
|
||||
import { dispatchGatewayRpcMethod } from './rpc-dispatch';
|
||||
|
||||
type RuntimeChangeBroadcast = {
|
||||
topics: RuntimeRefreshTopic[];
|
||||
@@ -1785,88 +1784,16 @@ export class GatewayManager extends EventEmitter {
|
||||
await this.init();
|
||||
}
|
||||
|
||||
const localDispatch = dispatchGatewayRpcMethod(
|
||||
method,
|
||||
params,
|
||||
(event) => this.broadcast(event),
|
||||
);
|
||||
if (localDispatch.handled) {
|
||||
return localDispatch.result;
|
||||
}
|
||||
|
||||
switch (method) {
|
||||
case 'chat.send': {
|
||||
const request = params as GatewayRpcParams['chat.send'];
|
||||
const sessionKey = normalizeAgentSessionKey(request.sessionKey);
|
||||
const messageText = extractTextFromRawMessage(request.message);
|
||||
const response = await this.rpcGateway('chat.send', {
|
||||
sessionKey,
|
||||
message: messageText,
|
||||
deliver: false,
|
||||
idempotencyKey: request.message.id || randomUUID(),
|
||||
}, { timeoutMs: 30_000 });
|
||||
|
||||
const runId = (
|
||||
isRecord(response) &&
|
||||
typeof response.runId === 'string' &&
|
||||
response.runId.trim()
|
||||
)
|
||||
? response.runId
|
||||
: '';
|
||||
|
||||
if (!runId) {
|
||||
throw new Error('OpenClaw Gateway chat.send did not return a runId');
|
||||
}
|
||||
|
||||
return { runId };
|
||||
}
|
||||
case 'chat.history': {
|
||||
const request = params as GatewayRpcParams['chat.history'];
|
||||
const response = await this.rpcGateway('chat.history', {
|
||||
sessionKey: normalizeAgentSessionKey(request.sessionKey),
|
||||
limit: request.limit ?? 50,
|
||||
}, { timeoutMs: 15_000 });
|
||||
|
||||
if (!isRecord(response) || !Array.isArray(response.messages)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return response.messages
|
||||
.map((message) => normalizeGatewayRawMessage(message))
|
||||
.filter((message): message is RawMessage => message !== null);
|
||||
}
|
||||
case 'chat.abort': {
|
||||
const request = params as GatewayRpcParams['chat.abort'];
|
||||
await this.rpcGateway('chat.abort', {
|
||||
sessionKey: normalizeAgentSessionKey(request.sessionKey),
|
||||
}, { timeoutMs: 10_000 });
|
||||
return;
|
||||
}
|
||||
case 'session.list': {
|
||||
const response = await this.rpcGateway('sessions.list', {}, { timeoutMs: 10_000 });
|
||||
if (!isRecord(response) || !Array.isArray(response.sessions)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return response.sessions
|
||||
.map((session) => (
|
||||
isRecord(session) && typeof session.key === 'string'
|
||||
? session.key
|
||||
: null
|
||||
))
|
||||
.filter((sessionKey): sessionKey is string => Boolean(sessionKey));
|
||||
}
|
||||
case 'session.delete': {
|
||||
const request = params as GatewayRpcParams['session.delete'];
|
||||
if (normalizeAgentSessionKey(request.sessionKey).endsWith(':main')) {
|
||||
return { success: false };
|
||||
}
|
||||
|
||||
await this.rpcGateway('sessions.delete', {
|
||||
key: normalizeAgentSessionKey(request.sessionKey),
|
||||
deleteTranscript: true,
|
||||
}, { timeoutMs: 15_000 });
|
||||
return { success: true };
|
||||
}
|
||||
case 'provider.list':
|
||||
return providerHandlers.handleProviderList();
|
||||
case 'provider.getDefault':
|
||||
return providerHandlers.handleProviderGetDefault();
|
||||
case 'skills.status':
|
||||
return skillHandlers.handleSkillsStatus();
|
||||
case 'skills.update':
|
||||
return skillHandlers.handleSkillsUpdate(params);
|
||||
default:
|
||||
throw new Error(`Unknown gateway RPC method: ${method}`);
|
||||
}
|
||||
|
||||
80
electron/gateway/rpc-dispatch.ts
Normal file
80
electron/gateway/rpc-dispatch.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { normalizeAgentSessionKey } from '@runtime/lib/models';
|
||||
import type { GatewayEvent, GatewayRpcParams } from './types';
|
||||
import * as chatHandlers from './handlers/chat';
|
||||
import * as providerHandlers from './handlers/provider';
|
||||
import * as skillHandlers from './handlers/skills';
|
||||
|
||||
type GatewayBroadcast = (event: GatewayEvent) => void;
|
||||
|
||||
export function dispatchGatewayRpcMethod(
|
||||
method: string,
|
||||
params: unknown,
|
||||
broadcast: GatewayBroadcast,
|
||||
): { handled: boolean; result?: unknown } {
|
||||
switch (method) {
|
||||
case 'chat.send':
|
||||
return {
|
||||
handled: true,
|
||||
result: chatHandlers.handleChatSend(
|
||||
params as GatewayRpcParams['chat.send'],
|
||||
broadcast,
|
||||
),
|
||||
};
|
||||
case 'chat.history':
|
||||
return {
|
||||
handled: true,
|
||||
result: chatHandlers.handleChatHistory(
|
||||
params as GatewayRpcParams['chat.history'],
|
||||
),
|
||||
};
|
||||
case 'chat.abort':
|
||||
return {
|
||||
handled: true,
|
||||
result: chatHandlers.handleChatAbort(
|
||||
params as GatewayRpcParams['chat.abort'],
|
||||
broadcast,
|
||||
),
|
||||
};
|
||||
case 'session.list':
|
||||
return {
|
||||
handled: true,
|
||||
result: chatHandlers.handleSessionList(),
|
||||
};
|
||||
case 'session.delete': {
|
||||
const request = params as GatewayRpcParams['session.delete'];
|
||||
if (normalizeAgentSessionKey(request.sessionKey).endsWith(':main')) {
|
||||
return {
|
||||
handled: true,
|
||||
result: { success: false },
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
handled: true,
|
||||
result: chatHandlers.handleSessionDelete(request),
|
||||
};
|
||||
}
|
||||
case 'provider.list':
|
||||
return {
|
||||
handled: true,
|
||||
result: providerHandlers.handleProviderList(),
|
||||
};
|
||||
case 'provider.getDefault':
|
||||
return {
|
||||
handled: true,
|
||||
result: providerHandlers.handleProviderGetDefault(),
|
||||
};
|
||||
case 'skills.status':
|
||||
return {
|
||||
handled: true,
|
||||
result: skillHandlers.handleSkillsStatus(),
|
||||
};
|
||||
case 'skills.update':
|
||||
return {
|
||||
handled: true,
|
||||
result: skillHandlers.handleSkillsUpdate(params),
|
||||
};
|
||||
default:
|
||||
return { handled: false };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user