auto-save 2026-05-11 14:56 (~5)
This commit is contained in:
@@ -1,11 +1,5 @@
|
|||||||
{
|
{
|
||||||
"entries": [
|
"entries": [
|
||||||
{
|
|
||||||
"files_changed": 1,
|
|
||||||
"message": "Codex 会话活跃 · 最近命令:codex · 分支 master · 1 项未提交变更 · 最近提交:auto-save 2026-05-10 06:46 (~1)",
|
|
||||||
"ts": "2026-05-09T22:48:29Z",
|
|
||||||
"type": "session-heartbeat"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"files_changed": 1,
|
"files_changed": 1,
|
||||||
"hash": "e433c1c",
|
"hash": "e433c1c",
|
||||||
@@ -3256,6 +3250,13 @@
|
|||||||
"type": "session-heartbeat",
|
"type": "session-heartbeat",
|
||||||
"message": "Codex 会话活跃 · 最近命令:codex · 分支 master · 1 项未提交变更 · 最近提交:auto-save 2026-05-11 14:45 (~1)",
|
"message": "Codex 会话活跃 · 最近命令:codex · 分支 master · 1 项未提交变更 · 最近提交:auto-save 2026-05-11 14:45 (~1)",
|
||||||
"files_changed": 1
|
"files_changed": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ts": "2026-05-11T14:50:39+08:00",
|
||||||
|
"type": "commit",
|
||||||
|
"message": "auto-save 2026-05-11 14:50 (~4)",
|
||||||
|
"hash": "d95aed8",
|
||||||
|
"files_changed": 4
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
4
RULES.md
4
RULES.md
@@ -19,7 +19,8 @@
|
|||||||
- 爱马仕前端「仪表盘」同步了上游 Hermes 的快捷入口板块,个人版展示主站、API、飞书机器人列表、文档/解析入口
|
- 爱马仕前端「仪表盘」同步了上游 Hermes 的快捷入口板块,个人版展示主站、API、飞书机器人列表、文档/解析入口
|
||||||
- 爱马仕前端「仪表盘」活动热力图已重做为带摘要、月份标尺、紧凑格子和细分色阶的活动卡片
|
- 爱马仕前端「仪表盘」活动热力图已重做为带摘要、月份标尺、紧凑格子和细分色阶的活动卡片
|
||||||
- 爱马仕前端「设置 → 连接」可自助维护 API 地址 / API Key 并测试连接;「对话 → 存周报」和「设置 → 周报记录」会在本地保存任务描述、上下文片段和最终周报
|
- 爱马仕前端「设置 → 连接」可自助维护 API 地址 / API Key 并测试连接;「对话 → 存周报」和「设置 → 周报记录」会在本地保存任务描述、上下文片段和最终周报
|
||||||
- 当前前端静态壳缓存版本:`hermes-ui-v17`
|
- 爱马仕前端「设置 → 模型与 MCP」可读取 / 写入 LXC 内 `/opt/hermes-agent/config.yaml`,保存后重启 Docker `hermes-agent`
|
||||||
|
- 当前前端静态壳缓存版本:`hermes-ui-v18`
|
||||||
- 文档 / 解析: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
|
||||||
@@ -63,5 +64,6 @@
|
|||||||
- 线上飞书桥接环境:`/etc/hermes-feishu-bridge.env`,mode 600
|
- 线上飞书桥接环境:`/etc/hermes-feishu-bridge.env`,mode 600
|
||||||
- 飞书后台配置所需回调 URL、verification token、notify token 备份:`/root/hermes-feishu-bridge.tokens`,mode 600
|
- 飞书后台配置所需回调 URL、verification token、notify token 备份:`/root/hermes-feishu-bridge.tokens`,mode 600
|
||||||
- 飞书自助配置接口 `POST /feishu/apps`、`DELETE /feishu/apps/{app_id}` 要求已登录爱马仕 cookie 且同源请求;未登录公网请求返回 401
|
- 飞书自助配置接口 `POST /feishu/apps`、`DELETE /feishu/apps/{app_id}` 要求已登录爱马仕 cookie 且同源请求;未登录公网请求返回 401
|
||||||
|
- Hermes 运行配置接口 `/feishu/hermes-config` 复用飞书桥接反代,要求已登录爱马仕 cookie 且同源请求;会通过 Incus 写入 LXC 配置并重启 `hermes-agent`
|
||||||
- 当前飞书桥接版本按明文事件回调处理;如果飞书后台开启事件加密,需要先补充解密支持
|
- 当前飞书桥接版本按明文事件回调处理;如果飞书后台开启事件加密,需要先补充解密支持
|
||||||
- 主站有 cookie 门禁;nginx 已对 `/feishu/` 单独放行并反代到飞书桥接服务
|
- 主站有 cookie 门禁;nginx 已对 `/feishu/` 单独放行并反代到飞书桥接服务
|
||||||
|
|||||||
94
src/app.js
94
src/app.js
@@ -594,6 +594,100 @@ async function testApiConnection() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let _hermesConfigLoaded = false;
|
||||||
|
let _hermesConfigLoading = false;
|
||||||
|
function setHermesConfigStatus(text, isError = false) {
|
||||||
|
const el = document.getElementById("hermesConfigStatus");
|
||||||
|
if (!el) return;
|
||||||
|
el.textContent = text;
|
||||||
|
el.style.color = isError ? "var(--err)" : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
async function refreshHermesConfig(force = false) {
|
||||||
|
if (_hermesConfigLoading || (_hermesConfigLoaded && !force)) return;
|
||||||
|
const modelEl = document.getElementById("hermesModelDefault");
|
||||||
|
if (!modelEl) return;
|
||||||
|
_hermesConfigLoading = true;
|
||||||
|
setHermesConfigStatus("正在读取线上配置...");
|
||||||
|
try {
|
||||||
|
const res = await fetch("/feishu/hermes-config", {
|
||||||
|
credentials: "same-origin",
|
||||||
|
cache: "no-store",
|
||||||
|
});
|
||||||
|
const data = await res.json().catch(() => ({}));
|
||||||
|
if (!res.ok || data.code !== 0) throw new Error(data.msg || ("HTTP " + res.status));
|
||||||
|
const config = data.config || {};
|
||||||
|
const model = config.model || {};
|
||||||
|
document.getElementById("hermesModelDefault").value = model.default || "";
|
||||||
|
document.getElementById("hermesModelProvider").value = model.provider || "";
|
||||||
|
document.getElementById("hermesModelBaseUrl").value = model.base_url || "";
|
||||||
|
document.getElementById("mcpServersYaml").value = config.mcp_servers_yaml || "";
|
||||||
|
if (model.default) syncModelPick(model.default);
|
||||||
|
_hermesConfigLoaded = true;
|
||||||
|
setHermesConfigStatus("已读取线上配置" + (config.lxc ? " · " + config.lxc : ""));
|
||||||
|
} catch (e) {
|
||||||
|
setHermesConfigStatus("读取失败: " + (e.message || e), true);
|
||||||
|
} finally {
|
||||||
|
_hermesConfigLoading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveHermesConfig() {
|
||||||
|
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) {
|
||||||
|
toast("默认模型不能为空");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!confirm("保存后会重启线上 Hermes agent,当前正在生成的任务可能中断。继续吗?")) return;
|
||||||
|
const btn = document.getElementById("hermesConfigSaveBtn");
|
||||||
|
const oldHTML = btn?.innerHTML;
|
||||||
|
if (btn) {
|
||||||
|
btn.disabled = true;
|
||||||
|
btn.textContent = "保存并重启中...";
|
||||||
|
}
|
||||||
|
setHermesConfigStatus("正在写入 config.yaml 并重启 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 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 || "";
|
||||||
|
_hermesConfigLoaded = false;
|
||||||
|
setHermesConfigStatus("已保存并重启 · 备份 " + (saved.backup || "已创建"));
|
||||||
|
toast("模型与 MCP 配置已生效");
|
||||||
|
setTimeout(() => {
|
||||||
|
pingBackend();
|
||||||
|
refreshDashboard();
|
||||||
|
}, 1800);
|
||||||
|
} catch (e) {
|
||||||
|
setHermesConfigStatus("保存失败: " + (e.message || e), true);
|
||||||
|
toast("保存失败: " + (e.message || e));
|
||||||
|
} finally {
|
||||||
|
if (btn) {
|
||||||
|
btn.disabled = false;
|
||||||
|
btn.innerHTML = oldHTML;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function fetchIP() {
|
async function fetchIP() {
|
||||||
const el = document.getElementById("statIP");
|
const el = document.getElementById("statIP");
|
||||||
if (el) el.textContent = location.hostname;
|
if (el) el.textContent = location.hostname;
|
||||||
|
|||||||
@@ -2504,6 +2504,14 @@ a { color: var(--orange-3); text-decoration: none; }
|
|||||||
gap: 16px;
|
gap: 16px;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
.settings-grid-3 {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
gap: 14px;
|
||||||
|
}
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.settings-grid-3 { grid-template-columns: 1fr; }
|
||||||
|
}
|
||||||
|
|
||||||
.settings-field {
|
.settings-field {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -2526,7 +2534,8 @@ a { color: var(--orange-3); text-decoration: none; }
|
|||||||
}
|
}
|
||||||
.settings-field.toggle-field > div:first-child { flex: 1 1 200px; min-width: 0; }
|
.settings-field.toggle-field > div:first-child { flex: 1 1 200px; min-width: 0; }
|
||||||
.settings-field input[type="text"],
|
.settings-field input[type="text"],
|
||||||
.settings-field input[type="password"] {
|
.settings-field input[type="password"],
|
||||||
|
.settings-field textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
@@ -2540,7 +2549,14 @@ 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:focus {
|
.settings-field textarea {
|
||||||
|
min-height: 156px;
|
||||||
|
resize: vertical;
|
||||||
|
line-height: 1.55;
|
||||||
|
font-family: "SF Mono", ui-monospace, Menlo, monospace;
|
||||||
|
}
|
||||||
|
.settings-field input:focus,
|
||||||
|
.settings-field textarea:focus {
|
||||||
border-color: var(--orange);
|
border-color: var(--orange);
|
||||||
box-shadow: 0 0 0 3px rgba(255,105,0,0.12);
|
box-shadow: 0 0 0 3px rgba(255,105,0,0.12);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// 爱马仕 Hermes · 轻量 Service Worker
|
// 爱马仕 Hermes · 轻量 Service Worker
|
||||||
// 静态壳走 network-first(拿不到再回退缓存),API 直通
|
// 静态壳走 network-first(拿不到再回退缓存),API 直通
|
||||||
const CACHE = "hermes-ui-v17";
|
const CACHE = "hermes-ui-v18";
|
||||||
const ASSETS = [
|
const ASSETS = [
|
||||||
"./",
|
"./",
|
||||||
"./index.html",
|
"./index.html",
|
||||||
|
|||||||
Reference in New Issue
Block a user