@tomorrowos/sdk 0.9.54 → 0.9.56
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/README.md +327 -123
- package/REPLIT_SETUP.md +24 -57
- package/REPLIT_UPGRADE.md +2 -2
- package/VERCEL_QUESTIONS.md +21 -26
- package/VERCEL_SETUP.md +49 -62
- package/package.json +1 -4
- package/templates/cms-starter/package.json +2 -2
- package/templates/cms-starter/policy.example.json +6 -7
- package/templates/cms-starter-v0/package.json +2 -2
- package/templates/cms-starter-v0/policy.example.json +6 -7
- package/BUILD_GUARDRAILS.md +0 -350
- package/LOVABLE_SETUP.md +0 -462
package/LOVABLE_SETUP.md
DELETED
|
@@ -1,462 +0,0 @@
|
|
|
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
|
-
- 閳ユ藩et up TomorrowOS on Lovable using LOVABLE_SETUP.md閳?
|
|
25
|
-
- 閳ユ窅onfigure 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閳ユ獨 default app is **Vite + React + Lovable Cloud** (Postgres / Auth / Storage / Edge Functions). That stack **cannot** replace `@tomorrowos/sdk`閳ユ獨 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 閳ユ笓ooks 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閳ユ彌3, 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 閳ユ阀ublish 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** 閳ユ法uestion 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 閳ユ窅loudinary閳?or 閳ユ脯es閳? 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閳ユ獨 required fields you do not yet have. Do not skip ahead to execution until Q1閳ユ彌3 are complete (Question 2 includes Cloudinary Secrets when applicable).
|
|
87
|
-
|
|
88
|
-
If the user says 閳ユ笍ust set it up, don閳ユ獩 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閳?.** Never use `LLM_PROMPT.md` on Lovable for this wizard.
|
|
99
|
-
2. **One question at a time.** Wait for the user閳ユ獨 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 閳?b閳?
|
|
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閳ユ獨 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 閳?b閳?
|
|
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閳ユ獨 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.54",
|
|
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閳ユ彌2 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閳ユ彌3
|
|
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
|
-
| 閳ユ窅MS閳?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閳ユ彌3; 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`).
|