diff --git a/Agent.md b/Agent.md index 926fefe3..ece380b0 100644 --- a/Agent.md +++ b/Agent.md @@ -66,7 +66,7 @@ EMRG is a self-evolving AI agent architecture experiment. Python implementation, - Streaming chat with delta rendering (16ms batching), markdown on done (marked + DOMPurify + local highlight.js subset), tool call status cards (2000-char truncation + expand) - Session list/switch/new/delete + right-click rename (context menu, #423) synced with daemon; own-stream busy lock (G65); broadcast streams from other clients tagged "来自其他客户端" - Disconnect/reconnect: red status dot, auto daemon respawn (stale-port detection), session resume, input bar restored on disconnect (no 30s fake-timeout) - - Unit tests `npm test` (107: 29 daemon_client + 22 app-commands + 31 renderer smoke + 15 i18n + 7 integration + 3 commands); RESPONSE_TYPES mirror daemon protocol verified against `daemon.py` + - Unit tests `npm test` (110: 29 daemon_client + 22 app-commands + 31 renderer smoke + 15 i18n + 7 integration + 3 commands + 3 build-config); RESPONSE_TYPES mirror daemon protocol verified against `daemon.py` - **Auto project tracking** — Automatically detects and records working directories; project-scoped sessions - **Rant-driven evolution** — User feedback via `/rant` drives automatic self-improvement cycles - **Headless GitHub auth** — Non-interactive evolution auto-extracts `GH_TOKEN` from git credential store (osxkeychain / credential helper); PR comment/LGTM queries fall back to REST API (GraphQL needs `read:org` scope) @@ -94,7 +94,7 @@ pkill -f "emrg.server"; rm -f ~/.emrg/emrgd.port; python -m emrg ``` Python: `uv run pytest tests/ -v` (674) — import check: `uv run python -c "from emrg.client.app import run_client"` -GUI: `cd emrg/gui && npm test` (107: 29 daemon_client + 22 app-commands + 31 renderer smoke + 15 i18n + 7 integration + 3 commands) — syntax: `node --check main.js preload.js daemon_client.js renderer/js/*.js` +GUI: `cd emrg/gui && npm test` (110: 29 daemon_client + 22 app-commands + 31 renderer smoke + 15 i18n + 7 integration + 3 commands + 3 build-config) — syntax: `node --check main.js preload.js daemon_client.js renderer/js/*.js` CI: `uv run pytest` + GUI tests + **actionlint workflow lint** (`rhysd/actionlint@v1.7.12` gate, #444 — workflow 解析错误在 PR CI 即失败,如 `if:` secrets 上下文) Re-trigger: `scripts/re-trigger-ci.sh [branch]` (workflow_dispatch, #527 — 替代空 commit 重触发:Actions outage 会整段丢弃 push 事件,dispatch 走 API 路径不受影响) diff --git a/README.cn.md b/README.cn.md index eb960fc1..35da8955 100644 --- a/README.cn.md +++ b/README.cn.md @@ -282,8 +282,8 @@ uv run python -m emrg # 启动 TUI cd emrg/gui npm ci # 安装依赖(生产模式可 --omit=dev) npm start # 启动 GUI(自动拉起 daemon) -npm test # 运行 Node 测试(107 项:29 daemon_client + 22 app-commands + 31 renderer smoke + 15 i18n + 7 integration + 3 commands;集成测试在 CI 跑,本地可 npm run test:integration) -npm test # 运行 Node 测试(107 项:29 daemon_client + 22 app-commands + 31 renderer smoke + 15 i18n + 7 integration + 3 commands;集成测试在 CI 跑,本地可 npm run test:integration) +npm test # 运行 Node 测试(110 项:29 daemon_client + 22 app-commands + 31 renderer smoke + 15 i18n + 7 integration + 3 commands + 3 build-config;集成测试在 CI 跑,本地可 npm run test:integration) +npm test # 运行 Node 测试(110 项:29 daemon_client + 22 app-commands + 31 renderer smoke + 15 i18n + 7 integration + 3 commands + 3 build-config;集成测试在 CI 跑,本地可 npm run test:integration) ``` CI 通过 GitHub Actions 自动运行测试并检查冲突标记(`.github/workflows/test.yml`)。 diff --git a/README.md b/README.md index 07424ed0..b7a5474a 100644 --- a/README.md +++ b/README.md @@ -281,7 +281,7 @@ uv run python -m emrg # launch TUI cd emrg/gui npm ci # install deps (production: --omit=dev) npm start # launch GUI (auto-starts daemon) -npm test # run Node tests (107: 29 daemon_client + 22 app-commands + 31 renderer smoke + 15 i18n + 7 integration + 3 commands; integration runs in CI, local: npm run test:integration) +npm test # run Node tests (110: 29 daemon_client + 22 app-commands + 31 renderer smoke + 15 i18n + 7 integration + 3 commands + 3 build-config; integration runs in CI, local: npm run test:integration) ``` CI runs tests and checks for conflict markers automatically via GitHub Actions (`.github/workflows/test.yml`). diff --git a/emrg/gui/package.json b/emrg/gui/package.json index 52ebc6c4..b893bbc0 100644 --- a/emrg/gui/package.json +++ b/emrg/gui/package.json @@ -34,6 +34,7 @@ "main.js", "preload.js", "renderer/**", + "vendor/**", "daemon_client.js", "package.json", "scripts/**" diff --git a/emrg/gui/renderer/js/markdown.js b/emrg/gui/renderer/js/markdown.js index d51521f4..af76f2db 100644 --- a/emrg/gui/renderer/js/markdown.js +++ b/emrg/gui/renderer/js/markdown.js @@ -46,7 +46,10 @@ function ensureConfigured() { } renderer.renderMarkdown = async function (mdText) { - if (!window.marked) return escapeHtml(mdText || ""); + if (!window.marked) { + console.warn("markdown vendor missing (marked not loaded)"); + return escapeHtml(mdText || ""); + } try { ensureConfigured(); const html = window.marked.parse(mdText || "", { async: true, gfm: true, breaks: false }); diff --git a/emrg/gui/test/build-config.test.js b/emrg/gui/test/build-config.test.js new file mode 100644 index 00000000..42bf0eaf --- /dev/null +++ b/emrg/gui/test/build-config.test.js @@ -0,0 +1,49 @@ +"use strict"; +/** + * build-config.test.js — 打包配置守卫(rant 2026-08-10T11:03:51)。 + * + * 打包版 GUI Markdown 不渲染的根因:electron-builder `files` 白名单漏掉 + * `vendor/**` → marked/DOMPurify/highlight 三个第三方脚本没进安装包 → + * renderMarkdown 静默降级纯文本(源码模式正常、打包版中招)。 + * + * 本测试直接钉死三类回归: + * 1. files 白名单必须包含 "vendor/**"(有人删掉即红) + * 2. vendor/ 目录必须存在全部 3 个运行时脚本(build-vendor.js 产物) + * 3. renderMarkdown 的 `!window.marked` 降级路径必须带 console.warn + * (静默降级 → 可诊断,问题可快速定位) + */ +const { test } = require("node:test"); +const assert = require("node:assert"); +const fs = require("node:fs"); +const path = require("node:path"); + +const GUI_ROOT = path.join(__dirname, ".."); +const PKG = JSON.parse(fs.readFileSync(path.join(GUI_ROOT, "package.json"), "utf-8")); +const VENDOR_DIR = path.join(GUI_ROOT, "vendor"); + +const REQUIRED_VENDOR_FILES = ["marked.min.js", "dompurify.min.js", "highlight.custom.js"]; + +test("electron-builder files whitelist bundles vendor/**", () => { + const files = (PKG.build && PKG.build.files) || []; + assert.ok( + files.includes("vendor/**"), + `build.files must include "vendor/**" — got: ${JSON.stringify(files)}` + ); +}); + +test("vendor directory ships all runtime scripts", () => { + for (const f of REQUIRED_VENDOR_FILES) { + const p = path.join(VENDOR_DIR, f); + assert.ok(fs.existsSync(p), `missing vendored script: ${f}`); + assert.ok(fs.statSync(p).size > 0, `vendored script is empty: ${f}`); + } +}); + +test("renderMarkdown fallback logs when marked is missing", () => { + const md = fs.readFileSync(path.join(GUI_ROOT, "renderer", "js", "markdown.js"), "utf-8"); + assert.match( + md, + /if \(!window\.marked\) \{[\s\S]*console\.warn\("markdown vendor missing \(marked not loaded\)"\)/, + "silent markdown degradation must be diagnostic (console.warn when marked is missing)" + ); +});