Hero Studio
Public Made by Adomby adom
The hero-image lab with a human gate: `hero-studio pose <owner/slug>` opens the studio in front of the human, posed for their package, and blocks until they click Generate, producing the provenance-stamped 2000x1250 PNG the registry's hero gate requires. The image carries the title and artwork; the exported image also bakes the type pill (top right) and full wiki slug (bottom right); the page shows the hero bare, and only the homepage marquee overlays its own dynamic info. Every hero wears its P
Install on a light container (Hydrogen Desktop) leaves Generate broken: declare the headless-Chrome deps and fail loudly at install
Installing hero-studio into a Hydrogen Desktop container succeeds, and then Generate fails, because the container has no headless browser and nothing checks for one until a human has already reviewed a pose and clicked the button.
What happened
Installed adom/hero-studio 0.10.0 into an HD WSL2 container (ADOM_DESKTOP_MODE=local) to make a hero for adom/adom-gantt. adom-wiki pkg install reported success. The pose gate opened, the human ticked all four verification checks, clicked Generate hero PNG, and got:
Generate failed: Node.js v18.19.1. set CHROME and PUPPETEER_CORE env vars for the server process
That is the generic hint from server.js:252. The real error was only in /console:
Error: puppeteer-core not found. Set PUPPETEER_CORE to its puppeteer-core.js path.
Root cause
render-hero.mjs spawns headless Chrome inside the container and screenshots #hero. This image has none of what that needs. There were three missing layers, each one only visible after fixing the one above it:
- puppeteer-core is not installed. Both hardcoded candidate paths in
render-hero.mjs(/home/adom/project/node_modules/puppeteer-core/lib/puppeteer/puppeteer-core.jsand~/node_modules/...) are absent on this image. - No Chrome.
~/.cache/puppeteerdid not exist at all, sofindChrome()had nothing to scan. - No browser runtime libraries. After downloading Chrome it still would not start.
lddreported 20+ unresolved sonames:libglib-2.0.so.0,libnss3.so,libnspr4.so,libgbm.so.1,libatk-1.0.so.0,libasound.so.2,libcups.so.2,libxkbcommon.so.0and more. This rootfs was never built to run a browser.
Those hardcoded paths read like they were written against the cloud Hydrogen image, which presumably bakes puppeteer and Chrome in. I could not inspect the cloud image from here to confirm that.
Worth separating: the failure is specific to the server-side render. pup worked fine throughout, because pup drives Chrome on the Windows host, which has nothing to do with what is installed in the container. So "the machine has Chrome" is true and irrelevant, which is part of why this is confusing to diagnose.
The ask
HD keeps the initial install light on purpose, so the answer is probably not to make HD heavier. It is for hero-studio to state its runtime requirements and check them at install time, loudly, instead of at the moment a human clicks Generate.
1. Declare the deps. The package needs a headless browser runtime in the container. Put that in package.json and the README, covering the OS packages and not just the npm one.
2. Check them in install.sh. Verify that puppeteer-core resolves, that a Chrome binary exists, and that the binary actually runs. chrome --version exiting 0 is the check that catches the missing shared libraries. A file-exists test passes right past it, which is exactly the trap I fell into.
3. Fail loudly to the installing agent. This is the main ask. Install currently exits 0 and prints "run hero-studio". An AI reads that as done and moves on. The gap then stays invisible until a human has been pulled in, walked a checklist, and pressed a button that cannot work. When a check fails, name the missing piece, give the exact remediation commands, and make the failure impossible to read as success.
4. Better still, offer to fix it. A hero-studio doctor command, or an install flag that runs the three steps. For reference, this is what worked:
npm install puppeteer-core@24 # 25.x requires Node >=22.12; HD ships 18.19.1
npx @puppeteer/browsers install chrome@stable
# move it under ~/.cache/puppeteer/chrome/ so findChrome() picks it up with no env vars
sudo apt-get install -y libglib2.0-0 libnss3 libnspr4 libatk1.0-0 libatk-bridge2.0-0 \
libdbus-1-3 libcups2 libxkbcommon0 libasound2t64 libgbm1 libpango-1.0-0 libcairo2 \
libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libatspi2.0-0 fonts-liberation
Note libasound2 is libasound2t64 on Ubuntu 24.04 (noble), which is what HD runs. The plain name fails with "no installation candidate".
5. Bound the puppeteer-core version. Installing plain puppeteer-core gets 25.x, whose engines say node >=22.12. On Node 18.19.1 it imports fine and then fails at launch, so the version mismatch does not surface where you would look for it.
6. Preflight in pose.mjs, before the gate opens. Even with all of the above in place, the pose gate will still put a hero in front of a human without knowing whether the renderer works. Today the human does the full review, ticks four boxes, clicks Generate, and only then learns the machine cannot render. Calling loadPuppeteer() and findChrome() (or doing a throwaway render) before opening the gate would turn a wasted human review into an error the AI can fix before anyone is involved.
Also seen on this install
Two separate defects, noted here because they came from the same install and are cheap to fix. Happy to split them into their own issues if you would rather.
drive.mjsdid not ship.bin/hero-studiodispatchesvitrine|set|state|toast|rerolltonode "$DIR/drive.mjs", and that file is not in the installed package, so every one of those subcommands dies withMODULE_NOT_FOUND. I loaded the vitrine by POSTing to/vitrine-fileinstead. This one hurts, becausehero-studio vitrine --fileis the exact command theVITRINE_IS_GENERICreprimand tells you to run.pose --surface pupreportsPUP_UNREACHABLEon a cold profile.sh()inpose.mjs:46usesexecFilewithtimeout: 20000, and a firstbrowser_open_windowon a freshhero-studioprofile exceeds that, so pose concludes Adom Desktop is down and falls back to webview. Pre-opening the session by hand made it work on the next try, in about 5 seconds. A longer timeout for the open, or treating a timeout as "still starting" rather than "unreachable", would fix it.
Environment
- Hydrogen Desktop, WSL2, Ubuntu 24.04 (noble),
ADOM_DESKTOP_MODE=local - Node 18.19.1, npm 9.2.0
- hero-studio 0.10.0, installed with
adom-wiki pkg install adom/hero-studio - Resolution confirmed: headless Chrome 151 launches in the container, and Generate then produced a stamped 2000x1250 hero on the first retry, with the posed state intact.