@tomorrowos/sdk 0.9.31 → 0.9.33

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.
package/VERCEL_SETUP.md CHANGED
@@ -5,8 +5,10 @@
5
5
  >
6
6
  > **On Vercel, this file is the setup questionnaire** (not `REPLIT_SETUP.md`, not `LLM_PROMPT.md`).
7
7
  >
8
- > Protocol id: `vercel-setup/1.10` — pairs with `@tomorrowos/sdk` **0.9.31+** (`cms-starter-v0`: Fluid Publish + root Next Preview proxy).
9
- > **IRON RULE — configure immediately:** After the user answers **each** question, **finish that question’s configuration before asking the next**. Never batch “answer Q1–Q3 first, then configure everything.”
8
+ > Protocol id: `vercel-setup/1.9` — pairs with `@tomorrowos/sdk` **0.9.33+** (`templates/cms-starter-v0`, Fluid Functions WebSockets).
9
+ >
10
+ > **Copy questions verbatim from this file.** Do not invent alternate Q2 options (no “Supabase Storage”, no “disable media”, no making Blob the recommended option).
11
+ > **`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.
10
12
 
11
13
  ## User trigger (copy-paste)
12
14
 
@@ -14,17 +16,13 @@ When a human pastes a prompt like this, **read this file and start Question 1 im
14
16
 
15
17
  ```text
16
18
  Follow @tomorrowos/sdk VERCEL_SETUP.md and set up my TomorrowOS CMS.
17
- Ask only the questions listed in VERCEL_SETUP.md, in order. Do not use LLM_PROMPT.md or REPLIT_SETUP.md.
18
- Do not ask for OpenAI / Anthropic / any LLM API keys — TomorrowOS CMS setup does not require them.
19
- If any step contradicts Vercel, adapt to settings Vercel can accept (document the adaptation).
20
- CRITICAL UX: After I answer each question, configure that step immediately (Env popup / brand.json write) BEFORE asking the next question. Do not wait until all three questions are answered.
21
- Question 2 options MUST be exactly: 1 Cloudinary (recommended), 2 Vercel Blob, 3 local disk NEVER invent Supabase Storage or “disable media uploads”.
22
- Question 3 must offer Option A (website URL brand.json) OR Option B (name + colours + background + logo) — NEVER ask only for display name.
23
- Prefer Supabase Session pooler URL or Neon pooled URL. Do not treat sandbox ENETUNREACH / IPv6 :5432 errors as a hard setup failure when a pooler URL is configured for production.
24
- Do not add CMS login/auth unless I explicitly ask. Do not skip steps.
25
- Use Next.js only as a v0/Preview shell with a reverse proxy; Production Publish must remain Fluid Function TomorrowOS (api/index.ts).
26
- Before claiming Publish works: Framework Other, Fluid on, GET /status JSON, GET / Control Panel, WebSocket upgrade 101 on / or /api.
27
- Do not put startCommand or processes in vercel.json. Use cms-starter-v0 layout. Do not create a second TomorrowOS in api/ws.ts.
19
+ Ask only the questions listed in VERCEL_SETUP.md, in order — copy Question 1/2/3 wording verbatim. Do not use LLM_PROMPT.md or REPLIT_SETUP.md.
20
+ Do not ask for OpenAI / Anthropic / any LLM API keys.
21
+ Question 2 MUST be exactly: 1 Cloudinary (recommended), 2 Vercel Blob, 3 local disk. NEVER offer Supabase Storage, “no media storage”, or Blob as recommended.
22
+ After I choose a database, YOU set TOMORROWOS_STORE and DATABASE_SSL automatically do not ask me to fill those two Env Vars.
23
+ If any step contradicts Vercel, adapt to settings Vercel can accept (document the adaptation). Prefer Supabase Session pooler or Neon pooled URL.
24
+ Question 3 only updates brand.json (website URL = brand reference only no CMS login unless I ask).
25
+ Use cms-starter-v0 layout. Publish = Fluid Function (api/index.ts). Preview may use Next proxy.
28
26
  ```
29
27
 
30
28
  Alternate triggers:
@@ -131,40 +129,43 @@ v0 Preview is often a **Next.js runtime** hardwired to `next dev`. A raw `tsx se
131
129
 
132
130
  ## Questionnaire scope (STRICT)
133
131
 
134
- Ask **only** these questions, in **this exact order**. After each answer, **configure that step immediately**, then proceed:
132
+ Ask **only** these questions, in **this exact order**. **Copy the “Ask exactly” blocks verbatim** do not paraphrase options.
133
+
134
+ | Step | Section | When |
135
+ |------|---------|------|
136
+ | 1 | **Question 1** — Database (Supabase → Neon → SQLite) | Always |
137
+ | 2 | **Question 2** — Media (**Cloudinary recommended** → Vercel Blob → local) | Always (after Q1) |
138
+ | 3 | **Question 3** — Brand / TomorrowOS look (`brand.json` only) | Always last |
135
139
 
136
- | Step | Section | When done, before next question |
137
- |------|---------|----------------------------------|
138
- | 1 | **Question 1** — Database (Supabase → Neon → SQLite) | Collect DB URL / confirm SQLite → set Env Vars (`SUPABASE_URL` or `DATABASE_URL` + auto `TOMORROWOS_STORE` / `DATABASE_SSL`) |
139
- | 2 | **Question 2** — Media (**Cloudinary recommended** → Vercel Blob → local) | If Cloudinary/Blob: **open Env popup immediately** and save tokens before Q3 |
140
- | 3 | **Question 3** — Brand (`brand.json`: name, colours, background, logo, or website URL) | Write `brand.json` immediately, then run final deploy checklist |
140
+ **That is the complete list.** Do **not** ask how many screens / devices.
141
141
 
142
- **Forbidden invented Question 2 options (never offer these):**
142
+ **Forbidden invented Question 2 options (never offer these in chat or UI):**
143
143
 
144
+ - ❌ “Vercel Blob **(recommended)**” as option 1 — Blob is option **2**, not recommended over Cloudinary
144
145
  - ❌ “Supabase Storage” / S3 bucket / `SUPABASE_SERVICE_ROLE_KEY` for media
145
- - ❌ “No media storage” / “disable uploads” / “remote URLs only” as a numbered choice
146
- - ❌ Replacing Cloudinary with Blob as the **recommended** option
146
+ - ❌ “No media storage” / “disable media uploads” / “remote URLs only” as a numbered choice
147
+ - ❌ Any media list that does not start with **Cloudinary (recommended)**
147
148
 
