mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-18 04:41:56 +00:00
fix(web): cross-platform sync-assets + surface build errors on failure
Three Windows-only bugs in the web-dashboard build path. Each is small, scoped, and verified end-to-end on Windows 11 — including under a stock cmd.exe / PowerShell console with its default cp1252 encoding. 1. `sync-assets` shells out to Unix-only commands web/package.json hard-codes `rm -rf … && cp -r …`. Neither exists on Windows cmd.exe. `hermes_cli/main.py::_build_web_ui` runs npm via subprocess (which on Windows defaults to cmd.exe), so the prebuild hook crashed before Vite ever ran and the dashboard never built. Fix: web/scripts/sync-assets.mjs — ~20 lines of Node using fs.rmSync + fs.cpSync (stdlib, Node >= 16.7). No new deps, identical behavior on POSIX and Windows. 2. Build failures were silent _build_web_ui ran both subprocess calls with capture_output=True and never relayed the captured buffers on failure. Users saw 'Web UI build failed' and nothing else — no stdout, no stderr, no hint that the real problem was 'rm is not recognized'. Fix: inner _relay() helper that decodes and prints stdout + stderr (utf-8, errors='replace') whenever a step returns non-zero. Replaces the existing stderr_tail-only relay on the build path; success path is unchanged. (stderr_tail is preserved for the stale-dist fallback branch added by #23817.) Salvaged from #13368 by @johnisag onto current main. Conflict resolution preserves main's improvements: - _run_npm_install_deterministic() (replaces bare subprocess.run for npm install) - npm-build retry-after-sleep for Windows boot-time races (#23817) - stale-dist fallback for non-interactive callers (#23817) Closes #25073, #13368.
This commit is contained in:
parent
19071529f6
commit
0854640537
3 changed files with 46 additions and 3 deletions
|
|
@ -5688,12 +5688,29 @@ def _build_web_ui(web_dir: Path, *, fatal: bool = False) -> bool:
|
||||||
print("Install Node.js, then run: cd web && npm install && npm run build")
|
print("Install Node.js, then run: cd web && npm install && npm run build")
|
||||||
return not fatal
|
return not fatal
|
||||||
print("→ Building web UI...")
|
print("→ Building web UI...")
|
||||||
|
|
||||||
|
def _relay(result: "subprocess.CompletedProcess") -> None:
|
||||||
|
"""Print captured npm output so users can see *why* a step failed.
|
||||||
|
|
||||||
|
Windows users hitting `rm -rf` / `cp -r` errors (or any other
|
||||||
|
sync-assets / Vite failure) would otherwise see only ``Web UI
|
||||||
|
build failed`` with no hint of the underlying cause, because
|
||||||
|
the npm calls run with ``capture_output=True``.
|
||||||
|
"""
|
||||||
|
for blob in (result.stdout, result.stderr):
|
||||||
|
if not blob:
|
||||||
|
continue
|
||||||
|
text = blob.decode("utf-8", errors="replace").rstrip() if isinstance(blob, bytes) else blob.rstrip()
|
||||||
|
if text:
|
||||||
|
print(text)
|
||||||
|
|
||||||
r1 = _run_npm_install_deterministic(npm, web_dir, extra_args=("--silent",))
|
r1 = _run_npm_install_deterministic(npm, web_dir, extra_args=("--silent",))
|
||||||
if r1.returncode != 0:
|
if r1.returncode != 0:
|
||||||
print(
|
print(
|
||||||
f" {'✗' if fatal else '⚠'} Web UI npm install failed"
|
f" {'✗' if fatal else '⚠'} Web UI npm install failed"
|
||||||
+ ("" if fatal else " (hermes web will not be available)")
|
+ ("" if fatal else " (hermes web will not be available)")
|
||||||
)
|
)
|
||||||
|
_relay(r1)
|
||||||
if fatal:
|
if fatal:
|
||||||
print(" Run manually: cd web && npm install && npm run build")
|
print(" Run manually: cd web && npm install && npm run build")
|
||||||
return False
|
return False
|
||||||
|
|
@ -5739,8 +5756,7 @@ def _build_web_ui(web_dir: Path, *, fatal: bool = False) -> bool:
|
||||||
f" {'✗' if fatal else '⚠'} Web UI build failed"
|
f" {'✗' if fatal else '⚠'} Web UI build failed"
|
||||||
+ ("" if fatal else " (hermes web will not be available)")
|
+ ("" if fatal else " (hermes web will not be available)")
|
||||||
)
|
)
|
||||||
if stderr_tail:
|
_relay(r2)
|
||||||
print(f" Build error:\n {stderr_tail}")
|
|
||||||
if fatal:
|
if fatal:
|
||||||
print(" Run manually: cd web && npm install && npm run build")
|
print(" Run manually: cd web && npm install && npm run build")
|
||||||
return False
|
return False
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"sync-assets": "node -e \"const fs=require('fs');fs.rmSync('public/fonts',{recursive:true,force:true});fs.rmSync('public/ds-assets',{recursive:true,force:true});fs.cpSync('node_modules/@nous-research/ui/dist/fonts','public/fonts',{recursive:true});fs.cpSync('node_modules/@nous-research/ui/dist/assets','public/ds-assets',{recursive:true});\"",
|
"sync-assets": "node scripts/sync-assets.mjs",
|
||||||
"predev": "npm run sync-assets",
|
"predev": "npm run sync-assets",
|
||||||
"prebuild": "npm run sync-assets",
|
"prebuild": "npm run sync-assets",
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
|
|
|
||||||
27
web/scripts/sync-assets.mjs
Normal file
27
web/scripts/sync-assets.mjs
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
// Cross-platform replacement for the previous shell pipeline:
|
||||||
|
//
|
||||||
|
// rm -rf public/fonts public/ds-assets
|
||||||
|
// && cp -r node_modules/@nous-research/ui/dist/fonts public/fonts
|
||||||
|
// && cp -r node_modules/@nous-research/ui/dist/assets public/ds-assets
|
||||||
|
//
|
||||||
|
// `rm -rf` / `cp -r` don't exist on Windows cmd.exe, so `npm run build`
|
||||||
|
// (invoked from Python via subprocess → cmd.exe) failed before Vite ran.
|
||||||
|
// Using Node's stdlib fs keeps this dependency-free and platform-neutral.
|
||||||
|
|
||||||
|
import { cpSync, rmSync } from "node:fs";
|
||||||
|
import { dirname, resolve } from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
|
||||||
|
const webRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
||||||
|
const uiDist = resolve(webRoot, "node_modules", "@nous-research", "ui", "dist");
|
||||||
|
|
||||||
|
const targets = [
|
||||||
|
{ from: resolve(uiDist, "fonts"), to: resolve(webRoot, "public", "fonts") },
|
||||||
|
{ from: resolve(uiDist, "assets"), to: resolve(webRoot, "public", "ds-assets") },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const { from, to } of targets) {
|
||||||
|
rmSync(to, { recursive: true, force: true });
|
||||||
|
cpSync(from, to, { recursive: true });
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue