auto-save 2026-05-11 15:46 (~6)
This commit is contained in:
180
src/app.js
180
src/app.js
@@ -125,15 +125,29 @@ function saveSettings(options = {}) {
|
||||
pingBackend();
|
||||
}
|
||||
|
||||
function ensureModelChoice(modelValue, labelValue = "") {
|
||||
const model = (modelValue || "").trim();
|
||||
if (!model) return;
|
||||
const label = (labelValue || model).trim();
|
||||
const pick = document.getElementById("modelPick");
|
||||
if (pick && !Array.from(pick.options).some(item => item.value === model)) {
|
||||
pick.appendChild(new Option(label, model));
|
||||
}
|
||||
const list = document.getElementById("modelOptions");
|
||||
if (list && !Array.from(list.options).some(item => item.value === model)) {
|
||||
const option = document.createElement("option");
|
||||
option.value = model;
|
||||
option.label = label;
|
||||
list.appendChild(option);
|
||||
}
|
||||
}
|
||||
|
||||
function syncModelPick(modelValue) {
|
||||
const model = (modelValue || state.model || "").trim();
|
||||
const pick = document.getElementById("modelPick");
|
||||
if (!pick || !model) return;
|
||||
ensureModelChoice(model);
|
||||
let option = Array.from(pick.options).find(item => item.value === model);
|
||||
if (!option) {
|
||||
option = new Option(model, model);
|
||||
pick.appendChild(option);
|
||||
}
|
||||
pick.value = model;
|
||||
state.model = model;
|
||||
const stat = document.getElementById("statModel");
|
||||
@@ -596,19 +610,30 @@ async function testApiConnection() {
|
||||
|
||||
let _hermesConfigLoaded = false;
|
||||
let _hermesConfigLoading = false;
|
||||
function setHermesConfigStatus(text, isError = false) {
|
||||
const el = document.getElementById("hermesConfigStatus");
|
||||
let _hermesConfigSnapshot = null;
|
||||
function setSettingsStatus(id, text, isError = false) {
|
||||
const el = document.getElementById(id);
|
||||
if (!el) return;
|
||||
el.textContent = text;
|
||||
el.style.color = isError ? "var(--err)" : "";
|
||||
}
|
||||
function setHermesModelStatus(text, isError = false) {
|
||||
setSettingsStatus("hermesModelStatus", text, isError);
|
||||
}
|
||||
function setHermesMcpStatus(text, isError = false) {
|
||||
setSettingsStatus("hermesMcpStatus", text, isError);
|
||||
}
|
||||
function setHermesConfigStatuses(text, isError = false) {
|
||||
setHermesModelStatus(text, isError);
|
||||
setHermesMcpStatus(text, isError);
|
||||
}
|
||||
|
||||
async function refreshHermesConfig(force = false) {
|
||||
if (_hermesConfigLoading || (_hermesConfigLoaded && !force)) return;
|
||||
const modelEl = document.getElementById("hermesModelDefault");
|
||||
if (!modelEl) return;
|
||||
_hermesConfigLoading = true;
|
||||
setHermesConfigStatus("正在读取线上配置...");
|
||||
setHermesConfigStatuses("正在读取线上配置...");
|
||||
try {
|
||||
const res = await fetch("/feishu/hermes-config", {
|
||||
credentials: "same-origin",
|
||||
@@ -622,63 +647,146 @@ async function refreshHermesConfig(force = false) {
|
||||
document.getElementById("hermesModelProvider").value = model.provider || "";
|
||||
document.getElementById("hermesModelBaseUrl").value = model.base_url || "";
|
||||
document.getElementById("mcpServersYaml").value = config.mcp_servers_yaml || "";
|
||||
_hermesConfigSnapshot = {
|
||||
model: {
|
||||
default: model.default || "",
|
||||
provider: model.provider || "",
|
||||
base_url: model.base_url || "",
|
||||
},
|
||||
mcp_servers_yaml: config.mcp_servers_yaml || "",
|
||||
};
|
||||
if (model.default) syncModelPick(model.default);
|
||||
_hermesConfigLoaded = true;
|
||||
setHermesConfigStatus("已读取线上配置" + (config.lxc ? " · " + config.lxc : ""));
|
||||
const suffix = config.lxc ? " · " + config.lxc : "";
|
||||
setHermesModelStatus("已读取模型配置" + suffix);
|
||||
setHermesMcpStatus("已读取 MCP 配置" + suffix);
|
||||
} catch (e) {
|
||||
setHermesConfigStatus("读取失败: " + (e.message || e), true);
|
||||
setHermesConfigStatuses("读取失败: " + (e.message || e), true);
|
||||
} finally {
|
||||
_hermesConfigLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function saveHermesConfig() {
|
||||
function readModelConfigFields() {
|
||||
const modelDefault = document.getElementById("hermesModelDefault")?.value.trim() || "";
|
||||
const provider = document.getElementById("hermesModelProvider")?.value.trim() || "openrouter";
|
||||
const baseUrl = document.getElementById("hermesModelBaseUrl")?.value.trim() || "";
|
||||
const mcpServersYaml = document.getElementById("mcpServersYaml")?.value || "";
|
||||
if (!modelDefault) {
|
||||
return { default: modelDefault, provider, base_url: baseUrl };
|
||||
}
|
||||
|
||||
function snapshotModelOrFields() {
|
||||
const model = _hermesConfigSnapshot?.model || {};
|
||||
if (model.default) return { ...model };
|
||||
const fields = readModelConfigFields();
|
||||
if (fields.default) return fields;
|
||||
return {
|
||||
default: state.model || "gemini-3-pro-preview",
|
||||
provider: fields.provider || "openrouter",
|
||||
base_url: fields.base_url || "",
|
||||
};
|
||||
}
|
||||
|
||||
async function postHermesRuntimeConfig(payload) {
|
||||
const res = await fetch("/feishu/hermes-config", {
|
||||
method: "POST",
|
||||
credentials: "same-origin",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
const data = await res.json().catch(() => ({}));
|
||||
if (!res.ok || data.code !== 0) throw new Error(data.msg || ("HTTP " + res.status));
|
||||
return data.config || {};
|
||||
}
|
||||
|
||||
async function saveModelConfig() {
|
||||
const model = readModelConfigFields();
|
||||
if (!model.default) {
|
||||
toast("默认模型不能为空");
|
||||
return;
|
||||
}
|
||||
if (!confirm("保存后会重启线上 Hermes agent,当前正在生成的任务可能中断。继续吗?")) return;
|
||||
const btn = document.getElementById("hermesConfigSaveBtn");
|
||||
if (!confirm("保存 AI 模型接入配置后会重启线上 Hermes agent,当前正在生成的任务可能中断。继续吗?")) return;
|
||||
const btn = document.getElementById("hermesModelSaveBtn");
|
||||
const oldHTML = btn?.innerHTML;
|
||||
if (btn) {
|
||||
btn.disabled = true;
|
||||
btn.textContent = "保存并重启中...";
|
||||
btn.textContent = "保存模型中...";
|
||||
}
|
||||
setHermesConfigStatus("正在写入 config.yaml 并重启 Hermes agent...");
|
||||
setHermesModelStatus("正在写入 model 配置并重启 Hermes agent...");
|
||||
try {
|
||||
const res = await fetch("/feishu/hermes-config", {
|
||||
method: "POST",
|
||||
credentials: "same-origin",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
model: {
|
||||
default: modelDefault,
|
||||
provider,
|
||||
base_url: baseUrl,
|
||||
},
|
||||
mcp_servers_yaml: mcpServersYaml,
|
||||
restart: true,
|
||||
}),
|
||||
const saved = await postHermesRuntimeConfig({
|
||||
model,
|
||||
mcp_servers_yaml: _hermesConfigSnapshot ? _hermesConfigSnapshot.mcp_servers_yaml : (document.getElementById("mcpServersYaml")?.value || ""),
|
||||
restart: true,
|
||||
});
|
||||
const data = await res.json().catch(() => ({}));
|
||||
if (!res.ok || data.code !== 0) throw new Error(data.msg || ("HTTP " + res.status));
|
||||
const saved = data.config || {};
|
||||
const savedModel = saved.model || {};
|
||||
if (savedModel.default) syncModelPick(savedModel.default);
|
||||
document.getElementById("mcpServersYaml").value = saved.mcp_servers_yaml || "";
|
||||
_hermesConfigSnapshot = {
|
||||
model: {
|
||||
default: savedModel.default || model.default,
|
||||
provider: savedModel.provider || model.provider,
|
||||
base_url: savedModel.base_url || model.base_url,
|
||||
},
|
||||
mcp_servers_yaml: _hermesConfigSnapshot ? _hermesConfigSnapshot.mcp_servers_yaml : (saved.mcp_servers_yaml || ""),
|
||||
};
|
||||
_hermesConfigLoaded = false;
|
||||
setHermesConfigStatus("已保存并重启 · 备份 " + (saved.backup || "已创建"));
|
||||
toast("模型与 MCP 配置已生效");
|
||||
setHermesModelStatus("模型配置已保存并重启 · 备份 " + (saved.backup || "已创建"));
|
||||
toast("AI 模型接入配置已生效");
|
||||
setTimeout(() => {
|
||||
pingBackend();
|
||||
refreshDashboard();
|
||||
}, 1800);
|
||||
} catch (e) {
|
||||
setHermesConfigStatus("保存失败: " + (e.message || e), true);
|
||||
setHermesModelStatus("保存失败: " + (e.message || e), true);
|
||||
toast("保存失败: " + (e.message || e));
|
||||
} finally {
|
||||
if (btn) {
|
||||
btn.disabled = false;
|
||||
btn.innerHTML = oldHTML;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function saveMcpConfig() {
|
||||
const mcpServersYaml = document.getElementById("mcpServersYaml")?.value || "";
|
||||
const model = snapshotModelOrFields();
|
||||
if (!model.default) {
|
||||
toast("先读取或填写默认模型");
|
||||
return;
|
||||
}
|
||||
if (!confirm("保存 MCP 工具接入配置后会重启线上 Hermes agent,当前正在生成的任务可能中断。继续吗?")) return;
|
||||
const btn = document.getElementById("hermesMcpSaveBtn");
|
||||
const oldHTML = btn?.innerHTML;
|
||||
if (btn) {
|
||||
btn.disabled = true;
|
||||
btn.textContent = "保存 MCP 中...";
|
||||
}
|
||||
setHermesMcpStatus("正在写入 mcp_servers 配置并重启 Hermes agent...");
|
||||
try {
|
||||
const saved = await postHermesRuntimeConfig({
|
||||
model,
|
||||
mcp_servers_yaml: mcpServersYaml,
|
||||
restart: true,
|
||||
});
|
||||
const savedModel = saved.model || model;
|
||||
if (savedModel.default) syncModelPick(savedModel.default);
|
||||
document.getElementById("mcpServersYaml").value = saved.mcp_servers_yaml || "";
|
||||
_hermesConfigSnapshot = {
|
||||
model: {
|
||||
default: savedModel.default || model.default,
|
||||
provider: savedModel.provider || model.provider,
|
||||
base_url: savedModel.base_url || model.base_url,
|
||||
},
|
||||
mcp_servers_yaml: saved.mcp_servers_yaml || "",
|
||||
};
|
||||
_hermesConfigLoaded = false;
|
||||
setHermesMcpStatus("MCP 配置已保存并重启 · 备份 " + (saved.backup || "已创建"));
|
||||
toast("MCP 工具接入配置已生效");
|
||||
setTimeout(() => {
|
||||
pingBackend();
|
||||
refreshDashboard();
|
||||
}, 1800);
|
||||
} catch (e) {
|
||||
setHermesMcpStatus("保存失败: " + (e.message || e), true);
|
||||
toast("保存失败: " + (e.message || e));
|
||||
} finally {
|
||||
if (btn) {
|
||||
|
||||
Reference in New Issue
Block a user