Skip to content

Commit 512a077

Browse files
committed
feat(web): add BYOK module with form UI and thinking config
1 parent ee508da commit 512a077

11 files changed

Lines changed: 1159 additions & 2 deletions

File tree

electron/main/ipc.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,18 @@ interface McpServer {
4444
env?: Record<string, string>;
4545
}
4646

47+
interface CustomModel {
48+
model_display_name?: string;
49+
model: string;
50+
base_url: string;
51+
api_key: string;
52+
provider: 'anthropic' | 'openai' | 'generic-chat-completion-api';
53+
max_tokens?: number;
54+
supports_images?: boolean;
55+
extra_args?: Record<string, unknown>;
56+
extra_headers?: Record<string, string>;
57+
}
58+
4759
interface DkConfig {
4860
ascii?: string;
4961
[key: string]: string | undefined;
@@ -360,6 +372,55 @@ Droid instructions here.
360372
await shell.openPath(mcpConfig);
361373
});
362374

375+
// BYOK (Custom Models) handlers
376+
ipcMain.handle('byok:list', async (): Promise<CustomModel[]> => {
377+
const configFile = path.join(FACTORY_DIR, 'config.json');
378+
try {
379+
const content = await fs.readFile(configFile, 'utf-8');
380+
const config = JSON.parse(content);
381+
return config.custom_models || [];
382+
} catch {
383+
return [];
384+
}
385+
});
386+
387+
ipcMain.handle('byok:remove', async (_event, index: number): Promise<void> => {
388+
const configFile = path.join(FACTORY_DIR, 'config.json');
389+
try {
390+
const content = await fs.readFile(configFile, 'utf-8');
391+
const config = JSON.parse(content);
392+
if (config.custom_models && index >= 0 && index < config.custom_models.length) {
393+
config.custom_models.splice(index, 1);
394+
await fs.writeFile(configFile, JSON.stringify(config, null, 2));
395+
}
396+
} catch {
397+
// Config doesn't exist or is invalid
398+
}
399+
});
400+
401+
ipcMain.handle('byok:update', async (_event, index: number, modelConfig: CustomModel): Promise<void> => {
402+
const configFile = path.join(FACTORY_DIR, 'config.json');
403+
let config: { custom_models?: CustomModel[]; [key: string]: unknown } = {};
404+
try {
405+
const content = await fs.readFile(configFile, 'utf-8');
406+
config = JSON.parse(content);
407+
} catch {
408+
// File doesn't exist or is invalid
409+
}
410+
if (!config.custom_models) {
411+
config.custom_models = [];
412+
}
413+
if (index === -1) {
414+
// Add new model
415+
config.custom_models.push(modelConfig);
416+
} else if (index >= 0 && index < config.custom_models.length) {
417+
// Update existing model
418+
config.custom_models[index] = modelConfig;
419+
}
420+
await fs.mkdir(FACTORY_DIR, { recursive: true });
421+
await fs.writeFile(configFile, JSON.stringify(config, null, 2));
422+
});
423+
363424
// Utility handlers
364425
ipcMain.handle('util:openPath', async (_event, filePath: string): Promise<void> => {
365426
await shell.openPath(filePath);

electron/preload/index.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,18 @@ export interface McpServer {
2626
env?: Record<string, string>;
2727
}
2828

29+
export interface CustomModel {
30+
model_display_name?: string;
31+
model: string;
32+
base_url: string;
33+
api_key: string;
34+
provider: 'anthropic' | 'openai' | 'generic-chat-completion-api';
35+
max_tokens?: number;
36+
supports_images?: boolean;
37+
extra_args?: Record<string, unknown>;
38+
extra_headers?: Record<string, string>;
39+
}
40+
2941
export interface DkCheckResult {
3042
installed: boolean;
3143
installCmd: string;
@@ -75,6 +87,10 @@ export interface OroioAPI {
7587
removeMcpServer: (name: string) => Promise<void>;
7688
updateMcpServer: (name: string, config: Omit<McpServer, 'name'>) => Promise<void>;
7789
openMcpConfig: () => Promise<void>;
90+
// BYOK (Custom Models)
91+
listCustomModels: () => Promise<CustomModel[]>;
92+
removeCustomModel: (index: number) => Promise<void>;
93+
updateCustomModel: (index: number, config: CustomModel) => Promise<void>;
7894
// Utilities
7995
openPath: (path: string) => Promise<void>;
8096
}
@@ -127,6 +143,10 @@ const api: OroioAPI = {
127143
removeMcpServer: (name: string) => ipcRenderer.invoke('mcp:remove', name),
128144
updateMcpServer: (name: string, config: Omit<McpServer, 'name'>) => ipcRenderer.invoke('mcp:update', name, config),
129145
openMcpConfig: () => ipcRenderer.invoke('mcp:openConfig'),
146+
// BYOK (Custom Models)
147+
listCustomModels: () => ipcRenderer.invoke('byok:list'),
148+
removeCustomModel: (index: number) => ipcRenderer.invoke('byok:remove', index),
149+
updateCustomModel: (index: number, config: CustomModel) => ipcRenderer.invoke('byok:update', index, config),
130150
// Utilities
131151
openPath: (p: string) => ipcRenderer.invoke('util:openPath', p),
132152
};

web/package-lock.json

Lines changed: 145 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

web/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@
1313
"@radix-ui/react-alert-dialog": "^1.1.15",
1414
"@radix-ui/react-dialog": "^1.1.15",
1515
"@radix-ui/react-dropdown-menu": "^2.1.16",
16+
"@radix-ui/react-label": "^2.1.8",
1617
"@radix-ui/react-navigation-menu": "^1.2.14",
1718
"@radix-ui/react-progress": "^1.1.8",
19+
"@radix-ui/react-select": "^2.2.6",
1820
"@radix-ui/react-slot": "^1.2.4",
21+
"@radix-ui/react-switch": "^1.2.6",
1922
"@radix-ui/react-tabs": "^1.1.13",
2023
"@radix-ui/react-tooltip": "^1.2.8",
2124
"@tailwindcss/vite": "^4.1.17",

web/src/App.tsx

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useState, useEffect, useCallback } from 'react';
2-
import { Key, Sparkles, Terminal, Bot, Plug, Github, Volume2, VolumeX, Sun, Moon, Command, Settings, Check, X } from 'lucide-react';
2+
import { Key, Sparkles, Terminal, Bot, Plug, Github, Volume2, VolumeX, Sun, Moon, Command, Settings, Check, X, Blocks, Cpu } from 'lucide-react';
33
import { Kbd } from '@/components/ui/kbd';
44
import { useSound } from '@/hooks/useSound';
55
import { Toaster } from 'sonner';
@@ -8,18 +8,21 @@ import SkillsManager from '@/components/SkillsManager';
88
import CommandsManager from '@/components/CommandsManager';
99
import DroidsManager from '@/components/DroidsManager';
1010
import McpManager from '@/components/McpManager';
11+
import ByokManager from '@/components/ByokManager';
1112
import PinDialog from '@/components/PinDialog';
1213
import { cn } from '@/lib/utils';
1314
import { checkAuth, authenticate, isElectron, checkDk, getDkConfig, setDkConfig, type DkConfig } from '@/utils/api';
1415

15-
type Tab = 'keys' | 'commands' | 'skills' | 'droids' | 'mcp';
16+
type Tab = 'keys' | 'commands' | 'skills' | 'droids' | 'mcp' | 'byok' | 'extensions';
1617

1718
const tabs: { id: Tab; label: string; icon: typeof Key }[] = [
1819
{ id: 'keys', label: 'KEYS', icon: Key },
1920
{ id: 'commands', label: 'COMMANDS', icon: Command },
2021
{ id: 'skills', label: 'SKILLS', icon: Sparkles },
2122
{ id: 'droids', label: 'DROIDS', icon: Bot },
2223
{ id: 'mcp', label: 'MCP', icon: Plug },
24+
{ id: 'byok', label: 'BYOK', icon: Cpu },
25+
{ id: 'extensions', label: 'EXT', icon: Blocks },
2326
];
2427

2528
export default function App() {
@@ -373,6 +376,14 @@ export default function App() {
373376
{activeTab === 'skills' && <SkillsManager />}
374377
{activeTab === 'droids' && <DroidsManager />}
375378
{activeTab === 'mcp' && <McpManager />}
379+
{activeTab === 'byok' && <ByokManager />}
380+
{activeTab === 'extensions' && (
381+
<div className="flex flex-col items-center justify-center h-64 text-muted-foreground">
382+
<Blocks className="h-10 w-10 mb-3 opacity-40" />
383+
<p className="text-sm">Extensions coming soon</p>
384+
<p className="text-xs text-muted-foreground/70 mt-1">Custom plugins and integrations</p>
385+
</div>
386+
)}
376387
</>
377388
)}
378389
</main>
@@ -468,6 +479,14 @@ export default function App() {
468479
{activeTab === 'skills' && <SkillsManager />}
469480
{activeTab === 'droids' && <DroidsManager />}
470481
{activeTab === 'mcp' && <McpManager />}
482+
{activeTab === 'byok' && <ByokManager />}
483+
{activeTab === 'extensions' && (
484+
<div className="flex flex-col items-center justify-center h-64 text-muted-foreground">
485+
<Blocks className="h-10 w-10 mb-3 opacity-40" />
486+
<p className="text-sm">Extensions coming soon</p>
487+
<p className="text-xs text-muted-foreground/70 mt-1">Custom plugins and integrations</p>
488+
</div>
489+
)}
471490
</>
472491
)}
473492
</main>

0 commit comments

Comments
 (0)