mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-08-09 15:43:44 +08:00
fix: 修复功能空壳问题 + 新增模型测试
- 服务管理:动态扫描 LaunchAgents plist,不再硬编码 4 个服务 - 服务启停:检查 launchctl 执行结果,失败时返回 stderr - 配置保存:Gateway/模型配置保存后自动重载 Gateway 服务使配置生效 - 模型测试:新增 test_model 命令,向 provider 发送 chat completion 验证连通性 - 新增 reqwest 依赖用于 HTTP 请求
This commit is contained in:
@@ -18,10 +18,13 @@ async function invoke(cmd, args = {}) {
|
||||
function mockInvoke(cmd, args) {
|
||||
const mocks = {
|
||||
get_services_status: () => [
|
||||
{ label: 'ai.openclaw.gateway', pid: 54284, running: true, description: 'OpenClaw Gateway' },
|
||||
{ label: 'com.openclaw.guardian.watch', pid: 54301, running: true, description: '健康监控 (60s)' },
|
||||
{ label: 'ai.openclaw.gateway', pid: null, running: false, description: 'OpenClaw Gateway' },
|
||||
{ label: 'com.cftunnel.cloudflared', pid: 35218, running: true, description: 'cftunnel 隧道服务' },
|
||||
{ label: 'com.openclaw.guardian.watch', pid: 55290, running: true, description: '健康监控 (60s)' },
|
||||
{ label: 'com.openclaw.guardian.backup', pid: null, running: false, description: '配置备份 (3600s)' },
|
||||
{ label: 'com.openclaw.watchdog', pid: 54320, running: true, description: '看门狗 (120s)' },
|
||||
{ label: 'com.openclaw.watchdog', pid: null, running: false, description: '看门狗 (120s)' },
|
||||
{ label: 'com.openclaw.webhook-router', pid: 38983, running: true, description: 'Webhook 路由' },
|
||||
{ label: 'com.openclaw.webhook-tunnel', pid: null, running: false, description: 'Webhook SSH 隧道' },
|
||||
],
|
||||
get_version_info: () => ({
|
||||
current: '2026.2.23',
|
||||
@@ -99,6 +102,8 @@ function mockInvoke(cmd, args) {
|
||||
start_service: () => true,
|
||||
stop_service: () => true,
|
||||
restart_service: () => true,
|
||||
reload_gateway: () => 'Gateway 已重载',
|
||||
test_model: ({ base_url, model_id }) => `模型 ${model_id} 连通正常 (mock)`,
|
||||
write_env_file: () => true,
|
||||
list_backups: () => [
|
||||
{ name: 'openclaw-20260226-143000.json', size: 8542, created_at: 1740577800 },
|
||||
@@ -138,6 +143,8 @@ export const api = {
|
||||
writeOpenclawConfig: (config) => invoke('write_openclaw_config', { config }),
|
||||
readMcpConfig: () => invoke('read_mcp_config'),
|
||||
writeMcpConfig: (config) => invoke('write_mcp_config', { config }),
|
||||
reloadGateway: () => invoke('reload_gateway'),
|
||||
testModel: (baseUrl, apiKey, modelId) => invoke('test_model', { base_url: baseUrl, api_key: apiKey, model_id: modelId }),
|
||||
|
||||
// 日志
|
||||
readLogTail: (logName, lines = 100) => invoke('read_log_tail', { logName, lines }),
|
||||
|
||||
@@ -122,7 +122,13 @@ async function saveConfig(page, state) {
|
||||
|
||||
try {
|
||||
await api.writeOpenclawConfig(state.config)
|
||||
toast('Gateway 配置已保存', 'success')
|
||||
toast('Gateway 配置已保存,正在重载服务...', 'info')
|
||||
try {
|
||||
await api.reloadGateway()
|
||||
toast('Gateway 已重载,配置已生效', 'success')
|
||||
} catch (e) {
|
||||
toast('配置已保存,但重载 Gateway 失败: ' + e, 'warning')
|
||||
}
|
||||
} catch (e) {
|
||||
toast('保存失败: ' + e, 'error')
|
||||
}
|
||||
|
||||
@@ -145,6 +145,7 @@ function renderModelCards(providerKey, models, primary) {
|
||||
</div>
|
||||
</div>
|
||||
<div style="display:flex;gap:6px;flex-shrink:0">
|
||||
<button class="btn btn-sm btn-secondary" data-action="test-model">测试</button>
|
||||
${!isPrimary ? `<button class="btn btn-sm btn-secondary" data-action="set-primary">设为主模型</button>` : ''}
|
||||
<button class="btn btn-sm btn-secondary" data-action="edit-model">编辑</button>
|
||||
<button class="btn btn-sm btn-danger" data-action="delete-model">删除</button>
|
||||
@@ -191,6 +192,10 @@ function bindProviderEvents(page, state) {
|
||||
renderProviders(page, state)
|
||||
renderDefaultBar(page, state)
|
||||
toast(`已设为主模型: ${full}`, 'success')
|
||||
} else if (action === 'test-model') {
|
||||
const card = btn.closest('.model-card')
|
||||
const idx = parseInt(card.dataset.index)
|
||||
testModel(btn, state, providerKey, idx)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -214,7 +219,13 @@ function bindTopActions(page, state) {
|
||||
btn.textContent = '保存中...'
|
||||
try {
|
||||
await api.writeOpenclawConfig(state.config)
|
||||
toast('模型配置已保存', 'success')
|
||||
toast('模型配置已保存,正在重载 Gateway...', 'info')
|
||||
try {
|
||||
await api.reloadGateway()
|
||||
toast('Gateway 已重载,模型配置已生效', 'success')
|
||||
} catch (e) {
|
||||
toast('配置已保存,但重载 Gateway 失败: ' + e, 'warning')
|
||||
}
|
||||
} catch (e) {
|
||||
toast('保存失败: ' + e, 'error')
|
||||
} finally {
|
||||
@@ -236,7 +247,13 @@ function bindTopActions(page, state) {
|
||||
applyDefaultModel(state)
|
||||
await api.writeOpenclawConfig(state.config)
|
||||
renderDefaultBar(page, state)
|
||||
toast('默认模型已应用', 'success')
|
||||
toast('默认模型已应用,正在重载 Gateway...', 'info')
|
||||
try {
|
||||
await api.reloadGateway()
|
||||
toast('Gateway 已重载,默认模型已生效', 'success')
|
||||
} catch (e) {
|
||||
toast('配置已保存,但重载 Gateway 失败: ' + e, 'warning')
|
||||
}
|
||||
} catch (e) {
|
||||
toast('应用失败: ' + e, 'error')
|
||||
} finally {
|
||||
@@ -350,3 +367,24 @@ function editModel(page, state, providerKey, idx) {
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// 测试模型连通性
|
||||
async function testModel(btn, state, providerKey, idx) {
|
||||
const provider = state.config.models.providers[providerKey]
|
||||
const model = provider.models[idx]
|
||||
const modelId = typeof model === 'string' ? model : model.id
|
||||
|
||||
btn.disabled = true
|
||||
const origText = btn.textContent
|
||||
btn.textContent = '测试中...'
|
||||
|
||||
try {
|
||||
const reply = await api.testModel(provider.baseUrl, provider.apiKey || '', modelId)
|
||||
toast(`${modelId} 连通正常: "${reply.slice(0, 60)}"`, 'success')
|
||||
} catch (e) {
|
||||
toast(`${modelId} 测试失败: ${e}`, 'error')
|
||||
} finally {
|
||||
btn.disabled = false
|
||||
btn.textContent = origText
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user