@tomorrowos/sdk 1.1.1 → 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,16 +1,20 @@
1
1
  # Vercel / v0 - Verbatim question bank
2
2
 
3
- > **Protocol:** `vercel-questions/1.6` (pairs with `VERCEL_SETUP.md` 1.16+ / `@tomorrowos/sdk` 0.9.70+)
3
+ > **Protocol:** `vercel-questions/1.7` (pairs with `VERCEL_SETUP.md` 1.18+ / `@tomorrowos/sdk` 0.9.70+)
4
4
  > **For Agents:** When asking the user a setup question, **copy-paste the block below for that step EXACTLY**.
5
5
  > Do **not** paraphrase, reorder, rename options, or invent alternatives (no S3, no Supabase Storage, no "Blob recommended", no "disable media").
6
6
  > After each answer, configure per `VERCEL_SETUP.md`, then paste the **next** block.
7
- > **Q1 and Q2 are choice questions. Q3 is a form only - never a multiple-choice question. Q4 is optional CMS password (`CMS_PASSWORD`).**
7
+ > **Q1 and Q2 are choice questions — present them as a clickable choice UI** (buttons / radio / option cards) using the exact option labels below. Do **not** ask the user to type `1` or `2` in chat when a choice UI is available (chat typing is fallback only).
8
+ > **Q3 is a form only - never a multiple-choice question. Q4 is optional CMS password (`CMS_PASSWORD`).**
8
9
  > **HARD STOP on Q3:** show the form, then **wait** for the user's reply. Optional fields do **not** mean you may auto-skip.
9
- > **Q4 is optional:** blank/skip is valid; they can set `CMS_PASSWORD` later.
10
+ > **Q4 is optional:** blank/skip is valid; they can set `CMS_PASSWORD` later.
11
+ > **After Q4 (or after Q3 if you deferred Q4):** run `VERCEL_SETUP.md` **IRON GATE — v0 Preview hygiene** before debugging Neon/Postgres or Blob.
10
12
 
11
13
  ---
12
14
 
13
- ## Q1 - Database (paste this entire block)
15
+ ## Q1 - Database (clickable choice UI — paste option text exactly)
16
+
17
+ **UX:** Open a **clickable choice UI** (two option buttons / radio cards). Labels must match the bold titles below.
14
18
 
15
19
  Which database should TomorrowOS use on Vercel?
16
20
 
@@ -18,7 +22,7 @@ Which database should TomorrowOS use on Vercel?
18
22
 
19
23
  **2. Neon Postgres** - serverless Postgres native to Vercel; use Neon's **pooled** connection string from the Neon dashboard (not the direct un-pooled host for serverless).
20
24
 
21
- Reply with **1** or **2** (or "Supabase" / "Neon").
25
+ **Fallback only** (no choice UI available): Reply with **1** or **2** (or "Supabase" / "Neon").
22
26
 
23
27
  ---
24
28
 
@@ -33,7 +37,9 @@ Never show blank Env fields for `TOMORROWOS_STORE` or `DATABASE_SSL`.
33
37
 
34
38
  ---
35
39
 
36
- ## Q2 - Media (paste this entire block)
40
+ ## Q2 - Media (clickable choice UI — same pattern as Q1)
41
+
42
+ **UX:** Open a **clickable choice UI** (two option buttons / radio cards). **Forbidden when UI exists:** chat-only “Reply with **1** or **2**”.
37
43
 
38
44
  How should playlist media (images/videos) be stored?
39
45
 
@@ -41,7 +47,7 @@ How should playlist media (images/videos) be stored?
41
47
 
42
48
  **2. Vercel Blob** - Vercel-native object storage; durable `https://*.public.blob.vercel-storage.com/...` URLs. Use when you want media on the same Vercel project without a Cloudinary account.
43
49
 
44
- Reply with **1** or **2** (or "Cloudinary" / "Vercel Blob").
50
+ **Fallback only** (no choice UI available): Reply with **1** or **2** (or "Cloudinary" / "Vercel Blob").
45
51
 
46
52
  **Forbidden replacements (never ask these):** Vercel Blob as recommended #1; Supabase Storage; S3 / R2 / B2 / `MEDIA_S3_*`; "no media storage" / disable uploads; local disk only.
47
53
 
package/VERCEL_SETUP.md CHANGED
@@ -5,11 +5,14 @@
5
5
  >
6
6
  > **On Vercel, this file is the setup questionnaire** (not `REPLIT_SETUP.md`).
7
7
  >
8
- > Protocol id: `vercel-setup/1.16` - pairs with `@tomorrowos/sdk` **0.9.70+** (`templates/cms-starter-v0`, Fluid Functions WebSockets).
8
+ > Protocol id: `vercel-setup/1.18` - pairs with `@tomorrowos/sdk` **0.9.70+** (`templates/cms-starter-v0`, Fluid Functions WebSockets).
9
9
  >
10
10
  > **Questions source of truth:** `VERCEL_QUESTIONS.md` - Agents must **open that file and paste each Q block verbatim**. Do not paraphrase.