148
- **Forbidden Question 3 shortcuts:**
149
+ **Forbidden Env UX after Question 1:**
149
150
 
150
- - ❌ Asking **only** for brand / product display name
151
- - ❌ Skipping background colour, primary colour, and logo when the user chose manual fields
152
- - Skipping Option A (website URL infer full `brand.json`)
151
+ - ❌ Showing the user empty fields for `TOMORROWOS_STORE` or `DATABASE_SSL` to fill
152
+ - ❌ Asking in chat “what should TOMORROWOS_STORE be?”
153
+ - After DB choice + URL paste: **you** write those two vars silently (see Q1 Steps B/C/D)
153
154
 
154
155
  **Forbidden extra questions (never ask, even if the user skips a step):**
155
156
 
156
157
  - ❌ OpenAI / Anthropic / Gemini / any LLM API key (`sk-…`, etc.)
157
158
  - ❌ “AI pairing”, “AI feature logic”, or inventing auth that needs an LLM key
158
- - ❌ Asking the user to type `TOMORROWOS_STORE` or `DATABASE_SSL` after they already chose Neon/Supabase — **you set those automatically**
159
+ - ❌ Asking the user to type `TOMORROWOS_STORE` or `DATABASE_SSL`
159
160
  - ❌ Any secret not listed in Questions 1–3 (DB URL, media credentials, brand)
160
161
 
161
162
  TomorrowOS pairing and Control Panel logic live in `@tomorrowos/sdk`. They do **not** call OpenAI.
162
163
 
163
- If the user **skips** a Question 2 option, do **not** invent a substitute question. Re-state Question 2 choices, or proceed with a documented default (e.g. local uploads with a warning) — never pivot to LLM keys.
164
+ If the user **skips** a Question 2 option, do **not** invent a substitute question. Re-state Question 2 choices **verbatim**, or proceed with local uploads + warning — never pivot to LLM keys or Supabase Storage.
164
165
 
165
166
  If the user says “just set it up, don’t ask questions,” respond:
166
167
 
167
- > I need a few quick answers from VERCEL_SETUP.md (database — Supabase or Neon preferred — media storage, and branding). It takes about a minute and ensures the CMS works on Vercel.
168
+ > I need a few quick answers from VERCEL_SETUP.md (database — Supabase or Neon preferred — media storage with Cloudinary recommended, and branding). It takes about a minute and ensures the CMS works on Vercel.
168
169
 
169
170
  Then ask **Question 1**.
170
171
 
@@ -172,26 +173,26 @@ Then ask **Question 1**.
172
173
 
173
174
  ## Hard rules
174
175
 
