feat: add i18n, platform channels page, and WeChat QR login

- Add vue-i18n with auto-detect browser language and manual toggle (EN/中文)
- Move platform channels to separate page with credential management
- Support Telegram, Discord, Slack, WhatsApp, Matrix, Feishu, Weixin, WeCom
- Add WeChat QR code login (opens in browser, polls status, auto-saves)
- Write platform credentials to ~/.hermes/.env matching hermes gateway setup
- Auto restart gateway after platform config changes
- Add settings store with per-section save for all config categories
- Persist session group collapse state across navigation
- Fix pre-existing TypeScript build errors

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
ekko
2026-04-13 15:15:14 +08:00
co-authored by Claude Opus 4.6
parent 9e069a20a1
commit e89a240f1d
42 changed files with 2627 additions and 378 deletions
+114
View File
@@ -0,0 +1,114 @@
import { request } from './client'
export interface DisplayConfig {
compact?: boolean
personality?: string
resume_display?: string
busy_input_mode?: string
bell_on_complete?: boolean
show_reasoning?: boolean
streaming?: boolean
inline_diffs?: boolean
show_cost?: boolean
skin?: string
}
export interface AgentConfig {
max_turns?: number
gateway_timeout?: number
restart_drain_timeout?: number
service_tier?: string
tool_use_enforcement?: string
}
export interface MemoryConfig {
memory_enabled?: boolean
user_profile_enabled?: boolean
memory_char_limit?: number
user_char_limit?: number
}
export interface SessionResetConfig {
mode?: string
idle_minutes?: number
at_hour?: number
}
export interface PrivacyConfig {
redact_pii?: boolean
}
export interface AppConfig {
display?: DisplayConfig
agent?: AgentConfig
memory?: MemoryConfig
session_reset?: SessionResetConfig
privacy?: PrivacyConfig
telegram?: Record<string, any>
discord?: Record<string, any>
slack?: Record<string, any>
whatsapp?: Record<string, any>
matrix?: Record<string, any>
weixin?: Record<string, any>
wecom?: Record<string, any>
feishu?: Record<string, any>
dingtalk?: Record<string, any>
platforms?: Record<string, any>
[key: string]: any
}
export async function fetchConfig(sections?: string[]): Promise<AppConfig> {
const query = sections ? `?sections=${sections.join(',')}` : ''
return request<AppConfig>(`/api/config${query}`)
}
export async function updateConfigSection(
section: string,
values: Record<string, any>,
): Promise<void> {
await request('/api/config', {
method: 'PUT',
body: JSON.stringify({ section, values }),
})
}
export async function saveCredentials(
platform: string,
values: Record<string, any>,
): Promise<void> {
await request('/api/config/credentials', {
method: 'PUT',
body: JSON.stringify({ platform, values }),
})
}
export interface WeixinQrCode {
qrcode: string
qrcode_url: string
}
export interface WeixinQrStatus {
status: 'wait' | 'scaned' | 'scaned_but_redirect' | 'expired' | 'confirmed'
account_id?: string
token?: string
base_url?: string
}
export async function fetchWeixinQrCode(): Promise<WeixinQrCode> {
return request<WeixinQrCode>('/api/weixin/qrcode')
}
export async function pollWeixinQrStatus(qrcode: string): Promise<WeixinQrStatus> {
return request<WeixinQrStatus>(`/api/weixin/qrcode/status?qrcode=${encodeURIComponent(qrcode)}`)
}
export async function saveWeixinCredentials(data: {
account_id: string
token: string
base_url?: string
}): Promise<void> {
await request('/api/weixin/save', {
method: 'POST',
body: JSON.stringify(data),
})
}