11
- > **Q1/Q2 = choice questions. Q3 = form only:** open **all seven optional inputs at once**, then **HARD STOP and WAIT** for submit or "skip". Never auto-skip because fields are optional. Never ask a branding choice first. Never show only Name + Primary colour. Logo = **Upload** above **Logo URL**.
11
+ > **Q1/Q2 = clickable choice UI (same pattern):** open a **button / radio / option-card UI** with the two fixed choices. Do **not** ask the user to type `1` or `2` in chat when a choice UI is available. Chat “reply 1 or 2” is fallback only.
12
+ > **Q3 = form only:** open **all seven optional inputs at once**, then **HARD STOP and WAIT** for submit or "skip". Never auto-skip because fields are optional. Never ask a branding choice first. Never show only Name + Primary colour. Logo = **Upload** above **Logo URL**.
12
13
  > **Q4 = optional `CMS_PASSWORD`:** blank/skip is valid; can set later. Control Panel auth only — never Vercel Deployment Protection / whole-site password.
14
+ > **After Q&A (before debugging DB):** run **IRON GATE — v0 Preview hygiene** (clear root Next leftovers, restart supervised `dev`, prove `GET /status` is TomorrowOS JSON). Do **not** spend turns “fixing Neon/Blob” while port 3000 is still a stale Next/Turbopack process.
15
+ > **Publish Output Directory:** must stay **empty / unset**. Build error `Output Directory "public" is empty` means the dashboard still points at `public/` — **clear it**; do **not** “fix” by stuffing `public/robots.txt` while leaving Output = `public`.
13
16
  > **Copy questions verbatim from `VERCEL_QUESTIONS.md`.** Do not invent alternate Q2 options (no "Supabase Storage", no S3, no "disable media", no making Blob the recommended option).
14
17
  > **`TOMORROWOS_STORE` and `DATABASE_SSL` are agent-only** - set them automatically after Q1; never put them in a user-facing Env form for the customer to type.
15
18
 
@@ -19,6 +22,7 @@ The following rules are **mandatory acceptance criteria**, not suggestions. The
19
22
 
20
23
  1. **One TomorrowOS instance only.** Use the shared instance exported through `api/index.ts`. **Never create a second `TomorrowOS` instance in `api/ws.ts`** or any other route.
21
24
  2. **Never use `public/` as the Vercel Output Directory.** The dashboard Output Directory must be **empty / unset**. Serve the Control Panel at runtime from `cms-panel/` through TomorrowOS `staticRoot`.
25
+ 2a. **Build error `The Output Directory "public" is empty`:** This is **not** fixed by adding `public/robots.txt` as the primary solution. It means Project Settings still has Output Directory = `public`. **Clear/unset Output Directory**, confirm Framework = Other, Fluid Function `api/index.ts` + rewrites, then redeploy. Filling `public/` only papers over a wrong static-output setting.
22
26
  3. **Preview may use Next only as an isolated reverse-proxy shell under `preview/`.** Preview must not replace TomorrowOS or determine the Production runtime.
23
27
  4. **Publish must use the Fluid Function.** Production must run `api/index.ts` ??the shared exported `http.Server`, with Fluid compute enabled. It must not use `next start`, a static export, or a bare `(req, res)` handler.
24
28
  5. **After every Production deployment, verify all three gates before saying setup is complete:**
@@ -38,10 +42,14 @@ When a human pastes a prompt like this, **read this file and start Question 1 im
38
42
  Follow @tomorrowos/sdk (an NPM package) VERCEL_SETUP.md and set up my TomorrowOS CMS from scratch.
39
43
 
40
44
  Open VERCEL_QUESTIONS.md first.
41
- For Q1, Q2, Q3: paste each question block VERBATIM. Do not rephrase options.
45
+ For Q1 and Q2: open a **clickable choice UI** (buttons / radio / option cards) using the verbatim options from VERCEL_QUESTIONS.md. Do not ask me to type 1 or 2 in chat when UI is available.
46
+ For Q1, Q2, Q3: use exact option wording from VERCEL_QUESTIONS.md. Do not rephrase options.
42
47
  For Q3: DO NOT ask a branding choice. Open one form with ALL SEVEN optional inputs at once, then HARD STOP and WAIT for my submit or "skip". Never auto-continue with defaults without my reply. Never only Name + Primary colour. Logo = Upload above Logo URL.
48
+ After Q3, paste Q4 (optional CMS password). Blank/skip is valid.
43
49
  After Q1 database choice, YOU set TOMORROWOS_STORE and DATABASE_SSL. Do not ask me to fill those.
44
50
  After Q2 media storage choice, YOU set TOMORROWOS_MEDIA. Do not ask me to fill those.
51
+ After answers: run IRON GATE v0 Preview hygiene BEFORE debugging Postgres/media — clear root Next leftovers, restart supervised dev, prove GET /status is TomorrowOS JSON.
52
+ Before Publish: Output Directory must be empty/unset (NOT public). If build says Output Directory public is empty, CLEAR Output Directory — do not stuff public/robots.txt as the fix.
45
53
  Run: npx @tomorrowos/sdk@latest init . --hosting v0 --force
46
54
  Keep cms-starter-v0 layout.
47
55
  Apply the NON-NEGOTIABLE VERCEL RUNTIME CONTRACT exactly:
@@ -114,18 +122,37 @@ almost always means **Vercel still thinks this is a Next.js / static deploy**, n
114
122
 
115
123
  **Setup is NOT complete** while Production `GET /` returns `404: NOT_FOUND` or WebSocket upgrade returns 200 HTML.
116
124
 
117
- ### Publish static trap ??`public/` served without Node (read this)
125
+ ### Publish static trap — `public/` served without Node (read this)
118
126
 
