@tomorrowos/sdk 0.9.21 → 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 +5 -3
- package/VERCEL_SETUP.md +337 -93
- 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`** (
|
|
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
|
|
|
@@ -71,8 +71,10 @@ Follow @tomorrowos/sdk VERCEL_SETUP.md and set up my TomorrowOS CMS.
|
|
|
71
71
|
IF any step contradicts Vercel, adapt to settings that Vercel can accept.
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
-
That wizard
|
|
75
|
-
|
|
74
|
+
That wizard asks: database (**Supabase** → **Neon** → SQLite demo), media (**Cloudinary** → **Vercel Blob** → local),
|
|
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"`.
|
|
76
78
|
|
|
77
79
|
### Replit upgrade (existing CMS)
|
|
78
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
|
|
|
@@ -15,8 +15,9 @@ When a human pastes a prompt like this, **read this file and start Question 1 im
|
|
|
15
15
|
Follow @tomorrowos/sdk VERCEL_SETUP.md and set up my TomorrowOS CMS.
|
|
16
16
|
Ask only the questions listed in VERCEL_SETUP.md, in order. Do not use LLM_PROMPT.md or REPLIT_SETUP.md.
|
|
17
17
|
If any step contradicts Vercel, adapt to settings Vercel can accept (document the adaptation).
|
|
18
|
-
After all setup questions are answered (including Cloudinary Env Vars if you chose
|
|
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
|
-
| Durable store = **
|
|
45
|
-
| Media = **Cloudinary**
|
|
46
|
-
| Env vars in **Vercel Project → Settings → Environment Variables** | Committing passwords /
|
|
47
|
+
| Durable store = **Postgres** (Supabase or Neon, pooled URL) | **SQLite / `data/*.db` as production store** on Vercel (ephemeral filesystem) |
|
|
48
|
+
| Media = **Cloudinary** or **Vercel Blob** (durable HTTPS URLs) | Relying on local `public/uploads` alone for production fleets |
|
|
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
|
|
|
@@ -56,15 +76,15 @@ Ask **only** these questions, in **this exact order**:
|
|
|
56
76
|
|
|
57
77
|
| Step | Section | When |
|
|
58
78
|
|------|---------|------|
|
|
59
|
-
| 1 | **Question 1** — Supabase
|
|
60
|
-
| 2 | **Question 2** — Media storage (
|
|
79
|
+
| 1 | **Question 1** — Database (Supabase → Neon → SQLite) | Always |
|
|
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
|
|
|
67
|
-
> I need a few quick answers from VERCEL_SETUP.md (
|
|
87
|
+
> I need a few quick answers from VERCEL_SETUP.md (database — Supabase or Neon preferred — media storage, and branding). It takes about a minute and ensures the CMS works on Vercel.
|
|
68
88
|
|
|
69
89
|
Then ask **Question 1**.
|
|
70
90
|
|
|
@@ -73,17 +93,18 @@ 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
|
|
77
|
-
3. **Do not invent** Cloudinary credentials,
|
|
78
|
-
4. **
|
|
79
|
-
5. **Always prefer
|
|
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.
|
|
97
|
+
3. **Do not invent** Cloudinary credentials, database URLs, Vercel Blob tokens, or brand colours.
|
|
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`.
|
|
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).
|
|
80
100
|
6. **Prefer `npx @tomorrowos/sdk init`** (or `templates/cms-starter`) as the seed. Do not rebuild pairing / WebSocket / playlist APIs.
|
|
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
|
-
9. **SQLite is not a production store on Vercel.**
|
|
84
|
-
10. **Enable Fluid compute** for WebSockets. Confirm Project settings after scaffold.
|
|
85
|
-
11. **
|
|
86
|
-
12.
|
|
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`.
|
|
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,7 +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
|
-
|
|
169
|
+
Add `@vercel/blob` only when the user chooses **Vercel Blob** in Question 2.
|
|
170
|
+
|
|
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)
|
|
141
174
|
|
|
142
175
|
```ts
|
|
143
176
|
import "dotenv/config";
|
|
@@ -150,15 +183,23 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
150
183
|
const brand = JSON.parse(readFileSync(join(__dirname, "brand.json"), "utf8"));
|
|
151
184
|
|
|
152
185
|
const store = createTomorrowOSStore({
|
|
153
|
-
//
|
|
154
|
-
databaseUrl:
|
|
186
|
+
// Supabase: SUPABASE_URL (pooler). Neon: DATABASE_URL (pooled).
|
|
187
|
+
databaseUrl:
|
|
188
|
+
process.env.SUPABASE_URL ||
|
|
189
|
+
process.env.NEON_DATABASE_URL ||
|
|
190
|
+
process.env.DATABASE_URL,
|
|
155
191
|
sqlitePath: join(__dirname, "data", "tomorrowos.db") // local/dev fallback only
|
|
156
192
|
});
|
|
157
193
|
|
|
158
194
|
const tomorrowos = new TomorrowOS({ brand, store });
|
|
159
195
|
|
|
196
|
+
const port =
|
|
197
|
+
Number(process.env.TOMORROWOS_INTERNAL_PORT) ||
|
|
198
|
+
Number(process.env.PORT) ||
|
|
199
|
+
3000;
|
|
200
|
+
|
|
160
201
|
const server = tomorrowos.listen({
|
|
161
|
-
port
|
|
202
|
+
port,
|
|
162
203
|
host: "0.0.0.0",
|
|
163
204
|
staticRoot: join(__dirname, "public")
|
|
164
205
|
});
|
|
@@ -167,9 +208,37 @@ const server = tomorrowos.listen({
|
|
|
167
208
|
export default server;
|
|
168
209
|
```
|
|
169
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
|
+
|
|
170
214
|
Load env with `dotenv` for local/`vercel dev`. On production Deploy, Vercel injects Environment Variables natively.
|
|
171
215
|
|
|
172
|
-
###
|
|
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)
|
|
173
242
|
|
|
174
243
|
Adapt to the project’s current Vercel Node-server / Fluid conventions. At minimum ensure:
|
|
175
244
|
|
|
@@ -181,35 +250,53 @@ Adapt to the project’s current Vercel Node-server / Fluid conventions. At mini
|
|
|
181
250
|
|
|
182
251
|
Also:
|
|
183
252
|
|
|
184
|
-
1. Project → **Fluid compute** enabled (required for WebSockets).
|
|
185
|
-
2. Framework Preset
|
|
186
|
-
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`).
|
|
187
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.
|
|
188
258
|
|
|
189
|
-
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.
|
|
190
260
|
|
|
191
261
|
### WebSocket / multi-instance note
|
|
192
262
|
|
|
193
263
|
- Device sockets are pinned to **one** function/instance. TomorrowOS device maps are **in-memory**.
|
|
194
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).
|
|
195
|
-
- Always use **
|
|
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.
|
|
196
267
|
|
|
197
268
|
### What not to create
|
|
198
269
|
|
|
199
270
|
- ❌ `.replit`, `.replit-artifact`
|
|
200
|
-
- ❌ Next.js
|
|
271
|
+
- ❌ Next.js as the **Production** CMS (App Router replacing TomorrowOS)
|
|
201
272
|
- ❌ Separate “API project” without the Control Panel
|
|
202
|
-
- ❌ Committing
|
|
273
|
+
- ❌ Committing database passwords, Cloudinary secrets, or Blob tokens
|
|
203
274
|
|
|
204
275
|
---
|
|
205
276
|
|
|
206
|
-
## Question 1 —
|
|
277
|
+
## Question 1 — Database (always)
|
|
207
278
|
|
|
208
|
-
>
|
|
279
|
+
> **Do not** ask screen counts. Present all three options; recommend **Supabase** for Vercel fleets.
|
|
280
|
+
|
|
281
|
+
### Step A — Ask storage choice
|
|
282
|
+
|
|
283
|
+
**Ask exactly:**
|
|
284
|
+
|
|
285
|
+
> Which database should TomorrowOS use on Vercel?
|
|
286
|
+
>
|
|
287
|
+
> **1. Supabase Postgres (recommended)** — durable pairing/playlists; use the **Session pooler** URL (`*.pooler.supabase.com:6543`).
|
|
288
|
+
>
|
|
289
|
+
> **2. Neon Postgres** — serverless Postgres native to Vercel; use Neon’s **pooled** connection string from the Neon dashboard (not the direct un-pooled host for serverless).
|
|
290
|
+
>
|
|
291
|
+
> **3. SQLite (`data/tomorrowos.db`)** — **demo / local only on Vercel.** The filesystem is ephemeral; pairings and playlists will not survive redeploys. Only choose this if you understand the limitation.
|
|
292
|
+
>
|
|
293
|
+
> Reply with **1**, **2**, or **3** (or “Supabase” / “Neon” / “SQLite”).
|
|
294
|
+
|
|
295
|
+
### Step B — If **1 / Supabase**
|
|
209
296
|
|
|
210
297
|
**Ask exactly:**
|
|
211
298
|
|
|
212
|
-
> Paste your Supabase Postgres
|
|
299
|
+
> Paste your Supabase Postgres connection string. I will store it as the Vercel Environment Variable **`SUPABASE_URL`**.
|
|
213
300
|
>
|
|
214
301
|
> In Supabase: **Project Settings → Database → Connection string → Connection pooling** (Session mode).
|
|
215
302
|
> Preferred shape:
|
|
@@ -230,9 +317,45 @@ If Vercel’s UI and this file disagree on bundling, **prefer whatever keeps a s
|
|
|
230
317
|
4. **Do not** commit the real connection string.
|
|
231
318
|
5. **Configuration over probing:** if local/`vercel dev` still shows `ENETUNREACH` / IPv6 errors, verify the string is the **pooler** (`pooler.supabase.com` + `:6543`). Treat remaining sandbox DNS issues as non-fatal when Production deploy Env Vars are correct — but **do** fix an obvious direct `:5432` URL before finishing.
|
|
232
319
|
|
|
233
|
-
|
|
320
|
+
### Step C — If **2 / Neon**
|
|
321
|
+
|
|
322
|
+
**Ask exactly:**
|
|
323
|
+
|
|
324
|
+
> Paste your **Neon pooled** Postgres connection string. I will store it as **`DATABASE_URL`** (or `NEON_DATABASE_URL` if you prefer — wire whichever you set in `server.ts`).
|
|
325
|
+
>
|
|
326
|
+
> In Neon: **Dashboard → Connection details → Pooled connection** (enable pooling).
|
|
327
|
+
> Typical shape:
|
|
328
|
+
> `postgresql://[user]:[password]@[endpoint].pooler.[region].aws.neon.tech/[dbname]?sslmode=require`
|
|
329
|
+
>
|
|
330
|
+
> **Do not** use the direct (non-pooled) connection string for Vercel serverless unless Neon docs explicitly say otherwise for your plan.
|
|
331
|
+
>
|
|
332
|
+
> Confirm SSL (`sslmode=require`) is on (usually **yes**).
|
|
333
|
+
|
|
334
|
+
**You must then:**
|
|
335
|
+
|
|
336
|
+
1. Set Vercel Env Vars:
|
|
337
|
+
- `DATABASE_URL=<pooled Neon string>` (or `NEON_DATABASE_URL` + read it in `server.ts`)
|
|
338
|
+
- `TOMORROWOS_STORE=postgres`
|
|
339
|
+
- `DATABASE_SSL=true` (unless user explicitly disables SSL)
|
|
340
|
+
2. Wire `server.ts` — `createTomorrowOSStore` uses `PostgresStore` for `postgres` / `supabase` drivers.
|
|
341
|
+
3. **Do not** commit the real connection string.
|
|
342
|
+
4. Same pooler guidance as Supabase: if you see `ENETUNREACH` on `:5432`, switch to Neon’s **pooled** URL.
|
|
343
|
+
|
|
344
|
+
### Step D — If **3 / SQLite**
|
|
345
|
+
|
|
346
|
+
**Warn exactly before continuing:**
|
|
347
|
+
|
|
348
|
+
> SQLite on Vercel is **not** suitable for production fleets — data in `data/tomorrowos.db` is lost when the instance is recycled or redeployed. Continue only for a quick demo.
|
|
349
|
+
|
|
350
|
+
Only proceed after the user explicitly confirms.
|
|
234
351
|
|
|
235
|
-
**
|
|
352
|
+
**You must then:**
|
|
353
|
+
|
|
354
|
+
1. Set `TOMORROWOS_STORE=sqlite` (or omit `DATABASE_URL` / `SUPABASE_URL` so the factory defaults to SQLite).
|
|
355
|
+
2. Keep `sqlitePath` in `server.ts` for `data/tomorrowos.db`.
|
|
356
|
+
3. Warn again in the final summary that they should move to Supabase or Neon for real devices.
|
|
357
|
+
|
|
358
|
+
**Later in Question 3:** set `cms.hostingTarget` to **`"vercel"`** (all Q1 branches).
|
|
236
359
|
|
|
237
360
|
---
|
|
238
361
|
|
|
@@ -244,30 +367,91 @@ If the user refuses Supabase, warn that Vercel cannot use durable SQLite for fle
|
|
|
244
367
|
|
|
245
368
|
> How should playlist media (images/videos) be stored?
|
|
246
369
|
>
|
|
247
|
-
> **
|
|
370
|
+
> **1. Cloudinary (recommended)** — durable public HTTPS URLs (`https://res.cloudinary.com/...`). Works out of the box with `@tomorrowos/sdk` auto-detection.
|
|
248
371
|
>
|
|
249
|
-
>
|
|
250
|
-
> - **Local disk only** — `public/uploads` (OK for quick local tests; **not** for production Vercel fleets).
|
|
372
|
+
> **2. Vercel Blob** — Vercel-native object storage; durable `https://*.public.blob.vercel-storage.com/...` URLs. Good when you want media on the same Vercel project without a Cloudinary account.
|
|
251
373
|
>
|
|
252
|
-
>
|
|
374
|
+
> **3. Local disk only** — `public/uploads` (OK for quick local tests; **not** for production Vercel fleets — files are ephemeral).
|
|
375
|
+
>
|
|
376
|
+
> Reply with **1**, **2**, or **3** (or “Cloudinary” / “Vercel Blob” / “local”).
|
|
377
|
+
|
|
378
|
+
### Step B — If **1 / Cloudinary** (same Question 2 — sequential prompts)
|
|
379
|
+
|
|
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.
|
|
381
|
+
|
|
382
|
+
#### B1 — Cloud name (ask alone)
|
|
383
|
+
|
|
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)
|
|
253
392
|
|
|
254
|
-
|
|
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:**
|
|
255
403
|
|
|
256
|
-
|
|
404
|
+
> Paste your Cloudinary **API Secret** only (`CLOUDINARY_API_SECRET`).
|
|
405
|
+
> This is the last Cloudinary credential.
|
|
257
406
|
|
|
258
|
-
|
|
259
|
-
- `CLOUDINARY_API_KEY`
|
|
260
|
-
- `CLOUDINARY_API_SECRET`
|
|
261
|
-
- Optional: `CLOUDINARY_FOLDER` (e.g. `tomorrowos`)
|
|
407
|
+
Save as Vercel Env Var `CLOUDINARY_API_SECRET`.
|
|
262
408
|
|
|
263
|
-
|
|
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.
|
|
264
419
|
|
|
265
420
|
The SDK auto-detects these env vars.
|
|
266
421
|
|
|
267
|
-
###
|
|
422
|
+
### Step C — If **2 / Vercel Blob** (same Question 2)
|
|
423
|
+
|
|
424
|
+
**Ask exactly:**
|
|
425
|
+
|
|
426
|
+
> I will enable **Vercel Blob** for media uploads.
|
|
427
|
+
>
|
|
428
|
+
> 1. In the Vercel project: **Storage → Create → Blob** (or link an existing Blob store to this project).
|
|
429
|
+
> 2. Confirm **`BLOB_READ_WRITE_TOKEN`** is available (Vercel usually injects it when Blob is linked).
|
|
430
|
+
>
|
|
431
|
+
> Paste the token only if it is not already set in your project Env Vars. Do you already have Blob linked on this Vercel project? (yes / no)
|
|
432
|
+
|
|
433
|
+
**You must then:**
|
|
434
|
+
|
|
435
|
+
1. Set Vercel Env Var: `BLOB_READ_WRITE_TOKEN=<token>` (if not auto-injected).
|
|
436
|
+
2. `npm install @vercel/blob` (add to `dependencies`).
|
|
437
|
+
3. Wire uploads so media returns **absolute HTTPS Blob URLs** stored in `uploaded_assets` (players need stable public URLs).
|
|
438
|
+
- The SDK **natively auto-detects Cloudinary** today. For Vercel Blob, add a thin upload bridge in the CMS project (e.g. custom route or middleware that calls `put()` from `@vercel/blob` and persists the returned `url` the same way Cloudinary URLs are stored).
|
|
439
|
+
- Minimum pattern:
|
|
440
|
+
|
|
441
|
+
```ts
|
|
442
|
+
import { put } from "@vercel/blob";
|
|
443
|
+
|
|
444
|
+
// On upload: const blob = await put(filename, body, { access: "public", token: process.env.BLOB_READ_WRITE_TOKEN });
|
|
445
|
+
// Store blob.url in uploaded_assets — same shape as Cloudinary secure URLs.
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
4. Warn: until the project’s Blob bridge is wired, Control Panel **Media** status may show local/ephemeral — verify uploads return `https://*.blob.vercel-storage.com/...` before calling setup complete.
|
|
449
|
+
5. Do **not** proceed to Question 3 until `BLOB_READ_WRITE_TOKEN` exists (or Blob is linked and token is confirmed in the Vercel dashboard).
|
|
450
|
+
|
|
451
|
+
### Step D — If **3 / local uploads**
|
|
268
452
|
|
|
269
453
|
1. `mkdir -p public/uploads`
|
|
270
|
-
2. Warn clearly: files may vanish on redeploy; players need stable HTTPS URLs — Cloudinary is strongly preferred for Vercel.
|
|
454
|
+
2. Warn clearly: files may vanish on redeploy; players need stable HTTPS URLs — Cloudinary or Vercel Blob is strongly preferred for Vercel.
|
|
271
455
|
|
|
272
456
|
---
|
|
273
457
|
|
|
@@ -275,10 +459,17 @@ The SDK auto-detects these env vars.
|
|
|
275
459
|
|
|
276
460
|
> Updates **only** `brand.json`. Does **not** change Vercel project settings, Env Vars, or `server.ts` store wiring beyond what Q1–Q2 already required.
|
|
277
461
|
|
|
462
|
+
### Step A — Ask branding input
|
|
463
|
+
|
|
278
464
|
**Ask exactly:**
|
|
279
465
|
|
|
280
|
-
> Let’s brand your TomorrowOS experience.
|
|
466
|
+
> Let’s brand your TomorrowOS experience. You can answer in either way:
|
|
467
|
+
>
|
|
468
|
+
> **Option A — Website URL only**
|
|
469
|
+
> Paste **one public website URL** (your company / venue site). I will infer colours, fonts, name, tagline, and logo from that page and write `brand.json` to match its look.
|
|
281
470
|
>
|
|
471
|
+
> **Option B — Manual fields**
|
|
472
|
+
> Provide:
|
|
282
473
|
> 1. **Product / venue name**
|
|
283
474
|
> 2. **Tagline** (optional)
|
|
284
475
|
> 3. **Primary colour** (hex, e.g. `#FF8A3D`)
|
|
@@ -287,12 +478,33 @@ The SDK auto-detects these env vars.
|
|
|
287
478
|
> 6. **Secondary / accent colour** (hex, optional)
|
|
288
479
|
> 7. **Logo** — upload SVG/PNG into the project, or a URL I can fetch into `./assets/`
|
|
289
480
|
|
|
290
|
-
|
|
481
|
+
### Step B — If the user gives **only a website URL** (Option A)
|
|
482
|
+
|
|
483
|
+
**You must:**
|
|
484
|
+
|
|
485
|
+
1. **Fetch and inspect** the page (HTTP GET the URL; follow one redirect if needed). Do not invent colours.
|
|
486
|
+
2. **Derive branding** using this priority order:
|
|
487
|
+
- **Name:** `<title>`, `og:site_name`, or prominent header / logo `alt` text (trim to ≤ 60 chars).
|
|
488
|
+
- **Tagline:** `meta[name="description"]`, `og:description`, or first hero subtitle (≤ 120 chars).
|
|
489
|
+
- **Primary colour:** `meta[name="theme-color"]`, CSS `--primary` / `--brand` variables, or dominant accent from linked stylesheets / inline styles (convert to `#RRGGBB`).
|
|
490
|
+
- **Background colour:** `body` / `:root` background (default `#FAFAF9` if light site).
|
|
491
|
+
- **Text colour:** main body text colour (default `#0A0908` if light site).
|
|
492
|
+
- **Secondary colour:** muted border / secondary button colour, or a tint of the primary.
|
|
493
|
+
- **Font:** first `font-family` on `body` (strip quotes; default `Inter` if generic system stack).
|
|
494
|
+
- **Logo:** prefer `og:image`, then `link[rel="icon"]` / apple-touch-icon, then header `<img>` logo. Download into `./assets/logo.png` or `./assets/logo.svg` and set `logoPath` accordingly. If only favicon exists, use it and note size in the summary.
|
|
495
|
+
3. **Contrast check:** if background is dark, set `activationScreen.theme` to `"dark"` and add `logoPathOnDark` when a light logo variant exists.
|
|
496
|
+
4. **Infer `cms.useCase`** from page content when obvious (restaurant / retail / corporate / etc.); otherwise `"other"`.
|
|
497
|
+
5. **Show a one-line summary** to the user (name + primary hex + logo path) before or while writing the file — do not ask a second branding question unless fetch failed.
|
|
498
|
+
6. If the URL is unreachable, blocked, or has no usable styles, say so and fall back to **Option B** manual questions for missing fields only.
|
|
499
|
+
|
|
500
|
+
### Step C — Write `brand.json`
|
|
501
|
+
|
|
502
|
+
Validate mentally against `brand.schema.json`. Minimum:
|
|
291
503
|
|
|
292
504
|
```json
|
|
293
505
|
{
|
|
294
|
-
"name": "<user name>",
|
|
295
|
-
"tagline": "<
|
|
506
|
+
"name": "<user name or site-derived name>",
|
|
507
|
+
"tagline": "<tagline or Digital signage>",
|
|
296
508
|
"targetPlatforms": ["tizen"],
|
|
297
509
|
"primaryColor": "#FF8A3D",
|
|
298
510
|
"secondaryColor": "#F5F3EF",
|
|
@@ -315,7 +527,7 @@ Then write `brand.json` (validate mentally against `brand.schema.json`). Minimum
|
|
|
315
527
|
}
|
|
316
528
|
```
|
|
317
529
|
|
|
318
|
-
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://`).
|
|
319
531
|
|
|
320
532
|
---
|
|
321
533
|
|
|
@@ -331,20 +543,27 @@ npx @tomorrowos/sdk@latest init .
|
|
|
331
543
|
|
|
332
544
|
Then:
|
|
333
545
|
|
|
334
|
-
1.
|
|
335
|
-
2.
|
|
336
|
-
3. Add/adjust `vercel.json` with **`fluid: true`** (or enable Fluid in the dashboard).
|
|
337
|
-
4.
|
|
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.
|
|
338
552
|
|
|
339
553
|
### B. Environment Variables (Vercel dashboard + local)
|
|
340
554
|
|
|
341
555
|
| Name | Required | Notes |
|
|
342
556
|
|------|----------|--------|
|
|
343
|
-
| `SUPABASE_URL` |
|
|
344
|
-
| `
|
|
557
|
+
| `SUPABASE_URL` | If Q1 = Supabase | **Session pooler** `:6543` |
|
|
558
|
+
| `DATABASE_URL` | If Q1 = Neon | **Pooled** Neon string |
|
|
559
|
+
| `TOMORROWOS_STORE` | Yes | `supabase`, `postgres`, or `sqlite` |
|
|
345
560
|
| `DATABASE_SSL` | Yes (typical) | `true` |
|
|
346
|
-
| `
|
|
347
|
-
| `
|
|
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) |
|
|
564
|
+
| `BLOB_READ_WRITE_TOKEN` | If Q2 = Vercel Blob | From Vercel Blob store |
|
|
565
|
+
| `TOMORROWOS_INTERNAL_PORT` | Preview only | e.g. `3001` when Next owns `:3000` |
|
|
566
|
+
| `PORT` | Optional | Vercel injects for Production |
|
|
348
567
|
|
|
349
568
|
Mirror non-secrets in `.env` for local `npm run dev` if helpful; never commit secrets.
|
|
350
569
|
|
|
@@ -353,34 +572,46 @@ Mirror non-secrets in `.env` for local `npm run dev` if helpful; never commit se
|
|
|
353
572
|
```bash
|
|
354
573
|
npm install
|
|
355
574
|
npm run start
|
|
356
|
-
#
|
|
575
|
+
# Preview / v0: npm run dev:preview (Next + TomorrowOS internal)
|
|
357
576
|
```
|
|
358
577
|
|
|
359
578
|
Confirm logs: `[TomorrowOS] listening on http://0.0.0.0:…`
|
|
360
|
-
Confirm browser:
|
|
579
|
+
Confirm browser:
|
|
361
580
|
|
|
362
|
-
|
|
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)
|
|
363
585
|
|
|
364
586
|
```bash
|
|
365
587
|
vercel --prod
|
|
366
588
|
# or git push → Vercel Git integration
|
|
367
589
|
```
|
|
368
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
|
+
|
|
369
598
|
Tell the user:
|
|
370
599
|
|
|
371
|
-
- **Control Panel:** `https://YOUR-PROJECT.vercel.app`
|
|
600
|
+
- **Control Panel (Publish):** `https://YOUR-PROJECT.vercel.app`
|
|
372
601
|
- **TV CMS endpoint:** same HTTPS origin (players use `wss://`)
|
|
602
|
+
- **Preview:** Next shell is for v0 only — do not pair devices to Preview
|
|
373
603
|
|
|
374
604
|
### E. Minimal verification only
|
|
375
605
|
|
|
376
606
|
**Do:**
|
|
377
607
|
|
|
378
|
-
1. Control Panel HTML at `/`
|
|
379
|
-
2.
|
|
380
|
-
3.
|
|
381
|
-
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
|
|
382
613
|
|
|
383
|
-
**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.
|
|
384
615
|
|
|
385
616
|
---
|
|
386
617
|
|
|
@@ -388,13 +619,18 @@ Tell the user:
|
|
|
388
619
|
|
|
389
620
|
| Symptom | Likely cause | Fix |
|
|
390
621
|
|---------|--------------|-----|
|
|
391
|
-
|
|
|
392
|
-
|
|
|
393
|
-
|
|
|
394
|
-
|
|
|
395
|
-
|
|
|
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` |
|
|
624
|
+
| `ENETUNREACH` / IPv6 / `:5432` | Direct Postgres URL | Use **pooled** URL (Supabase `:6543` or Neon pooler) |
|
|
625
|
+
| Control Panel OK, devices never stay paired after restart | SQLite / ephemeral disk | Use Supabase or Neon |
|
|
626
|
+
| Uploads break / broken thumbs in prod | Local `public/uploads` | Cloudinary or Vercel Blob |
|
|
627
|
+
| Blob uploads 401 / missing token | Blob not linked | Vercel Storage → Blob + `BLOB_READ_WRITE_TOKEN` |
|
|
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 |
|
|
396
631
|
| Secrets in git | Mistake | Rotate keys; move to Vercel Env Vars |
|
|
397
632
|
| Replit files present | Copied wrong protocol | Delete `.replit*`; use this file |
|
|
633
|
+
| Brand colours wrong from URL | SPA / blocked fetch | Manual Option B for colours + logo |
|
|
398
634
|
|
|
399
635
|
---
|
|
400
636
|
|
|
@@ -405,10 +641,11 @@ Tell the user:
|
|
|
405
641
|
| Secrets | Replit Secrets | Vercel Environment Variables |
|
|
406
642
|
| Deploy config | `.replit`, artifact `kind=web` | `vercel.json` + Fluid; **no** `.replit` |
|
|
407
643
|
| `hostingTarget` | `"here"` | `"vercel"` |
|
|
408
|
-
|
|
|
409
|
-
|
|
|
410
|
-
|
|
|
411
|
-
|
|
|
644
|
+
| Database | Supabase (primary) | Supabase **or Neon** (pooled); SQLite demo only |
|
|
645
|
+
| Media | Cloudinary or Replit Object Storage | Cloudinary **or Vercel Blob** |
|
|
646
|
+
| Preview | Replit Preview / Autoscale Node | **Next shell + proxy** when v0 hardwires Next |
|
|
647
|
+
| Publish | Long-lived Node | **Node `TomorrowOS.listen` only** (not Next) |
|
|
648
|
+
| Brand from URL | Not in Replit protocol | **Option A** in Question 3 — infer from website |
|
|
412
649
|
|
|
413
650
|
---
|
|
414
651
|
|
|
@@ -416,12 +653,19 @@ Tell the user:
|
|
|
416
653
|
|
|
417
654
|
After setup, report:
|
|
418
655
|
|
|
419
|
-
1. **Live URL** (Production)
|
|
420
|
-
2. **
|
|
421
|
-
3. **
|
|
422
|
-
4. **
|
|
423
|
-
5. **
|
|
424
|
-
6. **
|
|
425
|
-
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
|
|
426
670
|
|
|
427
|
-
|
|
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
|
},
|