auto-save 2026-05-11 15:46 (~6)
This commit is contained in:
@@ -1,18 +1,5 @@
|
|||||||
{
|
{
|
||||||
"entries": [
|
"entries": [
|
||||||
{
|
|
||||||
"files_changed": 1,
|
|
||||||
"hash": "f4789ab",
|
|
||||||
"message": "auto-save 2026-05-10 07:28 (~1)",
|
|
||||||
"ts": "2026-05-10T07:28:02+08:00",
|
|
||||||
"type": "commit"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"files_changed": 1,
|
|
||||||
"message": "Codex 会话活跃 · 最近命令:codex · 分支 master · 1 项未提交变更 · 最近提交:auto-save 2026-05-10 07:28 (~1)",
|
|
||||||
"ts": "2026-05-09T23:28:29Z",
|
|
||||||
"type": "session-heartbeat"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"files_changed": 1,
|
"files_changed": 1,
|
||||||
"hash": "a72c5bb",
|
"hash": "a72c5bb",
|
||||||
@@ -3259,6 +3246,19 @@
|
|||||||
"type": "session-heartbeat",
|
"type": "session-heartbeat",
|
||||||
"message": "Codex 会话活跃 · 最近命令:codex · 分支 master · 1 项未提交变更 · 最近提交:auto-save 2026-05-11 15:35 (~5)",
|
"message": "Codex 会话活跃 · 最近命令:codex · 分支 master · 1 项未提交变更 · 最近提交:auto-save 2026-05-11 15:35 (~5)",
|
||||||
"files_changed": 1
|
"files_changed": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ts": "2026-05-11T15:41:29+08:00",
|
||||||
|
"type": "commit",
|
||||||
|
"message": "auto-save 2026-05-11 15:41 (~1)",
|
||||||
|
"hash": "a3cc734",
|
||||||
|
"files_changed": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ts": "2026-05-11T07:46:28Z",
|
||||||
|
"type": "session-heartbeat",
|
||||||
|
"message": "Codex 会话活跃 · 最近命令:codex · 分支 master · 6 项未提交变更 · 最近提交:auto-save 2026-05-11 15:41 (~1)",
|
||||||
|
"files_changed": 6
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
4
RULES.md
4
RULES.md
@@ -19,8 +19,8 @@
|
|||||||
- 爱马仕前端「仪表盘」同步了上游 Hermes 的快捷入口板块,个人版展示主站、API、飞书机器人列表、文档/解析入口
|
- 爱马仕前端「仪表盘」同步了上游 Hermes 的快捷入口板块,个人版展示主站、API、飞书机器人列表、文档/解析入口
|
||||||
- 爱马仕前端「仪表盘」活动热力图已重做为带摘要、月份标尺、紧凑格子和细分色阶的活动卡片
|
- 爱马仕前端「仪表盘」活动热力图已重做为带摘要、月份标尺、紧凑格子和细分色阶的活动卡片
|
||||||
- 爱马仕前端「设置 → 连接」可自助维护 API 地址 / API Key 并测试连接;「对话 → 存周报」和「设置 → 周报记录」会在本地保存任务描述、上下文片段和最终周报
|
- 爱马仕前端「设置 → 连接」可自助维护 API 地址 / API Key 并测试连接;「对话 → 存周报」和「设置 → 周报记录」会在本地保存任务描述、上下文片段和最终周报
|
||||||
- 爱马仕前端「设置 → 模型与 MCP」可读取 / 写入 LXC 内 `/opt/hermes-agent/config.yaml`,保存后重启 Docker `hermes-agent`
|
- 爱马仕前端「设置 → AI 模型接入」和「设置 → MCP 工具接入」可分别维护 LXC 内 `/opt/hermes-agent/config.yaml` 的 `model` 与 `mcp_servers` 块,保存后重启 Docker `hermes-agent`
|
||||||
- 当前前端静态壳缓存版本:`hermes-ui-v26`
|
- 当前前端静态壳缓存版本:`hermes-ui-v27`
|
||||||
- 文档 / 解析:https://styles.kang-kang.com
|
- 文档 / 解析:https://styles.kang-kang.com
|
||||||
- 管理后台:待定
|
- 管理后台:待定
|
||||||
- 代码仓:https://git.kang-kang.com/kangwan/hermes-glass-ui-personal
|
- 代码仓:https://git.kang-kang.com/kangwan/hermes-glass-ui-personal
|
||||||
|
|||||||
180
src/app.js
180
src/app.js
@@ -125,15 +125,29 @@ function saveSettings(options = {}) {
|
|||||||
pingBackend();
|
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) {
|
function syncModelPick(modelValue) {
|
||||||
const model = (modelValue || state.model || "").trim();
|
const model = (modelValue || state.model || "").trim();
|
||||||
const pick = document.getElementById("modelPick");
|
const pick = document.getElementById("modelPick");
|
||||||
if (!pick || !model) return;
|
if (!pick || !model) return;
|
||||||
|
ensureModelChoice(model);
|
||||||
let option = Array.from(pick.options).find(item => item.value === 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;
|
pick.value = model;
|
||||||
state.model = model;
|
state.model = model;
|
||||||
const stat = document.getElementById("statModel");
|
const stat = document.getElementById("statModel");
|
||||||
@@ -596,19 +610,30 @@ async function testApiConnection() {
|
|||||||
|
|
||||||
let _hermesConfigLoaded = false;
|
let _hermesConfigLoaded = false;
|
||||||
let _hermesConfigLoading = false;
|
let _hermesConfigLoading = false;
|
||||||
function setHermesConfigStatus(text, isError = false) {
|
let _hermesConfigSnapshot = null;
|
||||||
const el = document.getElementById("hermesConfigStatus");
|
function setSettingsStatus(id, text, isError = false) {
|
||||||
|
const el = document.getElementById(id);
|
||||||
if (!el) return;
|
if (!el) return;
|
||||||
el.textContent = text;
|
el.textContent = text;
|
||||||
el.style.color = isError ? "var(--err)" : "";
|
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) {
|
async function refreshHermesConfig(force = false) {
|
||||||
if (_hermesConfigLoading || (_hermesConfigLoaded && !force)) return;
|
if (_hermesConfigLoading || (_hermesConfigLoaded && !force)) return;
|
||||||
const modelEl = document.getElementById("hermesModelDefault");
|
const modelEl = document.getElementById("hermesModelDefault");
|
||||||
if (!modelEl) return;
|
if (!modelEl) return;
|
||||||
_hermesConfigLoading = true;
|
_hermesConfigLoading = true;
|
||||||
setHermesConfigStatus("正在读取线上配置...");
|
setHermesConfigStatuses("正在读取线上配置...");
|
||||||
try {
|
try {
|
||||||
const res = await fetch("/feishu/hermes-config", {
|
const res = await fetch("/feishu/hermes-config", {
|
||||||
credentials: "same-origin",
|
credentials: "same-origin",
|
||||||
@@ -622,63 +647,146 @@ async function refreshHermesConfig(force = false) {
|
|||||||
document.getElementById("hermesModelProvider").value = model.provider || "";
|
document.getElementById("hermesModelProvider").value = model.provider || "";
|
||||||
document.getElementById("hermesModelBaseUrl").value = model.base_url || "";
|
document.getElementById("hermesModelBaseUrl").value = model.base_url || "";
|
||||||
document.getElementById("mcpServersYaml").value = config.mcp_servers_yaml || "";
|
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);
|
if (model.default) syncModelPick(model.default);
|
||||||
_hermesConfigLoaded = true;
|
_hermesConfigLoaded = true;
|
||||||
setHermesConfigStatus("已读取线上配置" + (config.lxc ? " · " + config.lxc : ""));
|
const suffix = config.lxc ? " · " + config.lxc : "";
|
||||||
|
setHermesModelStatus("已读取模型配置" + suffix);
|
||||||
|
setHermesMcpStatus("已读取 MCP 配置" + suffix);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setHermesConfigStatus("读取失败: " + (e.message || e), true);
|
setHermesConfigStatuses("读取失败: " + (e.message || e), true);
|
||||||
} finally {
|
} finally {
|
||||||
_hermesConfigLoading = false;
|
_hermesConfigLoading = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveHermesConfig() {
|
function readModelConfigFields() {
|
||||||
const modelDefault = document.getElementById("hermesModelDefault")?.value.trim() || "";
|
const modelDefault = document.getElementById("hermesModelDefault")?.value.trim() || "";
|
||||||
const provider = document.getElementById("hermesModelProvider")?.value.trim() || "openrouter";
|
const provider = document.getElementById("hermesModelProvider")?.value.trim() || "openrouter";
|
||||||
const baseUrl = document.getElementById("hermesModelBaseUrl")?.value.trim() || "";
|
const baseUrl = document.getElementById("hermesModelBaseUrl")?.value.trim() || "";
|
||||||
const mcpServersYaml = document.getElementById("mcpServersYaml")?.value || "";
|
return { default: modelDefault, provider, base_url: baseUrl };
|
||||||
if (!modelDefault) {
|
}
|
||||||
|
|
||||||
|
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("默认模型不能为空");
|
toast("默认模型不能为空");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!confirm("保存后会重启线上 Hermes agent,当前正在生成的任务可能中断。继续吗?")) return;
|
if (!confirm("保存 AI 模型接入配置后会重启线上 Hermes agent,当前正在生成的任务可能中断。继续吗?")) return;
|
||||||
const btn = document.getElementById("hermesConfigSaveBtn");
|
const btn = document.getElementById("hermesModelSaveBtn");
|
||||||
const oldHTML = btn?.innerHTML;
|
const oldHTML = btn?.innerHTML;
|
||||||
if (btn) {
|
if (btn) {
|
||||||
btn.disabled = true;
|
btn.disabled = true;
|
||||||
btn.textContent = "保存并重启中...";
|
btn.textContent = "保存模型中...";
|
||||||
}
|
}
|
||||||
setHermesConfigStatus("正在写入 config.yaml 并重启 Hermes agent...");
|
setHermesModelStatus("正在写入 model 配置并重启 Hermes agent...");
|
||||||
try {
|
try {
|
||||||
const res = await fetch("/feishu/hermes-config", {
|
const saved = await postHermesRuntimeConfig({
|
||||||
method: "POST",
|
model,
|
||||||
credentials: "same-origin",
|
mcp_servers_yaml: _hermesConfigSnapshot ? _hermesConfigSnapshot.mcp_servers_yaml : (document.getElementById("mcpServersYaml")?.value || ""),
|
||||||
headers: { "Content-Type": "application/json" },
|
restart: true,
|
||||||
body: JSON.stringify({
|
|
||||||
model: {
|
|
||||||
default: modelDefault,
|
|
||||||
provider,
|
|
||||||
base_url: baseUrl,
|
|
||||||
},
|
|
||||||
mcp_servers_yaml: mcpServersYaml,
|
|
||||||
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 || {};
|
const savedModel = saved.model || {};
|
||||||
if (savedModel.default) syncModelPick(savedModel.default);
|
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;
|
_hermesConfigLoaded = false;
|
||||||
setHermesConfigStatus("已保存并重启 · 备份 " + (saved.backup || "已创建"));
|
setHermesModelStatus("模型配置已保存并重启 · 备份 " + (saved.backup || "已创建"));
|
||||||
toast("模型与 MCP 配置已生效");
|
toast("AI 模型接入配置已生效");
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
pingBackend();
|
pingBackend();
|
||||||
refreshDashboard();
|
refreshDashboard();
|
||||||
}, 1800);
|
}, 1800);
|
||||||
} catch (e) {
|
} 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));
|
toast("保存失败: " + (e.message || e));
|
||||||
} finally {
|
} finally {
|
||||||
if (btn) {
|
if (btn) {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<link rel="icon" type="image/svg+xml" href="./icon.svg">
|
<link rel="icon" type="image/svg+xml" href="./icon.svg">
|
||||||
<link rel="apple-touch-icon" href="./icon.svg">
|
<link rel="apple-touch-icon" href="./icon.svg">
|
||||||
<title>爱马仕 · AI</title>
|
<title>爱马仕 · AI</title>
|
||||||
<link rel="stylesheet" href="./styles.css?v=20260511-settings-v26">
|
<link rel="stylesheet" href="./styles.css?v=20260511-settings-v27">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
@@ -308,10 +308,17 @@
|
|||||||
</button>
|
</button>
|
||||||
<div class="chat-model-pick">
|
<div class="chat-model-pick">
|
||||||
<select id="modelPick">
|
<select id="modelPick">
|
||||||
|
<option value="google/gemini-3.1-pro-preview">Gemini 3.1 Pro · OpenRouter</option>
|
||||||
<option value="gemini-3-pro-preview">Gemini 3 Pro</option>
|
<option value="gemini-3-pro-preview">Gemini 3 Pro</option>
|
||||||
<option value="gemini-2.5-pro">Gemini 2.5 Pro</option>
|
<option value="gemini-2.5-pro">Gemini 2.5 Pro</option>
|
||||||
<option value="gemini-2.5-flash">Gemini 2.5 Flash</option>
|
<option value="gemini-2.5-flash">Gemini 2.5 Flash</option>
|
||||||
</select>
|
</select>
|
||||||
|
<datalist id="modelOptions">
|
||||||
|
<option value="google/gemini-3.1-pro-preview" label="Gemini 3.1 Pro · OpenRouter"></option>
|
||||||
|
<option value="gemini-3-pro-preview" label="Gemini 3 Pro"></option>
|
||||||
|
<option value="gemini-2.5-pro" label="Gemini 2.5 Pro"></option>
|
||||||
|
<option value="gemini-2.5-flash" label="Gemini 2.5 Flash"></option>
|
||||||
|
</datalist>
|
||||||
</div>
|
</div>
|
||||||
<button class="chat-clear" onclick="saveWeeklyReportFromChat()" title="保存最近一条回答为周报记录">存周报</button>
|
<button class="chat-clear" onclick="saveWeeklyReportFromChat()" title="保存最近一条回答为周报记录">存周报</button>
|
||||||
<button class="chat-clear" id="clearBtn" title="清空对话">清空</button>
|
<button class="chat-clear" id="clearBtn" title="清空对话">清空</button>
|
||||||
@@ -386,12 +393,8 @@
|
|||||||
<input type="text" id="agentDesc" placeholder="一句话说明这个智能体擅长什么" maxlength="80">
|
<input type="text" id="agentDesc" placeholder="一句话说明这个智能体擅长什么" maxlength="80">
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-row">
|
<div class="setting-row">
|
||||||
<label>模型</label>
|
<label>模型 ID</label>
|
||||||
<select id="agentModel">
|
<input type="text" id="agentModel" list="modelOptions" placeholder="google/gemini-3.1-pro-preview" autocomplete="off">
|
||||||
<option value="gemini-3-pro-preview">Gemini 3 Pro</option>
|
|
||||||
<option value="gemini-2.5-pro">Gemini 2.5 Pro</option>
|
|
||||||
<option value="gemini-2.5-flash">Gemini 2.5 Flash</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-row setting-row-full">
|
<div class="setting-row setting-row-full">
|
||||||
<label>角色设定 (System Prompt)</label>
|
<label>角色设定 (System Prompt)</label>
|
||||||
@@ -1189,21 +1192,21 @@ git push # Gitea kangwan/hermes-glass-ui-personal
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 模型与 MCP -->
|
<!-- AI 模型接入 -->
|
||||||
<div class="settings-group wide">
|
<div class="settings-group wide">
|
||||||
<div class="settings-group-head">
|
<div class="settings-group-head">
|
||||||
<div class="settings-group-icon">
|
<div class="settings-group-icon">
|
||||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2v20"/><path d="M2 12h20"/><path d="M4.93 4.93 19.07 19.07"/><path d="M19.07 4.93 4.93 19.07"/></svg>
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2v20"/><path d="M2 12h20"/><path d="M4.93 4.93 19.07 19.07"/><path d="M19.07 4.93 4.93 19.07"/></svg>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="settings-group-title">模型与 MCP</div>
|
<div class="settings-group-title">AI 模型接入</div>
|
||||||
<div class="settings-group-desc">线上 Hermes agent 的默认模型和 MCP server 配置</div>
|
<div class="settings-group-desc">线上 Hermes agent 的默认模型、Provider 和模型网关地址</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-group-body">
|
<div class="settings-group-body">
|
||||||
<div class="settings-grid-3">
|
<div class="settings-grid-3">
|
||||||
<div class="settings-field">
|
<div class="settings-field">
|
||||||
<label for="hermesModelDefault">默认模型</label>
|
<label for="hermesModelDefault">默认模型 ID</label>
|
||||||
<input type="text" id="hermesModelDefault" placeholder="google/gemini-3.1-pro-preview" autocomplete="off">
|
<input type="text" id="hermesModelDefault" placeholder="google/gemini-3.1-pro-preview" autocomplete="off">
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-field">
|
<div class="settings-field">
|
||||||
@@ -1215,6 +1218,35 @@ git push # Gitea kangwan/hermes-glass-ui-personal
|
|||||||
<input type="text" id="hermesModelBaseUrl" placeholder="https://openrouter.ai/api/v1" autocomplete="off">
|
<input type="text" id="hermesModelBaseUrl" placeholder="https://openrouter.ai/api/v1" autocomplete="off">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="settings-grid-3">
|
||||||
|
<div class="settings-field">
|
||||||
|
<label for="hermesModelApiKeyRef">AI API Key</label>
|
||||||
|
<input type="password" id="hermesModelApiKeyRef" placeholder="服务器环境变量,不在浏览器显示" disabled>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="settings-actions">
|
||||||
|
<button class="glass-btn-sm" onclick="refreshHermesConfig(true)">读取模型配置</button>
|
||||||
|
<button class="glass-btn-sm primary" id="hermesModelSaveBtn" onclick="saveModelConfig()">
|
||||||
|
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><path d="M17 21v-8H7v8"/><path d="M7 3v5h8"/></svg>
|
||||||
|
保存模型并重启
|
||||||
|
</button>
|
||||||
|
<div class="settings-help" id="hermesModelStatus">打开设置页后自动读取。</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- MCP 工具接入 -->
|
||||||
|
<div class="settings-group wide">
|
||||||
|
<div class="settings-group-head">
|
||||||
|
<div class="settings-group-icon">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><path d="M3.3 7 12 12l8.7-5"/><path d="M12 22V12"/></svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="settings-group-title">MCP 工具接入</div>
|
||||||
|
<div class="settings-group-desc">外部工具、知识库和服务调用配置</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="settings-group-body">
|
||||||
<div class="settings-field">
|
<div class="settings-field">
|
||||||
<label for="mcpServersYaml">MCP Servers YAML</label>
|
<label for="mcpServersYaml">MCP Servers YAML</label>
|
||||||
<textarea id="mcpServersYaml" rows="8" spellcheck="false" placeholder='mcp_servers:
|
<textarea id="mcpServersYaml" rows="8" spellcheck="false" placeholder='mcp_servers:
|
||||||
@@ -1224,12 +1256,12 @@ git push # Gitea kangwan/hermes-glass-ui-personal
|
|||||||
<div class="settings-help">留空会移除 <code>mcp_servers</code>;保存会备份配置并重启 <code>hermes-agent</code>。</div>
|
<div class="settings-help">留空会移除 <code>mcp_servers</code>;保存会备份配置并重启 <code>hermes-agent</code>。</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="settings-actions">
|
<div class="settings-actions">
|
||||||
<button class="glass-btn-sm" onclick="refreshHermesConfig(true)">读取线上配置</button>
|
<button class="glass-btn-sm" onclick="refreshHermesConfig(true)">读取 MCP 配置</button>
|
||||||
<button class="glass-btn-sm primary" id="hermesConfigSaveBtn" onclick="saveHermesConfig()">
|
<button class="glass-btn-sm primary" id="hermesMcpSaveBtn" onclick="saveMcpConfig()">
|
||||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><path d="M17 21v-8H7v8"/><path d="M7 3v5h8"/></svg>
|
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><path d="M17 21v-8H7v8"/><path d="M7 3v5h8"/></svg>
|
||||||
保存并重启
|
保存 MCP 并重启
|
||||||
</button>
|
</button>
|
||||||
<div class="settings-help" id="hermesConfigStatus">打开设置页后自动读取。</div>
|
<div class="settings-help" id="hermesMcpStatus">打开设置页后自动读取。</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1376,6 +1408,6 @@ git push # Gitea kangwan/hermes-glass-ui-personal
|
|||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="./app.js?v=20260511-settings-v26"></script>
|
<script src="./app.js?v=20260511-settings-v27"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -2546,6 +2546,11 @@ a { color: var(--orange-3); text-decoration: none; }
|
|||||||
outline: none;
|
outline: none;
|
||||||
transition: border-color 0.2s, box-shadow 0.2s;
|
transition: border-color 0.2s, box-shadow 0.2s;
|
||||||
}
|
}
|
||||||
|
.settings-field input:disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.7;
|
||||||
|
color: var(--text-dim2);
|
||||||
|
}
|
||||||
.settings-field textarea {
|
.settings-field textarea {
|
||||||
min-height: 220px;
|
min-height: 220px;
|
||||||
resize: vertical;
|
resize: vertical;
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
// 爱马仕 Hermes · 轻量 Service Worker
|
// 爱马仕 Hermes · 轻量 Service Worker
|
||||||
// 静态壳走 network-first(拿不到再回退缓存),API 直通
|
// 静态壳走 network-first(拿不到再回退缓存),API 直通
|
||||||
const CACHE = "hermes-ui-v26";
|
const CACHE = "hermes-ui-v27";
|
||||||
const ASSETS = [
|
const ASSETS = [
|
||||||
"./",
|
"./",
|
||||||
"./index.html",
|
"./index.html",
|
||||||
"./styles.css?v=20260511-settings-v26",
|
"./styles.css?v=20260511-settings-v27",
|
||||||
"./app.js?v=20260511-settings-v26",
|
"./app.js?v=20260511-settings-v27",
|
||||||
"./manifest.webmanifest",
|
"./manifest.webmanifest",
|
||||||
"./icon.svg",
|
"./icon.svg",
|
||||||
];
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user