Feat/upgrade openclaw (#729)

This commit is contained in:
paisley
2026-04-01 14:22:47 +08:00
committed by GitHub
Unverified
parent bf5b089158
commit d34a88e629
24 changed files with 903 additions and 600 deletions

View File

@@ -20,7 +20,7 @@ export const providerIcons: Record<string, string> = {
siliconflow,
'minimax-portal': minimaxPortal,
'minimax-portal-cn': minimaxPortal,
'qwen-portal': qwenPortal,
'modelstudio': qwenPortal,
ollama,
custom,
};

View File

@@ -1134,6 +1134,9 @@ function AddProviderDialog({
};
const availableTypes = PROVIDER_TYPE_INFO.filter((type) => {
// Skip providers that are temporarily hidden from the UI.
if (type.hidden) return false;
// MiniMax portal variants are mutually exclusive — hide BOTH variants
// when either one already exists (account may have vendorId of either variant).
const hasMinimax = existingVendorIds.has('minimax-portal') || existingVendorIds.has('minimax-portal-cn');

View File

@@ -16,7 +16,7 @@ export const PROVIDER_TYPES = [
'siliconflow',
'minimax-portal',
'minimax-portal-cn',
'qwen-portal',
'modelstudio',
'ollama',
'custom',
] as const;
@@ -32,7 +32,7 @@ export const BUILTIN_PROVIDER_TYPES = [
'siliconflow',
'minimax-portal',
'minimax-portal-cn',
'qwen-portal',
'modelstudio',
'ollama',
] as const;
@@ -79,6 +79,8 @@ export interface ProviderTypeInfo {
codePlanPresetBaseUrl?: string;
codePlanPresetModelId?: string;
codePlanDocsUrl?: string;
/** If true, this provider is not shown in the "Add Provider" dialog. */
hidden?: boolean;
}
export type ProviderAuthMode =
@@ -172,7 +174,7 @@ export const PROVIDER_TYPE_INFO: ProviderTypeInfo[] = [
{ id: 'moonshot', name: 'Moonshot (CN)', icon: '🌙', placeholder: 'sk-...', model: 'Kimi', requiresApiKey: true, defaultBaseUrl: 'https://api.moonshot.cn/v1', defaultModelId: 'kimi-k2.5', docsUrl: 'https://platform.moonshot.cn/' },
{ id: 'siliconflow', name: 'SiliconFlow (CN)', icon: '🌊', placeholder: 'sk-...', model: 'Multi-Model', requiresApiKey: true, defaultBaseUrl: 'https://api.siliconflow.cn/v1', showModelId: true, showModelIdInDevModeOnly: true, modelIdPlaceholder: 'deepseek-ai/DeepSeek-V3', defaultModelId: 'deepseek-ai/DeepSeek-V3', docsUrl: 'https://docs.siliconflow.cn/cn/userguide/introduction' },
{ id: 'minimax-portal', name: 'MiniMax (Global)', icon: '☁️', placeholder: 'sk-...', model: 'MiniMax', requiresApiKey: false, isOAuth: true, supportsApiKey: true, defaultModelId: 'MiniMax-M2.7', showModelId: true, showModelIdInDevModeOnly: true, modelIdPlaceholder: 'MiniMax-M2.7', apiKeyUrl: 'https://platform.minimax.io' },
{ id: 'qwen-portal', name: 'Qwen (Global)', icon: '☁️', placeholder: 'sk-...', model: 'Qwen', requiresApiKey: false, isOAuth: true, defaultModelId: 'coder-model', showModelId: true, showModelIdInDevModeOnly: true, modelIdPlaceholder: 'coder-model' },
{ id: 'modelstudio', name: 'Model Studio', icon: '☁️', placeholder: 'sk-...', model: 'Qwen', requiresApiKey: true, defaultBaseUrl: 'https://coding.dashscope.aliyuncs.com/v1', showBaseUrl: true, defaultModelId: 'qwen3.5-plus', showModelId: true, showModelIdInDevModeOnly: true, modelIdPlaceholder: 'qwen3.5-plus', apiKeyUrl: 'https://bailian.console.aliyun.com/', hidden: true },
{ id: 'ark', name: 'ByteDance Ark', icon: 'A', placeholder: 'your-ark-api-key', model: 'Doubao', requiresApiKey: true, defaultBaseUrl: 'https://ark.cn-beijing.volces.com/api/v3', showBaseUrl: true, showModelId: true, modelIdPlaceholder: 'ep-20260228000000-xxxxx', docsUrl: 'https://www.volcengine.com/', codePlanPresetBaseUrl: 'https://ark.cn-beijing.volces.com/api/coding/v3', codePlanPresetModelId: 'ark-code-latest', codePlanDocsUrl: 'https://www.volcengine.com/docs/82379/1928261?lang=zh' },
{ id: 'ollama', name: 'Ollama', icon: '🦙', placeholder: 'Not required', requiresApiKey: false, defaultBaseUrl: 'http://localhost:11434/v1', showBaseUrl: true, showModelId: true, modelIdPlaceholder: 'qwen3:latest' },
{

View File

@@ -10,6 +10,7 @@ import type { GatewayStatus } from '../types/gateway';
let gatewayInitPromise: Promise<void> | null = null;
let gatewayEventUnsubscribers: Array<() => void> | null = null;
let gatewayReconcileTimer: ReturnType<typeof setInterval> | null = null;
const gatewayEventDedupe = new Map<string, number>();
const GATEWAY_EVENT_DEDUPE_TTL_MS = 30_000;
const LOAD_SESSIONS_MIN_INTERVAL_MS = 1_200;
@@ -283,6 +284,45 @@ export const useGatewayStore = create<GatewayState>((set, get) => ({
},
));
gatewayEventUnsubscribers = unsubscribers;
// Periodic reconciliation safety net: every 30 seconds, check if the
// renderer's view of gateway state has drifted from main process truth.
// This catches any future one-off IPC delivery failures without adding
// a constant polling load (single lightweight IPC invoke per interval).
// Clear any previous timer first to avoid leaks during HMR reloads.
if (gatewayReconcileTimer !== null) {
clearInterval(gatewayReconcileTimer);
}
gatewayReconcileTimer = setInterval(() => {
const ipc = window.electron?.ipcRenderer;
if (!ipc) return;
ipc.invoke('gateway:status')
.then((result: unknown) => {
const latest = result as GatewayStatus;
const current = get().status;
if (latest.state !== current.state) {
console.info(
`[gateway-store] reconciled stale state: ${current.state}${latest.state}`,
);
set({ status: latest });
}
})
.catch(() => { /* ignore */ });
}, 30_000);
}
// Re-fetch status after IPC listeners are registered to close the race
// window: if the gateway transitioned (e.g. starting → running) between
// the initial fetch and the IPC listener setup, that event was lost.
// A second fetch guarantees we pick up the latest state.
try {
const refreshed = await hostApiFetch<GatewayStatus>('/api/gateway/status');
const current = get().status;
if (refreshed.state !== current.state) {
set({ status: refreshed });
}
} catch {
// Best-effort; the IPC listener will eventually reconcile.
}
} catch (error) {
console.error('Failed to initialize Gateway:', error);