Skip to content

SMOODEV-2272: th heypage — dogfood HeyPage via the real product API#175

Merged
brentrager merged 4 commits into
mainfrom
SMOODEV-heypage-th-cli
Jul 5, 2026
Merged

SMOODEV-2272: th heypage — dogfood HeyPage via the real product API#175
brentrager merged 4 commits into
mainfrom
SMOODEV-heypage-th-cli

Conversation

@brentrager

Copy link
Copy Markdown
Contributor

Problem

The TS seed-heypage-examples script illegally read SST Resource.SecretKeySupabaseServiceKey and died, so the showcase examples on heypage.ai were never (re)generated legitimately.

Solution

A th heypage subcommand that dogfoods HeyPage by driving the same product API endpoints a customer / the web builder hits — so server-side @smooai/config owns every secret. No backdoor.

cmd endpoint
generate --brief <f> POST /organizations/{org}/content-items/generate (contentType: website)
build --brief <f> --name <n> [--publish] generate → POST heypage/sites → (optional) POST heypage/sites/{id}/publish → prints live /p/<slug>
publish --site <id> POST heypage/sites/{id}/publish
get --site <id> GET heypage/sites/{id}

Mirrors products.rs exactly (require_authed / require_active_org / print_json / read_body). --brief accepts the bare SiteBrief or a {brief, brand?, assetUrls?} wrapper.

Deliberate omissions

  • No --studio flag — the studio pipeline is selected server-side by the heypageStudioPipeline feature flag (ADR-065), not per-request.
  • No --url crawl seam — the generate endpoint takes brand/assetUrls directly; there is no URL→brand-extract endpoint. Follow-up if wanted.

Verification

  • cargo build / cargo clippy / cargo fmt --check green (pre-commit hook).
  • 3 unit tests for brief-shaping + URL extraction pass.
  • One real generate call reached prod api.smoo.ai and returned a well-formed 403 — client, auth, path, and body-shaping all work end-to-end; the only blocker to publishing the canonical showcase slugs is that the signed-in session isn't a member of the HeyPage Showcase org (63dcb347-…, provisioned under a dedicated user). See PR discussion for the exact build --publish commands + the org/auth note.

🤖 Generated with Claude Code

@changeset-bot

changeset-bot Bot commented Jul 5, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 6789ddd

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

brentrager and others added 2 commits July 4, 2026 23:38
…duct API

Replaces the broken TS seed-heypage-examples script (which illegally read
SST `Resource.SecretKeySupabaseServiceKey` and died) with a `th heypage`
subcommand that drives the SAME endpoints a customer/the web builder hits:

  generate  POST /organizations/{org}/content-items/generate (contentType=website)
  build     generate -> POST heypage/sites -> (optional) publish -> live /p/<slug>
  publish   POST heypage/sites/{id}/publish
  get       GET  heypage/sites/{id}

Going through the API means server-side @smooai/config owns every secret — no
backdoor. Mirrors products.rs (require_authed / require_active_org / print_json /
read_body). `--brief` accepts the bare SiteBrief or a {brief, brand?, assetUrls?}
wrapper.

No `--studio` flag: the studio pipeline is chosen server-side by the
heypageStudioPipeline feature flag, not per-request. No `--url` crawl seam: the
generate endpoint takes brand/assetUrls directly, there is no URL->brand extract
endpoint (follow-up).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`build` creates a NEW site each run (slug de-collides → duplicates), which is
wrong for refreshing the 6 LIVE showcase examples. `regen --site <id> --brief
<f>` mirrors the old seed `--regen`: generate a fresh spec → PUT a new revision
on each page the site already has (revisable_paths intersects generated-spec
paths with the site's existing paths; the PUT endpoint 404s on unknown paths) →
publish. The site's slug/URL are unchanged — no duplicate.

Build idempotency-by-slug and a `list` command aren't added: there is no
`GET /organizations/{org}/heypage/sites` (list) endpoint, so slug→id can't be
resolved via the API. Follow-up: add that route, then wire build idempotency.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@brentrager brentrager force-pushed the SMOODEV-heypage-th-cli branch from 43903fb to 5c272df Compare July 5, 2026 03:42
@brentrager

Copy link
Copy Markdown
Contributor Author

Update — idempotent regen + rebase

  • Added th heypage regen --site <id> --brief <f> [--org] — generate fresh spec → PUT a new revision on each page the site already has (revisable_paths intersects generated-spec paths with the site's existing paths; the PUT endpoint 404s on unknown paths) → publish. Site slug/URL unchanged, no duplicate. This is the path that refreshes the 6 live showcase examples.
  • Rebased on latest main. 4 unit tests green; cargo build/clippy/fmt --check clean via pre-commit.

Deferred follow-ups (not blocking)

  1. No GET /organizations/{org}/heypage/sites (list) endpoint — so slug→id can't be resolved via the API, which blocks build idempotency-by-slug and a th heypage list command. Add the route, then wire both.
  2. --url crawl seam — no URL→brand-extract endpoint exists (generate takes brand/assetUrls directly). Would need a crawl→palette route first.
  3. Logo-URL parity — the old TS seed uploaded brand SVGs to a public bucket and pinned them; the API path can only pass brief.brand.logo as a URL. Briefs currently carry palette only.

@brentrager brentrager enabled auto-merge (squash) July 5, 2026 03:46
brentrager and others added 2 commits July 4, 2026 23:54
…ing)

`th heypage list` → GET /organizations/{org}/heypage/sites (new backend route,
smooai #2536), printing slug + id. `regen` now takes `--slug <slug>` as an
alternative to `--site <id>` (mutually exclusive), resolving the id via that
list. So the showcase refresh loop is `th api login → th heypage list → th
heypage regen --slug <slug> --brief <f>` with no site ids to copy from a DB.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@brentrager brentrager merged commit 1e636fc into main Jul 5, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant