100 lines
4.5 KiB
Markdown
100 lines
4.5 KiB
Markdown
# Web Hermes Analysis + Notifications (2026-05)
|
|
|
|
Session learning from extending BOSS's local `xianyu-hunter` fork with Web-visible analysis and notification controls.
|
|
|
|
## Product shape BOSS expects
|
|
|
|
When adding analysis/notification capability, make it visible in the Web dashboard, not only as backend APIs:
|
|
|
|
- Header button: **通知设置**.
|
|
- Task controls: **执行分析** beside edit/duplicate/start/rerun/delete.
|
|
- New task tab: **Hermes 分析** with recommendation/caution/reject summary and item cards.
|
|
- Product list should show analysis verdict badges and concise reasons inline.
|
|
- Analysis should persist into `data/tasks.json` and generate a Markdown report under `reports/`.
|
|
|
|
## Backend API pattern
|
|
|
|
Add a lightweight local analysis layer and notifier:
|
|
|
|
- `lib/analyzer.mjs`
|
|
- `buildAnalysis(task, { limit })` returns `{ items, summary }`.
|
|
- `renderAnalysisMarkdown(task, summary)` writes `reports/<task_id>-analysis.md`.
|
|
- Include item-level `hermesAnalysis: { score, verdict, reason, flags, positives, analyzedAt }`.
|
|
- `lib/notifier.mjs`
|
|
- `GET /api/notify-config` and `PUT /api/notify-config` backed by `data/notify-config.json`.
|
|
- `sendNotifications(task, summary, { force })` supports:
|
|
- `hermes-webhook`: POST `{ message, target, source, taskId }`.
|
|
- `bark`: POST `{ title, body, group }` to `https://api.day.app/<key>` or configured endpoint.
|
|
- `pushplus`: POST `{ token, title, content, template:'txt' }`.
|
|
- `webhook`: generic JSON `{ title, message, taskId }`.
|
|
- `server.mjs`
|
|
- `POST /api/tasks/:id/analyze` should call `taskManager.analyzeTask`, write report, persist summary, and optionally notify.
|
|
|
|
## TaskManager persistence details
|
|
|
|
- Add `getTaskFull(id)` for routes that need full in-memory task data.
|
|
- Add `setTaskAnalysis(id, analysis)` that merges item `hermesAnalysis` into `products.fineFiltered`, stores `task.hermesAnalysis`, persists to disk, and emits `task:updated`.
|
|
- Include `hermesAnalysis` in `_serialize`, `_serializeFull`, and `_loadFromDisk`.
|
|
- Clear `task.hermesAnalysis = null` when resetting products/rerunning a task.
|
|
- New/duplicated tasks should initialize `hermesAnalysis: null`.
|
|
|
|
## Frontend implementation details
|
|
|
|
- Bump asset query versions in `index.html` after changing CSS/JS to avoid stale browser cache (`style.css?v=11`, `app.js?v=11`, etc.).
|
|
- `public/app.js` helpers:
|
|
- `analyzeTaskNow(id)` calls `POST /api/tasks/:id/analyze` with `{ notify: true }`, then refreshes task detail.
|
|
- `renderAnalysis()` renders empty state, stat cards, report path, and scored item cards.
|
|
- When rendering raw product rows, map analysis from `products.fineFiltered` by `id`; raw items often do not contain the merged `hermesAnalysis` directly.
|
|
- Preserve notification channel `id` when saving from the form; otherwise repeated saves create new channel IDs.
|
|
|
|
## Verification checklist
|
|
|
|
Run syntax checks:
|
|
|
|
```bash
|
|
cd /Users/chick/.Hermes/workspace/research/xianyu-hunter
|
|
node --check server.mjs
|
|
node --check lib/task.mjs
|
|
node --check lib/analyzer.mjs
|
|
node --check lib/notifier.mjs
|
|
node --check public/app.js
|
|
```
|
|
|
|
Restart exact stale listener if `xianyu_ops.mjs stop` says `no-recorded-server` but port is still occupied:
|
|
|
|
```bash
|
|
pid=$(lsof -nP -iTCP:3000 -sTCP:LISTEN -t || true)
|
|
[ -z "$pid" ] || kill "$pid"
|
|
node tools/xianyu_ops.mjs start --port 3000
|
|
```
|
|
|
|
Smoke test APIs:
|
|
|
|
```bash
|
|
curl -fsS http://127.0.0.1:3000/api/notify-config | python3 -m json.tool
|
|
curl -fsS http://127.0.0.1:3000/api/tasks -o /tmp/xh_tasks.json
|
|
TASK=$(python3 - <<'PY'
|
|
import json
|
|
j=json.load(open('/tmp/xh_tasks.json'))
|
|
print(j[0]['id'] if j else '')
|
|
PY
|
|
)
|
|
curl -fsS -X POST "http://127.0.0.1:3000/api/tasks/$TASK/analyze" \
|
|
-H 'Content-Type: application/json' \
|
|
-d '{"notify":false,"limit":10}' | python3 -m json.tool
|
|
```
|
|
|
|
Important pitfall: piping a large `/api/tasks` response directly into `python3 - <<'PY'` consumes stdin for the script, not JSON. Save curl output to a temp file first.
|
|
|
|
## Model-specific scoring pitfall: E3-1245v3
|
|
|
|
For CPU searches like `E3-1245v3`, scoring must distinguish true target models from related models used as SEO keywords.
|
|
|
|
Rules added in `lib/analyzer.mjs`:
|
|
|
|
- If the task query indicates `E3-1245v3`, require an exact-ish match: `e3\s*-?\s*1245\s*[_-]?\s*v3`.
|
|
- Penalize titles whose main model is another E3 v3, e.g. `E3-1275v3 ... 性能超 E3-1245v3`, because the title may only use 1245v3 as a comparison/keyword.
|
|
- Flag failure terms beyond obvious `坏/不亮/暗病`, including `不能进系统`.
|
|
|
|
This reduced false recommendations in the E3-1245v3 task from related CPUs and broken parts.
|