@tomorrowos/sdk 0.9.22 → 0.9.23
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 +4 -3
- package/VERCEL_SETUP.md +181 -57
- package/package.json +1 -1
- package/templates/cms-starter/package.json +2 -2
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
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 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, Fluid compute + WebSocket, `cms.hostingTarget: "vercel"`).
|
|
7
|
+
**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
8
|
|
|
9
9
|
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.
|
|
10
10
|
|
|
@@ -72,8 +72,9 @@ IF any step contradicts Vercel, adapt to settings that Vercel can accept.
|
|
|
72
72
|
```
|
|
73
73
|
|
|
74
74
|
That wizard asks: database (**Supabase** → **Neon** → SQLite demo), media (**Cloudinary** → **Vercel Blob** → local),
|
|
75
|
-
branding (manual fields or **infer from a website URL**)
|
|
76
|
-
`
|
|
75
|
+
branding (manual fields or **infer from a website URL**). Preview may use a thin **Next.js reverse proxy**;
|
|
76
|
+
**Publish stays pure Node** `TomorrowOS.listen` (not Next). Cloudinary credentials are collected one-at-a-time
|
|
77
|
+
(cloud name → API key → API secret). Sets `cms.hostingTarget` to `"vercel"`.
|
|
77
78
|
|
|
78
79
|
### Replit upgrade (existing CMS)
|
|
79
80
|
|
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.
|
|
8
|
+
> Protocol id: `vercel-setup/1.2` — pairs with `@tomorrowos/sdk` packages that ship `templates/cms-starter` and this file.
|
|
9
9
|
|
|
10
10
|
## User trigger (copy-paste)
|
|
11
11
|
|
|
@@ -17,6 +17,7 @@ Ask only the questions listed in VERCEL_SETUP.md, in order. Do not use LLM_PROMP
|
|
|
17
17
|
If any step contradicts Vercel, adapt to settings Vercel can accept (document the adaptation).
|
|
18
18
|
After all setup questions are answered (including Cloudinary or Vercel Blob Env Vars if you chose those), configure and deploy — minimal verification only (Control Panel at /). 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.
|
|
19
19
|
Question 3 only updates brand.json. Do not skip steps.
|
|
20
|
+
Use Next.js only as a v0/Preview shell with a reverse proxy; Production Publish must remain pure Node TomorrowOS.listen (not Next).
|
|
20
21
|
```
|
|
21
22
|
|
|
22
23
|
Alternate triggers:
|
|
@@ -28,7 +29,7 @@ When any of these fire, **ignore `REPLIT_SETUP.md` and `LLM_PROMPT.md`** for the
|
|
|
28
29
|
|
|
29
30
|
---
|
|
30
31
|
|
|
31
|
-
## IRON RULE:
|
|
32
|
+
## IRON RULE: Preview vs Publish (dual runtime)
|
|
32
33
|
|
|
33
34
|
> **This is the #1 deployability rule on Vercel. Non-negotiable.**
|
|
34
35
|
|
|
@@ -37,16 +38,35 @@ TomorrowOS CMS is **one Node process** that serves:
|
|
|
37
38
|
- Browser Control Panel at `GET /` (`public/` via `staticRoot`)
|
|
38
39
|
- Device **WebSocket** on the same origin (`https` → players use `wss`)
|
|
39
40
|
|
|
41
|
+
### Production / Publish — Node only (no Next.js)
|
|
42
|
+
|
|
40
43
|
| Required | Forbidden |
|
|
41
44
|
|----------|-----------|
|
|
42
|
-
| Single **Node HTTP server** entry (`tsx server.ts` / exported `http.Server` from `listen()`) | Next.js
|
|
45
|
+
| Single **Node HTTP server** entry (`tsx server.ts` / exported `http.Server` from `listen()`) | **Next.js as the Production CMS** (`next start`, Next App Router as the live app) |
|
|
43
46
|
| **Fluid compute** enabled (needed for WebSockets on Vercel) | Classic short-lived serverless with no WebSocket / no Fluid |
|
|
44
47
|
| Durable store = **Postgres** (Supabase or Neon, pooled URL) | **SQLite / `data/*.db` as production store** on Vercel (ephemeral filesystem) |
|
|
45
48
|
| Media = **Cloudinary** or **Vercel Blob** (durable HTTPS URLs) | Relying on local `public/uploads` alone for production fleets |
|
|
46
49
|
| Env vars in **Vercel Project → Settings → Environment Variables** | Committing passwords / API secrets / `.env` with secrets |
|
|
47
50
|
| `cms.hostingTarget`: `"vercel"` in `brand.json` | Creating `.replit` / Replit artifact files |
|
|
48
51
|
|
|
49
|
-
**
|
|
52
|
+
**Publish / Production Deploy** must run **`TomorrowOS.listen(...)`** directly. Do **not** put Next.js in front of Production traffic.
|
|
53
|
+
|
|
54
|
+
### Preview (v0 / Vercel Agent sandbox) — Next.js shell allowed
|
|
55
|
+
|
|
56
|
+
v0 Preview is often a **Next.js runtime** hardwired to `next dev`. A raw `tsx server.ts` may listen successfully while Preview still shows a blank page, because the sandbox intercepts the public port with Next before requests reach TomorrowOS.
|
|
57
|
+
|
|
58
|
+
**For Preview only**, you **must** keep a thin Next.js host that **reverse-proxies** HTTP to TomorrowOS on an **internal** port.
|
|
59
|
+
|
|
60
|
+
| Preview requirement | Detail |
|
|
61
|
+
|---------------------|--------|
|
|
62
|
+
| Next.js remains the Preview host | Satisfies v0 / sandbox expectations |
|
|
63
|
+
| TomorrowOS on internal port | e.g. `TOMORROWOS_INTERNAL_PORT=3001` (not the public Preview port) |
|
|
64
|
+
| Transparent HTTP reverse proxy | Next route/middleware forwards `/` (and Control Panel paths) → `http://127.0.0.1:3001` |
|
|
65
|
+
| Start both processes in Preview | Next + `tsx server.ts` (or a `dev:preview` script that starts both) |
|
|
66
|
+
|
|
67
|
+
**Preview WebSocket note:** HTTP proxy is enough for Control Panel HTML/API smoke checks. Device `wss` pairing may not work through a Next proxy in Preview — that is acceptable. **Pair devices against the Production Publish URL**, not v0 Preview.
|
|
68
|
+
|
|
69
|
+
**Do not** replace TomorrowOS with a Next-only CMS. Next is a **Preview adapter**, not the product.
|
|
50
70
|
|
|
51
71
|
---
|
|
52
72
|
|
|
@@ -60,7 +80,7 @@ Ask **only** these questions, in **this exact order**:
|
|
|
60
80
|
| 2 | **Question 2** — Media storage (Cloudinary → Vercel Blob → local) | Always (after Q1) |
|
|
61
81
|
| 3 | **Question 3** — Brand / TomorrowOS look (`brand.json` only) | Always last |
|
|
62
82
|
|
|
63
|
-
**That is the complete list.** Do **not** ask how many screens / devices.
|
|
83
|
+
**That is the complete list.** Do **not** ask how many screens / devices.
|
|
64
84
|
|
|
65
85
|
If the user says “just set it up, don’t ask questions,” respond:
|
|
66
86
|
|
|
@@ -73,7 +93,7 @@ Then ask **Question 1**.
|
|
|
73
93
|
## Hard rules
|
|
74
94
|
|
|
75
95
|
1. **Ask only VERCEL_SETUP.md questions 1–3.** Never use `LLM_PROMPT.md` or `REPLIT_SETUP.md` as the questionnaire.
|
|
76
|
-
2. **One question at a time
|
|
96
|
+
2. **One question / one credential at a time.** For Cloudinary: ask **cloud name**, then **API key alone**, then **API secret alone** — never one message that mixes key + secret.
|
|
77
97
|
3. **Do not invent** Cloudinary credentials, database URLs, Vercel Blob tokens, or brand colours.
|
|
78
98
|
4. **Postgres env naming:** prefer **`SUPABASE_URL`** when using Supabase; prefer **`DATABASE_URL`** (or `NEON_DATABASE_URL`) when using Neon. `createTomorrowOSStore` accepts `SUPABASE_URL` then `DATABASE_URL`.
|
|
79
99
|
5. **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).
|
|
@@ -81,9 +101,10 @@ Then ask **Question 1**.
|
|
|
81
101
|
7. **Never commit secrets.** Use Vercel Environment Variables (+ optional non-secret flags in committed `.env.example`).
|
|
82
102
|
8. **Skip Replit-only files:** do **not** create `.replit`, `.replit-artifact`, or Replit `deploymentTarget`.
|
|
83
103
|
9. **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`.
|
|
84
|
-
10. **Enable Fluid compute** for WebSockets. Confirm Project settings after scaffold.
|
|
85
|
-
11. **
|
|
86
|
-
12.
|
|
104
|
+
10. **Enable Fluid compute** for Production WebSockets. Confirm Project settings after scaffold.
|
|
105
|
+
11. **Production:** export the HTTP server Vercel can capture: `const server = tomorrowos.listen(...); export default server;` (or equivalent). **Do not** make `next start` the Production entrypoint.
|
|
106
|
+
12. **Preview:** if the environment is v0 / Next-hardwired, install the Next reverse-proxy shell (see **Preview adapter**). Do not leave Preview broken.
|
|
107
|
+
13. After Q&A: **configure → install → run/deploy → minimal verify** (Control Panel at `/`). Do not run a long test suite.
|
|
87
108
|
|
|
88
109
|
**Question order:** Q1 → Q2 → Q3 → execution checklist.
|
|
89
110
|
|
|
@@ -91,12 +112,13 @@ Then ask **Question 1**.
|
|
|
91
112
|
|
|
92
113
|
## Runtime & Vercel deploy rules (mandatory)
|
|
93
114
|
|
|
94
|
-
###
|
|
115
|
+
### Production runtime (Publish)
|
|
95
116
|
|
|
96
117
|
- Serve with **`tsx server.ts`** (or compiled Node that still calls `TomorrowOS.listen` with `staticRoot`).
|
|
97
118
|
- Keep **`@tomorrowos/sdk`** and put **`tsx` in `dependencies`** (not only `devDependencies`) if production runs via `tsx`.
|
|
98
119
|
- Node **20+**.
|
|
99
120
|
- **Do not** replace the SDK with a hand-rolled server that drops WebSocket upgrades.
|
|
121
|
+
- **Forbidden as the Production Deploy entrypoint:** `next start`, static export only, or any command that does not start `TomorrowOS.listen(...)`.
|
|
100
122
|
|
|
101
123
|
### Expected `package.json` scripts
|
|
102
124
|
|
|
@@ -104,14 +126,21 @@ Then ask **Question 1**.
|
|
|
104
126
|
{
|
|
105
127
|
"scripts": {
|
|
106
128
|
"dev": "tsx watch server.ts",
|
|
107
|
-
"
|
|
129
|
+
"dev:preview": "concurrently -k \"npm:dev:tomorrowos\" \"npm:dev:next\"",
|
|
130
|
+
"dev:tomorrowos": "cross-env PORT=3001 TOMORROWOS_INTERNAL_PORT=3001 tsx watch server.ts",
|
|
131
|
+
"dev:next": "next dev -p 3000",
|
|
132
|
+
"start": "tsx server.ts",
|
|
133
|
+
"build": "echo \"Production is Node — no Next production build required\""
|
|
108
134
|
}
|
|
109
135
|
}
|
|
110
136
|
```
|
|
111
137
|
|
|
112
|
-
|
|
138
|
+
Adapt script names / process runners as needed. **Invariant:**
|
|
139
|
+
|
|
140
|
+
- **`npm start` / Production** = TomorrowOS Node only.
|
|
141
|
+
- **Preview / v0** = Next on public port + TomorrowOS on internal port + proxy.
|
|
113
142
|
|
|
114
|
-
### Minimal dependencies
|
|
143
|
+
### Minimal dependencies (Production)
|
|
115
144
|
|
|
116
145
|
```json
|
|
117
146
|
{
|
|
@@ -137,9 +166,11 @@ Then ask **Question 1**.
|
|
|
137
166
|
|
|
138
167
|
Use the latest published `@tomorrowos/sdk` when scaffolding (do not invent versions).
|
|
139
168
|
|
|
140
|
-
Add `@vercel/blob`
|
|
169
|
+
Add `@vercel/blob` only when the user chooses **Vercel Blob** in Question 2.
|
|
141
170
|
|
|
142
|
-
|
|
171
|
+
For Preview shell only, add as needed: `next`, `react`, `react-dom`, and a process runner (`concurrently` or equivalent). These must **not** become the Production CMS.
|
|
172
|
+
|
|
173
|
+
### `server.ts` pattern (Vercel-adapted — Production)
|
|
143
174
|
|
|
144
175
|
```ts
|
|
145
176
|
import "dotenv/config";
|
|
@@ -162,8 +193,13 @@ const store = createTomorrowOSStore({
|
|
|
162
193
|
|
|
163
194
|
const tomorrowos = new TomorrowOS({ brand, store });
|
|
164
195
|
|
|
196
|
+
const port =
|
|
197
|
+
Number(process.env.TOMORROWOS_INTERNAL_PORT) ||
|
|
198
|
+
Number(process.env.PORT) ||
|
|
199
|
+
3000;
|
|
200
|
+
|
|
165
201
|
const server = tomorrowos.listen({
|
|
166
|
-
port
|
|
202
|
+
port,
|
|
167
203
|
host: "0.0.0.0",
|
|
168
204
|
staticRoot: join(__dirname, "public")
|
|
169
205
|
});
|
|
@@ -172,9 +208,37 @@ const server = tomorrowos.listen({
|
|
|
172
208
|
export default server;
|
|
173
209
|
```
|
|
174
210
|
|
|
211
|
+
- **Production:** `PORT` from Vercel → TomorrowOS listens on that port (no Next).
|
|
212
|
+
- **Preview:** set `TOMORROWOS_INTERNAL_PORT=3001` (or similar) so Next can own `:3000` and proxy inward.
|
|
213
|
+
|
|
175
214
|
Load env with `dotenv` for local/`vercel dev`. On production Deploy, Vercel injects Environment Variables natively.
|
|
176
215
|
|
|
177
|
-
###
|
|
216
|
+
### Preview adapter (v0 / Next reverse proxy) — required when Preview is Next-hardwired
|
|
217
|
+
|
|
218
|
+
Detect Preview / v0 (Next already present, or Preview blank while TomorrowOS logs “listening”). Then:
|
|
219
|
+
|
|
220
|
+
1. Keep a **minimal** Next app (App Router or Pages — whichever the sandbox expects).
|
|
221
|
+
2. TomorrowOS listens on **`127.0.0.1:TOMORROWOS_INTERNAL_PORT`** (default **3001**).
|
|
222
|
+
3. Add a **catch-all** Next route (or middleware) that proxies HTTP to TomorrowOS:
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
// Example: app/api/tomorrowos-proxy/[...path]/route.ts or a rewrite + route handler
|
|
226
|
+
// Forward method, headers, and body to http://127.0.0.1:3001/<path>
|
|
227
|
+
// Return the upstream status, headers, and body to the browser.
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Preferred UX: proxy **`/`** itself (rewrites) so the Preview URL shows the Control Panel at `/`, not only under `/api/...`.
|
|
231
|
+
|
|
232
|
+
4. Start **both** processes for Preview (`dev:preview` pattern above).
|
|
233
|
+
5. Document in the final summary: **Preview = Next shell + proxy; Publish = Node only.**
|
|
234
|
+
|
|
235
|
+
**Forbidden:**
|
|
236
|
+
|
|
237
|
+
- Making Next the Production CMS or the only server after Publish.
|
|
238
|
+
- Deleting TomorrowOS `server.ts` because Preview needed Next.
|
|
239
|
+
- Claiming setup complete if Production entrypoint is still `next start`.
|
|
240
|
+
|
|
241
|
+
### `vercel.json` (recommended minimum — Production)
|
|
178
242
|
|
|
179
243
|
Adapt to the project’s current Vercel Node-server / Fluid conventions. At minimum ensure:
|
|
180
244
|
|
|
@@ -186,23 +250,25 @@ Adapt to the project’s current Vercel Node-server / Fluid conventions. At mini
|
|
|
186
250
|
|
|
187
251
|
Also:
|
|
188
252
|
|
|
189
|
-
1. Project → **Fluid compute** enabled (required for WebSockets).
|
|
190
|
-
2. Framework Preset
|
|
191
|
-
3. Install / Start commands must end in running the CMS server (e.g. `npm run start`
|
|
253
|
+
1. Project → **Fluid compute** enabled (required for WebSockets on Publish).
|
|
254
|
+
2. Framework Preset for **Production:** **Other** (or Node) — **not** Next.js as the live CMS.
|
|
255
|
+
3. Install / Start commands for **Production** must end in running the CMS Node server (e.g. `npm run start` → `tsx server.ts`).
|
|
192
256
|
4. Do **not** enable “Output Directory” static-only hosting for this CMS.
|
|
257
|
+
5. If the project also contains a Next Preview shell, configure Vercel so **Production Deploy ignores Next as the app** (Node `server.ts` / `export default server` remains the capture path). Document any dashboard setting you change.
|
|
193
258
|
|
|
194
|
-
If Vercel’s UI and this file disagree on bundling, **prefer whatever keeps a single long-lived Node HTTP server with WebSocket upgrade on
|
|
259
|
+
If Vercel’s UI and this file disagree on bundling, **prefer whatever keeps a single long-lived Node HTTP server with WebSocket upgrade on `/` for Production**. Document the adaptation in the final summary.
|
|
195
260
|
|
|
196
261
|
### WebSocket / multi-instance note
|
|
197
262
|
|
|
198
263
|
- Device sockets are pinned to **one** function/instance. TomorrowOS device maps are **in-memory**.
|
|
199
264
|
- For large fleets on multi-instance Vercel, prefer **one stable instance / region**, or accept that reconnects may land on another instance until the product adds shared presence (out of scope for this setup).
|
|
200
265
|
- Always use **Postgres** (Supabase or Neon) so pairings/playlists survive cold starts and deploys.
|
|
266
|
+
- **Pair TVs to the Production HTTPS origin**, not the v0 Preview URL.
|
|
201
267
|
|
|
202
268
|
### What not to create
|
|
203
269
|
|
|
204
270
|
- ❌ `.replit`, `.replit-artifact`
|
|
205
|
-
- ❌ Next.js
|
|
271
|
+
- ❌ Next.js as the **Production** CMS (App Router replacing TomorrowOS)
|
|
206
272
|
- ❌ Separate “API project” without the Control Panel
|
|
207
273
|
- ❌ Committing database passwords, Cloudinary secrets, or Blob tokens
|
|
208
274
|
|
|
@@ -309,16 +375,47 @@ Only proceed after the user explicitly confirms.
|
|
|
309
375
|
>
|
|
310
376
|
> Reply with **1**, **2**, or **3** (or “Cloudinary” / “Vercel Blob” / “local”).
|
|
311
377
|
|
|
312
|
-
### Step B — If **1 / Cloudinary** (same Question 2)
|
|
378
|
+
### Step B — If **1 / Cloudinary** (same Question 2 — sequential prompts)
|
|
313
379
|
|
|
314
|
-
|
|
380
|
+
> **Critical UX rule:** Collect Cloudinary credentials in **separate messages**. Never ask for API key and API secret in the same message or the same form field group.
|
|
315
381
|
|
|
316
|
-
|
|
317
|
-
- `CLOUDINARY_API_KEY`
|
|
318
|
-
- `CLOUDINARY_API_SECRET`
|
|
319
|
-
- Optional: `CLOUDINARY_FOLDER` (e.g. `tomorrowos`)
|
|
382
|
+
#### B1 — Cloud name (ask alone)
|
|
320
383
|
|
|
321
|
-
|
|
384
|
+
**Ask exactly:**
|
|
385
|
+
|
|
386
|
+
> Paste your Cloudinary **Cloud name** only (`CLOUDINARY_CLOUD_NAME`).
|
|
387
|
+
> Find it at [cloudinary.com/console](https://cloudinary.com/console) → Dashboard.
|
|
388
|
+
|
|
389
|
+
Save as Vercel Env Var `CLOUDINARY_CLOUD_NAME`. Do **not** ask for key or secret yet.
|
|
390
|
+
|
|
391
|
+
#### B2 — API key (ask alone — after B1 is saved)
|
|
392
|
+
|
|
393
|
+
**Ask exactly:**
|
|
394
|
+
|
|
395
|
+
> Paste your Cloudinary **API Key** only (`CLOUDINARY_API_KEY`).
|
|
396
|
+
> Do not paste the API Secret in this step.
|
|
397
|
+
|
|
398
|
+
Save as Vercel Env Var `CLOUDINARY_API_KEY`. Do **not** ask for the secret in this message.
|
|
399
|
+
|
|
400
|
+
#### B3 — API secret (ask alone — after B2 is saved)
|
|
401
|
+
|
|
402
|
+
**Ask exactly:**
|
|
403
|
+
|
|
404
|
+
> Paste your Cloudinary **API Secret** only (`CLOUDINARY_API_SECRET`).
|
|
405
|
+
> This is the last Cloudinary credential.
|
|
406
|
+
|
|
407
|
+
Save as Vercel Env Var `CLOUDINARY_API_SECRET`.
|
|
408
|
+
|
|
409
|
+
Optional fourth message (only if useful):
|
|
410
|
+
|
|
411
|
+
> Optional: Cloudinary folder name (`CLOUDINARY_FOLDER`, e.g. `tomorrowos`). Press Enter to skip.
|
|
412
|
+
|
|
413
|
+
**You must then:**
|
|
414
|
+
|
|
415
|
+
1. Confirm all three required Env Vars exist: `CLOUDINARY_CLOUD_NAME`, `CLOUDINARY_API_KEY`, `CLOUDINARY_API_SECRET`.
|
|
416
|
+
2. **Do not** invent placeholders.
|
|
417
|
+
3. **Do not** proceed to Question 3 until all three exist.
|
|
418
|
+
4. **Do not** run a formal Cloudinary upload test unless the user asks.
|
|
322
419
|
|
|
323
420
|
The SDK auto-detects these env vars.
|
|
324
421
|
|
|
@@ -430,7 +527,7 @@ Validate mentally against `brand.schema.json`. Minimum:
|
|
|
430
527
|
}
|
|
431
528
|
```
|
|
432
529
|
|
|
433
|
-
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
|
|
530
|
+
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://`).
|
|
434
531
|
|
|
435
532
|
---
|
|
436
533
|
|
|
@@ -446,11 +543,12 @@ npx @tomorrowos/sdk@latest init .
|
|
|
446
543
|
|
|
447
544
|
Then:
|
|
448
545
|
|
|
449
|
-
1.
|
|
450
|
-
2.
|
|
451
|
-
3. Add/adjust `vercel.json` with **`fluid: true`** (or enable Fluid in the dashboard).
|
|
452
|
-
4.
|
|
453
|
-
5.
|
|
546
|
+
1. Keep TomorrowOS `server.ts` as the **Production** app (**export `listen()` HTTP server**).
|
|
547
|
+
2. If the environment is **v0 / Next Preview**, add the **Preview adapter** (Next shell + reverse proxy + internal TomorrowOS port). Do **not** make Next the Production CMS.
|
|
548
|
+
3. Add/adjust `vercel.json` with **`fluid: true`** (or enable Fluid in the dashboard) for Publish.
|
|
549
|
+
4. Ensure Production start command is **`npm run start` → `tsx server.ts`** (or equivalent Node), **not** `next start`.
|
|
550
|
+
5. Do **not** add `.replit*`.
|
|
551
|
+
6. If Q2 = Vercel Blob: install `@vercel/blob` and wire the upload bridge.
|
|
454
552
|
|
|
455
553
|
### B. Environment Variables (Vercel dashboard + local)
|
|
456
554
|
|
|
@@ -460,9 +558,12 @@ Then:
|
|
|
460
558
|
| `DATABASE_URL` | If Q1 = Neon | **Pooled** Neon string |
|
|
461
559
|
| `TOMORROWOS_STORE` | Yes | `supabase`, `postgres`, or `sqlite` |
|
|
462
560
|
| `DATABASE_SSL` | Yes (typical) | `true` |
|
|
463
|
-
| `
|
|
561
|
+
| `CLOUDINARY_CLOUD_NAME` | If Q2 = Cloudinary | Collected alone (B1) |
|
|
562
|
+
| `CLOUDINARY_API_KEY` | If Q2 = Cloudinary | Collected alone after cloud name (B2) |
|
|
563
|
+
| `CLOUDINARY_API_SECRET` | If Q2 = Cloudinary | Collected alone after API key (B3) |
|
|
464
564
|
| `BLOB_READ_WRITE_TOKEN` | If Q2 = Vercel Blob | From Vercel Blob store |
|
|
465
|
-
| `
|
|
565
|
+
| `TOMORROWOS_INTERNAL_PORT` | Preview only | e.g. `3001` when Next owns `:3000` |
|
|
566
|
+
| `PORT` | Optional | Vercel injects for Production |
|
|
466
567
|
|
|
467
568
|
Mirror non-secrets in `.env` for local `npm run dev` if helpful; never commit secrets.
|
|
468
569
|
|
|
@@ -471,34 +572,46 @@ Mirror non-secrets in `.env` for local `npm run dev` if helpful; never commit se
|
|
|
471
572
|
```bash
|
|
472
573
|
npm install
|
|
473
574
|
npm run start
|
|
474
|
-
#
|
|
575
|
+
# Preview / v0: npm run dev:preview (Next + TomorrowOS internal)
|
|
475
576
|
```
|
|
476
577
|
|
|
477
578
|
Confirm logs: `[TomorrowOS] listening on http://0.0.0.0:…`
|
|
478
|
-
Confirm browser:
|
|
579
|
+
Confirm browser:
|
|
479
580
|
|
|
480
|
-
|
|
581
|
+
- **Preview:** Control Panel visible through the Next proxy at `/`
|
|
582
|
+
- **Production start:** Control Panel served directly by TomorrowOS at `/`
|
|
583
|
+
|
|
584
|
+
### D. Deploy (Publish — Node only)
|
|
481
585
|
|
|
482
586
|
```bash
|
|
483
587
|
vercel --prod
|
|
484
588
|
# or git push → Vercel Git integration
|
|
485
589
|
```
|
|
486
590
|
|
|
591
|
+
Before finishing, verify Production settings:
|
|
592
|
+
|
|
593
|
+
1. Start / entry = **Node `TomorrowOS.listen`** (`npm run start` / exported `server`)
|
|
594
|
+
2. **Not** `next start` as Production
|
|
595
|
+
3. Fluid compute on
|
|
596
|
+
4. Env Vars present for Production environment
|
|
597
|
+
|
|
487
598
|
Tell the user:
|
|
488
599
|
|
|
489
|
-
- **Control Panel:** `https://YOUR-PROJECT.vercel.app`
|
|
600
|
+
- **Control Panel (Publish):** `https://YOUR-PROJECT.vercel.app`
|
|
490
601
|
- **TV CMS endpoint:** same HTTPS origin (players use `wss://`)
|
|
602
|
+
- **Preview:** Next shell is for v0 only — do not pair devices to Preview
|
|
491
603
|
|
|
492
604
|
### E. Minimal verification only
|
|
493
605
|
|
|
494
606
|
**Do:**
|
|
495
607
|
|
|
496
|
-
1. Control Panel HTML at `/`
|
|
497
|
-
2.
|
|
498
|
-
3.
|
|
499
|
-
4.
|
|
608
|
+
1. Preview: Control Panel HTML at `/` (via Next proxy if applicable)
|
|
609
|
+
2. Publish: Control Panel HTML at `/` from **Node** TomorrowOS
|
|
610
|
+
3. Server status: Media OK when Cloudinary or Blob configured
|
|
611
|
+
4. Database: if ERROR shows **`ENETUNREACH` … IPv6 … `:5432`**, switch to **pooled** URL (Supabase `:6543` or Neon pooler) and redeploy — call this out explicitly to the user
|
|
612
|
+
5. Fluid compute on for Production
|
|
500
613
|
|
|
501
|
-
**Do not block** on long WebSocket/device pairing tests unless the user asks.
|
|
614
|
+
**Do not block** on long WebSocket/device pairing tests unless the user asks. Prefer pairing against **Publish**, not Preview.
|
|
502
615
|
|
|
503
616
|
---
|
|
504
617
|
|
|
@@ -506,12 +619,15 @@ Tell the user:
|
|
|
506
619
|
|
|
507
620
|
| Symptom | Likely cause | Fix |
|
|
508
621
|
|---------|--------------|-----|
|
|
622
|
+
| Preview blank but logs show TomorrowOS listening | v0 Next intercepts public port | Add Next reverse proxy → internal TomorrowOS port |
|
|
623
|
+
| Preview works, Publish broken / Next-only | Production still on Next | Switch Production entry to `tsx server.ts` / `export default server` |
|
|
509
624
|
| `ENETUNREACH` / IPv6 / `:5432` | Direct Postgres URL | Use **pooled** URL (Supabase `:6543` or Neon pooler) |
|
|
510
625
|
| Control Panel OK, devices never stay paired after restart | SQLite / ephemeral disk | Use Supabase or Neon |
|
|
511
626
|
| Uploads break / broken thumbs in prod | Local `public/uploads` | Cloudinary or Vercel Blob |
|
|
512
627
|
| Blob uploads 401 / missing token | Blob not linked | Vercel Storage → Blob + `BLOB_READ_WRITE_TOKEN` |
|
|
513
|
-
| WebSocket fails
|
|
514
|
-
|
|
|
628
|
+
| WebSocket fails on Publish | Fluid off / static-only / Next as Production | Enable Fluid; sole Node `listen()` for Publish |
|
|
629
|
+
| Devices fail only on Preview | Next proxy cannot upgrade `wss` | Expected — pair on Publish URL |
|
|
630
|
+
| Agent mixed Cloudinary key+secret in one prompt | Violated Step B | Re-ask key alone, then secret alone |
|
|
515
631
|
| Secrets in git | Mistake | Rotate keys; move to Vercel Env Vars |
|
|
516
632
|
| Replit files present | Copied wrong protocol | Delete `.replit*`; use this file |
|
|
517
633
|
| Brand colours wrong from URL | SPA / blocked fetch | Manual Option B for colours + logo |
|
|
@@ -527,7 +643,8 @@ Tell the user:
|
|
|
527
643
|
| `hostingTarget` | `"here"` | `"vercel"` |
|
|
528
644
|
| Database | Supabase (primary) | Supabase **or Neon** (pooled); SQLite demo only |
|
|
529
645
|
| Media | Cloudinary or Replit Object Storage | Cloudinary **or Vercel Blob** |
|
|
530
|
-
|
|
|
646
|
+
| Preview | Replit Preview / Autoscale Node | **Next shell + proxy** when v0 hardwires Next |
|
|
647
|
+
| Publish | Long-lived Node | **Node `TomorrowOS.listen` only** (not Next) |
|
|
531
648
|
| Brand from URL | Not in Replit protocol | **Option A** in Question 3 — infer from website |
|
|
532
649
|
|
|
533
650
|
---
|
|
@@ -536,12 +653,19 @@ Tell the user:
|
|
|
536
653
|
|
|
537
654
|
After setup, report:
|
|
538
655
|
|
|
539
|
-
1. **Live URL** (Production)
|
|
540
|
-
2. **
|
|
541
|
-
3. **
|
|
542
|
-
4. **
|
|
543
|
-
5. **
|
|
544
|
-
6. **
|
|
545
|
-
7. **
|
|
656
|
+
1. **Live URL** (Production Publish)
|
|
657
|
+
2. **Preview note:** Next proxy shell used / not needed
|
|
658
|
+
3. **Env Vars set** (names only — never values)
|
|
659
|
+
4. **Store:** Supabase pooler / Neon pooled / SQLite (with warning)
|
|
660
|
+
5. **Media:** Cloudinary, Vercel Blob, or local (with warning)
|
|
661
|
+
6. **Brand:** name + `hostingTarget: vercel` (+ note if derived from website URL)
|
|
662
|
+
7. **Runtime split:** Preview = … ; Publish = Node TomorrowOS (confirm **not** Next)
|
|
663
|
+
8. **Open action:** if Database still ERROR, paste the pooler instruction again
|
|
664
|
+
|
|
665
|
+
Protocol complete when:
|
|
666
|
+
|
|
667
|
+
- **Preview** shows the Control Panel at `/` (via Next proxy if required), **and**
|
|
668
|
+
- **Publish** is configured for pure Node `TomorrowOS.listen` with Fluid, **and**
|
|
669
|
+
- Q1–Q3 configuration is saved
|
|
546
670
|
|
|
547
|
-
|
|
671
|
+
— even if a sandbox DB probe still flakes **after** the pooled URL is correctly set for Production.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tomorrowos/sdk",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.23",
|
|
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",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "my-cms",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.23",
|
|
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.
|
|
16
|
+
"@tomorrowos/sdk": "^0.9.23",
|
|
17
17
|
"dotenv": "^17.2.3",
|
|
18
18
|
"tsx": "^4.19.0"
|
|
19
19
|
},
|