@tomorrowos/sdk 0.9.35 → 0.9.40

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.
@@ -0,0 +1,462 @@
1
+ # Lovable Setup Protocol for TomorrowOS CMS
2
+
3
+ > **This file is a contract for Lovable AI / Lovable Agent.**
4
+ > Follow it literally. Ask questions in order. Do not invent answers. Do not write or rewrite application logic until the Q&A is complete. Prefer configuring Secrets, `.env`, `brand.json`, and `server.ts` store wiring over inventing a custom CMS.
5
+ >
6
+ > **On Lovable, this file is the ONLY questionnaire.**
7
+
8
+ ## User trigger (copy-paste)
9
+
10
+ When a human pastes a prompt like this, **read this file and start Question 1 immediately**:
11
+
12
+ ```text
13
+ Follow @tomorrowos/sdk LOVABLE_SETUP.md and set up my TomorrowOS CMS.
14
+ Ask only the questions listed in LOVABLE_SETUP.md, in order. Do not use LLM_PROMPT.md.
15
+ Do not build a Vite/React mock of TomorrowOS — use @tomorrowos/sdk cms-starter (Node + WebSocket).
16
+ Question 3 only updates brand.json.
17
+ After setup questions (including Cloudinary Secrets if chosen), configure and deploy — minimal verification only.
18
+ Never test Supabase connection — save SUPABASE_URL and move on. No Supabase or WebSocket tests.
19
+ Do not skip steps.
20
+ ```
21
+
22
+ Alternate triggers that also activate this protocol:
23
+
24
+ - “Set up TomorrowOS on Lovable using LOVABLE_SETUP.md”
25
+ - “Configure my TomorrowOS CMS (Lovable wizard)”
26
+
27
+ When any of these triggers fire, **ignore `LLM_PROMPT.md` entirely** for the Q&A phase.
28
+
29
+ **Before Question 1:** read **IRON RULE: real Node CMS only** below. If the project is already a default Lovable Vite + React app with no `@tomorrowos/sdk` server, **do not** keep building that as the TV CMS — seed or replace with the SDK `cms-starter` layout first.
30
+
31
+ ---
32
+
33
+ ## IRON RULE: real Node CMS only (Lovable Cloud Publish is not enough)
34
+
35
+ > **This is the #1 deployability rule. Non-negotiable.**
36
+
37
+ TomorrowOS CMS needs a **Node.js process** that:
38
+
39
+ - installs **`@tomorrowos/sdk`**
40
+ - runs `tsx server.ts` / `TomorrowOS.listen(...)`
41
+ - serves the Control Panel at **`GET /`**
42
+ - keeps **WebSocket** connections open for Tizen / BrightSign players
43
+
44
+ Lovable’s default app is **Vite + React + Lovable Cloud** (Postgres / Auth / Storage / Edge Functions). That stack **cannot** replace `@tomorrowos/sdk`’s long-lived WebSocket CMS by itself.
45
+
46
+ | Required | Forbidden |
47
+ |----------|-----------|
48
+ | Project seeded from **`npx @tomorrowos/sdk init`** (or `templates/cms-starter`) | Rebuilding pairing / WebSocket / `setPolicy` in React or Edge Functions |
49
+ | Live Node entry: `npm run start` → `tsx server.ts` | Claiming Lovable Cloud **Publish** alone is the TV CMS endpoint |
50
+ | Control Panel HTML at `/` via `staticRoot: public` | Vite-only SPA that “looks like” a CMS but has no TomorrowOS server |
51
+ | Durable DB via Supabase / Lovable Cloud Postgres URL | Inventing a hand-rolled store protocol |
52
+
53
+ ### How Publish works on Lovable (read carefully)
54
+
55
+ 1. **Lovable Agent** = wizard + editor (ask Q1–Q3, write Secrets / `brand.json` / `server.ts`).
56
+ 2. **GitHub sync** = required so the Node CMS can be hosted where Node + WebSocket work.
57
+ 3. **TV-facing CMS host** (pick one; do not invent a fourth):
58
+ - **Railway** (recommended companion) — long-lived Node, `npm run start`
59
+ - **Render / Fly.io** — same pattern as Railway
60
+ - **Vercel Fluid** — only if following **`VERCEL_SETUP.md`** / `cms-starter-v0` (do not mix Replit-style root `server.ts` with broken Vercel static deploy)
61
+
62
+ **Do not tell the user “Publish on Lovable Cloud” is done** unless the **Node CMS URL** (Railway / Render / Fly / Vercel Fluid) is live and serves the Control Panel at `/`.
63
+
64
+ Lovable Cloud remains useful for:
65
+
66
+ - **Secrets** UI
67
+ - **Postgres** (same family as Supabase — use as `SUPABASE_URL` / database URL)
68
+ - **Lovable Cloud Storage** (Object Storage replacement — see Question 2)
69
+
70
+ ---
71
+
72
+ ## Questionnaire scope (STRICT — read before asking anything)
73
+
74
+ Lovable Agent must ask **only** the questions defined in **this file**, in **this exact order**:
75
+
76
+ | Step | Section | When |
77
+ |------|---------|------|
78
+ | 1 | **Question 1** — Supabase / Lovable Cloud Postgres connection string | Always |
79
+ | 2 | **Question 2** — Media storage (+ Cloudinary Secrets if chosen) | Always (after Q1) |
80
+ | 3 | **Question 3** — Brand / TomorrowOS app look | Always last, before execution |
81
+
82
+ **That is the complete list.** There are no other setup questions. **Do not** ask how many screens / devices. There is **no** “Question 2b” — Cloudinary credentials are collected **inside Question 2**, immediately after the user chooses Cloudinary.
83
+
84
+ **Critical:** Question 2 is **not complete** when the user says “Cloudinary” or “yes”. You **must** immediately collect `CLOUDINARY_CLOUD_NAME`, `CLOUDINARY_API_KEY`, and `CLOUDINARY_API_SECRET` **before** Question 3. Do **not** skip to branding with placeholder or invented credentials.
85
+
86
+ If the user volunteers extra info early (e.g. brand name before Q3), **record it** and still ask the current question’s required fields you do not yet have. Do not skip ahead to execution until Q1–Q3 are complete (Question 2 includes Cloudinary Secrets when applicable).
87
+
88
+ If the user says “just set it up, don’t ask questions,” respond:
89
+
90
+ > I need a few quick answers from LOVABLE_SETUP.md (Postgres connection string, media storage — plus Cloudinary credentials if you choose Cloudinary — and branding). It takes about a minute and ensures the CMS deploys correctly with Lovable + a Node host.
91
+
92
+ Then ask **Question 1** — do not switch to `LLM_PROMPT.md`.
93
+
94
+ ---
95
+
96
+ ## Hard rules
97
+
98
+ 1. **Ask only LOVABLE_SETUP.md questions 1–3.** Never use `LLM_PROMPT.md` on Lovable for this wizard.
99
+ 2. **One question at a time.** Wait for the user’s answer before asking the next (unless they already answered several in one message). **Exception:** If the user chooses Cloudinary in Question 2, **stay on Question 2** and immediately collect Cloudinary Secrets — do **not** announce a separate “2b”.
100
+ 3. **Do not invent** Cloudinary credentials, database URLs, or brand colours. **Do not ask** screen / device count.
101
+ 4. **Prefer `SUPABASE_URL`** for the Postgres connection string Secret name (same as Replit). Lovable Cloud / linked Supabase both work. Do not commit passwords into git.
102
+ 5. **Prefer `npx @tomorrowos/sdk init`** (or copy `templates/cms-starter`) as the project seed. Do not rebuild pairing, WebSocket, or playlist APIs from scratch.
103
+ 6. **Never commit secrets.** Put credentials in Lovable **Cloud → Secrets** (and the Node host’s env vars: Railway / Render / Fly / Vercel).
104
+ 7. After Q&A, **configure and deploy** — do **not** run a long test suite (see **Post-Q&A: minimal verification only**).
105
+ 8. **Supabase / Lovable Postgres: configure only — never test.** Save `SUPABASE_URL` + `TOMORROWOS_STORE=supabase` and proceed. Do not ping Postgres or treat preview DB errors as a failed setup.
106
+ 9. **Do not** replace `@tomorrowos/sdk` with Edge Functions, React state, or a fake WebSocket.
107
+ 10. **Media durability:** Lovable has **no Replit Object Storage mount** on a local `public/uploads` disk. Use **Cloudinary** or **Lovable Cloud Storage** (see Question 2). Plain local `public/uploads` is ephemeral on most Node hosts unless a volume is attached.
108
+
109
+ **Question order (always):**
110
+
111
+ - Q1 → Q2 → Q3 → execution checklist
112
+
113
+ ---
114
+
115
+ ## Question 1 — Supabase / Lovable Cloud Postgres (always)
116
+
117
+ > **This is the first setup question.** Always configure durable Postgres. **Do not** ask how many screens / devices.
118
+
119
+ **Ask exactly:**
120
+
121
+ > Paste your Postgres connection string. I will store it as Secret **`SUPABASE_URL`** (works for Lovable Cloud DB or a linked Supabase project).
122
+ >
123
+ > Example shape: `postgresql://postgres.[PROJECT]:[PASSWORD]@aws-0-[REGION].pooler.supabase.com:6543/postgres`
124
+ >
125
+ > If you use **Lovable Cloud**, open **Cloud → Database** (or project connection settings) and copy the Postgres URI. Also confirm SSL (usually **yes**).
126
+
127
+ **You must then:**
128
+
129
+ 1. Save Lovable Cloud Secret: `SUPABASE_URL=<user value>`
130
+ 2. Save also:
131
+ - `TOMORROWOS_STORE=supabase`
132
+ - `DATABASE_SSL=true` (unless the user explicitly says SSL is off)
133
+ 3. Mirror the same env vars on the **Node host** (Railway / Render / Fly / Vercel) — Lovable Secrets alone do not reach a Railway process.
134
+ 4. Wire `server.ts` like the Replit starter:
135
+
136
+ ```ts
137
+ import "dotenv/config";
138
+ import { readFileSync } from "fs";
139
+ import { fileURLToPath } from "url";
140
+ import { dirname, join } from "path";
141
+ import { createTomorrowOSStore, TomorrowOS } from "@tomorrowos/sdk";
142
+
143
+ const __dirname = dirname(fileURLToPath(import.meta.url));
144
+ const brand = JSON.parse(readFileSync(join(__dirname, "brand.json"), "utf8"));
145
+
146
+ const store = createTomorrowOSStore({
147
+ databaseUrl: process.env.SUPABASE_URL || process.env.DATABASE_URL,
148
+ sqlitePath: join(__dirname, "data", "tomorrowos.db")
149
+ });
150
+
151
+ const tomorrowos = new TomorrowOS({ brand, store });
152
+
153
+ tomorrowos.listen({
154
+ port: Number(process.env.PORT) || 3000,
155
+ host: "0.0.0.0",
156
+ staticRoot: join(__dirname, "public")
157
+ });
158
+ ```
159
+
160
+ 5. **Do not** write the real password into committed files.
161
+ 6. **Do not** test the database connection after saving — configuration only.
162
+ 7. **Do not** proceed to Question 2 until `SUPABASE_URL` is saved.
163
+
164
+ If the user refuses Postgres, warn that fleets need a durable store, then offer SQLite only after they explicitly confirm (SQLite is a poor fit on ephemeral Lovable/Railway disks).
165
+
166
+ **Later in Question 3:** set `cms.hostingTarget` to `"self-hosted"` (Railway / Render / Fly) or `"vercel"` if using Vercel Fluid. Do **not** ask for `expectedScreens`; default e.g. `5`.
167
+
168
+ ---
169
+
170
+ ## Question 2 — Media storage (uploads / thumbnails)
171
+
172
+ ### Step A — Ask storage choice
173
+
174
+ **Ask exactly:**
175
+
176
+ > How should playlist media (images/videos) be stored?
177
+ >
178
+ > **Recommended: Cloudinary** — public HTTPS URLs; `@tomorrowos/sdk` auto-detects `CLOUDINARY_*` Secrets and `/media/upload` works out of the box.
179
+ >
180
+ > Alternatives:
181
+ > - **Lovable Cloud Storage** (replaces Replit Object Storage) — public Storage bucket on Lovable Cloud / Supabase Storage; durable HTTPS URLs for players.
182
+ > - **Local disk only** — `public/uploads` on the Node host (fine for quick tests; files may disappear on redeploy without a volume).
183
+ >
184
+ > Do you want me to set up **Cloudinary**? (yes / no — if no, say whether you want **Lovable Cloud Storage** or local only)
185
+
186
+ ### Step B — If YES / Cloudinary (same Question 2 — no “2b”)
187
+
188
+ **Do this immediately** when the user chooses Cloudinary. **Go straight to collecting Secrets.**
189
+
190
+ **Preferred:** Lovable **Cloud → Secrets** fields for:
191
+
192
+ - `CLOUDINARY_CLOUD_NAME`
193
+ - `CLOUDINARY_API_KEY`
194
+ - `CLOUDINARY_API_SECRET`
195
+ - Optional: `CLOUDINARY_FOLDER` (e.g. `tomorrowos`)
196
+
197
+ **If Secrets UI is unavailable, ask exactly:**
198
+
199
+ > Paste your Cloudinary credentials (from [cloudinary.com/console](https://cloudinary.com/console) → Dashboard → **API Keys**). I will store them as Secrets:
200
+ >
201
+ > 1. **`CLOUDINARY_CLOUD_NAME`**
202
+ > 2. **`CLOUDINARY_API_KEY`**
203
+ > 3. **`CLOUDINARY_API_SECRET`**
204
+ >
205
+ > Optional: **`CLOUDINARY_FOLDER`**
206
+
207
+ **You must then:**
208
+
209
+ 1. Save those Secrets in Lovable **and** on the Node host.
210
+ 2. **Do not** invent or placeholder credentials.
211
+ 3. **Do not** proceed to Question 3 until all three required Secrets exist.
212
+ 4. **Do not** run a Cloudinary upload test — configuration only.
213
+
214
+ The SDK auto-detects these env vars; new uploads return `https://res.cloudinary.com/...` URLs.
215
+
216
+ ### If NO — Lovable Cloud Storage (Object Storage replacement)
217
+
218
+ This is the Lovable equivalent of **Replit Object Storage**: durable files with public HTTPS URLs, not an ephemeral disk folder.
219
+
220
+ **Do this:**
221
+
222
+ 1. Open / use **Cloud → Storage**.
223
+ 2. Create a **public** bucket named **`tomorrowos-uploads`** (or confirm it exists). Public read is required so Tizen / BrightSign players can fetch media by URL.
224
+ 3. Save Secrets (Lovable + Node host) as needed for Storage access used by your upload path, typically:
225
+ - `SUPABASE_URL` (already from Q1 — API URL form may differ from Postgres URI; if the project exposes a separate API URL, store it as `SUPABASE_API_URL` only when required)
226
+ - Service role or upload-capable key **only in server-side Secrets**, never in client bundles
227
+ 4. Tell the user clearly:
228
+ - Playlist items must use **absolute `https://...` Storage (or Cloudinary) URLs**.
229
+ - `@tomorrowos/sdk` built-in `/media/upload` persists to **Cloudinary** or **local `public/uploads`**. It does **not** natively write to Lovable Storage yet.
230
+ - Therefore: **prefer Cloudinary for one-click Control Panel uploads**, or upload files into the `tomorrowos-uploads` bucket and paste/publicize the HTTPS object URLs into playlists.
231
+ 5. **Do not** claim that `public/uploads` on Lovable is durable like Replit Object Storage.
232
+ 6. **Do not** invent Cloudinary credentials.
233
+
234
+ ### If NO — local disk only
235
+
236
+ 1. Ensure `public/uploads` exists on the Node host (`mkdir -p public/uploads`).
237
+ 2. Warn: redeploys may wipe files unless the host has a persistent volume.
238
+ 3. Prefer Cloudinary or Lovable Cloud Storage for production.
239
+
240
+ ---
241
+
242
+ ## Question 3 — Brand / TomorrowOS app look (`brand.json` only)
243
+
244
+ > **Scope:** Question 3 answers **only** update **`brand.json`**.
245
+ > **Do not** change deploy host, Secrets, or `server.ts` from branding answers.
246
+
247
+ **Ask exactly (one message; user may answer in one reply):**
248
+
249
+ > Let’s brand your TomorrowOS experience. Please provide:
250
+ >
251
+ > 1. **Product / venue name** (shown on screens and the Control Panel)
252
+ > 2. **Tagline** (optional)
253
+ > 3. **Primary colour** (hex, e.g. `#FF8A3D`)
254
+ > 4. **Background colour** (hex, optional — default `#FAFAF9`)
255
+ > 5. **Text colour** (hex, optional — default `#0A0908`)
256
+ > 6. **Secondary / accent colour** (hex, optional)
257
+ > 7. **Logo** — upload an SVG/PNG into the project, or give a URL I can fetch into `./assets/`
258
+
259
+ If the user only gives a name and primary colour, use defaults for the rest and say what you assumed.
260
+
261
+ **Then write / update `brand.json` only** (validate mentally against `brand.schema.json`). Minimum example:
262
+
263
+ ```json
264
+ {
265
+ "name": "<user name>",
266
+ "tagline": "<user tagline or Digital signage>",
267
+ "targetPlatforms": ["tizen"],
268
+ "primaryColor": "#FF8A3D",
269
+ "secondaryColor": "#F5F3EF",
270
+ "backgroundColor": "#FAFAF9",
271
+ "textColor": "#0A0908",
272
+ "logoPath": "./assets/logo.svg",
273
+ "fontFamily": "Inter",
274
+ "cms": {
275
+ "useCase": "other",
276
+ "hostingTarget": "self-hosted",
277
+ "expectedScreens": 5,
278
+ "features": {
279
+ "bulkCommands": false,
280
+ "proofOfPlay": false,
281
+ "contentScheduling": true,
282
+ "userManagement": false
283
+ }
284
+ },
285
+ "protocolVersion": "1.0"
286
+ }
287
+ ```
288
+
289
+ - Default `cms.hostingTarget` to `"self-hosted"` for Railway / Render / Fly.
290
+ - Use `"vercel"` only when following the Vercel Fluid path.
291
+ - Set `cmsEndpoint` only if the user already knows the public `wss://…` URL; otherwise leave it out and tell them to point players at the published HTTPS CMS URL (player converts `https://` → `wss://`).
292
+
293
+ ---
294
+
295
+ ## After all answers — execution checklist
296
+
297
+ ### A. Seed the TomorrowOS CMS (not a Lovable React mock)
298
+
299
+ ```bash
300
+ npx @tomorrowos/sdk@latest init .
301
+ # If the directory is not empty and the user confirms overwrite of starter files only:
302
+ # npx @tomorrowos/sdk@latest init . --force
303
+ ```
304
+
305
+ If Lovable already generated a Vite app:
306
+
307
+ 1. **Keep** Lovable only as the editor / Secrets / Storage UI if useful.
308
+ 2. **Add** the cms-starter Node CMS at repo root (or a clear `cms/` folder that is the deploy root).
309
+ 3. **Do not** implement TomorrowOS protocol inside React pages.
310
+
311
+ ### B. Apply store + media Secrets
312
+
313
+ **Postgres (always from Question 1):**
314
+
315
+ ```env
316
+ TOMORROWOS_STORE=supabase
317
+ SUPABASE_URL=...
318
+ DATABASE_SSL=true
319
+ ```
320
+
321
+ **Cloudinary (if chosen):**
322
+
323
+ ```env
324
+ CLOUDINARY_CLOUD_NAME=...
325
+ CLOUDINARY_API_KEY=...
326
+ CLOUDINARY_API_SECRET=...
327
+ ```
328
+
329
+ Copy the same keys to the **Node host** env.
330
+
331
+ ### C. Wire `server.ts`
332
+
333
+ - `host: "0.0.0.0"`
334
+ - `port: Number(process.env.PORT) || 3000`
335
+ - `staticRoot: public`
336
+ - Supabase path: `databaseUrl: process.env.SUPABASE_URL || process.env.DATABASE_URL`
337
+ - Keep starter event handlers (paired / online policy push)
338
+
339
+ ### D. Runtime files for Node publish
340
+
341
+ ```json
342
+ {
343
+ "type": "module",
344
+ "engines": { "node": ">=20" },
345
+ "scripts": {
346
+ "dev": "tsx watch server.ts",
347
+ "start": "tsx server.ts"
348
+ },
349
+ "dependencies": {
350
+ "@tomorrowos/sdk": "^0.9.40",
351
+ "dotenv": "^17.2.3",
352
+ "tsx": "^4.19.0"
353
+ }
354
+ }
355
+ ```
356
+
357
+ - Keep **`tsx` in `dependencies`** (not only `devDependencies`).
358
+ - Bump `@tomorrowos/sdk` to latest when scaffolding.
359
+
360
+ ### E. GitHub sync + deploy Node CMS
361
+
362
+ 1. Enable **GitHub sync** from Lovable (or push the cms-starter repo).
363
+ 2. Create a **Railway** (or Render / Fly) service from that repo.
364
+ 3. Set Start Command: `npm run start`
365
+ 4. Paste Secrets from Q1–Q2 into the host.
366
+ 5. Deploy and open the public HTTPS URL.
367
+
368
+ **If the user insists on Vercel:** stop using this Railway-oriented finish path and follow **`VERCEL_SETUP.md`** (`npx tomorrowos init --hosting v0`) instead — do not half-migrate.
369
+
370
+ ### F. Minimal verification only
371
+
372
+ 1. `GET /` shows TomorrowOS Control Panel HTML.
373
+ 2. Host logs show `[TomorrowOS] listening on http://0.0.0.0:...`
374
+ 3. **Do not** run Supabase connection tests, Cloudinary upload tests, or WebSocket console gates.
375
+
376
+ ### G. Tell the user next steps (no code)
377
+
378
+ 1. Keep the **Node CMS host** running (Railway Always On / paid plan as needed).
379
+ 2. **Control Panel URL:** `https://YOUR-CMS-HOST`
380
+ 3. On the TomorrowOS player, enter CMS URL: `https://YOUR-CMS-HOST/`
381
+ 4. Enter the 6-character pairing code into the Control Panel.
382
+ 5. Create a playlist, add media (Cloudinary upload or Lovable Storage HTTPS URLs), Publish to the device.
383
+ 6. Point them at `PLAYER_INSTALL.md` for player install.
384
+
385
+ ---
386
+
387
+ ## Post-Q&A: minimal verification only
388
+
389
+ ### Do (maximum)
390
+
391
+ 1. Confirm real **`@tomorrowos/sdk`** server (not a React mock).
392
+ 2. Write Secrets / `.env` / `brand.json` / `server.ts` from answers.
393
+ 3. Deploy Node host → Preview Control Panel at `/`.
394
+
395
+ ### Do NOT run after setup (unless user asks or deploy fails)
396
+
397
+ - ❌ Supabase connection test
398
+ - ❌ Cloudinary upload test
399
+ - ❌ Formal WebSocket console gates
400
+ - ❌ Long troubleshooting when `/` already shows the Control Panel
401
+
402
+ ---
403
+
404
+ ## What not to ask / change
405
+
406
+ - **`LLM_PROMPT.md`** — not used for this Lovable wizard
407
+ - Standalone screen-count / platform / hosting questionnaires outside Q1–Q3
408
+ - Replacing `@tomorrowos/sdk` with Edge Functions or React
409
+ - Saying **Lovable Cloud Publish** alone is enough for TVs
410
+ - Treating **Lovable Cloud Storage** as automatic `public/uploads` persistence (it is **not** a disk mount like Replit Object Storage)
411
+ - Skipping Cloudinary Secrets after the user chooses Cloudinary
412
+ - Changing deploy wiring based on Question 3 branding answers
413
+
414
+ ---
415
+
416
+ ## SQLite fallback — only if user refuses Postgres
417
+
418
+ ```ts
419
+ const store = createTomorrowOSStore({
420
+ sqlitePath: join(__dirname, "data", "tomorrowos.db")
421
+ });
422
+ ```
423
+
424
+ ```env
425
+ TOMORROWOS_STORE=sqlite
426
+ ```
427
+
428
+ Warn that SQLite on ephemeral hosts loses pairings on redeploy.
429
+
430
+ ---
431
+
432
+ ## Failure recovery cheat sheet
433
+
434
+ | Symptom | Likely cause | Fix |
435
+ |---------|--------------|-----|
436
+ | “CMS” is a React app with no pairing | Agent built a Lovable mock | Re-seed with `npx @tomorrowos/sdk init`; deploy Node host |
437
+ | Lovable Publish URL does not pair TVs | Expected — no WebSocket CMS there | Deploy Railway / Render / Fly / Vercel Fluid; use that URL |
438
+ | `tsx: not found` on host | `tsx` only in devDependencies | Move `tsx` to `dependencies` |
439
+ | Uploads vanish after redeploy | Local `public/uploads` | Cloudinary or Lovable Cloud Storage public URLs |
440
+ | Storage files 403 on player | Private bucket | Make `tomorrowos-uploads` **public** read |
441
+ | Supabase errors in preview | Preview network / wrong URI | Config-only; validate on published Node host |
442
+ | Pairing works on one host not another | Different CMS process / Secrets | Compare env and `listening` logs |
443
+
444
+ ---
445
+
446
+ ## Replit → Lovable mapping (for Agents)
447
+
448
+ | Replit | Lovable |
449
+ |--------|---------|
450
+ | Replit Secrets | Lovable **Cloud → Secrets** (+ copy to Node host) |
451
+ | Replit Object Storage / `public/uploads` | **Lovable Cloud Storage** public bucket `tomorrowos-uploads` (or Cloudinary) |
452
+ | `.replit` + Autoscale Publish | **GitHub sync + Railway/Render/Fly** `npm run start` (or **VERCEL_SETUP.md**) |
453
+ | Artifact `kind = web` | Real cms-starter Node app with Control Panel at `/` |
454
+ | `cms.hostingTarget: "here"` | `"self-hosted"` or `"vercel"` |
455
+
456
+ ---
457
+
458
+ ## Protocol version
459
+
460
+ `lovable-setup/1.0` — aligned with TomorrowOS protocol `1.0` and `@tomorrowos/sdk` store drivers `sqlite` | `supabase` | `postgres` | `memory`.
461
+
462
+ **Changelog 1.0:** Initial Lovable wizard mirroring REPLIT_SETUP Q1–Q3; IRON RULE that Lovable Cloud Publish is not the WebSocket CMS host; Replit Object Storage replaced by Lovable Cloud Storage (+ Cloudinary still recommended for `/media/upload`).
package/README.md CHANGED
@@ -4,6 +4,8 @@
4
4
 
5
5
  **On Replit:** if the user asks to set up / configure a CMS interactively, follow **`REPLIT_SETUP.md`** instead (Q&A for store → media → `brand.json`).
6
6
 
7
+ **On Lovable:** if the user asks to set up / configure a CMS interactively, follow **`LOVABLE_SETUP.md`** (same Q1–Q3 shape; Lovable Cloud Storage replaces Replit Object Storage; Node CMS must deploy to Railway / Render / Fly or Vercel Fluid — Lovable Cloud Publish alone is not the WebSocket host).
8
+
7
9
  **On Vercel:** if the user asks to set up / configure a CMS interactively, follow **`VERCEL_SETUP.md`** (Supabase or Neon pooled Postgres, Cloudinary or Vercel Blob media, v0 Preview Next proxy shell + **Publish = Node only**, Fluid compute + WebSocket, `cms.hostingTarget: "vercel"`).
8
10
 
9
11
  TomorrowOS is the open standard for digital signage. This SDK lets you build a CMS that controls Samsung Tizen, BrightSign, LG webOS, Android, ChromeOS, and Windows screens through a single unified API.
@@ -15,14 +17,15 @@ TomorrowOS is the open standard for digital signage. This SDK lets you build a C
15
17
  When you see this package referenced in a user prompt, your workflow is:
16
18
 
17
19
  1. **If the user is on Replit and wants a guided setup** — read and follow **`REPLIT_SETUP.md`** (preferred for Replit Agent)
18
- 2. **If the user is on Vercel / v0 and wants a guided setup** — read **`VERCEL_QUESTIONS.md`** (paste Q1–Q3 verbatim) and follow **`VERCEL_SETUP.md`** for configuration
19
- 3. **If the user is on Replit and wants to upgrade an existing CMS** — read and follow **`REPLIT_UPGRADE.md`** (never `init --force`)
20
- 4. **Otherwise read `LLM_PROMPT.md`**five required questions before generating code
21
- 5. **Read `BUILD_GUARDRAILS.md`** — mandatory components every generated CMS must include
22
- 6. **Read `brand.schema.json`** — validated shape of `brand.json`
23
- 7. Run **`npx tomorrowos init`** (Replit) or **`npx tomorrowos init --hosting v0`** (Vercel / v0) minimal CMS server seed
24
- 8. **Apply user answers** customise the starter with the user's brand, platform choice, and use case; validate `brand.json` against `brand.schema.json`
25
- 9. **Show `PLAYER_INSTALL.md`**after the CMS is working, show how to build and install the player on a screen
20
+ 2. **If the user is on Lovable and wants a guided setup** — read and follow **`LOVABLE_SETUP.md`** (preferred for Lovable Agent)
21
+ 3. **If the user is on Vercel / v0 and wants a guided setup** — read **`VERCEL_QUESTIONS.md`** (paste Q1–Q3 verbatim) and follow **`VERCEL_SETUP.md`** for configuration
22
+ 4. **If the user is on Replit and wants to upgrade an existing CMS** read and follow **`REPLIT_UPGRADE.md`** (never `init --force`)
23
+ 5. **Otherwise read `LLM_PROMPT.md`** — five required questions before generating code
24
+ 6. **Read `BUILD_GUARDRAILS.md`** — mandatory components every generated CMS must include
25
+ 7. **Read `brand.schema.json`** — validated shape of `brand.json`
26
+ 8. Run **`npx tomorrowos init`** (Replit / Lovable→Node host) or **`npx tomorrowos init --hosting v0`** (Vercel / v0) minimal CMS server seed
27
+ 9. **Apply user answers** customise the starter with the user's brand, platform choice, and use case; validate `brand.json` against `brand.schema.json`
28
+ 10. **Show `PLAYER_INSTALL.md`** — after the CMS is working, show how to build and install the player on a screen
26
29
 
27
30
  You must not write WebSocket transport, pairing wire format, or platform-specific player bridge code in the CMS — the SDK handles server-side device sessions and HTTP helpers. You customise UI, branding, and how you call the SDK.
28
31
 
@@ -61,12 +64,32 @@ In Replit Agent, paste:
61
64
  ```text
62
65
  Follow @tomorrowos/sdk REPLIT_SETUP.md and set up my TomorrowOS CMS.
63
66
  Ask me the questions in order. Do not skip steps.
67
+ Collect SUPABASE_URL once only. Question 3 branding is all optional (defaults from brand.json).
64
68
  ```
65
69
 
66
- That wizard configures Supabase, prefers Cloudinary for media, and writes
67
- `brand.json` from your answers. On Replit, Supabase must use Secret
70
+ That wizard configures Supabase (**connection string once only**), prefers Cloudinary for media, and writes
71
+ `brand.json` from optional branding answers (missing fields keep starter defaults). On Replit, Supabase must use Secret
68
72
  **`SUPABASE_URL`** (not the reserved `DATABASE_URL`).
69
73
 
74
+ ### Lovable guided setup
75
+
76
+ In Lovable Agent, paste:
77
+
78
+ ```text
79
+ Follow @tomorrowos/sdk LOVABLE_SETUP.md and set up my TomorrowOS CMS.
80
+ Ask only the questions listed in LOVABLE_SETUP.md, in order. Do not use LLM_PROMPT.md.
81
+ Do not build a Vite/React mock of TomorrowOS — use @tomorrowos/sdk cms-starter (Node + WebSocket).
82
+ Question 3 only updates brand.json.
83
+ After setup questions (including Cloudinary Secrets if chosen), configure and deploy — minimal verification only.
84
+ Never test Supabase connection — save SUPABASE_URL and move on. No Supabase or WebSocket tests.
85
+ Do not skip steps.
86
+ ```
87
+
88
+ That wizard mirrors Replit Q1–Q3. Media alternative to Cloudinary is **Lovable Cloud Storage**
89
+ (public bucket `tomorrowos-uploads`) instead of Replit Object Storage. The TV-facing CMS
90
+ must still run on a **Node host** (Railway / Render / Fly, or Vercel Fluid via `VERCEL_SETUP.md`) —
91
+ Lovable Cloud Publish alone is not a WebSocket CMS.
92
+
70
93
  ### Vercel guided setup
71
94
 
72
95
  In Vercel Agent (or any AI setup prompt), paste:
@@ -175,6 +198,7 @@ See `PLAYER_INSTALL.md` for installation notes.
175
198
  |------|---------|
176
199
  | `LLM_PROMPT.md` | Five-question elicitation for LLMs |
177
200
  | `REPLIT_SETUP.md` | Replit Agent guided CMS setup |
201
+ | `LOVABLE_SETUP.md` | Lovable Agent guided CMS setup (Node host + Lovable Cloud Storage) |
178
202
  | `VERCEL_SETUP.md` | Vercel / v0 guided CMS setup (configure after each answer) |
179
203
  | `VERCEL_QUESTIONS.md` | Verbatim Q1–Q3 text Agents must paste (do not paraphrase) |
180
204
  | `REPLIT_UPGRADE.md` | Replit Agent upgrade to latest SDK (no init) |
package/REPLIT_SETUP.md CHANGED
@@ -12,7 +12,8 @@ When a human pastes a prompt like this, **read this file and start Question 1 im
12
12
  ```text
13
13
  Follow @tomorrowos/sdk REPLIT_SETUP.md and set up my TomorrowOS CMS.
14
14
  Ask only the questions listed in REPLIT_SETUP.md, in order. Do not use LLM_PROMPT.md.
15
- Artifact kind must be **web** (never **api**). Question 3 only updates **brand.json**.
15
+ Artifact kind must be **web** (never **api**). Collect SUPABASE_URL once only never ask for the connection string twice.
16
+ Question 3 branding fields are all optional; missing values keep starter brand.json defaults.
16
17
  After all setup questions are answered (including Cloudinary Secrets if you chose Cloudinary), configure and start — minimal verification only (Web app type + Preview at /). **Never test Supabase connection** — save `SUPABASE_URL` and move on. No Supabase or WebSocket tests.
17
18
  Do not skip steps.
18
19
  ```
@@ -58,20 +59,23 @@ Replit Agent must ask **only** the questions defined in **this file**, in **this
58
59
 
59
60
  | Step | Section | When |
60
61
  |------|---------|------|
61
- | 1 | **Question 1** — Supabase connection string | Always |
62
+ | 1 | **Question 1** — Supabase connection string (**once**) | Always |
62
63
  | 2 | **Question 2** — Media storage (+ Cloudinary Secrets if chosen) | Always (after Q1) |
63
- | 3 | **Question 3** — Brand / TomorrowOS app look | Always last, before execution |
64
+ | 3 | **Question 3** — Brand / TomorrowOS app look (**all fields optional**) | Always last, before execution |
64
65
 
65
66
  **That is the complete list.** There are no other setup questions. **Do not** ask how many screens / devices. There is **no** “Question 2b” — Cloudinary credentials are collected **inside Question 2**, immediately after the user chooses Cloudinary.
66
67
 
68
+ **Critical — Question 1 (Supabase) collected once only:** Asking for the Postgres connection string **is** selecting Supabase. Collect `SUPABASE_URL` in **one** step (Secrets UI **or** chat paste — not both). After it is saved, **never** ask for the connection string / `SUPABASE_URL` again (not in Q2, Q3, execution, Publish, or “confirm Secrets”).
69
+
67
70
  **Critical:** Question 2 is **not complete** when the user says “Cloudinary” or “yes”. You **must** immediately show the Cloudinary Secrets input (same step — do **not** label a new question) and collect `CLOUDINARY_CLOUD_NAME`, `CLOUDINARY_API_KEY`, and `CLOUDINARY_API_SECRET` **before** Question 3. Do **not** skip to branding with placeholder or invented credentials.
68
71
 
72
+ **Critical — Question 3:** Every branding field is **optional**. Empty / skip / “use defaults” → keep values from the starter **`brand.json`**. Do **not** re-ask for missing name or colours.
69
73
 
70
- If the user volunteers extra info early (e.g. brand name before Q3), **record it** and still ask the current question’s required fields you do not yet have. Do not skip ahead to execution until Q1–Q3 are complete (Question 2 includes Cloudinary Secrets when applicable).
74
+ If the user volunteers extra info early (e.g. brand name before Q3), **record it** and still ask the current question’s required fields you do not yet have. Do not skip ahead to execution until Q1–Q3 are complete (Question 2 includes Cloudinary Secrets when applicable). For Q3, “complete” includes the user skipping or leaving fields blank.
71
75
 
72
76
  If the user says “just set it up, don’t ask questions,” respond:
73
77
 
74
- > I need a few quick answers from REPLIT_SETUP.md (Supabase connection string, media storage — plus Cloudinary credentials if you choose Cloudinary — and branding). It takes about a minute and ensures the CMS deploys correctly on Replit.
78
+ > I need a few quick answers from REPLIT_SETUP.md (Supabase connection string once, media storage — plus Cloudinary credentials if you choose Cloudinary — and optional branding). It takes about a minute and ensures the CMS deploys correctly on Replit.
75
79
 
76
80
  Then ask **Question 1** — do not switch to `LLM_PROMPT.md`.
77
81
 
@@ -83,6 +87,8 @@ Then ask **Question 1** — do not switch to `LLM_PROMPT.md`.
83
87
  2. **One question at a time.** Wait for the user’s answer before asking the next question (unless they already answered several in one message). **Exception:** If the user chooses Cloudinary in Question 2, **stay on Question 2** and immediately show the Secrets form — do **not** open a separate “2b” step or announce a new question number.
84
88
  3. **Do not invent** Cloudinary credentials, Supabase URLs, or brand colours. **Do not ask** screen / device count.
85
89
  4. **Do not use Replit’s reserved `DATABASE_URL` Secret for Supabase.** On Replit, `DATABASE_URL` is often pre-claimed. Always use **`SUPABASE_URL`** for TomorrowOS ↔ Supabase.
90
+ 4a. **Never collect `SUPABASE_URL` twice.** One Question 1 capture is enough for the whole wizard and execution checklist.
91
+ 4b. **Question 3 fields are all optional.** Missing values → keep starter `brand.json` defaults; do not block or re-prompt.
86
92
  5. **Prefer `npx @tomorrowos/sdk init`** (or the package’s `templates/cms-starter`) as the project seed. Do not rebuild pairing, WebSocket, or playlist APIs from scratch.
87
93
  6. **Never commit secrets** into git. Put credentials only in Replit **Secrets** (and optionally mirror non-secret flags in `.env` that reference env vars).
88
94
  7. After Q&A, **configure and start** — do **not** run a long test suite (see **Post-Q&A: minimal verification only**).
@@ -372,26 +378,38 @@ Then (and only then) check: API vs Web type → `.replit` ports/`deploymentTarge
372
378
 
373
379
  ---
374
380
 
375
- ## Question 1 — Supabase connection (always)
381
+ ## Question 1 — Supabase connection (always, **once**)
376
382
 
377
383
  > **This is the first setup question.** Always configure Supabase. **Do not** ask how many screens / devices.
378
384
 
385
+ ### Collect `SUPABASE_URL` exactly once (IRON RULE)
386
+
387
+ Pasting / saving the connection string **is** choosing Supabase. Treat that as the full Question 1 answer.
388
+
389
+ | Allowed (pick **one**) | Forbidden |
390
+ |------------------------|-----------|
391
+ | Open the Replit **Secrets** UI for `SUPABASE_URL` **or** ask the user to paste the string in chat | Asking “Do you want Supabase?” and then asking for the connection string again |
392
+ | Save Secret once, then move on | Opening Secrets **and** also asking them to paste the same URI in chat |
393
+ | Reuse the saved Secret in execution | Asking again in Q2 / Q3 / “confirm your Supabase Secret” / Publish / checklist |
394
+
395
+ If `SUPABASE_URL` is **already** present in Replit Secrets when Question 1 starts, **do not** ask for it again — confirm you will use the existing Secret and proceed to Question 2 (unless the user explicitly wants to replace it).
396
+
379
397
  **Preferred on Replit:** open an **input / Secrets** UI where the user pastes the connection string into a field for:
380
398
 
381
399
  - `SUPABASE_URL`
382
400
 
383
- **Ask exactly (copy wording; do not substitute LLM_PROMPT questions):**
401
+ **Only if** the Secrets UI is unavailable, ask exactly in chat (copy wording; do not substitute LLM_PROMPT questions). **Do not** also open a second Secrets prompt for the same value:
384
402
 
385
403
  > Paste your Supabase Postgres connection string below. I will store it as the Replit Secret **`SUPABASE_URL`** (not `DATABASE_URL` — that name is often reserved by Replit).
386
404
  >
387
405
  > Example shape: `postgresql://postgres.[PROJECT]:[PASSWORD]@aws-0-[REGION].pooler.supabase.com:6543/postgres`
388
406
  >
389
- > Also confirm whether SSL is required (usually **yes** for Supabase).
407
+ > SSL is assumed **yes** for Supabase unless you say otherwise — you do not need to answer a separate SSL question if you already pasted the URI.
390
408
 
391
409
  **You must then:**
392
410
 
393
- 1. Create / update Replit Secret: `SUPABASE_URL=<user value>`
394
- 2. Create / update Replit Secret or `.env`:
411
+ 1. Create / update Replit Secret: `SUPABASE_URL=<user value>` (**once**).
412
+ 2. Create / update Replit Secret or `.env` flags only (not a second paste of the URI):
395
413
  - `TOMORROWOS_STORE=supabase`
396
414
  - `DATABASE_SSL=true` (unless the user explicitly says SSL is off)
397
415
  3. Update `server.ts` so the store uses Supabase via `SUPABASE_URL`:
@@ -421,17 +439,12 @@ tomorrowos.listen({
421
439
  });
422
440
  ```
423
441
 
424
- 4. Ensure `.env` / Secrets include:
425
-
426
- ```env
427
- TOMORROWOS_STORE=supabase
428
- SUPABASE_URL=postgresql://...
429
- DATABASE_SSL=true
430
- ```
442
+ 4. Ensure `.env` / Secrets include `TOMORROWOS_STORE=supabase` and `DATABASE_SSL=true`. The connection string itself lives **only** in the Secret already saved in step 1 — **do not** ask the user to paste it into `.env` or chat again.
431
443
 
432
444
  5. **Do not** write the real password into committed files.
433
445
  6. **Do not** test the Supabase connection after saving the Secret — **configuration only**. No probe, no `npm run start` just to verify DB, no fixing “connection failure” in dev. Saving `SUPABASE_URL` completes Question 1.
434
- 7. **Do not** proceed to Question 2 until `SUPABASE_URL` is saved.
446
+ 7. **Do not** proceed to Question 2 until `SUPABASE_URL` is saved (or was already present and reused).
447
+ 8. **After Question 1 completes, never re-request `SUPABASE_URL`.** Execution checklist step B must **reuse** the existing Secret — wire env / `server.ts` silently.
435
448
 
436
449
  If the user refuses Supabase, warn that Replit fleets need a durable Postgres store, then offer SQLite only after they explicitly confirm.
437
450
 
@@ -499,35 +512,45 @@ The SDK auto-detects these env vars and uploads media to Cloudinary. New uploads
499
512
 
500
513
  ---
501
514
 
502
- ## Question 3 — Brand / TomorrowOS app look (`brand.json` only)
515
+ ## Question 3 — Brand / TomorrowOS app look (`brand.json` only; **all fields optional**)
503
516
 
504
517
  > **Scope:** Question 3 answers **only** update **`brand.json`** — colours, name, tagline, logo path, etc. They configure how the **TomorrowOS player app** looks and metadata flags.
505
518
  >
506
519
  > **Question 3 does NOT affect:** Replit Publish, artifact `kind`, `.replit`, `server.ts`, Secrets, Supabase, Cloudinary, ports, deployment target, or CMS server wiring. **Never** change deploy/runtime config based on branding answers.
520
+ >
521
+ > **Every field below is optional.** The user may skip the whole question, answer only some fields, or say “use defaults”. For anything missing, **keep the existing starter `brand.json` values** (from `templates/cms-starter/brand.json` / project `brand.json` after `init`). **Do not** invent a second branding prompt. **Do not** block setup because name or colours were omitted.
507
522
 
508
523
  > **This is the only branding / platform / use-case question block.** Do not ask separate LLM_PROMPT “target platform”, “use case”, or “hosting” questions before this. **Do not** ask screen count.
509
524
 
510
- **Ask exactly (one message; user may answer in one reply):**
525
+ **Ask exactly (one message; user may answer in one reply, partially, or skip):**
511
526
 
512
- > Let’s brand your TomorrowOS experience. Please provide:
527
+ > Let’s brand your TomorrowOS experience (**all optional** — reply “skip” or leave blank to keep the starter `brand.json` defaults):
513
528
  >
514
- > 1. **Product / venue name** (shown on screens and the Control Panel)
515
- > 2. **Tagline** (optional)
516
- > 3. **Primary colour** (hex, e.g. `#FF8A3D`)
517
- > 4. **Background colour** (hex, optional — default `#FAFAF9`)
518
- > 5. **Text colour** (hex, optional — default `#0A0908`)
519
- > 6. **Secondary / accent colour** (hex, optional)
520
- > 7. **Logo** — upload an SVG/PNG into the project, or give a URL I can fetch into `./assets/`
529
+ > 1. **Product / venue name** (optional default from `brand.json`, e.g. `My Venue`)
530
+ > 2. **Tagline** (optional — default e.g. `Digital signage`)
531
+ > 3. **Primary colour** (optional hex — default e.g. `#FF8A3D`)
532
+ > 4. **Background colour** (optional — default e.g. `#FAFAF9`)
533
+ > 5. **Text colour** (optional — default e.g. `#0A0908`)
534
+ > 6. **Secondary / accent colour** (optional — default e.g. `#F5F3EF`)
535
+ > 7. **Logo** (optional) — upload an SVG/PNG, or a URL to fetch into `./assets/` (otherwise keep `./assets/logo.svg`)
536
+
537
+ **Defaults rule (mandatory):**
521
538
 
539
+ 1. Start from the current project **`brand.json`** (after `init`, this matches the SDK starter).
540
+ 2. Overlay **only** fields the user actually provided.
541
+ 3. If the user gives **no** product name → keep existing `"name"`.
542
+ 4. If the user gives **no** colours → keep existing `primaryColor` / `backgroundColor` / `textColor` / `secondaryColor`.
543
+ 5. If the user says **skip** / **defaults** / **n/a** / sends an empty answer → leave `brand.json` unchanged (except set `cms.hostingTarget` to `"here"` if needed — see below).
544
+ 6. **Never** re-ask for missing branding fields. **Never** treat blank name/colour as an error.
522
545
 
523
- If the user only gives a name and primary colour, use defaults for the rest and say what you assumed.
546
+ **Then write / update `brand.json` only** at the project root (validate mentally against `brand.schema.json` in the SDK). **Do not** modify `.replit`, `server.ts`, Secrets, or deployment settings from these answers.
524
547
 
525
- **Then write / update `brand.json` only** at the project root (validate mentally against `brand.schema.json` in the SDK). **Do not** modify `.replit`, `server.ts`, Secrets, or deployment settings from these answers. Minimum example:
548
+ Example when the user provided only a name (other fields kept from starter):
526
549
 
527
550
  ```json
528
551
  {
529
- "name": "<user name>",
530
- "tagline": "<user tagline or Digital signage>",
552
+ "name": "<user name or keep starter name>",
553
+ "tagline": "Digital signage",
531
554
  "targetPlatforms": ["tizen"],
532
555
  "primaryColor": "#FF8A3D",
533
556
  "secondaryColor": "#F5F3EF",
@@ -575,14 +598,17 @@ If `init` is inappropriate (existing customised project), merge carefully:
575
598
 
576
599
  ### B. Apply store + media Secrets
577
600
 
578
- **Supabase (always from Question 1):**
601
+ **Supabase (always from Question 1 — reuse; do not ask again):**
602
+
603
+ Use the **`SUPABASE_URL` already saved** in Question 1. Wire flags only:
579
604
 
580
605
  ```env
581
606
  TOMORROWOS_STORE=supabase
582
- SUPABASE_URL=...
583
607
  DATABASE_SSL=true
584
608
  ```
585
609
 
610
+ **Do not** prompt the user to paste the Postgres connection string a second time. **Do not** open another Secrets form for `SUPABASE_URL` unless the Secret is missing (should not happen if Q1 completed).
611
+
586
612
  **Cloudinary (if chosen):**
587
613
 
588
614
  ```env
@@ -659,6 +685,7 @@ Do **not**:
659
685
  - Replace `@tomorrowos/sdk` with a hand-rolled server
660
686
  - Change the production start command away from `tsx server.ts` / `npm run start`
661
687
  - Point Supabase at Replit’s reserved `DATABASE_URL` when `SUPABASE_URL` should be used
688
+ - **Ask for `SUPABASE_URL` / the Postgres connection string more than once** (Secrets UI + chat, or again during execution / Publish)
662
689
  - Skip Cloudinary recommendation without stating the persistence tradeoff
663
690
  - **Skip Cloudinary Secrets** after the user chooses Cloudinary — always collect `CLOUDINARY_*` inside Question 2 before Question 3
664
691
  - **Announce “Question 2b”** or a separate follow-up question — show the Secrets form immediately instead
@@ -669,6 +696,8 @@ Do **not**:
669
696
  - Create the Repl as **API-only** or publish with **Static Deployment** only
670
697
  - Set or leave artifact **`kind = "api"`** for the TomorrowOS CMS
671
698
  - Change `.replit`, `server.ts`, Secrets, or deployment config based on **Question 3** branding answers (those go in **`brand.json` only**)
699
+ - **Require** product name, colours, or logo in Question 3 — all branding fields are optional; missing → starter `brand.json` defaults
700
+ - **Re-ask** Question 3 for blank name/colour instead of keeping defaults
672
701
  - Omit `[[ports]]` / `deploymentTarget` from `.replit` when setting up for Publish
673
702
  - Run **Supabase connection tests**, **WebSocket console tests**, or other post-setup smoke tests unless Preview fails or the user asks
674
703
  - **Debug or “fix” Supabase** because dev logs show `getaddrinfo ENOTFOUND` or `database connection failure` — save config and finish setup instead
@@ -712,7 +741,9 @@ TOMORROWOS_STORE=sqlite
712
741
 
713
742
  ## Protocol version
714
743
 
715
- `replit-setup/1.8` — aligned with TomorrowOS protocol `1.0` and `@tomorrowos/sdk` store drivers `sqlite` | `supabase` | `postgres` | `memory`.
744
+ `replit-setup/1.9` — aligned with TomorrowOS protocol `1.0` and `@tomorrowos/sdk` store drivers `sqlite` | `supabase` | `postgres` | `memory`.
745
+
746
+ **Changelog 1.9:** Question 1 collects `SUPABASE_URL` **once only** (no second Secrets/chat prompt in execution). Question 3 branding fields are **all optional**; missing values keep starter `brand.json` defaults.
716
747
 
717
748
  **Changelog 1.8:** Question 1 is always Supabase connection string (input / Secret). Removed screen-count branching (≤5 SQLite vs >5 Supabase). Media storage is Question 2; branding is Question 3.
718
749
 
@@ -1,6 +1,6 @@
1
1
  # Vercel / v0 — Verbatim question bank
2
2
 
3
- > **Protocol:** `vercel-questions/1.0` (pairs with `VERCEL_SETUP.md` 1.9+ / `@tomorrowos/sdk` 0.9.35+)
3
+ > **Protocol:** `vercel-questions/1.0` (pairs with `VERCEL_SETUP.md` 1.9+ / `@tomorrowos/sdk` 0.9.40+)
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.
package/VERCEL_SETUP.md CHANGED
@@ -5,7 +5,7 @@
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.9` — pairs with `@tomorrowos/sdk` **0.9.35+** (`templates/cms-starter-v0`, Fluid Functions WebSockets).
8
+ > Protocol id: `vercel-setup/1.9` — pairs with `@tomorrowos/sdk` **0.9.40+** (`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
11
  > **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).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tomorrowos/sdk",
3
- "version": "0.9.35",
3
+ "version": "0.9.40",
4
4
  "description": "TomorrowOS CMS server SDK - WebSocket transport, pairing, device commands, optional static CMS UI. Includes CLI (tomorrowos init / build) and starter templates.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -20,6 +20,7 @@
20
20
  "README.md",
21
21
  "LLM_PROMPT.md",
22
22
  "REPLIT_SETUP.md",
23
+ "LOVABLE_SETUP.md",
23
24
  "VERCEL_SETUP.md",
24
25
  "VERCEL_QUESTIONS.md",
25
26
  "REPLIT_UPGRADE.md",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "my-cms",
3
- "version": "0.9.35",
3
+ "version": "0.9.40",
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",
@@ -13,7 +13,7 @@
13
13
  "build-player": "tomorrowos build --platform tizen"
14
14
  },
15
15
  "dependencies": {
16
- "@tomorrowos/sdk": "^0.9.35",
16
+ "@tomorrowos/sdk": "^0.9.40",
17
17
  "dotenv": "^17.2.3",
18
18
  "tsx": "^4.19.0"
19
19
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "my-cms",
3
- "version": "0.9.35",
3
+ "version": "0.9.40",
4
4
  "description": "TomorrowOS CMS for Vercel / v0 Publish (Fluid Function + WebSockets + optional Next Preview).",
5
5
  "private": true,
6
6
  "type": "module",
@@ -17,7 +17,7 @@
17
17
  "build-player": "tomorrowos build --platform tizen"
18
18
  },
19
19
  "dependencies": {
20
- "@tomorrowos/sdk": "^0.9.35",
20
+ "@tomorrowos/sdk": "^0.9.40",
21
21
  "dotenv": "^17.2.3",
22
22
  "tsx": "^4.19.0"
23
23
  },