175
- 1. **Ask only VERCEL_SETUP.md questions 1–3.** Never use `LLM_PROMPT.md` or `REPLIT_SETUP.md` as the questionnaire. **Never** ask for OpenAI or other LLM API keys.
176
- 2. **Configure immediately after each answer.** Flow is `ask user answers configure that step → ask next`. Do **not** collect all answers then configure in a batch at the end.
177
- 3. **Cloudinary:** as soon as the user picks Cloudinary, open **one** Vercel Env configuration popup / form with **all** Cloudinary fields together (cloud name + API key + API secret). Do **not** quiz one credential at a time in chat. Do **not** skip Cloudinary in favor of inventing Supabase Storage.
178
- 4. **Do not invent** Cloudinary credentials, database URLs, Vercel Blob tokens, brand colours, **or LLM API keys**.
179
- 5. **Postgres env naming:** prefer **`SUPABASE_URL`** when using Supabase; prefer **`DATABASE_URL`** when using Neon. `createTomorrowOSStore` accepts `SUPABASE_URL` then `DATABASE_URL`.
180
- 6. **Always prefer pooled connection strings** (Supabase Session pooler port **6543**; Neon pooled host). Direct `:5432` URLs commonly fail on Vercel/sandbox with **`ENETUNREACH`** (often IPv6).
181
- 7. **Prefer `npx @tomorrowos/sdk init --hosting v0`** (template `cms-starter-v0`). For Replit/Railway use default `init` (`cms-starter`). Do not rebuild pairing / WebSocket / playlist APIs.
182
- 8. **Never commit secrets.** Use Vercel Environment Variables (+ optional non-secret flags in committed `.env.example`).
183
- 9. **Skip Replit-only files:** do **not** create `.replit`, `.replit-artifact`, or Replit `deploymentTarget`.
184
- 10. **SQLite is not a production store on Vercel.** Offer it only as option **3** after explicit warning. Keep `sqlitePath` as local-dev fallback in `createTomorrowOSStore`.
185
- 11. **Enable Fluid compute** for Production WebSockets ([docs](https://vercel.com/docs/functions/websockets)). Confirm Project settings after scaffold.
186
- 12. **Production:** one Vercel Function exports `http.Server` from `TomorrowOS.listen` (`api/index.ts`). **Do not** make `next start` the Production entrypoint. **Do not** spin a second TomorrowOS in `api/ws.ts`.
187
- 13. After Q1–Q3 each configured: **install deploy minimal verify** (Control Panel + WS 101). Do not run a long test suite.
188
- 14. **No inventing CMS login.** A branding URL never means “build auth.” Only add login if the user **explicitly** requests it.
189
- 15. **Skip invent.** If the user skips media choice or a credential, do not replace it with OpenAI/auth/login questions. Stay on Q1–Q3 only.
190
- 16. **Neon / Supabase helpers are agent-owned.** After the user pastes the DB URL, **you** set `TOMORROWOS_STORE` and `DATABASE_SSL` automatically — **do not** ask the user to fill those two.
191
- 17. **Publish gate:** do not mark setup complete while Production returns `404: NOT_FOUND`, `/status` returns HTML, **or** WebSocket upgrade returns 200 HTML instead of 101.
192
- 18. **Scheme A is doc-driven:** Production = Fluid Function + `export default server` per Vercel WebSockets docs — Replit starter stays unchanged.
193
-
194
- **Question order:** Q1 → **configure Q1** Q2 **configure Q2** → Q3 → **write brand.json** → deploy checklist.
176
+ 1. **Ask only VERCEL_SETUP.md questions 1–3, verbatim.** Never use `LLM_PROMPT.md` or `REPLIT_SETUP.md` as the questionnaire. **Never** ask for OpenAI or other LLM API keys.
177
+ 2. **Question 2 options are fixed:** (1) Cloudinary **(recommended)**, (2) Vercel Blob, (3) local disk. Do not reorder; do not invent Supabase Storage / disable-uploads.
178
+ 3. **Cloudinary:** open **one** Vercel Env popup with **all** Cloudinary fields together. Do **not** quiz one credential at a time.
179
+ 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`; SQLite → `TOMORROWOS_STORE=sqlite` (no `DATABASE_SSL` required).
180
+ 5. **Do not invent** Cloudinary credentials, database URLs, Vercel Blob tokens, brand colours, **or LLM API keys**.
181
+ 6. **Postgres env naming:** prefer **`SUPABASE_URL`** for Supabase; prefer **`DATABASE_URL`** for Neon.
182
+ 7. **Always prefer pooled connection strings** (Supabase Session pooler **6543**; Neon pooled host).
183
+ 8. **Prefer `npx @tomorrowos/sdk init --hosting v0`**. For Replit/Railway use default `init`. Do not rebuild pairing / WebSocket APIs.
184
+ 9. **Never commit secrets.** Use Vercel Environment Variables.
185
+ 10. **Skip Replit-only files.**
186
+ 11. **SQLite is not a production store on Vercel.**
187
+ 12. **Enable Fluid compute** for Production WebSockets.
188
+ 13. **Production:** `api/index.ts` exports `http.Server` from `TomorrowOS.listen`. Not `next start`. No second TomorrowOS in `api/ws.ts`.
189
+ 14. **Preview:** Next reverse-proxy shell when v0 is Next-hardwired.
190
+ 15. After Q&A: **configure install deploy minimal verify**.
191
+ 16. **No inventing CMS login** from a branding URL.
192
+ 17. **Skip invent.** Stay on Q1–Q3 only.
193
+ 18. **Publish gate:** `/status` JSON + WebSocket 101; no static `public/` trap.
194
+
195
+ **Question order:** Q1 → (auto-set store env) → Q2 → Q3 → execution checklist.
195
196
 
196
197
  ---
197
198
 
@@ -205,10 +206,10 @@ Then ask **Question 1**.
205
206
 
206
207
  | Layer | Preview (v0) | Publish (Production) |
207
208
  |-------|----------------|----------------------|
208
- | Host | **Root Next** `app/[[...slug]]/route.ts` proxies TomorrowOS `:3001` | **Vercel Function** `api/index.ts` → `export default server` |
209
- | Static UI | Proxied from TomorrowOS `cms-panel/` | Served by **`staticRoot`** inside `listen()` |
210
- | WebSocket | Optional / may fail in Preview | **Required** — Fluid Function upgrade |
211
- | Next files | **Root** `app/` + `next.config.mjs` (required for v0) | Ignored when Framework = Other + no-op `build` + Function entry |
209
+ | Host | Next.js shell + HTTP proxy | **Vercel Function** `api/index.ts` → `export default server` |
210
+ | Static UI | Proxied from TomorrowOS | Served by **`staticRoot`** (`cms-panel/`) inside `listen()` — **not** Vercel Output Directory |
211
+ | WebSocket | Optional / may fail in Preview | **Required** — Fluid Function upgrade (`wss://` → `/` rewritten to `/api`, or direct `/api`) |
212
+ | Next files | Allowed under `preview/` | **Must not** live at project root |
212
213
 
213
214
  **Root cause of “Control Panel works, TV cannot connect”:** HTTP can be served without WebSocket upgrades. Classic root-only `server.ts` / plain serverless handlers often return **200 HTML** on `Upgrade` instead of **101**. Official pattern is a Function that exports `http.Server` with `ws` attached ([docs](https://vercel.com/docs/functions/websockets)).
214
215
 
@@ -217,41 +218,48 @@ Then ask **Question 1**.
217
218
  **Production (Vercel / v0 starter):**
218
219
 
219
220
  ```
220
- cms-app.ts
221
- api/index.ts ← Publish Function
222
- server.ts ← local listen entry
223
- app/ Preview ONLY (Next proxy catch-all)
224
- layout.tsx
225
- [[...slug]]/route.ts
226
- next.config.mjs ← Preview only
227
- vercel.json
228
- cms-panel/
221
+ cms-app.ts ← shared TomorrowOS.listen + export const server
222
+ api/index.ts ← export { server as default } from "../cms-app.js"
223
+ server.ts ← local/Preview: same default export (npm start / internal port)
224
+ vercel.json fluid + rewrites + functions.maxDuration
225
+ package.json
229
226
  brand.json
230
- package.json "dev": "npm run dev:preview"
227
+ cms-panel/ CMS static UI (NOT named public/ — avoids static-output trap)
228
+ preview/ ← Next proxy for v0 Preview only
229
+ ```
230
+
231
+ **Preview only — keep ALL Next under `preview/`:**
232
+
233
+ ```
234
+ preview/
235
+ next.config.mjs
236
+ app/
237
+
231
238
  ```
232
239
 
233
240
  **Do NOT:**
234
241
 
235
- - ❌ Add `app/page.tsx` that returns null (v0 shows “Your v0 generation will show here”)
242
+ - ❌ Leave `next.config.mjs` + `app/` at project root
236
243
  - ❌ Set Output Directory to `public`, `cms-panel`, or `.`
237
- - ❌ Create a **second** TomorrowOS instance in `api/ws.ts`
238
- - ❌ Use `next build` / `next start` for Production
239
- - ❌ Put `startCommand` / `processes` in `vercel.json`
244
+ - ❌ Create a **second** TomorrowOS instance in `api/ws.ts` (separate isolate = broken pairing memory)
245
+ - ❌ Use `(req, res) =>` serverless handlers that drop `upgrade`
246
+ - ❌ Put `startCommand` / `processes` in `vercel.json` (v0 schema rejects them)
240
247
 
241
248
  **`package.json` scripts:**
242
249
 
243
250
  ```json
244
251
  {
245
252
  "scripts": {
246
- "dev": "npm run dev:preview",
253
+ "dev": "tsx watch server.ts",
247
254
  "dev:preview": "concurrently -k \"npm:dev:tomorrowos\" \"npm:dev:next\"",
248
255
  "dev:tomorrowos": "cross-env TOMORROWOS_INTERNAL_PORT=3001 tsx watch server.ts",
249
- "dev:next": "next dev -p 3000",
256
+ "dev:next": "cd preview && next dev -p 3000",
250
257
  "start": "tsx server.ts",
251
258
  "build": "node -e \"process.exit(0)\""
252
259
  }
253
260
  }
254
261
  ```
262
+
255
263
  - **Local / Railway-style:** `npm start` → `tsx server.ts` → SDK calls `server.listen` (no `VERCEL` env).
256
264
  - **Vercel Production:** Function loads `api/index.ts`; SDK skips `listen` when `process.env.VERCEL` is set and Vercel owns the socket.
257
265
 
@@ -272,7 +280,7 @@ const server = tomorrowos.listen({
272
280
  export { server, tomorrowos };
273
281
  ```
274
282
 
275
- SDK behaviour (0.9.30+):
283
+ SDK behaviour (0.9.32+):
276
284
 
277
285
  - Accepts WebSocket upgrades on `/`, `/api`, `/api/ws` (players may use either)
278
286
  - When `VERCEL` is set: **`autoListen` defaults to false** — do not bind a port yourself
@@ -374,7 +382,7 @@ If (1)+(2) pass but (3) fails → Function is serving HTTP only. Re-check Fluid,
374
382
 
375
383
  - Serve TomorrowOS via **Vercel Function** `api/index.ts` → `export default server` (Fluid + WebSockets).
376
384
  - Keep root **`server.ts`** for **local** `npm start` / Preview internal port only (same `cms-app.ts`).
377
- - Keep **`@tomorrowos/sdk`** (0.9.30+) and put **`tsx` in `dependencies`** for local start.
385
+ - Keep **`@tomorrowos/sdk`** (0.9.32+) and put **`tsx` in `dependencies`** for local start.
378
386
  - Node **20+**.
379
387
  - **Do not** replace the SDK with a hand-rolled server that drops WebSocket upgrades.
380
388
  - **Forbidden as the Production CMS:** `next start`, static export only, or a bare `(req,res)` handler without `http.Server` + `ws`.
@@ -384,10 +392,10 @@ If (1)+(2) pass but (3) fails → Function is serving HTTP only. Re-check Fluid,
384
392
  ```json
385
393
  {
386
394
  "scripts": {
387
- "dev": "npm run dev:preview",
395
+ "dev": "tsx watch server.ts",
388
396
  "dev:preview": "concurrently -k \"npm:dev:tomorrowos\" \"npm:dev:next\"",
389
397
  "dev:tomorrowos": "cross-env PORT=3001 TOMORROWOS_INTERNAL_PORT=3001 tsx watch server.ts",
390
- "dev:next": "next dev -p 3000",
398
+ "dev:next": "cd preview && next dev -p 3000",
391
399
  "start": "tsx server.ts",
392
400
  "build": "node -e \"process.exit(0)\""
393
401
  }
@@ -396,10 +404,10 @@ If (1)+(2) pass but (3) fails → Function is serving HTTP only. Re-check Fluid,
396
404
 
397
405
  Adapt script names / process runners as needed. **Invariant:**
398
406
 
399
- - **Local / v0 Preview `npm run dev`** = TomorrowOS `:3001` + Next `:3000` proxy.
407
+ - **Local `npm start`** = TomorrowOS Node with `server.listen`.
400
408
  - **Vercel Production** = Function `api/index.ts` (SDK `autoListen=false` when `VERCEL` is set).
401
409
  - **`build`** = no-op exit 0 (never `next build` for Production).
402
- - **Do not** ship an empty `app/page.tsx` (v0 placeholder).
410
+ - **Preview / v0** = Next on public port + TomorrowOS on internal port + proxy.
403
411
 
404
412
  ### Minimal dependencies (Production)
405
413
 
@@ -477,37 +485,32 @@ export default server;
477
485
 
478
486
  Load env with `dotenv` for local/`vercel dev`. On production Deploy, Vercel injects Environment Variables natively.
479
487
 
480
- ### Preview adapter (v0 / Next reverse proxy) — required for Preview UI
488
+ ### Preview adapter (v0 / Next reverse proxy) — required when Preview is Next-hardwired
481
489
 
482
- v0 Preview is hardwired to **root** Next (`app/`, `next.config.mjs`, `next dev`). An empty `app/page.tsx` (or returning `null`) produces **“Your v0 generation will show here”** and never shows the Control Panel.
490
+ Detect Preview / v0 (Next already present, or Preview blank while TomorrowOS logs “listening”). Then:
483
491
 
484
- **Required layout (cms-starter-v0):**
492
+ 1. Keep a **minimal** Next app **isolated when possible** (e.g. under `preview/`) so Production Framework detection does not latch onto root `next.config.*`.
493
+ 2. TomorrowOS listens on **`127.0.0.1:TOMORROWOS_INTERNAL_PORT`** (default **3001**).
494
+ 3. Add a **catch-all** Next route (or middleware) that proxies HTTP to TomorrowOS:
485
495
 
486
- ```
487
- app/
488
- layout.tsx ← minimal shell
489
- [[...slug]]/route.ts ← proxy ALL methods to http://127.0.0.1:3001
490
- next.config.mjs ← Preview only (no empty page.tsx)
491
- cms-app.ts / server.ts ← TomorrowOS on TOMORROWOS_INTERNAL_PORT=3001
492
- api/index.ts ← Production Function only (Publish unchanged)
496
+ ```ts
497
+ // Example: preview app proxy handler
498
+ // Forward method, headers, and body to http://127.0.0.1:3001/<path>
499
+ // Return the upstream status, headers, and body to the browser.
493
500
  ```
494
501
 
495
- **Agent must:**
502
+ Preferred UX: proxy **`/`** itself (rewrites) so the Preview URL shows the Control Panel at `/`, not only under `/api/...`.
496
503
 
497
- 1. Put the Next proxy at **project root** `app/` (v0 does not reliably use `preview/` alone).
498
- 2. **Do not** add `app/page.tsx` that returns null / placeholder copy.
499
- 3. Implement `app/[[...slug]]/route.ts` that forwards method, headers, and body to `TOMORROWOS_INTERNAL_URL` (default `http://127.0.0.1:3001`) and returns upstream status/body. On connection failure, return a short HTML 502 explaining `npm run dev`.
500
- 4. Set `"dev": "npm run dev:preview"` so the default v0/dev command starts **both** TomorrowOS (:3001) and Next (:3000).
501
- 5. Keep Production on Fluid Function (`api/index.ts`, `framework: null`, no-op `build`). **Do not** make `next start` / `next build` the Publish path.
502
- 6. Document: **Preview = Next proxy + TomorrowOS :3001; Publish = api/index.ts Fluid (unchanged).**
504
+ 4. Start **both** processes for Preview (`dev:preview` pattern above).
505
+ 5. Document in the final summary: **Preview = Next shell + proxy; Publish = Node only.**
503
506
 
504
507
  **Forbidden:**
505
508
 
506
- - Empty v0 placeholder page as the Preview UX
507
- - Making Next the Production CMS
508
- - Deleting `api/index.ts` / `cms-app.ts` to “fix” Preview
509
- - Claiming Preview complete while the iframe still says “Your v0 generation will show here”
510
- - Claiming setup complete if Production entrypoint is still `next start` or `GET /` is `404: NOT_FOUND`
509
+ - Making Next the Production CMS or the only server after Publish.
510
+ - Deleting TomorrowOS `server.ts` because Preview needed Next.
511
+ - Claiming setup complete if Production entrypoint is still `next start`.
512
+ - Claiming setup complete if Production `GET /` is `404: NOT_FOUND`.
513
+ - Creating root `api/index.*` + `routes` catch-all that exports a raw `http.Server` “to fix 404”.
511
514
 
512
515
  ### `vercel.json` (Production — explicit Node, not Next)
513
516
 
@@ -566,7 +569,17 @@ If Vercel’s UI and this file disagree on bundling, **prefer whatever keeps a s
566
569
  ## Question 1 — Database (always)
567
570
 
568
571
  > **Do not** ask screen counts. Present all three options; recommend **Supabase** for Vercel fleets.
569
- > **After the user picks 1 / 2 / 3:** finish Step B/C/D (collect URL or confirm SQLite + set Env Vars) **before** asking Question 2.
572
+ > **After the user picks a database:** collect the URL if needed, then **you** set `TOMORROWOS_STORE` + `DATABASE_SSL` (see mapping below). The customer must **never** be asked to type those two names or values.
573
+
574
+ ### Agent-owned env mapping (mandatory)
575
+
576
+ | User chose | You set (silently) | User only provides |
577
+ |------------|--------------------|--------------------|
578
+ | 1 Supabase | `TOMORROWOS_STORE=supabase`, `DATABASE_SSL=true` | `SUPABASE_URL` (pooler string) |
579
+ | 2 Neon | `TOMORROWOS_STORE=postgres`, `DATABASE_SSL=true` | `DATABASE_URL` (pooled string) |
580
+ | 3 SQLite | `TOMORROWOS_STORE=sqlite` | Confirmation only |
581
+
582
+ 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.
570
583
 
571
584
  ### Step A — Ask storage choice
572
585
 
@@ -584,11 +597,9 @@ If Vercel’s UI and this file disagree on bundling, **prefer whatever keeps a s
584
597
 
585
598
  ### Step B — If **1 / Supabase**
586
599
 
587
- **Immediately** (same turn as acknowledging their choice do not ask Q2 yet) ask for the connection string:
588
-
589
- **Ask exactly (connection string only):**
600
+ **Ask exactly (connection string only — not TOMORROWOS_STORE / DATABASE_SSL):**
590
601
 
591
- > Paste your Supabase Postgres connection string. I will store it as **`SUPABASE_URL`**.
602
+ > Paste your Supabase Postgres connection string. I will store it as **`SUPABASE_URL`**. I will set **`TOMORROWOS_STORE`** and **`DATABASE_SSL`** for you automatically.
592
603
  >
593
604
  > In Supabase: **Project Settings → Database → Connection string → Connection pooling** (Session mode).
594
605
  > Preferred shape:
@@ -596,41 +607,37 @@ If Vercel’s UI and this file disagree on bundling, **prefer whatever keeps a s
596
607
  >
597
608
  > **Do not** use the direct host `db.*.supabase.co:5432` for Vercel.
598
609
 
599
- **You must then (automatic — do NOT ask the user to type these):**
610
+ **You must then (automatic — do NOT ask the user to type store/ssl vars):**
600
611
 
601
- 1. Set Vercel Env Vars **now** (open Env UI if available):
602
- - `SUPABASE_URL=<user pooler string>`
603
- - `TOMORROWOS_STORE=supabase` ← **agent sets this**
604
- - `DATABASE_SSL=true` ← **agent sets this** (unless user already said SSL is off)
605
- 2. Wire store in `cms-app.ts` / `server.ts` as in **Runtime & Vercel deploy rules**.
612
+ 1. Set Vercel Env Vars in one step:
613
+ - `SUPABASE_URL=<user pooler string>` ← from user
614
+ - `TOMORROWOS_STORE=supabase` ← **you set; never ask**
615
+ - `DATABASE_SSL=true` ← **you set; never ask**
616
+ 2. Wire `cms-app.ts` / `server.ts` store as in **Runtime & Vercel deploy rules**.
606
617
  3. Optional committed `.env.example` with **placeholders only**.
607
618
  4. **Do not** commit the real connection string.
608
- 5. **Do not** open a second form asking for `TOMORROWOS_STORE` or `DATABASE_SSL`.
609
- 6. **Only after Env Vars are saved** → ask **Question 2**.
619
+ 5. **Do not** open a form whose blank fields include `TOMORROWOS_STORE` or `DATABASE_SSL`.
610
620
 
611
621
  ### Step C — If **2 / Neon**
612
622
 
613
- **Immediately** ask for the connection string (do not ask Q2 yet):
614
-
615
623
  **Ask exactly (connection string only):**
616
624
 
617
- > Paste your **Neon pooled** Postgres connection string. I will store it as **`DATABASE_URL`**.
625
+ > Paste your **Neon pooled** Postgres connection string. I will store it as **`DATABASE_URL`**. I will set **`TOMORROWOS_STORE`** and **`DATABASE_SSL`** for you automatically.
618
626
  >
619
627
  > In Neon: **Dashboard → Connection details → Pooled connection**.
620
628
  > Typical shape:
621
629
  > `postgresql://[user]:[password]@[endpoint]-pooler.[region].aws.neon.tech/[dbname]?sslmode=require`
622
630
 
623
- **You must then (automatic — do NOT ask the user to type these):**
631
+ **You must then (automatic):**
624
632
 
625
- 1. Set Vercel Env Vars **now**:
626
- - `DATABASE_URL=<user pooled string>`
627
- - `TOMORROWOS_STORE=postgres` ← **agent sets this**
628
- - `DATABASE_SSL=true` ← **agent sets this**
633
+ 1. Set Vercel Env Vars:
634
+ - `DATABASE_URL=<user pooled string>` ← from user
635
+ - `TOMORROWOS_STORE=postgres` ← **you set; never ask**
636
+ - `DATABASE_SSL=true` ← **you set; never ask**
629
637
  2. Wire `createTomorrowOSStore` for Postgres.
630
638
  3. **Do not** commit the real connection string.
631
- 4. **Do not** ask the user to fill `TOMORROWOS_STORE` or `DATABASE_SSL`.
639
+ 4. **Do not** ask the user to fill `TOMORROWOS_STORE` or `DATABASE_SSL` in chat or Env UI.
632
640
  5. If you see `ENETUNREACH` on `:5432`, switch to Neon’s **pooled** URL.
633
- 6. **Only after Env Vars are saved** → ask **Question 2**.
634
641
 
635
642
  ### Step D — If **3 / SQLite**
636
643
 
@@ -643,9 +650,8 @@ Only proceed after the user explicitly confirms.
643
650
  **You must then (automatic):**
644
651
 
645
652
  1. Set `TOMORROWOS_STORE=sqlite` yourself (do not ask the user to type it).
646
- 2. Keep `sqlitePath` in `cms-app.ts` for `data/tomorrowos.db`.
653
+ 2. Keep `sqlitePath` for `data/tomorrowos.db`.
647
654
  3. Warn again in the final summary that they should move to Supabase or Neon for real devices.
648
- 4. **Then** ask **Question 2**.
649
655
 
650
656
  **Later in Question 3:** set `cms.hostingTarget` to **`"vercel"`** (all Q1 branches).
651
657
 
@@ -655,11 +661,11 @@ Only proceed after the user explicitly confirms.
655
661
 
656
662
  > **This question is only about media files (images/videos).**
657
663
  > 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.
658
- > **Copy the three options below verbatim.** Do **not** substitute Supabase Storage,disable uploads”, or make Vercel Blob the recommended option.
664
+ > **Copy the three options below verbatim.** Wrong examples that must **never** appear: “Vercel Blob (recommended)”, Supabase Storage”, “No media storage / disable uploads”.
659
665
 
660
666
  ### Step A — Ask storage choice
661
667
 
662
- **Ask exactly (wording must match):**
668
+ **Ask exactly (wording must match — Cloudinary is recommended):**
663
669
 
664
670
  > How should playlist media (images/videos) be stored?
665
671
  >
@@ -667,13 +673,15 @@ Only proceed after the user explicitly confirms.
667
673
  >
668
674
  > **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.
669
675
  >
670
- > **3. Local disk only** — `cms-panel/uploads` (OK for quick local tests; **not** for production Vercel fleets — files are ephemeral).
676
+ > **3. Local disk only** — `cms-panel/uploads` or `public/uploads` (OK for quick local tests; **not** for production Vercel fleets — files are ephemeral).
671
677
  >
672
678
  > Reply with **1**, **2**, or **3** (or “Cloudinary” / “Vercel Blob” / “local”).
673
679
 
674
- ### Step B If **1 / Cloudinary** (configure **immediately** one Env popup)
680
+ If you catch yourself about to offer Blob-as-recommended, Supabase Storage, or “no media”, **stop and paste the block above instead**.
675
681
 
676
- > **Critical UX rule:** As soon as the user chooses Cloudinary, **immediately** open the **Vercel Environment Variables configuration popup / form** with **all Cloudinary fields in one UI**. Do **not** ask Q3 first. Do **not** ask for cloud name, then key, then secret in separate chat messages.
682
+ ### Step B If **1 / Cloudinary** (same Question 2 **one Env popup**)
683
+
684
+ > **Critical UX rule:** Immediately open the **Vercel Environment Variables configuration popup / form** with **all Cloudinary fields in one UI**. Do **not** ask for cloud name, then key, then secret in separate chat messages.
677
685
 
678
686
  **Preferred:** Vercel Env Vars UI (or Agent secrets form) with these fields together:
679
687
 
@@ -699,13 +707,10 @@ Only proceed after the user explicitly confirms.
699
707
  2. **Do not** invent placeholders.
700
708
  3. **Do not** proceed to Question 3 until all three required vars exist.
701
709
  4. **Do not** run a formal Cloudinary upload test unless the user asks.
702
- 5. **Only after Cloudinary Env Vars are saved** → ask **Question 3**.
703
710
 
704
711
  The SDK auto-detects these env vars.
705
712
 
706
- ### Step C — If **2 / Vercel Blob** (configure **immediately**)
707
-
708
- **Immediately** (do not ask Q3 yet):
713
+ ### Step C — If **2 / Vercel Blob** (same Question 2)
709
714
 
710
715
  **Ask exactly:**
711
716
 
@@ -733,51 +738,46 @@ import { put } from "@vercel/blob";
733
738
 
734
739
  4. Warn: until the project’s Blob bridge is wired, Control Panel **Media** status may show local/ephemeral — verify uploads return `https://*.blob.vercel-storage.com/...` before calling setup complete.
735
740
  5. Do **not** proceed to Question 3 until `BLOB_READ_WRITE_TOKEN` exists (or Blob is linked and token is confirmed in the Vercel dashboard).
736
- 6. **Only then** → ask **Question 3**.
737
741
 
738
742
  ### Step D — If **3 / local uploads**
739
743
 
740
- 1. Ensure `cms-panel/uploads` exists (or `public/uploads` on Replit-style starters).
741
- 2. Warn clearly: files may vanish on redeploy; players need stable HTTPS URLs — **Cloudinary is strongly preferred** on Vercel.
742
- 3. **Then** ask **Question 3**.
744
+ 1. `mkdir -p public/uploads`
745
+ 2. Warn clearly: files may vanish on redeploy; players need stable HTTPS URLs — Cloudinary or Vercel Blob is strongly preferred for Vercel.
743
746
 
744
747
  ---
745
748
 
746
749
  ## Question 3 — Brand (`brand.json` only)
747
750
 
748
- > Updates **only** `brand.json`. Does **not** change Vercel project settings, Env Vars, or store wiring beyond what Q1–Q2 already required.
751
+ > Updates **only** `brand.json`. Does **not** change Vercel project settings, Env Vars, or `server.ts` store wiring beyond what Q1–Q2 already required.
749
752
  >
750
753
  > **IRON RULE — website URL ≠ build a website / login / auth CMS.**
751
- > If the user pastes a URL (or says “make it look like this site”), that input is **reference material for `brand.json` only** (name, colours, fonts, logo, tagline, **background**).
754
+ > If the user pastes a URL (or says “make it look like this site”), that input is **reference material for `brand.json` only** (name, colours, fonts, logo, tagline).
752
755
  > **Do not** scaffold a login page, signup, OAuth, gated dashboard, or copy the reference site’s IA/pages — **unless the user explicitly asks for CMS login / auth**.
753
- > Default TomorrowOS Control Panel has **no login**. Keep it that way.
754
- > **Never ask only for a display name.** Always offer Option A (URL) **or** the full Option B field list below.
756
+ > Default TomorrowOS Control Panel has **no login**. Keep it that way.
755
757
 
756
758
  ### Step A — Ask branding input
757
759
 
758
760
  **Ask exactly:**
759
761
 
760
- > Let’s brand your TomorrowOS experience. Choose **one**:
762
+ > Let’s brand your TomorrowOS experience. You can answer in either way:
761
763
  >
762
- > **Option A — Website URL (recommended if you have a site)**
763
- > Paste **one public website URL**. I will use it **only** to fill **`brand.json`**: product name, tagline, primary / secondary / **background** / text colours, fonts, and logo.
764
- > I will **not** rebuild that website or add login — unless you explicitly ask for login/auth later.
764
+ > **Option A — Website URL only (brand reference)**
765
+ > Paste **one public website URL**. I will use it **only** to infer colours, fonts, name, tagline, and logo for **`brand.json`**.
766
+ > I will **not** rebuild that website, add a login page, or change Control Panel features — unless you explicitly ask for login/auth later.
765
767
  >
766
- > **Option B — Manual brand fields**
767
- > Send as many of these as you can (name + at least one colour + logo is ideal):
768
- > 1. **Product / venue name** (required)
768
+ > **Option B — Manual fields**
769
+ > Provide:
770
+ > 1. **Product / venue name**
769
771
  > 2. **Tagline** (optional)
770
772
  > 3. **Primary colour** (hex, e.g. `#FF8A3D`)
771
- > 4. **Background colour** (hex — page / panel background)
773
+ > 4. **Background colour** (hex, optional default `#FAFAF9`)
772
774
  > 5. **Text colour** (hex, optional — default `#0A0908`)
773
775
  > 6. **Secondary / accent colour** (hex, optional)
774
- > 7. **Logo** — upload SVG/PNG into the project, or a public image URL I can fetch into `./assets/`
775
- >
776
- > Reply with a URL **or** the manual fields. Do not send only a name with no colours/logo unless you want defaults (I will confirm defaults before writing).
776
+ > 7. **Logo** — upload SVG/PNG into the project, or a URL I can fetch into `./assets/`
777
777
 
778
778
  ### Step B — If the user gives **only a website URL** (Option A)
779
779
 
780
- **You must (immediately — do not defer brand write to “end of setup”):**
780
+ **You must:**
781
781
 
782
782
  1. **Fetch and inspect** the page (HTTP GET the URL; follow one redirect if needed). Do not invent colours.
783
783
  2. **Write / update `brand.json` only** from what you infer. Touch nothing else for this step (no new pages, no auth, no Next marketing site, no login UI).
@@ -792,8 +792,7 @@ import { put } from "@vercel/blob";
792
792
  - **Logo:** prefer `og:image`, then `link[rel="icon"]` / apple-touch-icon, then header `<img>` logo. Download into `./assets/logo.png` or `./assets/logo.svg` and set `logoPath` accordingly. If only favicon exists, use it and note size in the summary.
793
793
  4. **Contrast check:** if background is dark, set `activationScreen.theme` to `"dark"` and add `logoPathOnDark` when a light logo variant exists.
794
794
  5. **Infer `cms.useCase`** from page content when obvious (restaurant / retail / corporate / etc.); otherwise `"other"`.
795
- 6. **Show a one-line summary** to the user (name + primary + background hex + logo path) while writing the file — do not ask a second branding question unless fetch failed.
796
- 7. **Then** run the final deploy checklist (seed layout already done / Publish).
795
+ 6. **Show a one-line summary** to the user (name + primary hex + logo path) before or while writing the file — do not ask a second branding question unless fetch failed.
797
796
  7. If the URL is unreachable, blocked, or has no usable styles, say so and fall back to **Option B** manual questions for missing fields only.
798
797
 
799
798
  **Explicitly forbidden when the user only gave a URL (no login request):**
@@ -838,31 +837,27 @@ Validate mentally against `brand.schema.json`. Minimum:
838
837
 
839
838
  Always set **`cms.hostingTarget`: `"vercel"`** without asking. Default `expectedScreens` to `5` unless the user already volunteered a number. Set `cmsEndpoint` only if they already know the public `https://…vercel.app` URL; otherwise tell them to pair TVs with the **Published** HTTPS origin (player maps `https://` → `wss://`).
840
839
 
841
- **If Option B (manual):** map every field the user provided into `brand.json` **immediately** (`name`, `primaryColor`, `backgroundColor`, `textColor`, `secondaryColor`, `logoPath`, `tagline`). If they only sent a name, ask once for missing **background**, **primary colour**, and **logo** — do not proceed to deploy with name-only branding without confirming defaults.
842
-
843
840
  ---
844
841
 
845
- ## After Q1–Q3 configuredfinal deploy checklist
846
-
847
- > Env Vars for Q1/Q2 and `brand.json` for Q3 should **already be done** during each question. This section is **scaffold + Publish verify only** — do not re-ask for Cloudinary/DB secrets here.
842
+ ## After all answersexecution checklist
848
843
 
849
- ### A. Seed the project (do this early if the repo is empty — before or during Q1)
844
+ ### A. Seed the project
850
845
 
851
846
  ```bash
852
- npx @tomorrowos/sdk@latest init . --hosting v0
847
+ npx @tomorrowos/sdk@latest init .
853
848
  # If not empty and user confirms starter overwrite:
854
- # npx @tomorrowos/sdk@latest init . --hosting v0 --force
849
+ # npx @tomorrowos/sdk@latest init . --force
855
850
  ```
856
851
 
857
852
  Then (**Scheme A — mandatory for Publish**):
858
853
 
859
- 1. Ensure **`cms-app.ts`** + **`api/index.ts`** (`export default server`) + **`cms-panel/`**.
860
- 2. If Preview needs Next: keep Next under **`preview/`** only.
861
- 3. Write `vercel.json` per **Scheme A3** (`fluid`, rewrites, `maxDuration: 300` — **no** `startCommand`).
862
- 4. **Force Project Settings** per **Scheme A4** (Framework = Other, Output Directory empty, Fluid on).
863
- 5. Do **not** add a second TomorrowOS in `api/ws.ts`.
854
+ 1. Keep **`server.ts`** at **project root** with top-level `listen()` + `export default server`.
855
+ 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.
856
+ 3. Write `vercel.json` per **Scheme A3** (`fluid`, `framework: null`, no-op `buildCommand` — **no** `startCommand`).
857
+ 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.
858
+ 5. Do **not** add root `api/` catch-all for raw `http.Server`.
864
859
  6. Do **not** add `.replit*`.
865
- 7. If Q2 = Vercel Blob: install `@vercel/blob` and wire the upload bridge (should already be started in Q2).
860
+ 7. If Q2 = Vercel Blob: install `@vercel/blob` and wire the upload bridge.
866
861
 
867
862
  ### B. Environment Variables (Vercel dashboard + local)
868
863
 
@@ -870,8 +865,8 @@ Then (**Scheme A — mandatory for Publish**):
870
865
  |------|----------|--------------|
871
866
  | `SUPABASE_URL` | If Q1 = Supabase | User pastes URL |
872
867
  | `DATABASE_URL` | If Q1 = Neon | User pastes URL |
873
- | `TOMORROWOS_STORE` | Yes | **Agent auto-sets** (`supabase` / `postgres` / `sqlite`) |
874
- | `DATABASE_SSL` | Yes for Postgres | **Agent auto-sets** `true` |
868
+ | `TOMORROWOS_STORE` | Yes | **Agent auto-sets only** — never a blank field for the user (`supabase` / `postgres` / `sqlite`) |
869
+ | `DATABASE_SSL` | Yes for Postgres | **Agent auto-sets only** — never a blank field for the user (`true`) |
875
870
  | `CLOUDINARY_*` | If Q2 = Cloudinary | User via **one Env popup** (all fields) |
876
871
  | `BLOB_READ_WRITE_TOKEN` | If Q2 = Vercel Blob | User / Blob link |
877
872
  | `TOMORROWOS_INTERNAL_PORT` | Preview only | Agent (e.g. `3001`) |
@@ -945,7 +940,7 @@ Tell the user:
945
940
  | Separate `api/ws.ts` with a second `new TomorrowOS` | Two isolates — pairing memory split | **One** Function only (`api/index.ts` → shared `cms-app.ts`) |
946
941
  | `vercel inspect` shows Output = `public if it exists` | Framework=Other static default | Force Output Directory **empty**; use `cms-panel/`; Path B redeploy |
947
942
  | Build runs `next build` / missing routes-manifest | Next auto-detected from root `next.config` / `app/` | Move Next to `preview/`; Framework null/Other; `build` = no-op |
948
- | Preview blank / “Your v0 generation will show here” | Empty `app/page.tsx` or Next not proxying / TomorrowOS not on :3001 | Root `app/[[...slug]]/route.ts` proxy; `"dev": "npm run dev:preview"`; no null page |
943
+ | Preview blank but logs show TomorrowOS listening | v0 Next intercepts public port | Add Next reverse proxy internal TomorrowOS port |
949
944
  | Preview works, Publish broken / Next-only | Production still on Next | Switch Production to Fluid Function `api/index.ts` |
950
945
  | `ENETUNREACH` / IPv6 / `:5432` | Direct Postgres URL | Use **pooled** URL (Supabase `:6543` or Neon pooler) |
951
946
  | Control Panel OK, devices never stay paired after restart | SQLite / ephemeral disk | Use Supabase or Neon |
@@ -953,10 +948,9 @@ Tell the user:
953
948
  | Blob uploads 401 / missing token | Blob not linked | Vercel Storage → Blob + `BLOB_READ_WRITE_TOKEN` |
954
949
  | WebSocket fails on Publish | Fluid off / static-only / Next as Production / no Function WS | Enable Fluid; Scheme A; test 101 upgrade |
955
950
  | Devices fail only on Preview | Next proxy cannot upgrade `wss` | Expected — pair on Publish URL |
956
- | Agent asked user to fill `TOMORROWOS_STORE` / `DATABASE_SSL` | Off-protocol | Agent must auto-set those after Neon/Supabase choice |
957
- | Agent offered Supabase Storage / “disable media” for Q2 | Invented options | Re-ask Q2 verbatim: Cloudinary Blob local |
958
- | Agent asked only brand display name for Q3 | Shortcut | Re-ask Q3 Option A (URL) or full Option B (colours + background + logo) |
959
- | Agent batched all Q&A then configured Env at the end | Off-protocol UX | Stop; configure the current question’s Env before asking the next |
951
+ | Agent asked user to fill `TOMORROWOS_STORE` / `DATABASE_SSL` | Off-protocol | Agent must auto-set after DB choice; remove those fields from user Env forms |
952
+ | Agent offered Blob-as-recommended / Supabase Storage / “no media” for Q2 | Invented options | Re-ask Q2 verbatim: **1 Cloudinary (recommended)**, 2 Blob, 3 local |
953
+ | Agent asked Cloudinary key/secret in three chat turns | Off-protocol | Use **one Env popup** with all Cloudinary fields |
960
954
  | Agent asked for OpenAI / `sk-` key during setup | Hallucinated “AI pairing” requirement | **Refuse.** TomorrowOS does not need LLM keys. Return to Q2 media choices only |
961
955
  | Agent built a login page after user pasted a URL | Misread brand reference as product scope | Remove login; keep starter Control Panel; apply URL only to `brand.json` |
962
956
  | Secrets in git | Mistake | Rotate keys; move to Vercel Env Vars |
@@ -972,11 +966,10 @@ Tell the user:
972
966
  | Deploy config | `.replit`, artifact `kind=web` | `vercel.json` + Fluid; **Framework = Other**; **no** `.replit` |
973
967
  | `hostingTarget` | `"here"` | `"vercel"` |
974
968
  | Database | Supabase (primary) | Supabase **or Neon** (pooled); SQLite demo only |
975
- | Media | Cloudinary or Replit Object Storage | **Cloudinary (recommended)** or Vercel Blob (not Supabase Storage) |
969
+ | Media | Cloudinary or Replit Object Storage | Cloudinary **or Vercel Blob** |
976
970
  | Preview | Replit Preview / Autoscale Node | **Next shell + proxy** when v0 hardwires Next |
977
- | Publish | Long-lived Node | **Fluid Function + WebSockets** (not Next); gate on `/status` JSON + WS 101 |
978
- | Brand from URL | Not in Replit protocol | **Option A** — full **`brand.json`** (name/colours/background/logo); never invent login |
979
- | Configure timing | Per Replit protocol | **After each question immediately** (never batch Q1–Q3 then configure) |
971
+ | Publish | Long-lived Node | **Node `TomorrowOS.listen` only** (not Next); gate on no `404 NOT_FOUND` |
972
+ | Brand from URL | Not in Replit protocol | **Option A** — infer **`brand.json` only**; never invent login |
980
973
 
981
974
  ---
982
975