mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 03:13:41 +08:00
fix(dsh): persist managed runtime settings (#626)
This commit is contained in:
parent
631b8ab02a
commit
7e2dc55b5d
@ -0,0 +1,52 @@
|
|||||||
|
import { describe, expect, it } from 'vitest'
|
||||||
|
import {
|
||||||
|
createEmptyDshConfigForm,
|
||||||
|
formToManagedConfig,
|
||||||
|
managedConfigToForm,
|
||||||
|
} from '../configMapping'
|
||||||
|
|
||||||
|
describe('DSH managed configuration mapping', () => {
|
||||||
|
it('maps canonical managed keys into the settings form', () => {
|
||||||
|
expect(managedConfigToForm({
|
||||||
|
'dsh.executable_path': '/opt/dsh/bin',
|
||||||
|
'dsh.cordis_config_path': '/opt/dsh/cordis.yml',
|
||||||
|
'dsh.working_directory': '/srv/workspace',
|
||||||
|
'dsh.base_url': 'https://api.deepseek.com',
|
||||||
|
'dsh.model_name': 'deepseek-chat',
|
||||||
|
'dsh.api_key': 'plain-test-key',
|
||||||
|
})).toEqual({
|
||||||
|
executable_path: '/opt/dsh/bin',
|
||||||
|
cordis_config_path: '/opt/dsh/cordis.yml',
|
||||||
|
working_directory: '/srv/workspace',
|
||||||
|
base_url: 'https://api.deepseek.com',
|
||||||
|
model_name: 'deepseek-chat',
|
||||||
|
api_key: 'plain-test-key',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('uses empty form values for missing managed keys and masked secrets', () => {
|
||||||
|
expect(managedConfigToForm({ 'dsh.api_key': '****abcd' }))
|
||||||
|
.toEqual(createEmptyDshConfigForm())
|
||||||
|
})
|
||||||
|
|
||||||
|
it('maps every settings form field to the canonical backend key', () => {
|
||||||
|
const payload = formToManagedConfig({
|
||||||
|
executable_path: '/opt/dsh/bin',
|
||||||
|
cordis_config_path: '/opt/dsh/cordis.yml',
|
||||||
|
working_directory: '/srv/workspace',
|
||||||
|
base_url: 'https://api.deepseek.com',
|
||||||
|
model_name: 'deepseek-chat',
|
||||||
|
api_key: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(payload).toEqual({
|
||||||
|
'dsh.executable_path': '/opt/dsh/bin',
|
||||||
|
'dsh.cordis_config_path': '/opt/dsh/cordis.yml',
|
||||||
|
'dsh.working_directory': '/srv/workspace',
|
||||||
|
'dsh.base_url': 'https://api.deepseek.com',
|
||||||
|
'dsh.model_name': 'deepseek-chat',
|
||||||
|
'dsh.api_key': '',
|
||||||
|
})
|
||||||
|
expect(payload).not.toHaveProperty('executable_path')
|
||||||
|
})
|
||||||
|
})
|
||||||
47
mateclaw-ui/src/views/Settings/Dsh/configMapping.ts
Normal file
47
mateclaw-ui/src/views/Settings/Dsh/configMapping.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
export interface DshConfigForm {
|
||||||
|
executable_path: string
|
||||||
|
cordis_config_path: string
|
||||||
|
working_directory: string
|
||||||
|
base_url: string
|
||||||
|
model_name: string
|
||||||
|
api_key: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const MANAGED_KEYS = {
|
||||||
|
executable_path: 'dsh.executable_path',
|
||||||
|
cordis_config_path: 'dsh.cordis_config_path',
|
||||||
|
working_directory: 'dsh.working_directory',
|
||||||
|
base_url: 'dsh.base_url',
|
||||||
|
model_name: 'dsh.model_name',
|
||||||
|
api_key: 'dsh.api_key',
|
||||||
|
} as const satisfies Record<keyof DshConfigForm, string>
|
||||||
|
|
||||||
|
const FORM_FIELDS = Object.keys(MANAGED_KEYS) as Array<keyof DshConfigForm>
|
||||||
|
|
||||||
|
export function createEmptyDshConfigForm(): DshConfigForm {
|
||||||
|
return {
|
||||||
|
executable_path: '',
|
||||||
|
cordis_config_path: '',
|
||||||
|
working_directory: '',
|
||||||
|
base_url: '',
|
||||||
|
model_name: '',
|
||||||
|
api_key: '',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function managedConfigToForm(
|
||||||
|
managed: Record<string, string | null | undefined>,
|
||||||
|
): DshConfigForm {
|
||||||
|
const form = createEmptyDshConfigForm()
|
||||||
|
for (const field of FORM_FIELDS) {
|
||||||
|
form[field] = managed[MANAGED_KEYS[field]] || ''
|
||||||
|
}
|
||||||
|
if (form.api_key.startsWith('****')) form.api_key = ''
|
||||||
|
return form
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formToManagedConfig(form: DshConfigForm): Record<string, string> {
|
||||||
|
return Object.fromEntries(
|
||||||
|
FORM_FIELDS.map(field => [MANAGED_KEYS[field], form[field]]),
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -56,12 +56,17 @@
|
|||||||
import { computed, onMounted, reactive, ref } from 'vue'
|
import { computed, onMounted, reactive, ref } from 'vue'
|
||||||
import { dshApi } from '@/api'
|
import { dshApi } from '@/api'
|
||||||
import { mcToast } from '@/composables/useMcToast'
|
import { mcToast } from '@/composables/useMcToast'
|
||||||
|
import {
|
||||||
|
createEmptyDshConfigForm,
|
||||||
|
formToManagedConfig,
|
||||||
|
managedConfigToForm,
|
||||||
|
} from './configMapping'
|
||||||
|
|
||||||
const loading = ref(true)
|
const loading = ref(true)
|
||||||
const busy = ref(false)
|
const busy = ref(false)
|
||||||
const error = ref('')
|
const error = ref('')
|
||||||
const status = reactive<any>({ state: 'NOT_INSTALLED', installed: false, enabled: false, config: {}, artifactManifestConfigured: false })
|
const status = reactive<any>({ state: 'NOT_INSTALLED', installed: false, enabled: false, config: {}, artifactManifestConfigured: false })
|
||||||
const form = reactive<Record<string, string>>({ executable_path: '', cordis_config_path: '', working_directory: '', base_url: '', model_name: '', api_key: '' })
|
const form = reactive(createEmptyDshConfigForm())
|
||||||
const state = computed(() => String(status.state || 'NOT_INSTALLED'))
|
const state = computed(() => String(status.state || 'NOT_INSTALLED'))
|
||||||
const stateLabel = computed(() => ({ NOT_INSTALLED: '未安装', INSTALLING: '安装中', INSTALLED_UNCONFIGURED: '已安装待验证', CONFIG_INVALID: '配置不完整', CHECKING: '检测中', CHECK_FAILED: '检测失败', READY: '已就绪', ENABLED: '已启用' } as Record<string, string>)[state.value] || state.value)
|
const stateLabel = computed(() => ({ NOT_INSTALLED: '未安装', INSTALLING: '安装中', INSTALLED_UNCONFIGURED: '已安装待验证', CONFIG_INVALID: '配置不完整', CHECKING: '检测中', CHECK_FAILED: '检测失败', READY: '已就绪', ENABLED: '已启用' } as Record<string, string>)[state.value] || state.value)
|
||||||
const canEnable = computed(() => status.installed && status.config?.workingDirectory && state.value !== 'CONFIG_INVALID')
|
const canEnable = computed(() => status.installed && status.config?.workingDirectory && state.value !== 'CONFIG_INVALID')
|
||||||
@ -75,14 +80,12 @@ const steps = computed(() => [
|
|||||||
function applyResponse(response: any) {
|
function applyResponse(response: any) {
|
||||||
const data = response?.data ?? response
|
const data = response?.data ?? response
|
||||||
Object.assign(status, data)
|
Object.assign(status, data)
|
||||||
const managed = data?.managed || {}
|
Object.assign(form, managedConfigToForm(data?.managed || {}))
|
||||||
for (const key of Object.keys(form)) form[key] = managed[key] || ''
|
|
||||||
if (form.api_key.startsWith('****')) form.api_key = ''
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function load() { loading.value = true; error.value = ''; try { applyResponse(await dshApi.status()) } catch (e: any) { error.value = e?.message || '读取 DSH 状态失败' } finally { loading.value = false } }
|
async function load() { loading.value = true; error.value = ''; try { applyResponse(await dshApi.status()) } catch (e: any) { error.value = e?.message || '读取 DSH 状态失败' } finally { loading.value = false } }
|
||||||
async function run(action: () => Promise<any>, message: string) { busy.value = true; error.value = ''; try { applyResponse(await action()); mcToast.success(message) } catch (e: any) { error.value = e?.message || '操作失败'; mcToast.error(error.value) } finally { busy.value = false } }
|
async function run(action: () => Promise<any>, message: string) { busy.value = true; error.value = ''; try { applyResponse(await action()); mcToast.success(message) } catch (e: any) { error.value = e?.message || '操作失败'; mcToast.error(error.value) } finally { busy.value = false } }
|
||||||
function save() { return run(() => dshApi.saveConfig(form), 'DSH 配置已保存') }
|
function save() { return run(() => dshApi.saveConfig(formToManagedConfig(form)), 'DSH 配置已保存') }
|
||||||
function verify() { return run(dshApi.verify, 'DSH 配置验证完成') }
|
function verify() { return run(dshApi.verify, 'DSH 配置验证完成') }
|
||||||
function install() { return run(dshApi.install, 'DSH 安装完成') }
|
function install() { return run(dshApi.install, 'DSH 安装完成') }
|
||||||
function testConnection() { return run(async () => { const response: any = await dshApi.testConnection(); const data = response?.data ?? response; if (data?.success === false) throw new Error(data.message || 'DSH 进程测试失败'); return response }, 'DSH 进程测试通过') }
|
function testConnection() { return run(async () => { const response: any = await dshApi.testConnection(); const data = response?.data ?? response; if (data?.success === false) throw new Error(data.message || 'DSH 进程测试失败'); return response }, 'DSH 进程测试通过') }
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user