feat(hub,web): support custom Claude models via settings.json (customClaudeModels)

This commit is contained in:
2026-08-05 11:16:41 +08:00
parent 27bc6bade3
commit f2cc29f5d7
6 changed files with 66 additions and 2 deletions
+2
View File
@@ -22,6 +22,8 @@ export interface Settings {
corsOrigins?: string[]
/** Per-hub relay auth key issued by the relay server (/issue) */
relayAuthKey?: string
/** Custom model names offered in the Claude model picker (e.g. DeepSeek via ANTHROPIC_BASE_URL). */
customClaudeModels?: string[]
}
export function getSettingsFile(dataDir: string): string {
+2 -1
View File
@@ -259,7 +259,8 @@ export async function startHub(options: StartHubOptions = {}): Promise<HubInstan
socketEngine: socketServer.engine,
corsOrigins,
relayMode: relayFlag.enabled,
officialWebUrl
officialWebUrl,
dataDir: config.dataDir
})
// Start the bot if configured
+23
View File
@@ -0,0 +1,23 @@
import { Hono } from 'hono'
import type { WebAppEnv } from '../middleware/auth'
import { getSettingsFile, readSettings } from '../../config/settings'
/**
* Custom Claude model names configured in settings.json
* (`customClaudeModels`). Claude Code has no model catalog API like Codex,
* so users routing Claude through a custom ANTHROPIC_BASE_URL list their
* endpoint's model names here to surface them in the New Session picker.
*/
export function createClaudeModelsRoutes(dataDir: string): Hono<WebAppEnv> {
const app = new Hono<WebAppEnv>()
app.get('/claude/custom-models', async (c) => {
const settings = await readSettings(getSettingsFile(dataDir))
const models = Array.isArray(settings?.customClaudeModels)
? settings.customClaudeModels
: []
return c.json({ models })
})
return app
}
+5
View File
@@ -26,6 +26,7 @@ import { createCodexDesktopRoutes } from './routes/codexDesktop'
import { createPushRoutes } from './routes/push'
import { createDevicesRoutes } from './routes/devices'
import { createVoiceRoutes } from './routes/voice'
import { createClaudeModelsRoutes } from './routes/claudeModels'
import type { SSEManager } from '../sse/sseManager'
import type { VisibilityTracker } from '../visibility/visibilityTracker'
import type { Server as BunServer, ServerWebSocket } from 'bun'
@@ -220,6 +221,7 @@ function createWebApp(options: {
embeddedAssetMap: Map<string, EmbeddedWebAsset> | null
relayMode?: boolean
officialWebUrl?: string
dataDir: string
}): Hono<WebAppEnv> {
const app = new Hono<WebAppEnv>()
@@ -259,6 +261,7 @@ function createWebApp(options: {
getSyncEngine: options.getSyncEngine
}))
app.route('/api', createPushRoutes(options.store, options.vapidPublicKey))
app.route('/api', createClaudeModelsRoutes(options.dataDir))
app.route('/api', createDevicesRoutes(options.store))
app.route('/api', createVoiceRoutes())
@@ -376,6 +379,7 @@ export async function startWebServer(options: {
corsOrigins?: string[]
relayMode?: boolean
officialWebUrl?: string
dataDir: string
}): Promise<BunServer<WebSocketData>> {
const isCompiled = isBunCompiled()
const embeddedAssetMap = isCompiled ? await loadEmbeddedAssetMap() : null
@@ -388,6 +392,7 @@ export async function startWebServer(options: {
vapidPublicKey: options.vapidPublicKey,
corsOrigins: options.corsOrigins,
embeddedAssetMap,
dataDir: options.dataDir,
relayMode: options.relayMode,
officialWebUrl: options.officialWebUrl
})