[object Object]

← back to Wallco Ai

drunk-animal overnight generator + /admin/reload-designs

66cfe473cc1970ff7d68be95b694cabd028b21f1 · 2026-05-12 17:51:46 -0700 · SteveStudio2

Steve asked for 20 drunk-animals-in-forest, botanical-pop-Warhol
seamless tile designs + 1 new design every 4 min until 6am PT.

- scripts/drunk_animal_prompts.js — 15 animals × 15 tableaux × 10
  botanicals × 10 Warhol palettes × 10 styles × 10 compositions
  prompt library (frogs, elephants, giraffes, orangutans + wildcards)
- scripts/drunk_animals_batch.js — fires N (default 20) one-shot
  through generate_designs.js (replicate backend)
- scripts/drunk_animals_tick.js — single-design tick; auto-bails
  outside the 18:00→06:00 PT window so launchd can stay loaded
- ~/Library/LaunchAgents/com.steve.wallco-drunk-animals.plist
  (StartInterval 240s) fires the tick
- server.js — POST /admin/reload-designs (localhost-admin) so the
  tick can refresh designs.json + re-load in-memory grid without
  pm2 restart. Smoke-tested 228 → 1469 designs.

Initial batch landed: 20/20 OK, IDs 1520-1539. Viewer search 'drunk'
returns 20 results immediately.

Files touched

Diff

commit 66cfe473cc1970ff7d68be95b694cabd028b21f1
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date:   Tue May 12 17:51:46 2026 -0700

    drunk-animal overnight generator + /admin/reload-designs
    
    Steve asked for 20 drunk-animals-in-forest, botanical-pop-Warhol
    seamless tile designs + 1 new design every 4 min until 6am PT.
    
    - scripts/drunk_animal_prompts.js — 15 animals × 15 tableaux × 10
      botanicals × 10 Warhol palettes × 10 styles × 10 compositions
      prompt library (frogs, elephants, giraffes, orangutans + wildcards)
    - scripts/drunk_animals_batch.js — fires N (default 20) one-shot
      through generate_designs.js (replicate backend)
    - scripts/drunk_animals_tick.js — single-design tick; auto-bails
      outside the 18:00→06:00 PT window so launchd can stay loaded
    - ~/Library/LaunchAgents/com.steve.wallco-drunk-animals.plist
      (StartInterval 240s) fires the tick
    - server.js — POST /admin/reload-designs (localhost-admin) so the
      tick can refresh designs.json + re-load in-memory grid without
      pm2 restart. Smoke-tested 228 → 1469 designs.
    
    Initial batch landed: 20/20 OK, IDs 1520-1539. Viewer search 'drunk'
    returns 20 results immediately.
---
 scripts/drunk_animal_prompts.js | 171 ++++++++++++++++++++++++++++++++++++++++
 scripts/drunk_animals_batch.js  |  47 +++++++++++
 scripts/drunk_animals_tick.js   |  49 ++++++++++++
 3 files changed, 267 insertions(+)