119
- A common **worse** failure mode than `404: NOT_FOUND`:
120
-
121
- - `GET /` loads the Control Panel HTML (from static `public/index.html`)
127
+ **Symptom A — wrong runtime (common worse failure than `404: NOT_FOUND`):**
128
+ - `GET /` loads the Control Panel HTML (from static files)
122
129
  - `GET /status`, `/devices`, etc. return **HTML** (Vercel 404 page)
123
130
  - The panel shows **CMS unreachable** and console:
124
131
  `Unexpected token 'T', "The page c"... is not valid JSON`
125
132
 
126
- **Root cause:** Vercel deployed `public/` as a **static site** (Output Directory = `public` or Framework=Other default *?public if it exists??). **No Function** is running TomorrowOS.
133
+ **Root cause A:** Vercel deployed `public/` as a **static site** (Output Directory = `public` or Framework=Other default “public if it exists”). **No Function** is running TomorrowOS.
134
+
135
+ **Symptom B — build fails before deploy:**
136
+ ```text
137
+ Error: The Output Directory "public" is empty.
138
+ ```
139
+ **Root cause B:** Project Settings **Output Directory is still `public`**, but this CMS has **no** (or empty) static export folder — UI lives in `cms-panel/` and is served by the Fluid Function.
140
+
141
+ **Correct fix (mandatory for A and B):**
142
+
143
+ 1. Set **Output Directory** = **empty / unset** (dashboard and/or project settings API).
144
+ 2. Framework Preset = **Other** / null; Fluid on; `api/index.ts` + rewrites per Scheme A.
145
+ 3. Redeploy. Verify `GET /status` is **JSON** and WebSocket is **101**.
127
146
 
128
- **Fix (Scheme A):** use `cms-panel/` (not `public/`), Fluid Function `api/index.ts` + rewrites, Output Directory empty. Static UI is served **at runtime** via `staticRoot` in `listen()`.
147
+ **Forbidden “fixes”:**
148
+
149
+ - Adding `public/robots.txt` / `.gitkeep` **so a static Output Directory = `public` build passes** — that leaves Production on the static trap path.
150
+ - Setting Output Directory to `cms-panel`, `.`, or `public`.
151
+ - Claiming Publish succeeded because the build log turned green while Output is still `public`.
152
+
153
+ **Optional:** If some tooling creates an empty `public/` folder, delete it or ignore it — **never** point Output Directory at it.
154
+
155
+ **Scheme A reminder:** use `cms-panel/` (not `public/`), Fluid Function `api/index.ts` + rewrites, Output Directory empty. Static UI is served **at runtime** via `staticRoot` in `listen()`.
129
156
 
130
157
  **Publish gate (mandatory):** after deploy, `GET /status` must return **JSON** starting with `{`, not HTML starting with `<` or `The page`. **And** WebSocket upgrade on `/` or `/api` must return **101**.
131
158
 
@@ -203,7 +230,8 @@ Then ask **Question 1**.
203
230
 
204
231
  ## Hard rules
205
232
 
206
- 1. **Open `VERCEL_QUESTIONS.md` first.** Paste Q1 → configure → paste Q2 → configure → paste Q3 → paste Q4. Never paraphrase question options. Never use `REPLIT_SETUP.md` as the questionnaire. **Never** ask for OpenAI or other LLM API keys.
233
+ 1. **Open `VERCEL_QUESTIONS.md` first.** Ask Q1 → configure → Q2 → configure → Q3 → Q4. Never paraphrase question options. Never use `REPLIT_SETUP.md` as the questionnaire. **Never** ask for OpenAI or other LLM API keys.
234
+ 1a. **Q1 and Q2 UX:** Prefer a **clickable choice UI** (same pattern for both): two option buttons/cards matching the verbatim labels. **Forbidden when UI exists:** chat-only “Reply with 1 or 2”. Chat typing is fallback only if the platform cannot render choices.
207
235
  2. **Question 2 options are fixed:** (1) Cloudinary **(recommended)**, (2) Vercel Blob. Do not reorder; do not invent Supabase Storage, local disk, or disable-uploads.
208
236
  3. **Cloudinary:** open **one** Vercel Env popup with **all** Cloudinary fields together. Do **not** quiz one credential at a time.
209
237
  4. **`TOMORROWOS_STORE` + `DATABASE_SSL` = agent-owned.** After Q1 choice (and URL if needed), set them yourself in Vercel Env Vars. **Never** include them as blank fields for the customer. Mapping: Supabase ? `TOMORROWOS_STORE=supabase` + `DATABASE_SSL=true`; Neon ? `TOMORROWOS_STORE=postgres` + `DATABASE_SSL=true`. **Do not offer SQLite as a questionnaire choice** ? it is ephemeral on Vercel and unsuitable for production.
@@ -221,13 +249,76 @@ Then ask **Question 1**.
221
249
  16. **No inventing CMS login** from a branding URL.
222
250
  17. **Skip → invent.** Stay on Q1–Q4 only.
223
251
  18. **Publish gate:** `/status` JSON + WebSocket 101; no static `public/` trap.
