feat(chat): write API keys to OpenClaw and embed Control UI for chat

Part 1: API Key Integration
- Create electron/utils/openclaw-auth.ts to write keys to
  ~/.openclaw/agents/main/agent/auth-profiles.json
- Update provider:save and provider:setApiKey IPC handlers to
  persist keys to OpenClaw auth-profiles alongside ClawX storage
- Save API key to OpenClaw on successful validation in Setup wizard
- Pass provider API keys as environment variables when starting
  the Gateway process (ANTHROPIC_API_KEY, OPENROUTER_API_KEY, etc.)

Part 2: Embed OpenClaw Control UI for Chat
- Replace custom Chat UI with <webview> embedding the Gateway's
  built-in Control UI at http://127.0.0.1:{port}/?token={token}
- Add gateway:getControlUiUrl IPC handler to provide tokenized URL
- Enable webviewTag in Electron BrowserWindow preferences
- Override X-Frame-Options/CSP headers to allow webview embedding
- Suppress noisy control-ui token_mismatch stderr messages
- Add loading/error states for the embedded webview

This fixes the "No API key found for provider" error and replaces
the buggy custom chat implementation with OpenClaw's battle-tested
Control UI.
This commit is contained in:
Haze
2026-02-06 03:12:17 +08:00
Unverified
parent b01952fba7
commit 284861a0f5
7 changed files with 414 additions and 380 deletions

View File

@@ -20,6 +20,8 @@ import {
type ProviderConfig,
} from '../utils/secure-storage';
import { getOpenClawStatus } from '../utils/paths';
import { getSetting } from '../utils/store';
import { saveProviderKeyToOpenClaw } from '../utils/openclaw-auth';
/**
* Register all IPC handlers
@@ -104,6 +106,20 @@ function registerGatewayHandlers(
}
});
// Get the Control UI URL with token for embedding
ipcMain.handle('gateway:getControlUiUrl', async () => {
try {
const status = gatewayManager.getStatus();
const token = await getSetting('gatewayToken');
const port = status.port || 18789;
// Pass token as query param - Control UI will store it in localStorage
const url = `http://127.0.0.1:${port}/?token=${encodeURIComponent(token)}`;
return { success: true, url, port, token };
} catch (error) {
return { success: false, error: String(error) };
}
});
// Health check
ipcMain.handle('gateway:health', async () => {
try {
@@ -203,6 +219,13 @@ function registerProviderHandlers(): void {
// Store the API key if provided
if (apiKey) {
await storeApiKey(config.id, apiKey);
// Also write to OpenClaw auth-profiles.json so the gateway can use it
try {
saveProviderKeyToOpenClaw(config.type, apiKey);
} catch (err) {
console.warn('Failed to save key to OpenClaw auth-profiles:', err);
}
}
return { success: true };
@@ -225,6 +248,17 @@ function registerProviderHandlers(): void {
ipcMain.handle('provider:setApiKey', async (_, providerId: string, apiKey: string) => {
try {
await storeApiKey(providerId, apiKey);
// Also write to OpenClaw auth-profiles.json
// Resolve provider type from stored config, or use providerId as type
const provider = await getProvider(providerId);
const providerType = provider?.type || providerId;
try {
saveProviderKeyToOpenClaw(providerType, apiKey);
} catch (err) {
console.warn('Failed to save key to OpenClaw auth-profiles:', err);
}
return { success: true };
} catch (error) {
return { success: false, error: String(error) };