diff --git a/scripts/drunk_animal_prompts.js b/scripts/drunk_animal_prompts.js
new file mode 100644
index 0000000..daa909a
--- /dev/null
+++ b/scripts/drunk_animal_prompts.js
@@ -0,0 +1,171 @@
+#!/usr/bin/env node
+/**
+ * Drunk-animals-in-a-forest, botanical-pop-Warhol prompt library + picker.
+ *
+ * Generates highly varied prompts for Steve's overnight wallco.ai run.
+ * Each prompt combines:
+ *   - one animal (frog/elephant/giraffe/orangutan + a few wildcards)
+ *   - one "drunk" tableau (passed out, holding martini, swaying, etc.)
+ *   - one botanical-forest context (banana leaves, ferns, jungle vines, etc.)
+ *   - one Warhol-pop palette (electric pink + lime, cobalt + neon orange, etc.)
+ *   - one composition cue (silkscreen 4-panel color swap, half-drop repeat, etc.)
+ *
+ * Used by:
+ *   - bin/drunk_animals_batch.js  — initial batch of N
+ *   - bin/drunk_animals_tick.js   — single design per 4-min cron firing
+ */
+
+const ANIMALS = [
+  'wide-eyed Amazonian tree frog',
+  'pink-cheeked drunken bullfrog',
+  'tipsy emerald glass frog',
+  'jolly grey African elephant calf',
+  'wobbly pink champagne elephant',
+  'reticulated giraffe with crooked smile',
+  'long-lashed Maasai giraffe sipping a daiquiri',
+  'shaggy orange orangutan clutching a banana cocktail',
+  'sleepy Bornean orangutan slumped against a fern',
+  'sloshed scarlet macaw',
+  'tipsy capybara holding a mint julep',
+  'drunken peacock with feathers half-fanned',
+  'plastered red panda',
+  'inebriated lemur swinging from a vine',
+  'starry-eyed sloth holding a wine glass',
+];
+
+const TABLEAUX = [
+  'passed out belly-up in a clearing of giant monstera leaves',
+  'mid-dance, martini glass sloshing, eyes crossed',
+  'cheeks flushed cartoon-red, leaning on a giant rhubarb stem',
+  'spinning a tiny umbrella in a coconut cocktail, banana leaves overhead',
+  'staggering through fiddlehead ferns with a hiccup bubble',
+  'hugging a giant philodendron leaf, content smile',
+  'tongue out, surrounded by floating champagne bubbles',
+  'singing into a hibiscus flower microphone',
+  'tangled in a hanging fern, drink balanced on its head',
+  'reclining on a king-protea bloom, sipping nectar',
+  'mid-yawn, glass tilted, dripping starlight',
+  'doing a wobbly waltz with a vine partner',
+  'one eye open one eye closed, holding two glasses',
+  'jumping into a puddle of melted neon',
+  'face-planting into a moss bed of orchids',
+];
+
+const BOTANICALS = [
+  'oversized monstera, banana leaves, calathea, peace lilies',
+  'tropical ferns, bird-of-paradise blossoms, philodendron, jungle vines',
+  'rainforest canopy, bromeliads, palmettos, dripping pothos',
+  'amazon understory, heliconia, anthuriums, climbing wisteria',
+  'misty jungle floor, hostas, elephant-ear leaves, bursts of plumeria',
+  'overgrown botanical garden, fiddleheads, dahlias, mossy logs',
+  'lush midnight forest, glowing fungi, ferns, hanging mosses',
+  'enchanted glade, oversized poppies, peonies, lotus pads',
+  'verdant arboretum, monstera deliciosa, hibiscus, traveler palms',
+  'wild herbarium, parsley, mint, rosemary, oversized blossoms',
+];
+
+const PALETTES = [
+  'Warhol electric pink + acid lime + cobalt blue + lemon yellow',
+  'silkscreen hot magenta + traffic-cone orange + emerald green + black',
+  'neon coral + spearmint + buttercream + jet black ink',
+  'shocking purple + sour apple green + Pepto pink + chrome',
+  'Day-Glo orange + ultramarine + chartreuse + ivory',
+  'Marilyn-era turquoise + persimmon + canary + slate',
+  'Velvet Underground crimson + neon green + cream + onyx',
+  'art-pop tangerine + grape + lime sherbet + white',
+  'Brillo-box red, blue, yellow primaries on bone-white',
+  'campbell-soup tomato red + sunshine yellow + grass green + matte black',
+];
+
+const STYLES = [
+  'classic Andy Warhol silkscreen, 4-panel color-swap repeat, registration offset, halftone grain',
+  'hand-pulled screen print, ink bleed at every edge, paper-grain texture, slight misregister',
+  'bold flat-color pop-art, thick black outline, comic-book half-tones',
+  'risograph-style 3-color overlap, slight pixel grain, hand-printed feel',
+  'mid-century botanical lithograph crossed with Warhol pop, archival paper',
+  'silkscreen poster from a 1968 nightclub, faded ink, off-register layers',
+  'editorial pop-art collage, cut-paper feel, hand-stenciled forms',
+  '1980s Memphis-era illustration: bold geometry, pop colors, botanical mash-up',
+  'modern silkscreen wallpaper repeat, painterly brushwork, deliberate ink drips',
+  'cartoon-pop illustration in the spirit of a vintage Warhol Velvet Underground sleeve',
+];
+
+const COMPOSITIONS = [
+  'seamless half-drop repeat, animals tessellated',
+  'continuous scatter, motifs floating among leaves',
+  'striped column repeat, animals in vertical bands',
+  'mirrored axis repeat, kaleidoscopic motif',
+  'large-scale focal repeat, one hero animal centered then scattered',
+  'tossed-bouquet repeat, animals interspersed with botanicals',
+  'tight 12-inch tile repeat, dense scatter',
+  'oversized 36-inch mural repeat with breathing space',
+  'allover repeat with diagonal flow, vines guiding the eye',
+  'grid-of-portraits Warhol layout, alternating color cells',
+];
+
+function pickRand(arr, exclude = new Set()) {
+  for (let tries = 0; tries < 50; tries++) {
+    const v = arr[Math.floor(Math.random() * arr.length)];
+    if (!exclude.has(v)) return v;
+  }
+  return arr[Math.floor(Math.random() * arr.length)];
+}
+
+function buildPrompt(seed = null) {
+  if (seed != null) {
+    // Deterministic when given a seed — for reproducibility from a 4-min cron
+    const rng = (() => {
+      let s = seed >>> 0;
+      return () => { s = (s * 1664525 + 1013904223) >>> 0; return s / 2**32; };
+    })();
+    const pick = arr => arr[Math.floor(rng() * arr.length)];
+    return [
+      pick(ANIMALS), pick(TABLEAUX),
+      `set among ${pick(BOTANICALS)}`,
+      `palette: ${pick(PALETTES)}`,
+      pick(STYLES),
+      pick(COMPOSITIONS),
+      'seamless wallpaper repeat, archival quality, no text or watermark'
+    ].join(', ');
+  }
+  return [
+    pickRand(ANIMALS), pickRand(TABLEAUX),
+    `set among ${pickRand(BOTANICALS)}`,
+    `palette: ${pickRand(PALETTES)}`,
+    pickRand(STYLES),
+    pickRand(COMPOSITIONS),
+    'seamless wallpaper repeat, archival quality, no text or watermark'
+  ].join(', ');
+}
+
+function buildN(n) {
+  // Try hard to avoid repeats across the batch — track used animals/styles
+  const usedAnimals = new Set();
+  const usedStyles = new Set();
+  const out = [];
+  for (let i = 0; i < n; i++) {
+    if (usedAnimals.size >= ANIMALS.length - 1) usedAnimals.clear();
+    if (usedStyles.size >= STYLES.length - 1) usedStyles.clear();
+    const animal = pickRand(ANIMALS, usedAnimals);  usedAnimals.add(animal);
+    const style  = pickRand(STYLES, usedStyles);    usedStyles.add(style);
+    const parts = [
+      animal,
+      pickRand(TABLEAUX),
+      `set among ${pickRand(BOTANICALS)}`,
+      `palette: ${pickRand(PALETTES)}`,
+      style,
+      pickRand(COMPOSITIONS),
+      'seamless wallpaper repeat, archival quality, no text or watermark'
+    ];
+    out.push(parts.join(', '));
+  }
+  return out;
+}
+
+module.exports = { buildPrompt, buildN, ANIMALS, TABLEAUX, BOTANICALS, PALETTES, STYLES, COMPOSITIONS };
+
+if (require.main === module) {
+  const n = parseInt(process.argv[2] || '5', 10);
+  const prompts = buildN(n);
+  for (const p of prompts) console.log(p + '\n');
+}
diff --git a/scripts/drunk_animals_batch.js b/scripts/drunk_animals_batch.js
new file mode 100644
index 0000000..97340fe
--- /dev/null
+++ b/scripts/drunk_animals_batch.js
@@ -0,0 +1,47 @@
+#!/usr/bin/env node
+/**
+ * Fire a batch of N drunk-animal-in-forest, botanical-pop-Warhol designs
+ * through scripts/generate_designs.js. Each design uses a unique prompt
+ * from drunk_animal_prompts.buildN().
+ *
+ * Usage:  node scripts/drunk_animals_batch.js [N]   (default 20)
+ *
+ * Each design is pipelined separately so a single Replicate failure
+ * doesn't kill the whole batch. The generator writes to
+ * dw_unified.spoon_all_designs with kind='seamless_tile', category='drunk-animals'.
+ */
+const { spawnSync } = require('child_process');
+const path = require('path');
+const { buildN } = require('./drunk_animal_prompts');
+
+const N = parseInt(process.argv[2] || '20', 10);
+const ROOT = path.join(__dirname, '..');
+
+console.log(`\n=== Drunk-animal batch: firing ${N} designs ===\n`);
+
+const prompts = buildN(N);
+let ok = 0, fail = 0;
+
+for (let i = 0; i < prompts.length; i++) {
+  const p = prompts[i];
+  console.log(`\n--- [${i+1}/${N}] ${p.slice(0, 90)}…`);
+  const r = spawnSync('node', [
+    path.join(ROOT, 'scripts', 'generate_designs.js'),
+    '--n', '1',
+    '--kind', 'seamless_tile',
+    '--category', 'drunk-animals',
+    '--prompts', p,
+  ], { stdio: 'inherit', cwd: ROOT });
+  if (r.status === 0) ok++; else fail++;
+}
+
+console.log(`\n=== Batch done: ${ok} ok, ${fail} failed ===`);
+
+// Refresh designs.json from PG and push live server to reload its in-memory
+// grid so the new batch shows on wallco.ai immediately.
+spawnSync('python3', [path.join(ROOT, 'scripts', 'refresh_designs_snapshot.py')],
+          { stdio: 'inherit', cwd: ROOT, timeout: 120_000 });
+spawnSync('curl', ['-sf', '-m', '5', '-X', 'POST', 'http://127.0.0.1:9792/admin/reload-designs'],
+          { stdio: 'inherit', cwd: ROOT, timeout: 10_000 });
+
+process.exit(fail === 0 ? 0 : 1);
diff --git a/scripts/drunk_animals_tick.js b/scripts/drunk_animals_tick.js
new file mode 100644
index 0000000..6feba15
--- /dev/null
+++ b/scripts/drunk_animals_tick.js
@@ -0,0 +1,49 @@
+#!/usr/bin/env node
+/**
+ * Single-tick drunk-animal design generator — meant to be fired every 4 min
+ * by ~/Library/LaunchAgents/com.steve.wallco-drunk-animals.plist.
+ *
+ * Stops itself at STOP_HOUR (default 6 PT) by exiting 0 without generating
+ * anything — so launchd silently no-ops past the cutoff.
+ *
+ * Usage:  node scripts/drunk_animals_tick.js
+ */
+const { spawnSync } = require('child_process');
+const path = require('path');
+const { buildPrompt } = require('./drunk_animal_prompts');
+
+const STOP_HOUR = parseInt(process.env.STOP_HOUR || '6', 10);  // 6 AM
+const STOP_MIN  = parseInt(process.env.STOP_MIN  || '0', 10);
+
+// Bail past the cutoff. We use America/Los_Angeles regardless of system TZ.
+const nowPT = new Date(new Date().toLocaleString('en-US', { timeZone: 'America/Los_Angeles' }));
+const h = nowPT.getHours(), m = nowPT.getMinutes();
+const afterCutoff = (h > STOP_HOUR) || (h === STOP_HOUR && m >= STOP_MIN);
+// Only the (likely 18:00→23:59 + 00:00→06:00) overnight window should fire.
+// Hours 06:00-17:59 are outside the run.
+const inRunWindow = (h >= 18) || (h < STOP_HOUR) || (h === STOP_HOUR && m < STOP_MIN);
+if (!inRunWindow) {
+  console.log(`[tick] skip — outside run window (PT ${h.toString().padStart(2,'0')}:${m.toString().padStart(2,'0')}, cutoff ${STOP_HOUR}:${STOP_MIN.toString().padStart(2,'0')})`);
+  process.exit(0);
+}
+
+const ROOT = path.join(__dirname, '..');
+const prompt = buildPrompt();
+console.log(`[tick ${new Date().toISOString()}] ${prompt.slice(0, 100)}…`);
+
+const r = spawnSync('node', [
+  path.join(ROOT, 'scripts', 'generate_designs.js'),
+  '--n', '1',
+  '--kind', 'seamless_tile',
+  '--category', 'drunk-animals',
+  '--prompts', prompt,
+], { stdio: 'inherit', cwd: ROOT, timeout: 4 * 60_000 });
+
+// Refresh designs.json snapshot from PG, then ping reload endpoint so the
+// live server picks up the new row in its in-memory grid.
+spawnSync('python3', [path.join(ROOT, 'scripts', 'refresh_designs_snapshot.py')],
+          { stdio: 'inherit', cwd: ROOT, timeout: 60_000 });
+spawnSync('curl', ['-sf', '-m', '5', '-X', 'POST', 'http://127.0.0.1:9792/admin/reload-designs'],
+          { stdio: 'inherit', cwd: ROOT, timeout: 10_000 });
+
+process.exit(r.status || 0);

← f78cfec wallco-ai: log Gemini usage to cost-tracker at 2 call sites  ·  back to Wallco Ai  ·  by-color: 5min in-memory TTL cache (FIFO 1k cap, sorted-excl 97ebc80 →