224
- 19. **Q3 is a form, not a question ? and you must WAIT.** After Q2, open one dialog with **all seven** optional inputs visible together, then **stop and wait** for submit or explicit **"skip"**. **Never** auto-continue because fields are optional. **Never** ask a preliminary branding choice. **Never** show only Product Name + Primary colour. **Never** use multiple-choice / Option A-B for branding. Logo = **Upload** above **Logo URL**.
252
+ 19. **Q3 is a form, not a question — and you must WAIT.** After Q2, open one dialog with **all seven** optional inputs visible together, then **stop and wait** for submit or explicit **"skip"**. **Never** auto-continue because fields are optional. **Never** ask a preliminary branding choice. **Never** show only Product Name + Primary colour. **Never** use multiple-choice / Option A-B for branding. Logo = **Upload** above **Logo URL**.
253
+ 20. **IRON GATE after Q&A:** Before blaming Neon / Blob / migrations, complete **IRON GATE — v0 Preview hygiene** below. Preview 500s are usually leftover root Next or a stale supervised `next-server`, not a bad Q1/Q2 choice.
254
+
255
+ **Question order:** Q1 -> (auto-set store env) -> Q2 -> Q3 form (**wait for user**) -> Q4 optional `CMS_PASSWORD` -> **IRON GATE** -> execution checklist.
256
+
257
+ ---
258
+
259
+ ## IRON GATE — v0 Preview hygiene (run after Q&A, before DB rabbit holes)
260
+
261
+ > **Why this exists:** On v0, agents often answer Q1–Q3, run `init`, then spend a long time “fixing Postgres” while port **3000** is still the original **Next/Turbopack** starter (or a manually spawned `tsx` without injected env). Neon vs Blob choices are **not** the usual cause.
262
+
263
+ ### Do this in order — stop when each check passes
264
+
265
+ 1. **Identify what owns `:3000` (empirical, 30 seconds)**
266
+ - If process/logs look like `next-server` / Turbopack / `ENOENT .../app` → you are **not** talking to TomorrowOS yet.
267
+ - If `GET /status` returns TomorrowOS **JSON** (`{"status":...}`) → proceed.
268
+ - **Forbidden:** debugging `DATABASE_URL` / Neon pooler while `/status` is HTML, Next error overlay, or Turbopack crash.
269
+
270
+ 2. **Clear forbidden root Next leftovers (from the original v0 starter)**
271
+ Remove from **project root** (keep the SDK shell under `preview/`):
272
+ `app/`, `pages/`, root `next.config.*`, root `next-env.d.ts`, root `postcss.config.*`, leftover `components/` / `lib/` from the starter, `components.json` — anything that makes Framework detection or v0 Preview latch onto Next at root.
273
+ **Do not** delete `preview/` (that is the sanctioned Next reverse-proxy shell).
274
+ Empty leftover dirs after deleting files.
275
+
276
+ 3. **Confirm scripts + layout**
277
+ - Root `package.json` `"dev"` / `"start"` run TomorrowOS (`tsx` …), not `next dev` at root.
278
+ - `preview/` exists if Preview stays Next-hardwired; use `dev:preview` (Next public + TomorrowOS on **3001**) when required.
279
+ - `cms-panel/` (or template static root) is TomorrowOS UI — **not** Vercel Output Directory `public/`.
280
+
281
+ 4. **Restart the platform-supervised `dev` — do not double-bind**
282
+ - After rewriting `package.json` scripts, the **old** supervised `next-server` may still hold `:3000`. Kill/restart so the supervisor re-execs the **new** `dev` script.
283
+ - **Forbidden:** spawning a second manual `pnpm dev` / `tsx` that fights the supervised process (`EADDRINUSE`) **and** lacks v0-injected env.
284
+ - Prefer the **v0/Vercel managed** process (it inherits injected secrets). Only start TomorrowOS yourself if you also load the same env (see **Env loading trap**).
285
+
286
+ 5. **Prove TomorrowOS before touching DB**
287
+ - `GET /status` → **200 JSON** from TomorrowOS (not Next HTML, not `The page could not be found`).
288
+ - `GET /` → Control Panel HTML (or CMS login HTML if `CMS_PASSWORD` set).
289
+ - Only **then** interpret Database/Media connector errors as real store/media issues.
290
+
291
+ ### Env loading trap (v0 / Preview vs Production)
292
+
293
+ | Context | How secrets arrive | What `import "dotenv/config"` sees |
294
+ |---------|-------------------|-------------------------------------|
295
+ | **Production Deploy** | Vercel injects into `process.env` | N/A — platform env wins |
296
+ | **v0 / Agent Preview** | Often written to **`.env.development.local`** (and/or injected only into the **supervised** Node process) | **Only `.env`** by default — **not** `.env.development.local` |
297
+ | **Manual Bash `pnpm dev`** | Usually **no** injected `DATABASE_URL` / Blob token unless you export them | Easy false “Postgres broken” |
298
+
299
+ **Rules:**
300
+
301
+ - Non-secret flags may live in committed `.env` (`TOMORROWOS_STORE`, `DATABASE_SSL`, `TOMORROWOS_MEDIA`).
302
+ - Secrets (`DATABASE_URL`, `SUPABASE_URL`, `BLOB_READ_WRITE_TOKEN`, `CMS_PASSWORD`, Cloudinary keys) belong in **Vercel Env** / v0 injection — never commit them.
303
+ - If logs say store=`postgres` but `DATABASE_URL` is missing in a **manual** process: load `.env` **and** `.env.development.local`, or restart the **supervised** server — do **not** immediately change Neon URLs.
304
+ - Preview flakes ≠ Production failure. Fix Preview hygiene first; do not “repair” Production Framework because Preview 500’d.
225
305
 
