From 998e8605c6df0fb4b179082a841a33d5aed3e7ba Mon Sep 17 00:00:00 2001 From: SirKentut <81878031+SirKentut@users.noreply.github.com> Date: Sun, 14 Jun 2026 02:35:19 -0700 Subject: [PATCH] [pierre] fix: self-heal half-installed vite warm cache An npm install killed mid-warm leaves node_modules/ populated but no .bin/, so _ensure_warm_cache (which only checked the dir exists) trusted the half-tree forever and every app symlinked to it died with `vite: command not found`. Gate the cache on .bin/vite existing, and harden the app's frontend/run.sh skip-install guard the same way. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../apps/outputs/view_builder_templates.py | 24 ++++++++++++++----- .../outputs/webapp_template/frontend/run.sh | 9 +++++-- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/backend/apps/outputs/view_builder_templates.py b/backend/apps/outputs/view_builder_templates.py index e5715b95..281f68ec 100644 --- a/backend/apps/outputs/view_builder_templates.py +++ b/backend/apps/outputs/view_builder_templates.py @@ -286,15 +286,27 @@ def _warm_cache_dir() -> str: return os.path.join(base, _warm_cache_digest()) +def _warm_cache_is_complete(cache_modules: str) -> bool: + """A populated node_modules/ dir is not proof of a *finished* install. + npm links package bins (node_modules/.bin/*) in the final phase, so an + install killed partway (e.g. Electron quit mid-warm) leaves the package + trees on disk but no .bin/. The old `os.path.isdir(node_modules)` check + then trusted that half-tree forever, every app symlinked to it, and + `npm run dev` died with `vite: command not found`. Require the one bin + every webapp-template app actually launches with so a partial cache is + treated as not-ready and repopulated instead of cached as good.""" + return os.path.exists(os.path.join(cache_modules, ".bin", "vite")) + + def _ensure_warm_cache() -> str | None: - """Populate the warm-cache node_modules if missing. Returns the - absolute path to the populated `node_modules` directory, or None on - failure. Thread-safe; concurrent callers block on a single install - instead of racing. Idempotent and fast after the first call.""" + """Populate the warm-cache node_modules if missing or incomplete. + Returns the absolute path to the populated `node_modules` directory, or + None on failure. Thread-safe; concurrent callers block on a single + install instead of racing. Idempotent and fast after the first call.""" cache_dir = _warm_cache_dir() cache_modules = os.path.join(cache_dir, "node_modules") - if os.path.isdir(cache_modules): + if _warm_cache_is_complete(cache_modules): return cache_modules # Prefer a pre-extracted bundled tree: junction the workspace straight at it, @@ -305,7 +317,7 @@ def _ensure_warm_cache() -> str | None: return bundled with _warm_cache_lock: - if os.path.isdir(cache_modules): + if _warm_cache_is_complete(cache_modules): return cache_modules # Fast path: pre-built archive shipped inside the release. The # build script generates this so users hitting OpenSwarm for the diff --git a/backend/apps/outputs/webapp_template/frontend/run.sh b/backend/apps/outputs/webapp_template/frontend/run.sh index 1ea9dfe8..8c4e611c 100755 --- a/backend/apps/outputs/webapp_template/frontend/run.sh +++ b/backend/apps/outputs/webapp_template/frontend/run.sh @@ -43,8 +43,13 @@ fi # to vite. Only run npm install when node_modules is genuinely missing # or empty — e.g. a workspace seeded before the warm-cache existed, or # the user's cache was cleared. -if [ -d node_modules ] && [ -n "$(ls -A node_modules 2>/dev/null)" ]; then - echo "Dependencies already present — skipping install." +# A non-empty node_modules is NOT proof of a finished install. +# non-empty -> skip" check then trusted that, and `npm run dev` died with +# `vite: command not found`. Gate on the bin we actually launch with so a +# broken/partial tree self-heals via install instead of being skipped. +#So thats why i explicitly have "/.bin/vite" +if [ -e node_modules/.bin/vite ]; then + echo "Dependencies already present - skipping install." else echo "Installing dependencies..." "$NPM" install --prefer-offline --no-audit --no-fund