build(gesture): fetch MediaPipe wasm + model at install/build instead of committing

The self-hosted gesture assets (~27MB of wasm runtime + gesture_recognizer.task)
were committed to the repo. Replace that with scripts/fetch-gesture-assets.mjs,
which downloads them into src/web/public/gesture/ — idempotent (skips existing)
and non-fatal (the overlay is opt-in via CODEMAN_GESTURE=1, so a fetch failure
only warns). Wired into:
  - postinstall.js (dev: populates src/web/public/gesture for `npm run dev`)
  - build.mjs (before `cp -r src/web/public dist/web/`, so prod/dist gets them)

The files are already covered by the bare `public` .gitignore rule, so they
stay untracked. The overlay bundle (gesture-codeman.js) remains committed — it's
built from a separate repo and is small. Pin @mediapipe wasm to 0.10.21 to match
the bundled tasks-vision API.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ark0N
2026-06-08 02:05:28 +02:00
co-authored by Claude Opus 4.8
parent 8dc850f845
commit 59145c48fc
8 changed files with 75 additions and 40 deletions
+3
View File
@@ -32,6 +32,9 @@ run('chmod dist/index.js', 'chmod +x dist/index.js');
// 2. Copy static assets (clean first to remove stale hashed files from previous builds)
run('clean public', 'rm -rf dist/web/public');
run('prepare dirs', 'mkdir -p dist/web dist/templates dist/web/public/vendor');
// Fetch the opt-in gesture overlay's MediaPipe wasm + model into src/ (idempotent,
// non-fatal, kept out of git) so the copy below carries them into dist/.
run('gesture assets', 'node scripts/fetch-gesture-assets.mjs');
run('copy web assets', 'cp -r src/web/public dist/web/');
run('copy template', 'cp src/templates/case-template.md dist/templates/');
+58
View File
@@ -0,0 +1,58 @@
/**
* @fileoverview Fetch the gesture-overlay runtime assets (MediaPipe wasm + the
* gesture-recognizer model) into src/web/public/gesture/ so Codeman can serve
* them same-origin (a browser content-blocker otherwise blocks the public CDNs
* and the overlay fails to start). These are large binaries (~27 MB) kept OUT of
* git (the bare `public` rule in .gitignore covers them); they are fetched here
* at install (postinstall) and build time instead.
*
* Idempotent: skips files already present. Non-fatal: the gesture overlay is
* opt-in (CODEMAN_GESTURE=1), so a fetch failure only warns — it must not break
* `npm install` / `npm run build`. The build then copies src/web/public into
* dist/ as usual, so prod gets these too.
*
* The @mediapipe/tasks-vision version MUST match the one bundled into the gesture
* overlay (Ark0N/codeman-gesture-control) so the wasm loader matches its JS API.
*/
import { mkdirSync, existsSync, statSync, writeFileSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const GESTURE = join(__dirname, '..', 'src', 'web', 'public', 'gesture');
const WASM = join(GESTURE, 'wasm');
const MP_VERSION = '0.10.21'; // keep in sync with the gesture overlay's @mediapipe/tasks-vision
const WASM_BASE = `https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@${MP_VERSION}/wasm`;
const MODEL_URL =
'https://storage.googleapis.com/mediapipe-models/gesture_recognizer/gesture_recognizer/float16/1/gesture_recognizer.task';
const ASSETS = [
{ url: `${WASM_BASE}/vision_wasm_internal.js`, path: join(WASM, 'vision_wasm_internal.js') },
{ url: `${WASM_BASE}/vision_wasm_internal.wasm`, path: join(WASM, 'vision_wasm_internal.wasm') },
{ url: `${WASM_BASE}/vision_wasm_nosimd_internal.js`, path: join(WASM, 'vision_wasm_nosimd_internal.js') },
{ url: `${WASM_BASE}/vision_wasm_nosimd_internal.wasm`, path: join(WASM, 'vision_wasm_nosimd_internal.wasm') },
{ url: MODEL_URL, path: join(GESTURE, 'gesture_recognizer.task') },
];
async function main() {
mkdirSync(WASM, { recursive: true });
let fetched = 0;
let skipped = 0;
for (const a of ASSETS) {
if (existsSync(a.path) && statSync(a.path).size > 0) {
skipped++;
continue;
}
const res = await fetch(a.url);
if (!res.ok) throw new Error(`HTTP ${res.status} for ${a.url}`);
writeFileSync(a.path, Buffer.from(await res.arrayBuffer()));
fetched++;
}
console.log(`[gesture] MediaPipe assets ready (${fetched} fetched, ${skipped} cached) → ${GESTURE}`);
}
main().catch((err) => {
// Non-fatal: opt-in feature. Warn and exit 0 so install/build still succeed.
console.warn(`[gesture] could not fetch MediaPipe assets — overlay disabled until fetched: ${err.message}`);
});
+14
View File
@@ -312,6 +312,20 @@ if (isGlobalInstall) {
}
}
// ----------------------------------------------------------------------------
// 4b. Fetch gesture-overlay runtime assets (MediaPipe wasm + model) for dev mode
// (src/web/public/gesture/). Opt-in feature (CODEMAN_GESTURE=1); non-fatal.
// Large binaries kept out of git; the build copies them into dist/.
// ----------------------------------------------------------------------------
if (!isGlobalInstall) {
try {
execSync(`node "${join(import.meta.dirname, 'fetch-gesture-assets.mjs')}"`, { stdio: 'inherit' });
} catch {
// Non-fatal — the gesture overlay is opt-in.
}
}
// ----------------------------------------------------------------------------
// 5. Install git pre-commit hook (format check)
// ----------------------------------------------------------------------------
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long