Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Agent.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -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)
Expand DownExpand Up@@ -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 路径不受影响)

Expand Down
4 changes: 2 additions & 2 deletions README.cn.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -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`)。
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -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`).
Expand Down
1 change: 1 addition & 0 deletions emrg/gui/package.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -34,6 +34,7 @@
"main.js",
"preload.js",
"renderer/**",
"vendor/**",
"daemon_client.js",
"package.json",
"scripts/**"
Expand Down
5 changes: 4 additions & 1 deletion emrg/gui/renderer/js/markdown.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -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 });
Expand Down
49 changes: 49 additions & 0 deletions emrg/gui/test/build-config.test.js
Original file line numberDiff line numberDiff line change
@@ -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)"
);
});
Loading