← Commit history

Publish 2.0.336

John Lauer ·65f1f8dd11 ·1mo ago ·parent f82c68a
3 files changed +44−14
package.json+1−1
@@ -1,6 +1,6 @@ {   "slug": "pup-bridge",-  "version": "2.0.335",+  "version": "2.0.336",   "type": "app",   "description": "The pup SKILL PACK \u2014 the AI skills that teach a cloud/container AI how to drive the Adom Desktop Puppeteer (pup) bridge (browser_* verbs): the everyday `pup` skill plus focused sub-skills for windows/sessions/tabs, screenshots+recording, browsers+Chrome install, and driving the Adom wiki. Installs into ~/.claude/skills + ~/.codex/skills. DOCS ONLY \u2014 this is NOT the bridge runtime. The bridge itself (the Node program that drives the browser) runs on the DESKTOP: Adom Desktop bundles it and auto-installs/updates it, and it's published separately on the Releases tab as adom-bridge-puppeteer-v<ver>.zip. The pkg version is kept in lockstep with the Release version so the page's max version row always carries BOTH a pkg tarball (skills) and a runtime zip (bridge) \u2014 a skills-only or runtime-only bump on the top row breaks constraint installs. TL;DR: pkg install = the skills for your AI; the bridge = the Release zip, which Adom Desktop manages for you.",   "keywords": [
page.json+1−1
@@ -4,7 +4,7 @@   "slug": "pup-bridge",   "title": "Pup - Puppeteer Bridge",   "brief": "pup is the AI's own browser: a real, full Chrome on the user's desktop that the AI fully controls (a sandbox, not the user's signed-in browser). Rides Bridge; pup_* verbs open windows and tabs, navigate, screenshot, and eval JS.",-  "version": "2.0.335",+  "version": "2.0.336",   "tags": [     "skills",     "pup",
tools/pup-dashboard+42−12
@@ -89,6 +89,8 @@ async function sendToNewThread(phrase) {   // the crash family dead on the gate-fixed build (33ac2074, six raw-CDP typing helpers that ran   // outside the concurrency gate), ~20 min of the kill condition with zero process deaths.   if (!SAY.includes(phrase)) return { ok: false, why: 'unknown phrase' };+  const guard = await shellSupportsSend();+  if (!guard.ok) return { ok: false, why: guard.why };   // 45s, per the skill: "Opening drives the editor (extension command, dual detection, tab   // placement) and legitimately takes 10-30s on a busy workbench; every CDP touch also serializes   // through one gate ... A 5-10s client timeout manufactures phantom failures." The old code used@@ -105,16 +107,38 @@ async function sendToNewThread(phrase) {   return { ok: true, sid: j.sid || null, deliveredTo: j.delivered_to || null }; } -// NO VERSION GATE (removed 2026-08-24). The previous build_epoch gate was measured, not guessed,-// but it gated on the wrong thing and would have refused honest sends forever: Hydrogen's control-// API exposes NO release version anywhere the dashboard can read. Measured today on AdomLapper:-// GET /version returns an endpoint list; GET /health and GET /buildinfo return only build_sha /-// build_epoch / build_stamp / build_local; and the shell page carries no version global. So-// "gate on release version >= 1.0.142" is not implementable from the browser, and gating on-// build_epoch instead pins the feature to ONE machine's incremental build clock -- every user on a-// stock signed release (whose epoch is older) would be refused even though their shell is fine.-// The failure path already handles a shell that cannot serve the call: the request fails, the toast-// says why, and the phrase is copied. That is a capability probe, and it is honest on every build.+// RELEASE-VERSION GATE, on the ONE endpoint that actually carries a release version.+// Corrected twice, so the record matters:+//   build_sha     - LIES on incremental builds (the build thread had to verify by code marker).+//   build_epoch   - ACTIVELY HARMFUL as a gate: it pins the feature to one machine's build clock,+//                   so every user on a stock signed release (older epoch) is refused forever.+//   /version, /health, /buildinfo, page globals - carry NO release version (probed 2026-08-24).+//   GET /updater/status -> updater.current_version - DOES. Measured on AdomLapper: "1.0.142".+// The gate exists because a pre-1.0.142 shell still carries the crash family that took John's+// session out this morning, and the user on a stock old release is exactly the person least able+// to diagnose it. UNREADABLE FAILS OPEN: refusing on a field we cannot read would block legitimate+// users, and the send path already fails honestly (toast + copy) when the shell cannot serve it.+const SEND_MIN_RELEASE = [1, 0, 142];+function cmpver(a, b) {+  for (let i = 0; i < 3; i++) { const d = (a[i] || 0) - (b[i] || 0); if (d) return d < 0 ? -1 : 1; }+  return 0;+}+let _shellGate = { at: 0, ok: true, why: '' };+async function shellSupportsSend() {+  if (Date.now() - _shellGate.at < 60000) return _shellGate;+  const r = await hget('/updater/status');+  const b = (r && r.json) || null;+  const u = (b && (b.updater || b)) || null;+  const raw = u && typeof u.current_version === 'string' ? u.current_version.trim() : '';+  const m = raw.match(/^(\d+)\.(\d+)\.(\d+)/);+  let out;+  if (!m) out = { ok: true, why: '' };   // endpoint down or version unparseable: FAIL OPEN+  else if (cmpver([+m[1], +m[2], +m[3]], SEND_MIN_RELEASE) < 0) {+    out = { ok: false, why: 'Your Hydrogen (' + raw + ') predates the Send to AI fix. Update to 1.0.142 or newer to enable it; the phrase was copied instead.' };+  } else out = { ok: true, why: '' };+  _shellGate = { at: Date.now(), ...out };+  return _shellGate;+}  let TOOL_VERSION = '0'; try { TOOL_VERSION = String(require(path.join(__dirname, '..', 'package.json')).version || '0'); } catch (e) {}@@ -348,12 +372,18 @@ body{margin:0;background:#0d1117;color:var(--text);font:14px/1.5 var(--sans)} .play-sub{font-size:12px;color:var(--muted);margin-top:8px} /* prompts TABLE, not pills */ table.prompts{width:100%;border-collapse:collapse;table-layout:fixed}-table.prompts tr{height:40px;border-bottom:1px solid var(--line);cursor:pointer}+table.prompts tr{height:40px;border-bottom:1px solid var(--line);cursor:pointer;display:grid;grid-template-columns:minmax(0,max-content) 72px;justify-content:start;align-items:center}+/* The TABLE still spans the full width; the ROW packs its content to the left so Copy and+   Send sit just after the text they act on. Without this, deleting the layout cap put the+   icons ~2000px from their phrase at a 2500px viewport (measured). minmax(0,max-content),+   not plain max-content: the 0 floor lets the phrase cell shrink below its natural width+   at narrow viewports so the ellipsis still works, while max-content stops it from+   stretching to the far edge when there is room to spare. */ table.prompts tr:last-child{border-bottom:0} table.prompts tr:hover{background:rgba(255,255,255,.03)} table.prompts td.phrase{font-size:14px;color:var(--text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;padding-right:12px} table.prompts td.phrase em{color:var(--teal);font-style:normal}-table.prompts td.acts{width:72px;text-align:right;white-space:nowrap}+table.prompts td.acts{width:72px;text-align:right;white-space:nowrap;justify-self:end} table.prompts td.acts .ibtn{display:inline-flex;vertical-align:middle;margin-left:6px} .newbie.closed{display:none} /* 1.13.0: the panel FULLY closes (no residual one-liner) and the app UI moves up to fill the space;