226
- **Question order:** Q1 -> (auto-set store env) -> Q2 -> Q3 form (**wait for user**) -> Q4 optional `CMS_PASSWORD` -> execution checklist.
306
+ ### Preview decision tree (after hygiene)
307
+
308
+ ```
309
+ Is GET /status TomorrowOS JSON on the Preview URL?
310
+ YES → continue checklist (Publish gates still required).
311
+ NO → What owns the public port?
312
+ next-server / Turbopack / ENOENT app/ → clear root Next + restart supervised dev (steps 2–4).
313
+ tsx TomorrowOS listening but Preview blank → v0 still hardwires Next: use preview/ reverse-proxy + TOMORROWOS_INTERNAL_PORT=3001 + dev:preview.
314
+ EADDRINUSE :3000 → kill stale Next/tsx; one process only.
315
+ ```
316
+
317
+ **Reminder:** Q1 Neon / Q2 Blob are valid protocol choices. A long Preview outage after those answers is almost always **process/layout/env injection**, not “wrong database/media product.”
227
318
 
228
319
  ---
229
320
 
230
- ## Scheme A ??Vercel Functions + WebSockets (mandatory for Production)
321
+ ## Scheme A — Vercel Functions + WebSockets (mandatory for Production)
231
322
 
232
323
  > **Goal:** Preview may use Next + proxy; **Publish** runs TomorrowOS as a **Vercel Function** (`api/index.ts`) on **Fluid**, with WebSocket support per [Vercel WebSockets docs](https://vercel.com/docs/functions/websockets).
233
324
  > **Scope:** Layout + `vercel.json` + dashboard. Prefer `npx @tomorrowos/sdk@latest init --hosting v0` (`cms-starter-v0`).
@@ -351,16 +442,20 @@ SDK behaviour (0.9.32+):
351
442
  - `"buildCommand"` = no-op ??**never** `next build` for Production
352
443
  - Dashboard Output Directory must stay **empty**
353
444
 
354
- ### A4 ??Vercel Project Settings (dashboard ??mandatory)
445
+ ### A4 — Vercel Project Settings (dashboard — mandatory)
355
446
 
356
447
  | Setting | Required value |
357
448
  |---------|----------------|
358
- | Framework Preset | **Other** / null ??**not Next.js** |
449
+ | Framework Preset | **Other** / null — **not Next.js** |
359
450
  | Install Command | `npm install` |
360
451
  | Build Command | `npm run build` (no-op) |
361
- | **Output Directory** | **empty** |
452
+ | **Output Directory** | **empty / unset** — **never `public`** |
362
453
  | Fluid compute | **On** |
363
- | WebSockets | Account must allow Functions WebSockets (see docs ?Permissions Required?? |
454
+ | WebSockets | Account must allow Functions WebSockets (see docs — Permissions Required) |
455
+
456
+ **If Publish build fails with `The Output Directory "public" is empty`:** the dashboard still has Output = `public`. **Clear/unset it** via Project Settings (or settings API), then redeploy. Do **not** add `public/robots.txt` (or any static filler) as the primary fix while Output stays `public`.
457
+
458
+ **`vercel.json` must not set `outputDirectory`.** Leaving it out is correct; dashboard Output must still be forced empty because Framework=Other may default to “public if it exists”.
364
459
 
365
460
  ### A5 ??Publish acceptance (all must pass)
366
461
 
@@ -511,12 +606,14 @@ const server = tomorrowos.listen({
511
606
  export default server;
512
607
  ```
513
608
 
514
- - **Production:** `PORT` from Vercel ??TomorrowOS listens on that port (no Next).
609
+ - **Production:** `PORT` from Vercel — TomorrowOS listens on that port (no Next).
515
610
  - **Preview:** set `TOMORROWOS_INTERNAL_PORT=3001` (or similar) so Next can own `:3000` and proxy inward.
516
611
 
517
- Load env with `dotenv` for local/`vercel dev`. On production Deploy, Vercel injects Environment Variables natively.
612
+ Load env with `dotenv` for local/`vercel dev` (loads **`.env` only**). On production Deploy, Vercel injects Environment Variables natively.
613
+
614
+ **v0 / Preview caveat:** platform secrets often land in **`.env.development.local`** or only in the **supervised** process env. TomorrowOS `import "dotenv/config"` does **not** auto-load `.env.development.local`. See **IRON GATE — Env loading trap**. Never treat a manual Bash server missing `DATABASE_URL` as proof that Neon is misconfigured.
518
615
 
519
- ### Preview adapter (v0 / Next reverse proxy) ??required when Preview is Next-hardwired
616
+ ### Preview adapter (v0 / Next reverse proxy) — required when Preview is Next-hardwired
520
617
 
521
618
  Detect Preview / v0 (Next already present, or Preview blank while TomorrowOS logs ?listening??. Then:
522
619
 
@@ -612,17 +709,26 @@ If Vercel?s UI and this file disagree on bundling, **prefer whatever keeps a sin
612
709
 
613
710
  If you open an Env configuration UI for Q1, fields visible to the user may include **only** `SUPABASE_URL` or `DATABASE_URL`. **Do not** show blank `TOMORROWOS_STORE` / `DATABASE_SSL` inputs ? write those yourself in the same step.
614
711
 
615
- ### Step A ? Ask storage choice
712
+ ### Step A — Ask database choice (**clickable UI required**)
616
713
 
617
- **Ask exactly:**
714
+ **UX:** Open a **choice UI** the user can **click** — two option buttons / radio cards / select list. Labels must match the verbatim options below. Same clickable pattern as Q2.
715
+
716
+ | # | Exact option label (UI) |
717
+ |---|-------------------------|
718
+ | 1 | **Supabase Postgres (recommended)** |
719
+ | 2 | **Neon Postgres** |
720
+
721
+ Supporting copy under each option may match `VERCEL_QUESTIONS.md`. **Do not** default to a chat message that only says “Reply with 1 or 2” when the platform can render choices.
722
+
723
+ **Ask exactly (wording / option text must match):**
618
724
 
619
725
  > Which database should TomorrowOS use on Vercel?
620
726
  >
621
- > **1. Supabase Postgres (recommended)** ? durable pairing/playlists; use the **Session pooler** URL (`*.pooler.supabase.com:6543`).
727
+ > **1. Supabase Postgres (recommended)** — durable pairing/playlists; use the **Session pooler** URL (`*.pooler.supabase.com:6543`).
622
728
  >
623
- > **2. Neon Postgres** ? serverless Postgres native to Vercel; use Neon's **pooled** connection string from the Neon dashboard (not the direct un-pooled host for serverless).
624
- >
625
- > Reply with **1** or **2** (or "Supabase" / "Neon").
729
+ > **2. Neon Postgres** — serverless Postgres native to Vercel; use Neon's **pooled** connection string from the Neon dashboard (not the direct un-pooled host for serverless).
730
+
731
+ **Fallback only** (no choice UI available): allow chat reply **1** / **2** / "Supabase" / "Neon".
626
732
 
627
733
  ### Step B ??If **1 / Supabase**
628
734
 
@@ -678,19 +784,26 @@ If you open an Env configuration UI for Q1, fields visible to the user may inclu
678
784
  > It is **not** about OpenAI, AI pairing, or LLM keys. If you are about to ask for an `sk-` key, **stop** ??you are off-protocol.
679
785
  > **Copy the two options below verbatim.** Wrong examples that must **never** appear: "Vercel Blob (recommended)", "Supabase Storage", "No media storage / disable uploads", "local disk only".
680
786
 
681
- ### Step A ? Ask storage choice
787
+ ### Step A — Ask storage choice (**clickable UI required — same as Q1**)
788
+
789
+ **UX:** Open a **choice UI** the user can **click** — two option buttons / radio cards. **Forbidden when UI exists:** chat-only “Reply with **1** or **2**”.
682
790
 
683
- **Ask exactly (wording must match ? Cloudinary is recommended):**
791
+ | # | Exact option label (UI) |
792
+ |---|-------------------------|
793
+ | 1 | **Cloudinary (recommended)** |
794
+ | 2 | **Vercel Blob** |
795
+
796
+ **Ask exactly (wording must match — Cloudinary is recommended):**
684
797
 
685
798
  > How should playlist media (images/videos) be stored?
686
799
  >
687
- > **1. Cloudinary (recommended)** ? durable public HTTPS URLs (`https://res.cloudinary.com/...`). Works out of the box with `@tomorrowos/sdk` auto-detection. **Prefer this on Vercel.**
688
- >
689
- > **2. Vercel Blob** ? Vercel-native object storage; durable `https://*.public.blob.vercel-storage.com/...` URLs. Use when you want media on the same Vercel project without a Cloudinary account.
800
+ > **1. Cloudinary (recommended)** — durable public HTTPS URLs (`https://res.cloudinary.com/...`). Works out of the box with `@tomorrowos/sdk` auto-detection. **Prefer this on Vercel.**
690
801
  >
691
- > Reply with **1** or **2** (or "Cloudinary" / "Vercel Blob").
802
+ > **2. Vercel Blob** — Vercel-native object storage; durable `https://*.public.blob.vercel-storage.com/...` URLs. Use when you want media on the same Vercel project without a Cloudinary account.
803
+
804
+ **Fallback only** (no choice UI): chat reply **1** / **2** / "Cloudinary" / "Vercel Blob".
692
805
 
693
- If you catch yourself about to offer Blob-as-recommended, Supabase Storage, local disk, or "no media" ? **stop and paste the block above instead**.
806
+ If you catch yourself about to offer Blob-as-recommended, Supabase Storage, local disk, or "no media" — **stop and use the block above instead**.
694
807
 
695
808
  ### Step B ??If **1 / Cloudinary** (same Question 2 ??**one Env popup**)
696
809
 
@@ -874,26 +987,36 @@ Set `cmsEndpoint` only if they already know the public `https://?vercel.app` URL
874
987
 
875
988
  ---
876
989
 
877
- ## After all answers ? execution checklist
990
+ ## After all answers — execution checklist
991
+
992
+ ### A0. IRON GATE first (mandatory)
993
+
994
+ Complete **IRON GATE — v0 Preview hygiene** before long Postgres/media debugging:
995
+
996
+ 1. Prove `GET /status` is TomorrowOS JSON (or clear root Next + restart supervised `dev` until it is).
997
+ 2. Remove root `app/` / `next.config.*` leftovers; keep `preview/` only for the proxy shell.
998
+ 3. Do not spawn a second manual server that lacks injected env / fights `:3000`.
999
+ 4. Only then treat Database/Media connector errors as real.
878
1000
 
879
1001
  ### A. Seed the project
880
1002
 
881
1003
  ```bash
882
- npx @tomorrowos/sdk@latest init .
1004
+ npx @tomorrowos/sdk@latest init . --hosting v0
883
1005
  # If not empty and user confirms starter overwrite:
884
- # npx @tomorrowos/sdk@latest init . --force
1006
+ # npx @tomorrowos/sdk@latest init . --hosting v0 --force
885
1007
  ```
886
1008
 
887
- Then (**Scheme A ??mandatory for Publish**):
1009
+ Then (**Scheme A — mandatory for Publish**):
888
1010
 
889
1011
  1. Keep **`server.ts`** at **project root** with top-level `listen()` + `export default server`.
890
- 2. If Preview needs Next: **move** `next.config.*`, `app/`, Next `tsconfig` / `postcss` / `next-env.d.ts` into **`preview/`** ??remove them from root so Production is not mistaken for Next/static.
891
- 3. Write `vercel.json` per **Scheme A3** (`fluid`, `framework: null`, no-op `buildCommand` ??**no** `startCommand`).
892
- 4. **Force Project Settings** per **Scheme A4** (Framework = Other, Output Directory empty, Start = `npm run start`, Fluid on). Patch dashboard via API if it still says Next.js.
1012
+ 2. **Immediately** move leftover v0 Next from root into **`preview/`** or delete root Next files (`next.config.*`, `app/`, Next `tsconfig` / `postcss` / `next-env.d.ts`, starter `components/`). Production must not see root Next.
1013
+ 3. Write `vercel.json` per **Scheme A3** (`fluid`, `framework: null`, no-op `buildCommand` — **no** `startCommand`).
1014
+ 4. **Force Project Settings** per **Scheme A4** (Framework = Other, Output Directory **empty/unset**, Start = `npm run start`, Fluid on). Patch dashboard via API if it still says Next.js **or** Output = `public`.
1015
+ 4a. **Pre-Publish Output check:** If settings show Output Directory = `public` (or build fails with `Output Directory "public" is empty`), **clear Output Directory** and redeploy. Do **not** add `public/robots.txt` to satisfy a static output path.
893
1016
  5. Do **not** add root `api/` catch-all for raw `http.Server`.
894
1017
  6. Do **not** add `.replit*`.
895
1018
  7. If Q2 = Vercel Blob: set `BLOB_READ_WRITE_TOKEN` + `TOMORROWOS_MEDIA=vercel-blob` (SDK-native; no custom bridge).
896
-
1019
+ 8. **Restart** the platform-supervised Preview `dev` after script/layout changes (stale `next-server` is the #1 false “DB failure”).
897
1020
  ### B. Environment Variables (Vercel dashboard + local)
898
1021
 
899
1022
  | Name | Required | Who fills it |
@@ -909,14 +1032,16 @@ Then (**Scheme A ??mandatory for Publish**):
909
1032
  | `PORT` | Optional | Vercel injects for Production |
910
1033
  | `CMS_PASSWORD` | Optional (Q4) | User if set; blank/skip = omit (can add later) |
911
1034
 
912
- Mirror non-secrets in `.env` for local `npm run dev` if helpful; never commit secrets.
1035
+ Mirror non-secrets in `.env` for local `npm run start` if helpful; never commit secrets.
1036
+ On v0 Preview, secrets may exist only in **`.env.development.local`** / supervised env — see **Env loading trap**. Do not “fix Neon” until the process that serves `/status` actually has `DATABASE_URL` / `SUPABASE_URL`.
913
1037
 
914
1038
  ### C. Install and run
915
1039
 
916
1040
  ```bash
917
1041
  npm install
918
1042
  npm run start
919
- # Preview / v0: npm run dev:preview (Next + TomorrowOS internal)
1043
+ # Preview / v0 when Next-hardwired: npm run dev:preview (Next :3000 + TomorrowOS :3001)
1044
+ # After changing package.json "dev": restart the supervised process — do not leave stale next-server on :3000
920
1045
  ```
921
1046
 
922
1047
  Confirm logs: `[TomorrowOS] listening on http://0.0.0.0:?`
@@ -994,6 +1119,14 @@ More detail: https://docs.tomorrowos.org/docs/os/tizen and https://docs.tomorrow
994
1119
 
995
1120
  | Symptom | Likely cause | Fix |
996
1121
  |---------|--------------|-----|
1122
+ | Build: `Output Directory "public" is empty` | Dashboard Output Directory still = `public` on a Function/CMS app | **Unset Output Directory**; Framework Other; Fluid `api/index.ts`; redeploy. Do **not** primary-fix with `public/robots.txt` |
1123
+ | Agent asked Q2 as chat “type 1 or 2” while choice UI available | Off-protocol Q2 UX | Re-ask Q2 with **clickable** Cloudinary / Vercel Blob options (same pattern as Q1) |
1124
+ | Preview 500 / Turbopack / `ENOENT .../app` after Q1–Q3 | Leftover **root** Next from v0 starter; not Neon/Blob | **IRON GATE:** delete/move root `app/` + `next.config.*`; keep `preview/`; restart supervised `dev`; prove `/status` JSON |
1125
+ | `Internal Server Error` + logs blame missing `DATABASE_URL` in a **manual** Bash server | `dotenv` loaded `.env` only; secret is in `.env.development.local` / supervised env | Restart **supervised** Preview process; or load both env files — do **not** immediately change Neon URL |
1126
+ | `EADDRINUSE :3000` while debugging | Second `pnpm dev` / stale `next-server` after script rewrite | Kill stale Next/tsx; one process; let supervisor re-exec new `"dev"` |
1127
+ | Preview still Next after `package.json` `"dev"` → `tsx` | Supervised process never restarted | Kill old tree; wait for supervisor / restart Preview |
1128
+ | Agent spent many turns on Postgres before `/status` JSON | Skipped IRON GATE | Stop; run hygiene steps 1–5; only then debug store |
1129
+ | Q1=Neon / Q2=Blob then long Preview outage | Usually **not** the product choice | Same as root-Next / env-injection failures; choices are valid |
997
1130
  | Publish `404: NOT_FOUND` | Framework Preset still **Next.js** / no Function | Scheme A: Framework = **Other**, Fluid on, `api/index.ts` + rewrites; redeploy |
998
1131
  | Panel loads but **CMS unreachable** / `Unexpected token 'T', "The page c"...` | **`public/` deployed as static site** ??no Function | Rename to `cms-panel/`; Output empty; Scheme A Function entry; **Path B** if needed |
999
1132
  | Only `/api/status` returns JSON, `/status` is 404 | Missing **rewrites** to `/api` | Add Scheme A3 rewrite `/(.*) ??/api` (exclude existing `/api/`); redeploy |
@@ -1069,7 +1202,11 @@ Protocol complete when:
1069
1202
 
1070
1203
  ## Protocol version
1071
1204
 
1072
- `vercel-setup/1.16` - pairs with `@tomorrowos/sdk` 0.9.70+ and `VERCEL_QUESTIONS.md` 1.6.
1205
+ `vercel-setup/1.18` - pairs with `@tomorrowos/sdk` 0.9.70+ and `VERCEL_QUESTIONS.md` 1.7.
1206
+
1207
+ **Changelog 1.18:** Q1/Q2 must use **clickable choice UI** (not chat “type 1 or 2” when UI exists). Publish build error `Output Directory "public" is empty` → **clear Output Directory** (forbidden primary fix: stuff `public/robots.txt` while Output stays `public`). Strengthens static-output trap docs.
1208
+
1209
+ **Changelog 1.17:** After Q&A, agents **must** run **IRON GATE — v0 Preview hygiene** before DB rabbit holes: clear root Next leftovers, restart supervised `dev`, prove `GET /status` is TomorrowOS JSON. Documents `.env` vs `.env.development.local` / supervised-env trap. Preview decision tree + cheat-sheet rows for Turbopack/`ENOENT`, missing `DATABASE_URL` in manual Bash, `EADDRINUSE`, stale `next-server`. Neon/Blob choices are not the usual Preview failure cause.
1073
1210
 
1074
1211
  **Changelog 1.16:** Question 4 is optional Control Panel Env `CMS_PASSWORD` (blank/skip allowed; can set later). Do not use Vercel Deployment Protection / whole-site password for devices.
1075
1212
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tomorrowos/sdk",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "description": "Server SDK for building and self-hosting digital signage CMS products with TomorrowOS players.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "my-cms",
3
- "version": "1.1.0",
3
+ "version": "1.1.3",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "my-cms",
9
- "version": "1.1.0",
9
+ "version": "1.1.3",
10
10
  "dependencies": {
11
- "@tomorrowos/sdk": "^1.1.0",
11
+ "@tomorrowos/sdk": "^1.1.3",
12
12
  "dotenv": "^17.2.3",
13
13
  "tsx": "^4.19.0"
14
14
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "my-cms",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "description": "CMS server on @tomorrowos/sdk. Add your UI (React, static files, etc.) alongside this server.",
5
5
  "private": true,
6
6
  "type": "module",
@@ -12,7 +12,7 @@
12
12
  "start": "tsx server.ts"
13
13
  },
14
14
  "dependencies": {
15
- "@tomorrowos/sdk": "^1.1.1",
15
+ "@tomorrowos/sdk": "^1.1.3",
16
16
  "dotenv": "^17.2.3",
17
17
  "tsx": "^4.19.0"
18
18
  },
@@ -10,6 +10,8 @@ This starter is for **Vercel Publish** and **v0**. It follows
10
10
  - **`preview/`** — Next.js shell for v0 Preview only
11
11
  - **`vercel.json`** — `fluid`, rewrites → `/api`, `maxDuration`
12
12
 
13
+ **Publish setting (critical):** Vercel **Output Directory** must stay **empty / unset**. If the build fails with `The Output Directory "public" is empty`, clear Output Directory in Project Settings — do **not** add filler files under `public/` while Output is still set to `public`.
14
+
13
15
  ## Scaffold
14
16
 
15
17
  ```bash
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "my-cms",
3
- "version": "1.1.0",
3
+ "version": "1.1.3",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "my-cms",
9
- "version": "1.1.0",
9
+ "version": "1.1.3",
10
10
  "dependencies": {
11
- "@tomorrowos/sdk": "^1.1.0",
11
+ "@tomorrowos/sdk": "^1.1.3",
12
12
  "dotenv": "^17.2.3",
13
13
  "tsx": "^4.19.0"
14
14
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "my-cms",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "description": "TomorrowOS CMS for Vercel / v0 Publish (Fluid Function + WebSockets + optional Next Preview).",
5
5
  "private": true,
6
6
  "type": "module",
@@ -16,7 +16,7 @@
16
16
  "build": "node -e \"process.exit(0)\""
17
17
  },
18
18
  "dependencies": {
19
- "@tomorrowos/sdk": "^1.1.1",
19
+ "@tomorrowos/sdk": "^1.1.3",
20
20
  "dotenv": "^17.2.3",
21
21
  "tsx": "^4.19.0"
22
22
  },