From 585dfaf9502283f11307eab906585425f3d700aa Mon Sep 17 00:00:00 2001 From: Matt Bernstein Date: Thu, 17 Sep 2026 21:48:01 -0400 Subject: [PATCH] docs: document FIT-2869 local interface preview for agents Align the public create-interface-skill with the thin SDK preview model: two-port localhost isolation, verified asset cache, fail-closed auth, and the narrow Save BFF so agents stop assuming Streamer/browser tokens. Co-authored-by: Cursor --- README.md | 13 +++++- SKILL.md | 32 +++++++++++++- references/local-preview.md | 86 +++++++++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+), 3 deletions(-) create mode 100644 references/local-preview.md diff --git a/README.md b/README.md index b2ec1f1..252285b 100644 --- a/README.md +++ b/README.md @@ -84,12 +84,22 @@ Or validate a single file: label-studio-sdk interface validate ./Screen.jsx ``` -Preview locally: +Preview locally (Enterprise URL + API key required for first-run asset cache; +live reload then stays on two `127.0.0.1` ports — see `references/local-preview.md`): ```bash +export LABEL_STUDIO_URL="https://app.humansignal.com" +export LABEL_STUDIO_API_KEY="YOUR_API_KEY" label-studio-sdk interface preview . ``` +Optional: + +```bash +label-studio-sdk interface preview . --offline # verified cache only +label-studio-sdk interface doctor # cache / auth / setup checks +``` + Sync a draft back to Label Studio: ```bash @@ -111,6 +121,7 @@ references/ authoring-rules.md claude-design-conversion.md examples.md + local-preview.md runtime-contract.md text-spans.md ``` diff --git a/SKILL.md b/SKILL.md index 026ef38..b5ff959 100644 --- a/SKILL.md +++ b/SKILL.md @@ -84,6 +84,8 @@ Use these references as needed: serialization patterns. - `references/claude-design-conversion.md`: convert Claude Design or React prototypes into the single-file Interface format. +- `references/local-preview.md`: SDK `interface preview` cache, two-port + localhost isolation, Save gateway, offline/auth failure modes (FIT-2869). ## Local Validation @@ -116,17 +118,43 @@ label-studio-sdk interface validate . --json ``` If validation passes and the user wants a visual check, use preview from the -same interface directory: +same interface directory (requires Label Studio Enterprise for first-run asset +download; live reload then stays on localhost): ```bash +export LABEL_STUDIO_URL="https://app.humansignal.com" # or your LSE origin +export LABEL_STUDIO_API_KEY="YOUR_API_KEY" label-studio-sdk interface preview . ``` +Useful variants: + +```bash +label-studio-sdk interface preview Screen.jsx --task task.json +label-studio-sdk interface preview . --no-open +label-studio-sdk interface preview . --offline # needs a prior verified cache +``` + +Preview notes agents must respect: + +- First uncached run authenticates against LSE, downloads version-compatible + playground + sandbox artifacts, verifies them, and stores an origin/protocol- + scoped user cache. Bad/missing tokens fail closed (no anonymous bootstrap). +- Runtime uses **two** `127.0.0.1` listeners (playground host + sandbox) with + capability URLs. Treat printed URLs as workstation-local secrets. +- The API token stays in the CLI process — never in browser JS, HTML, query + params, or storage. Save goes through a narrow local BFF (workspaces + + interface create/update only), not a general API proxy. +- Live reload is localhost SSE only (no Django playground stream, Redis, or + Streamer). `--offline` requires an existing verified cache; protocol mismatch + fails before the browser opens. +- Prefer `pull`/`sync` for durable server versions; playground Save is for + create/update while iterating. See `references/local-preview.md` for details. + If the SDK CLI is not installed, do not block. Perform static checks: plain JSX only, no `import`/`require`/`export`, no TypeScript syntax, a trailing parenthesized object literal with `default`, stable region IDs, and aligned `paramsSchema`/`outputSchema`/`getResults`/`parseResults`. - ## Output Expectations For simple requests, return the complete `.jsx` file and a short note naming the diff --git a/references/local-preview.md b/references/local-preview.md new file mode 100644 index 0000000..b300fa3 --- /dev/null +++ b/references/local-preview.md @@ -0,0 +1,86 @@ +# Local preview (`label-studio-sdk interface preview`) + +Use this reference when helping a user run or debug the SDK local Interfaces +playground (FIT-2869 thin local preview). Authoring rules for the JSX module +itself stay in `authoring-rules.md` and `runtime-contract.md`. + +## What preview does + +1. Resolves `Screen.jsx` (+ optional `task.json` / `sample.json`) from a + directory or explicit paths. +2. Downloads **version-compatible** playground + sandbox static artifacts from + the configured Label Studio Enterprise origin (first run / cache refresh). +3. Verifies fingerprints and stores them in an **origin- and protocol-scoped** + user cache under the platform cache dir + (`label-studio-sdk/interface-preview`). +4. Starts **two** listeners bound only to `127.0.0.1`: + - **Host** — playground UI, localhost SSE for file updates, narrow Save BFF + - **Sandbox** — isolated editor-shell iframe +5. Both origins use unguessable **capability** URL prefixes. Print them for the + user but treat them as workstation-local secrets (do not paste into tickets, + chats, or public docs). + +Live reload is **localhost SSE only**. It does not use Django playground +streams, Redis, Streamer, WebSockets, or long-lived Label Studio connections. + +## Auth and credentials + +Set once per shell (or pass `--lse-url` / `--token`): + +```bash +export LABEL_STUDIO_URL="https://app.humansignal.com" +export LABEL_STUDIO_API_KEY="YOUR_API_KEY" +``` + +Rules: + +- The API key stays in the **CLI process**. It is never placed in browser JS, + HTML, query parameters, or local storage. +- First uncached download probes auth (`whoami`) and **fails closed** on + `401`/`403` when no verified cache exists (no anonymous asset bootstrap). +- Later starts may revalidate the manifest; if LSE is unreachable or auth fails + but a verified cache exists, preview can start from cache with a stale-artifact + warning. +- `--offline` skips network entirely and **requires** a verified cache. +- Protocol mismatch between SDK and LSE fails **before** the browser opens. +- View-Only seats cannot mint the PAT needed for bootstrap or Save. +- Cookie SSO / OAuth / IAP browser bootstrap is out of scope for this path. + +## Save from the playground + +- Save can appear without a token, but create/update cannot succeed without + valid API auth. +- The browser talks only to the local Save gateway. The gateway allowlists + workspace lookup and interface create/update — it is **not** a general API + proxy. +- After `pull`, Save updates the sidecar-bound interface id for that origin. + A new scaffold creates an interface, then sticky-binds that id for later + Update. +- Prefer `interface sync` / `pull` for durable draft/publish workflows; use + playground Save while iterating visually. + +## Proxy / allowlist notes + +Customers who lock down static paths need access to: + +- `/react-app/local-playground/` +- `/react-app/editor-standalone/` + +Or they must populate the preview cache from a reachable instance before going +air-gapped / `--offline`. + +## Agent checklist + +When the user asks to preview locally: + +1. Confirm `label-studio-sdk` is on `PATH` and Node/npm exist (`interface doctor`). +2. Confirm `LABEL_STUDIO_URL` + `LABEL_STUDIO_API_KEY` for first run. +3. Run `interface preview .` from the scaffold directory (or pass file + `--task`). +4. If auth fails with an empty cache, stop — do not invent ORM/API workarounds. +5. If offline/air-gapped, verify a prior successful warm cache or use `--offline` + only after one successful online run. +6. Point users at `interface sync` when they need a published/draft version in + the product UI. + +Canonical CLI details also live in the SDK's `interface-cli.md` shipped with +`label-studio-sdk`.