HTML Cabinet
Session log · 2026-05-09
Session log · ~25 min · Claude Code

Connect your agent.

A cut-off save that wasn't actually cut off, then a new onboarding flow that abstracts the words "token" and "mint" out of the user-facing experience. One file changed. One commit shipped.

00TL;DR

01The article wasn't actually broken

Session opened with a forwarded transcript: claude.ai had been continuing Thariq Shihipar's Unreasonable Effectiveness of HTML as a v2 with seven interactive demos, and the message log appeared to truncate mid-byline (<div class="byline-). The ask: finish what got cut off.

Before rebuilding 60KB of HTML, I checked the cabinet's version history. v2 was already there:

v1 · initial publish 28,675 B
2026-05-10 05:32 UTC · the safe, stately first cut
v2 · show, don't tell 61,059 B
2026-05-10 05:46 UTC · drag-drop kanban, animated SVG, easing picker

The display layer had truncated the visible tool call. The POST /api/save went through anyway — the cabinet had the full 61KB payload with all seven demos.

A cut-off transcript isn't a cut-off save. Always check the destination, not the camera.

02The connect-flow refactor

You asked for two things, in two messages:

  1. Replace the post-mint screen with a shareable URL a user can paste into Claude Code — like the teenyapp /agent/<token>/agents.md pattern. The agent fetches it, reads the embedded instructions + token, and starts using the app as a memory bank.
  2. And while we're at it: stop saying mint and agent token. Just say connect your agent.

Both ship as one change. Toggle:

post-mint modal · same screen, different framing
Before: raw token + bearer header instructions
After: single URL, instructions abstracted away

The framing flip is small but load-bearing. Before, the user has to know what a Bearer token is, where to put it, and that there's a separate doc to read. After, they paste one link and the agent figures the rest out — by reading a markdown file the cabinet generates on the fly with the credentials baked in.

03The new route

The whole flow leans on one new endpoint: GET /agent/:token/agents.md. It validates the token's shape, looks it up, touches last_used, and returns a personalized markdown file with the bearer baked into every curl example.

// Agent instructions for a specific token. Designed to be pasted into Claude Code:
// the URL contains both the credentials and the instructions, so the agent can
// fetch it once and start using the cabinet as an HTML memory bank.
userApp.get('/agent/:token/agents.md', async (c) => {
  const token = c.req.param('token')
  const headers = { 'content-type': 'text/markdown; charset=utf-8' }
  if (!token || !/^agt_[a-f0-9]{48}$/.test(token)) {
    return new Response('# Not found\n\nInvalid token format.\n', { status: 404, headers })
  }
  const tokR = await db.rawSQL({
    q: 'SELECT name FROM tokens WHERE token = ? LIMIT 1',
    v: [token],
  }).run()
  const tok = tokR[0]
  if (!tok) return new Response('# Not found\n\nThis token does not exist...', { status: 404, headers })
  // Touch last_used so the user can see this connection is active.
  await db.rawSQL({ q: `UPDATE tokens SET last_used = CURRENT_TIMESTAMP WHERE token = ?`, v: [token] }).run()

  const md = `# HTML Cabinet — your shareable HTML memory bank
  ...
  > **Connection name:** ${tok.name}
  ...
  curl -X POST ${BASE_URL}/api/save \
    -H 'Authorization: Bearer ${token}'
  ...`
  return new Response(md, { status: 200, headers })
})

The token regex (/^agt_[a-f0-9]{48}$/) matches the format produced by generateToken(): 24 random bytes hex-encoded with the agt_ prefix. Anything else is rejected before the DB lookup, so the route is cheap to hammer.

04Try the cabinet's read API

This page is a doc in the cabinet — saved via the very flow described above. Click below to fetch its public version metadata. Same path Claude would use to recall this artifact in a future session.

Ping /api/docs/:slug/versions

live
GET https://html.app.teenyapp.com/api/docs/session-2026-05-09-connect-flow/versions

05Timeline

The actual order of operations. Click any step to expand.

  1. Discovered v2 of Thariq's article was already savedstep 1
    User pasted the cut-off claude.ai transcript and asked to finish what got truncated. Instead of immediately rebuilding from ~/Downloads/html.txt, hit the cabinet's version history endpoint. v2 was there at 61KB with the exact "show, don't tell" message claude.ai had drafted. The transcript display had truncated mid-byline; the underlying POST /api/save succeeded.
  2. User confirmed: "v2 already exists, check"step 2
    Re-checked /api/docs/unreasonable-effectiveness-of-html/versions. Confirmed v2 byte size matched the previously-drafted message. No reproduction needed.
  3. New ask: change the post-mint screenstep 3
    User wanted the modal to show a shareable URL like the teenyapp /agent/<token>/agents.md pattern, so a user can paste it into Claude Code as a memory bank pointer.
  4. Refinement: "abstract out 'mint' and 'agent token' too"step 4
    Renamed the primary CTA from Mint an agent token to Connect your agent. Modal headings became Connect your agentYour agent is connected. Three-step explainer on the homepage rewritten around the link-paste flow.
  5. Saved worker.ts locally, made 5 targeted editsstep 5
    Pulled the file via GET /api/v1/projects/html/files?path=worker.ts. Edits: (a) header route comment, (b) tokenModalHTML() rewrite, (c) homepage hero copy, (d) three-step explainer, (e) entire /how-to page rewrite + new /agent/:token/agents.md route + agent_url field on /api/tokens response.
  6. First PUT failed — body was JSON-wrappedstep 6
    Sent the file as {"content": "..."}. The endpoint stored that literal string as the file content, breaking the build at virtual:worker.ts:1:10. Recovered by sending raw bytes with Content-Type: text/plain and the If-Match header from the previous etag.
  7. Build green · committed ad16840e3154step 7
    Bundle reported {"bundle":{"ok":true,"output":[]}}. Posted the commit with message "connect-flow: agent link replaces raw token; new /agent/:token/agents.md route".
  8. Verified end-to-endstep 8
    Minted a fresh token via POST /api/tokens — response now includes agent_url. Fetched /agent/<token>/agents.md — got 200 with personalized markdown ("Connection name: verify-connect-flow"). Hit a fake token — got 404 with markdown body.
  9. You asked for this. Saving the session reportstep 9
    You minted a fresh connection ("teest"), pasted its agent_url, and asked me to save a report of everything I did. I fetched the agents.md as the actual test of the new flow, extracted the bearer, and POSTed this HTML back via /api/save. The page you're reading is itself a verification that the connect flow works.

06Meta

File
worker.ts
Edits
5 surgical (no full rewrite)
Lines
+161 / −82 net (≈ +79 added)
Final size
40,669 B (etag 11)
Commit
ad16840e3154
Parent
d2145f873732
Bundle
cbe932420d5b
Schema changes
none — uses existing tokens table
New routes
GET /agent/:token/agents.md
Modified routes
POST /api/tokens (added agent_url field)
Saved by the agt_7912… connection · live at /d/session-2026-05-09-connect-flow · version history