@tomorrowos/sdk 0.9.24 → 0.9.26
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 +2 -2
- package/VERCEL_SETUP.md +173 -114
- package/package.json +1 -1
- package/templates/cms-starter/package.json +2 -2
package/README.md
CHANGED
|
@@ -73,8 +73,8 @@ IF any step contradicts Vercel, adapt to settings that Vercel can accept.
|
|
|
73
73
|
|
|
74
74
|
That wizard asks: database (**Supabase** → **Neon** → SQLite demo), media (**Cloudinary** → **Vercel Blob** → local),
|
|
75
75
|
branding (manual fields or **website URL → `brand.json` only**, no login unless asked). Preview may use a thin **Next.js reverse proxy**;
|
|
76
|
-
**Publish stays pure Node** `TomorrowOS.listen` (
|
|
77
|
-
|
|
76
|
+
**Publish stays pure Node** `TomorrowOS.listen` (Framework Preset = Other — gate on no `404 NOT_FOUND`). Cloudinary uses **one Env popup** (all fields).
|
|
77
|
+
Neon/Supabase: agent auto-sets `TOMORROWOS_STORE` + `DATABASE_SSL`. Sets `cms.hostingTarget` to `"vercel"`.
|
|
78
78
|
|
|
79
79
|
### Replit upgrade (existing CMS)
|
|
80
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.5` — pairs with `@tomorrowos/sdk` packages that ship `templates/cms-starter` and this file.
|
|
9
9
|
|
|
10
10
|
## User trigger (copy-paste)
|
|
11
11
|
|
|
@@ -14,10 +14,12 @@ When a human pastes a prompt like this, **read this file and start Question 1 im
|
|
|
14
14
|
```text
|
|
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
|
+
Do not ask for OpenAI / Anthropic / any LLM API keys — TomorrowOS CMS setup does not require them.
|
|
17
18
|
If any step contradicts Vercel, adapt to settings Vercel can accept (document the adaptation).
|
|
18
19
|
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
20
|
Question 3 only updates brand.json from colours/logo/name (a website URL is brand reference only — do not add CMS login/auth unless the user explicitly asks). Do not skip steps.
|
|
20
21
|
Use Next.js only as a v0/Preview shell with a reverse proxy; Production Publish must remain pure Node TomorrowOS.listen (not Next).
|
|
22
|
+
Before claiming Publish works: Framework Preset must be Other/null (not Next.js), startCommand must run TomorrowOS Node, and GET / on the production URL must return the Control Panel (not 404 NOT_FOUND).
|
|
21
23
|
```
|
|
22
24
|
|
|
23
25
|
Alternate triggers:
|
|
@@ -43,6 +45,7 @@ TomorrowOS CMS is **one Node process** that serves:
|
|
|
43
45
|
| Required | Forbidden |
|
|
44
46
|
|----------|-----------|
|
|
45
47
|
| 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) |
|
|
48
|
+
| **Framework Preset = Other / `null`** (dashboard **and** project settings API) | Leaving Framework Preset as **Next.js** (causes `404: NOT_FOUND`) |
|
|
46
49
|
| **Fluid compute** enabled (needed for WebSockets on Vercel) | Classic short-lived serverless with no WebSocket / no Fluid |
|
|
47
50
|
| Durable store = **Postgres** (Supabase or Neon, pooled URL) | **SQLite / `data/*.db` as production store** on Vercel (ephemeral filesystem) |
|
|
48
51
|
| Media = **Cloudinary** or **Vercel Blob** (durable HTTPS URLs) | Relying on local `public/uploads` alone for production fleets |
|
|
@@ -51,6 +54,34 @@ TomorrowOS CMS is **one Node process** that serves:
|
|
|
51
54
|
|
|
52
55
|
**Publish / Production Deploy** must run **`TomorrowOS.listen(...)`** directly. Do **not** put Next.js in front of Production traffic.
|
|
53
56
|
|
|
57
|
+
### Publish `404: NOT_FOUND` — mandatory prevention (read before every deploy)
|
|
58
|
+
|
|
59
|
+
The production URL showing:
|
|
60
|
+
|
|
61
|
+
```text
|
|
62
|
+
404: NOT_FOUND
|
|
63
|
+
Code: NOT_FOUND
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
almost always means **Vercel still thinks this is a Next.js / static deploy**, not a Node TomorrowOS server.
|
|
67
|
+
|
|
68
|
+
**Before every Production deploy, you MUST verify all of the following:**
|
|
69
|
+
|
|
70
|
+
1. **Project → Settings → General → Framework Preset = Other** (or unset / null).
|
|
71
|
+
If it still says **Next.js**, patch it via Vercel API / CLI / dashboard **before** redeploying.
|
|
72
|
+
`"framework": null` in `vercel.json` alone is **not enough** if the dashboard still says Next.js.
|
|
73
|
+
2. **Install Command** = `npm install` / `pnpm install` / `yarn` (normal install).
|
|
74
|
+
3. **Build Command** = a **no-op that exits 0** (e.g. `node -e "process.exit(0)"` or `npm run build` where `build` is that no-op).
|
|
75
|
+
**Never** run `next build` for Production.
|
|
76
|
+
4. **Start / Dev Command for Production** = `npm run start` / `pnpm start` → **`tsx server.ts`** (or `.mts`) that calls `TomorrowOS.listen` and **`export default server`**.
|
|
77
|
+
5. **Output Directory** = **empty / unset**. Do **not** set `outputDirectory: "."` as a fake static site.
|
|
78
|
+
6. **Do not** create a root `api/index.ts` that exports a raw `http.Server` and catch-all `routes` to “fix” 404 — Vercel Fluid does **not** treat that as a supported handler for this CMS. That pattern causes more 404s.
|
|
79
|
+
7. If Preview needs Next, keep Next files **isolated** so Production auto-detect cannot latch onto them:
|
|
80
|
+
- Prefer `preview/` (or similar) for `next.config.*`, `app/`, `pages/` used only by v0 Preview, **or**
|
|
81
|
+
- Ensure Production Framework Preset is forced to **Other** and Start Command never runs `next`.
|
|
82
|
+
|
|
83
|
+
**Setup is NOT complete** while Production `GET /` returns `404: NOT_FOUND`. Fix Framework Preset + Start Command and redeploy.
|
|
84
|
+
|
|
54
85
|
### Preview (v0 / Vercel Agent sandbox) — Next.js shell allowed
|
|
55
86
|
|
|
56
87
|
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.
|
|
@@ -82,6 +113,17 @@ Ask **only** these questions, in **this exact order**:
|
|
|
82
113
|
|
|
83
114
|
**That is the complete list.** Do **not** ask how many screens / devices.
|
|
84
115
|
|
|
116
|
+
**Forbidden extra questions (never ask, even if the user skips a step):**
|
|
117
|
+
|
|
118
|
+
- ❌ OpenAI / Anthropic / Gemini / any LLM API key (`sk-…`, etc.)
|
|
119
|
+
- ❌ “AI pairing”, “AI feature logic”, or inventing auth that needs an LLM key
|
|
120
|
+
- ❌ Asking the user to type `TOMORROWOS_STORE` or `DATABASE_SSL` after they already chose Neon/Supabase — **you set those automatically**
|
|
121
|
+
- ❌ Any secret not listed in Questions 1–3 (DB URL, media credentials, brand)
|
|
122
|
+
|
|
123
|
+
TomorrowOS pairing and Control Panel logic live in `@tomorrowos/sdk`. They do **not** call OpenAI.
|
|
124
|
+
|
|
125
|
+
If the user **skips** a Question 2 option, do **not** invent a substitute question. Re-state Question 2 choices, or proceed with a documented default (e.g. local uploads with a warning) — never pivot to LLM keys.
|
|
126
|
+
|
|
85
127
|
If the user says “just set it up, don’t ask questions,” respond:
|
|
86
128
|
|
|
87
129
|
> 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.
|
|
@@ -92,10 +134,10 @@ Then ask **Question 1**.
|
|
|
92
134
|
|
|
93
135
|
## Hard rules
|
|
94
136
|
|
|
95
|
-
1. **Ask only VERCEL_SETUP.md questions 1–3.** Never use `LLM_PROMPT.md` or `REPLIT_SETUP.md` as the questionnaire.
|
|
96
|
-
2. **
|
|
97
|
-
3. **Do not invent** Cloudinary credentials, database URLs, Vercel Blob tokens, or
|
|
98
|
-
4. **Postgres env naming:** prefer **`SUPABASE_URL`** when using Supabase; prefer **`DATABASE_URL`**
|
|
137
|
+
1. **Ask only VERCEL_SETUP.md questions 1–3.** Never use `LLM_PROMPT.md` or `REPLIT_SETUP.md` as the questionnaire. **Never** ask for OpenAI or other LLM API keys.
|
|
138
|
+
2. **Cloudinary:** open **one** Vercel Env configuration popup / form with **all** Cloudinary fields together (cloud name + API key + API secret). Do **not** quiz the user one credential at a time in chat.
|
|
139
|
+
3. **Do not invent** Cloudinary credentials, database URLs, Vercel Blob tokens, brand colours, **or LLM API keys**.
|
|
140
|
+
4. **Postgres env naming:** prefer **`SUPABASE_URL`** when using Supabase; prefer **`DATABASE_URL`** when using Neon. `createTomorrowOSStore` accepts `SUPABASE_URL` then `DATABASE_URL`.
|
|
99
141
|
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).
|
|
100
142
|
6. **Prefer `npx @tomorrowos/sdk init`** (or `templates/cms-starter`) as the seed. Do not rebuild pairing / WebSocket / playlist APIs.
|
|
101
143
|
7. **Never commit secrets.** Use Vercel Environment Variables (+ optional non-secret flags in committed `.env.example`).
|
|
@@ -106,6 +148,9 @@ Then ask **Question 1**.
|
|
|
106
148
|
12. **Preview:** if the environment is v0 / Next-hardwired, install the Next reverse-proxy shell (see **Preview adapter**). Do not leave Preview broken.
|
|
107
149
|
13. After Q&A: **configure → install → run/deploy → minimal verify** (Control Panel at `/`). Do not run a long test suite.
|
|
108
150
|
14. **No inventing CMS login.** A branding URL never means “build auth.” Only add login if the user **explicitly** requests it.
|
|
151
|
+
15. **Skip ≠ invent.** If the user skips media choice or a credential, do not replace it with OpenAI/auth/login questions. Stay on Q1–Q3 only.
|
|
152
|
+
16. **Neon / Supabase helpers are agent-owned.** After the user pastes the DB URL, **you** set `TOMORROWOS_STORE` and `DATABASE_SSL` automatically — **do not** ask the user to fill those two.
|
|
153
|
+
17. **Publish gate:** do not mark setup complete while Production returns `404: NOT_FOUND`. Fix Framework Preset + Start Command first (see **Publish 404** section).
|
|
109
154
|
|
|
110
155
|
**Question order:** Q1 → Q2 → Q3 → execution checklist.
|
|
111
156
|
|
|
@@ -115,11 +160,11 @@ Then ask **Question 1**.
|
|
|
115
160
|
|
|
116
161
|
### Production runtime (Publish)
|
|
117
162
|
|
|
118
|
-
- Serve with **`tsx server.ts`** (or compiled Node that still calls `TomorrowOS.listen` with `staticRoot`).
|
|
163
|
+
- Serve with **`tsx server.ts`** (or `server.mts` / compiled Node that still calls `TomorrowOS.listen` with `staticRoot`).
|
|
119
164
|
- Keep **`@tomorrowos/sdk`** and put **`tsx` in `dependencies`** (not only `devDependencies`) if production runs via `tsx`.
|
|
120
165
|
- Node **20+**.
|
|
121
166
|
- **Do not** replace the SDK with a hand-rolled server that drops WebSocket upgrades.
|
|
122
|
-
- **Forbidden as the Production Deploy entrypoint:** `next start`, static export only, or any command that does not start `TomorrowOS.listen(...)`.
|
|
167
|
+
- **Forbidden as the Production Deploy entrypoint:** `next start`, static export only, root `api/` fake catch-all for raw `http.Server`, or any command that does not start `TomorrowOS.listen(...)`.
|
|
123
168
|
|
|
124
169
|
### Expected `package.json` scripts
|
|
125
170
|
|
|
@@ -131,7 +176,7 @@ Then ask **Question 1**.
|
|
|
131
176
|
"dev:tomorrowos": "cross-env PORT=3001 TOMORROWOS_INTERNAL_PORT=3001 tsx watch server.ts",
|
|
132
177
|
"dev:next": "next dev -p 3000",
|
|
133
178
|
"start": "tsx server.ts",
|
|
134
|
-
"build": "
|
|
179
|
+
"build": "node -e \"process.exit(0)\""
|
|
135
180
|
}
|
|
136
181
|
}
|
|
137
182
|
```
|
|
@@ -139,6 +184,7 @@ Then ask **Question 1**.
|
|
|
139
184
|
Adapt script names / process runners as needed. **Invariant:**
|
|
140
185
|
|
|
141
186
|
- **`npm start` / Production** = TomorrowOS Node only.
|
|
187
|
+
- **`build`** = no-op exit 0 (never `next build` for Production).
|
|
142
188
|
- **Preview / v0** = Next on public port + TomorrowOS on internal port + proxy.
|
|
143
189
|
|
|
144
190
|
### Minimal dependencies (Production)
|
|
@@ -160,7 +206,8 @@ Adapt script names / process runners as needed. **Invariant:**
|
|
|
160
206
|
},
|
|
161
207
|
"scripts": {
|
|
162
208
|
"dev": "tsx watch server.ts",
|
|
163
|
-
"start": "tsx server.ts"
|
|
209
|
+
"start": "tsx server.ts",
|
|
210
|
+
"build": "node -e \"process.exit(0)\""
|
|
164
211
|
}
|
|
165
212
|
}
|
|
166
213
|
```
|
|
@@ -173,6 +220,8 @@ For Preview shell only, add as needed: `next`, `react`, `react-dom`, and a proce
|
|
|
173
220
|
|
|
174
221
|
### `server.ts` pattern (Vercel-adapted — Production)
|
|
175
222
|
|
|
223
|
+
Keep the server at **project root** (e.g. `server.ts` / `server.mts`). Do **not** move Production into `api/` solely to satisfy Vercel routing myths.
|
|
224
|
+
|
|
176
225
|
```ts
|
|
177
226
|
import "dotenv/config";
|
|
178
227
|
import { readFileSync } from "fs";
|
|
@@ -218,12 +267,12 @@ Load env with `dotenv` for local/`vercel dev`. On production Deploy, Vercel inje
|
|
|
218
267
|
|
|
219
268
|
Detect Preview / v0 (Next already present, or Preview blank while TomorrowOS logs “listening”). Then:
|
|
220
269
|
|
|
221
|
-
1. Keep a **minimal** Next app (
|
|
270
|
+
1. Keep a **minimal** Next app **isolated when possible** (e.g. under `preview/`) so Production Framework detection does not latch onto root `next.config.*`.
|
|
222
271
|
2. TomorrowOS listens on **`127.0.0.1:TOMORROWOS_INTERNAL_PORT`** (default **3001**).
|
|
223
272
|
3. Add a **catch-all** Next route (or middleware) that proxies HTTP to TomorrowOS:
|
|
224
273
|
|
|
225
274
|
```ts
|
|
226
|
-
// Example: app
|
|
275
|
+
// Example: preview app proxy handler
|
|
227
276
|
// Forward method, headers, and body to http://127.0.0.1:3001/<path>
|
|
228
277
|
// Return the upstream status, headers, and body to the browser.
|
|
229
278
|
```
|
|
@@ -238,24 +287,41 @@ Preferred UX: proxy **`/`** itself (rewrites) so the Preview URL shows the Contr
|
|
|
238
287
|
- Making Next the Production CMS or the only server after Publish.
|
|
239
288
|
- Deleting TomorrowOS `server.ts` because Preview needed Next.
|
|
240
289
|
- Claiming setup complete if Production entrypoint is still `next start`.
|
|
290
|
+
- Claiming setup complete if Production `GET /` is `404: NOT_FOUND`.
|
|
291
|
+
- Creating root `api/index.*` + `routes` catch-all that exports a raw `http.Server` “to fix 404”.
|
|
241
292
|
|
|
242
|
-
### `vercel.json` (
|
|
293
|
+
### `vercel.json` (Production — explicit Node, not Next)
|
|
243
294
|
|
|
244
|
-
|
|
295
|
+
Write `vercel.json` so Production cannot be mistaken for Next. Minimum:
|
|
245
296
|
|
|
246
297
|
```json
|
|
247
298
|
{
|
|
248
|
-
"fluid": true
|
|
299
|
+
"fluid": true,
|
|
300
|
+
"framework": null,
|
|
301
|
+
"installCommand": "npm install",
|
|
302
|
+
"buildCommand": "npm run build",
|
|
303
|
+
"outputDirectory": null
|
|
249
304
|
}
|
|
250
305
|
```
|
|
251
306
|
|
|
252
|
-
|
|
307
|
+
Use `pnpm` / `yarn` variants of install/build if that is the project’s package manager.
|
|
308
|
+
|
|
309
|
+
**Also force Project Settings (dashboard or Vercel API) — this is mandatory:**
|
|
310
|
+
|
|
311
|
+
| Setting | Required value |
|
|
312
|
+
|---------|----------------|
|
|
313
|
+
| Framework Preset | **Other** / null — **not Next.js** |
|
|
314
|
+
| Install Command | `npm install` (or pnpm/yarn) |
|
|
315
|
+
| Build Command | `npm run build` (no-op exit 0) |
|
|
316
|
+
| Output Directory | **empty** |
|
|
317
|
+
| Start Command | `npm run start` → `tsx server.ts` (or `.mts`) |
|
|
318
|
+
| Fluid compute | **On** |
|
|
319
|
+
|
|
320
|
+
If `next` / `next.config.*` exist for Preview and the dashboard still auto-selects Next.js:
|
|
253
321
|
|
|
254
|
-
1.
|
|
255
|
-
2.
|
|
256
|
-
3.
|
|
257
|
-
4. Do **not** enable “Output Directory” static-only hosting for this CMS.
|
|
258
|
-
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.
|
|
322
|
+
1. Patch Framework Preset to **Other** via API/CLI/dashboard.
|
|
323
|
+
2. Redeploy.
|
|
324
|
+
3. Hit Production `GET /` — must be Control Panel HTML, not `404: NOT_FOUND`.
|
|
259
325
|
|
|
260
326
|
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.
|
|
261
327
|
|
|
@@ -270,6 +336,7 @@ If Vercel’s UI and this file disagree on bundling, **prefer whatever keeps a s
|
|
|
270
336
|
|
|
271
337
|
- ❌ `.replit`, `.replit-artifact`
|
|
272
338
|
- ❌ Next.js as the **Production** CMS (App Router replacing TomorrowOS)
|
|
339
|
+
- ❌ Root `api/` catch-all exporting raw `http.Server` to “fix” Publish 404
|
|
273
340
|
- ❌ Separate “API project” without the Control Panel
|
|
274
341
|
- ❌ Committing database passwords, Cloudinary secrets, or Blob tokens
|
|
275
342
|
|
|
@@ -295,52 +362,47 @@ If Vercel’s UI and this file disagree on bundling, **prefer whatever keeps a s
|
|
|
295
362
|
|
|
296
363
|
### Step B — If **1 / Supabase**
|
|
297
364
|
|
|
298
|
-
**Ask exactly:**
|
|
365
|
+
**Ask exactly (connection string only):**
|
|
299
366
|
|
|
300
|
-
> Paste your Supabase Postgres connection string. I will store it as
|
|
367
|
+
> Paste your Supabase Postgres connection string. I will store it as **`SUPABASE_URL`**.
|
|
301
368
|
>
|
|
302
369
|
> In Supabase: **Project Settings → Database → Connection string → Connection pooling** (Session mode).
|
|
303
370
|
> Preferred shape:
|
|
304
371
|
> `postgresql://postgres.[PROJECT]:[PASSWORD]@aws-0-[REGION].pooler.supabase.com:6543/postgres`
|
|
305
372
|
>
|
|
306
|
-
> **Do not** use the direct host `db.*.supabase.co:5432` for Vercel
|
|
307
|
-
>
|
|
308
|
-
> Confirm SSL is required (usually **yes** for Supabase).
|
|
373
|
+
> **Do not** use the direct host `db.*.supabase.co:5432` for Vercel.
|
|
309
374
|
|
|
310
|
-
**You must then:**
|
|
375
|
+
**You must then (automatic — do NOT ask the user to type these):**
|
|
311
376
|
|
|
312
|
-
1. Set Vercel Env Vars
|
|
313
|
-
- `SUPABASE_URL=<pooler string>`
|
|
314
|
-
- `TOMORROWOS_STORE=supabase`
|
|
315
|
-
- `DATABASE_SSL=true` (unless user
|
|
377
|
+
1. Set Vercel Env Vars:
|
|
378
|
+
- `SUPABASE_URL=<user pooler string>`
|
|
379
|
+
- `TOMORROWOS_STORE=supabase` ← **agent sets this**
|
|
380
|
+
- `DATABASE_SSL=true` ← **agent sets this** (unless user already said SSL is off)
|
|
316
381
|
2. Wire `server.ts` as in **Runtime & Vercel deploy rules**.
|
|
317
|
-
3. Optional committed `.env.example` with **placeholders only
|
|
382
|
+
3. Optional committed `.env.example` with **placeholders only**.
|
|
318
383
|
4. **Do not** commit the real connection string.
|
|
319
|
-
5. **
|
|
384
|
+
5. **Do not** open a second form asking for `TOMORROWOS_STORE` or `DATABASE_SSL`.
|
|
320
385
|
|
|
321
386
|
### Step C — If **2 / Neon**
|
|
322
387
|
|
|
323
|
-
**Ask exactly:**
|
|
388
|
+
**Ask exactly (connection string only):**
|
|
324
389
|
|
|
325
|
-
> Paste your **Neon pooled** Postgres connection string. I will store it as **`DATABASE_URL
|
|
390
|
+
> Paste your **Neon pooled** Postgres connection string. I will store it as **`DATABASE_URL`**.
|
|
326
391
|
>
|
|
327
|
-
> In Neon: **Dashboard → Connection details → Pooled connection
|
|
392
|
+
> In Neon: **Dashboard → Connection details → Pooled connection**.
|
|
328
393
|
> Typical shape:
|
|
329
|
-
> `postgresql://[user]:[password]@[endpoint]
|
|
330
|
-
>
|
|
331
|
-
> **Do not** use the direct (non-pooled) connection string for Vercel serverless unless Neon docs explicitly say otherwise for your plan.
|
|
332
|
-
>
|
|
333
|
-
> Confirm SSL (`sslmode=require`) is on (usually **yes**).
|
|
394
|
+
> `postgresql://[user]:[password]@[endpoint]-pooler.[region].aws.neon.tech/[dbname]?sslmode=require`
|
|
334
395
|
|
|
335
|
-
**You must then:**
|
|
396
|
+
**You must then (automatic — do NOT ask the user to type these):**
|
|
336
397
|
|
|
337
398
|
1. Set Vercel Env Vars:
|
|
338
|
-
- `DATABASE_URL=<pooled
|
|
339
|
-
- `TOMORROWOS_STORE=postgres`
|
|
340
|
-
- `DATABASE_SSL=true`
|
|
399
|
+
- `DATABASE_URL=<user pooled string>`
|
|
400
|
+
- `TOMORROWOS_STORE=postgres` ← **agent sets this**
|
|
401
|
+
- `DATABASE_SSL=true` ← **agent sets this**
|
|
341
402
|
2. Wire `server.ts` — `createTomorrowOSStore` uses `PostgresStore` for `postgres` / `supabase` drivers.
|
|
342
403
|
3. **Do not** commit the real connection string.
|
|
343
|
-
4.
|
|
404
|
+
4. **Do not** ask the user to fill `TOMORROWOS_STORE` or `DATABASE_SSL` in chat or in an Env popup. Those are derived from choosing Neon.
|
|
405
|
+
5. If you see `ENETUNREACH` on `:5432`, switch to Neon’s **pooled** URL.
|
|
344
406
|
|
|
345
407
|
### Step D — If **3 / SQLite**
|
|
346
408
|
|
|
@@ -350,9 +412,9 @@ If Vercel’s UI and this file disagree on bundling, **prefer whatever keeps a s
|
|
|
350
412
|
|
|
351
413
|
Only proceed after the user explicitly confirms.
|
|
352
414
|
|
|
353
|
-
**You must then:**
|
|
415
|
+
**You must then (automatic):**
|
|
354
416
|
|
|
355
|
-
1. Set `TOMORROWOS_STORE=sqlite` (
|
|
417
|
+
1. Set `TOMORROWOS_STORE=sqlite` yourself (do not ask the user to type it).
|
|
356
418
|
2. Keep `sqlitePath` in `server.ts` for `data/tomorrowos.db`.
|
|
357
419
|
3. Warn again in the final summary that they should move to Supabase or Neon for real devices.
|
|
358
420
|
|
|
@@ -362,6 +424,9 @@ Only proceed after the user explicitly confirms.
|
|
|
362
424
|
|
|
363
425
|
## Question 2 — Media storage
|
|
364
426
|
|
|
427
|
+
> **This question is only about media files (images/videos).**
|
|
428
|
+
> It is **not** about OpenAI, AI pairing, or LLM keys. If you are about to ask for an `sk-` key, **stop** — you are off-protocol.
|
|
429
|
+
|
|
365
430
|
### Step A — Ask storage choice
|
|
366
431
|
|
|
367
432
|
**Ask exactly:**
|
|
@@ -376,46 +441,33 @@ Only proceed after the user explicitly confirms.
|
|
|
376
441
|
>
|
|
377
442
|
> Reply with **1**, **2**, or **3** (or “Cloudinary” / “Vercel Blob” / “local”).
|
|
378
443
|
|
|
379
|
-
### Step B — If **1 / Cloudinary** (same Question 2 —
|
|
380
|
-
|
|
381
|
-
> **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.
|
|
382
|
-
|
|
383
|
-
#### B1 — Cloud name (ask alone)
|
|
384
|
-
|
|
385
|
-
**Ask exactly:**
|
|
386
|
-
|
|
387
|
-
> Paste your Cloudinary **Cloud name** only (`CLOUDINARY_CLOUD_NAME`).
|
|
388
|
-
> Find it at [cloudinary.com/console](https://cloudinary.com/console) → Dashboard.
|
|
389
|
-
|
|
390
|
-
Save as Vercel Env Var `CLOUDINARY_CLOUD_NAME`. Do **not** ask for key or secret yet.
|
|
444
|
+
### Step B — If **1 / Cloudinary** (same Question 2 — **one Env popup**)
|
|
391
445
|
|
|
392
|
-
|
|
446
|
+
> **Critical UX rule:** Immediately open the **Vercel Environment Variables configuration popup / form** with **all Cloudinary fields in one UI**. Do **not** ask for cloud name, then key, then secret in separate chat messages.
|
|
393
447
|
|
|
394
|
-
**
|
|
395
|
-
|
|
396
|
-
> Paste your Cloudinary **API Key** only (`CLOUDINARY_API_KEY`).
|
|
397
|
-
> Do not paste the API Secret in this step.
|
|
398
|
-
|
|
399
|
-
Save as Vercel Env Var `CLOUDINARY_API_KEY`. Do **not** ask for the secret in this message.
|
|
400
|
-
|
|
401
|
-
#### B3 — API secret (ask alone — after B2 is saved)
|
|
402
|
-
|
|
403
|
-
**Ask exactly:**
|
|
448
|
+
**Preferred:** Vercel Env Vars UI (or Agent secrets form) with these fields together:
|
|
404
449
|
|
|
405
|
-
|
|
406
|
-
|
|
450
|
+
| Field | Env name | Required |
|
|
451
|
+
|-------|----------|----------|
|
|
452
|
+
| Cloud name | `CLOUDINARY_CLOUD_NAME` | Yes |
|
|
453
|
+
| API Key | `CLOUDINARY_API_KEY` | Yes |
|
|
454
|
+
| API Secret | `CLOUDINARY_API_SECRET` | Yes |
|
|
455
|
+
| Folder (optional) | `CLOUDINARY_FOLDER` | No (e.g. `tomorrowos`) |
|
|
407
456
|
|
|
408
|
-
|
|
457
|
+
**If a multi-field popup is unavailable, ask once in a single message** (still not three turns):
|
|
409
458
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
>
|
|
459
|
+
> Paste Cloudinary credentials from [cloudinary.com/console](https://cloudinary.com/console) (I will store them as Vercel Env Vars):
|
|
460
|
+
>
|
|
461
|
+
> 1. `CLOUDINARY_CLOUD_NAME`
|
|
462
|
+
> 2. `CLOUDINARY_API_KEY`
|
|
463
|
+
> 3. `CLOUDINARY_API_SECRET`
|
|
464
|
+
> 4. optional `CLOUDINARY_FOLDER`
|
|
413
465
|
|
|
414
466
|
**You must then:**
|
|
415
467
|
|
|
416
|
-
1.
|
|
468
|
+
1. Save all three required Env Vars for Production + Preview + Development as appropriate.
|
|
417
469
|
2. **Do not** invent placeholders.
|
|
418
|
-
3. **Do not** proceed to Question 3 until all three exist.
|
|
470
|
+
3. **Do not** proceed to Question 3 until all three required vars exist.
|
|
419
471
|
4. **Do not** run a formal Cloudinary upload test unless the user asks.
|
|
420
472
|
|
|
421
473
|
The SDK auto-detects these env vars.
|
|
@@ -433,7 +485,7 @@ The SDK auto-detects these env vars.
|
|
|
433
485
|
|
|
434
486
|
**You must then:**
|
|
435
487
|
|
|
436
|
-
1. Set Vercel Env Var: `BLOB_READ_WRITE_TOKEN=<token>` (if not auto-injected).
|
|
488
|
+
1. Set Vercel Env Var: `BLOB_READ_WRITE_TOKEN=<token>` (if not auto-injected). Prefer the Env popup when available.
|
|
437
489
|
2. `npm install @vercel/blob` (add to `dependencies`).
|
|
438
490
|
3. Wire uploads so media returns **absolute HTTPS Blob URLs** stored in `uploaded_assets` (players need stable public URLs).
|
|
439
491
|
- 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).
|
|
@@ -561,26 +613,26 @@ npx @tomorrowos/sdk@latest init .
|
|
|
561
613
|
|
|
562
614
|
Then:
|
|
563
615
|
|
|
564
|
-
1. Keep TomorrowOS `server.ts` as the **Production** app (**export `listen()` HTTP server**).
|
|
565
|
-
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.
|
|
566
|
-
3.
|
|
567
|
-
4.
|
|
568
|
-
5.
|
|
569
|
-
6.
|
|
616
|
+
1. Keep TomorrowOS `server.ts` / `server.mts` at **root** as the **Production** app (**export `listen()` HTTP server**).
|
|
617
|
+
2. If the environment is **v0 / Next Preview**, add the **Preview adapter** (Next shell + reverse proxy + internal TomorrowOS port). Isolate Next configs when possible. Do **not** make Next the Production CMS.
|
|
618
|
+
3. Write `vercel.json` with **`fluid: true`**, **`framework: null`**, no-op **`buildCommand`**, and **empty outputDirectory**.
|
|
619
|
+
4. **Force Project Settings:** Framework Preset = **Other**, Start Command = `npm run start` (Node), Fluid on.
|
|
620
|
+
5. Ensure Production start command is **`tsx server.ts`** (or `.mts`), **not** `next start`.
|
|
621
|
+
6. Do **not** add root `api/` catch-all for raw `http.Server`.
|
|
622
|
+
7. Do **not** add `.replit*`.
|
|
623
|
+
8. If Q2 = Vercel Blob: install `@vercel/blob` and wire the upload bridge.
|
|
570
624
|
|
|
571
625
|
### B. Environment Variables (Vercel dashboard + local)
|
|
572
626
|
|
|
573
|
-
| Name | Required |
|
|
574
|
-
|
|
575
|
-
| `SUPABASE_URL` | If Q1 = Supabase |
|
|
576
|
-
| `DATABASE_URL` | If Q1 = Neon |
|
|
577
|
-
| `TOMORROWOS_STORE` | Yes | `supabase
|
|
578
|
-
| `DATABASE_SSL` | Yes
|
|
579
|
-
| `
|
|
580
|
-
| `
|
|
581
|
-
| `
|
|
582
|
-
| `BLOB_READ_WRITE_TOKEN` | If Q2 = Vercel Blob | From Vercel Blob store |
|
|
583
|
-
| `TOMORROWOS_INTERNAL_PORT` | Preview only | e.g. `3001` when Next owns `:3000` |
|
|
627
|
+
| Name | Required | Who fills it |
|
|
628
|
+
|------|----------|--------------|
|
|
629
|
+
| `SUPABASE_URL` | If Q1 = Supabase | User pastes URL |
|
|
630
|
+
| `DATABASE_URL` | If Q1 = Neon | User pastes URL |
|
|
631
|
+
| `TOMORROWOS_STORE` | Yes | **Agent auto-sets** (`supabase` / `postgres` / `sqlite`) |
|
|
632
|
+
| `DATABASE_SSL` | Yes for Postgres | **Agent auto-sets** `true` |
|
|
633
|
+
| `CLOUDINARY_*` | If Q2 = Cloudinary | User via **one Env popup** (all fields) |
|
|
634
|
+
| `BLOB_READ_WRITE_TOKEN` | If Q2 = Vercel Blob | User / Blob link |
|
|
635
|
+
| `TOMORROWOS_INTERNAL_PORT` | Preview only | Agent (e.g. `3001`) |
|
|
584
636
|
| `PORT` | Optional | Vercel injects for Production |
|
|
585
637
|
|
|
586
638
|
Mirror non-secrets in `.env` for local `npm run dev` if helpful; never commit secrets.
|
|
@@ -606,12 +658,15 @@ vercel --prod
|
|
|
606
658
|
# or git push → Vercel Git integration
|
|
607
659
|
```
|
|
608
660
|
|
|
609
|
-
|
|
661
|
+
**Publish acceptance checklist (all must pass):**
|
|
610
662
|
|
|
611
|
-
1.
|
|
612
|
-
2.
|
|
663
|
+
1. Framework Preset = **Other** / null (dashboard confirms — not Next.js)
|
|
664
|
+
2. Start Command = Node `TomorrowOS.listen` (`npm run start`)
|
|
613
665
|
3. Fluid compute on
|
|
614
|
-
4. Env Vars present for Production
|
|
666
|
+
4. Env Vars present for Production
|
|
667
|
+
5. Production `GET /` returns Control Panel HTML — **not** `404: NOT_FOUND`
|
|
668
|
+
|
|
669
|
+
If step 5 fails: do **not** invent `api/` + `routes`. Patch Framework Preset + Start Command, redeploy, re-check `/`.
|
|
615
670
|
|
|
616
671
|
Tell the user:
|
|
617
672
|
|
|
@@ -624,7 +679,7 @@ Tell the user:
|
|
|
624
679
|
**Do:**
|
|
625
680
|
|
|
626
681
|
1. Preview: Control Panel HTML at `/` (via Next proxy if applicable)
|
|
627
|
-
2. Publish: Control Panel HTML at `/` from **Node** TomorrowOS
|
|
682
|
+
2. Publish: Control Panel HTML at `/` from **Node** TomorrowOS (block on `404: NOT_FOUND`)
|
|
628
683
|
3. Server status: Media OK when Cloudinary or Blob configured
|
|
629
684
|
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
|
|
630
685
|
5. Fluid compute on for Production
|
|
@@ -637,6 +692,8 @@ Tell the user:
|
|
|
637
692
|
|
|
638
693
|
| Symptom | Likely cause | Fix |
|
|
639
694
|
|---------|--------------|-----|
|
|
695
|
+
| Publish `404: NOT_FOUND` | Framework Preset still **Next.js** / no Node start | Set Framework = **Other**, Start = `npm run start`, Fluid on; redeploy. Do **not** add root `api/` catch-all |
|
|
696
|
+
| Build runs `next build` / missing routes-manifest | Next auto-detected from `next.config` / deps | Force Framework null/Other; `build` = no-op; isolate Preview Next files |
|
|
640
697
|
| Preview blank but logs show TomorrowOS listening | v0 Next intercepts public port | Add Next reverse proxy → internal TomorrowOS port |
|
|
641
698
|
| Preview works, Publish broken / Next-only | Production still on Next | Switch Production entry to `tsx server.ts` / `export default server` |
|
|
642
699
|
| `ENETUNREACH` / IPv6 / `:5432` | Direct Postgres URL | Use **pooled** URL (Supabase `:6543` or Neon pooler) |
|
|
@@ -645,11 +702,12 @@ Tell the user:
|
|
|
645
702
|
| Blob uploads 401 / missing token | Blob not linked | Vercel Storage → Blob + `BLOB_READ_WRITE_TOKEN` |
|
|
646
703
|
| WebSocket fails on Publish | Fluid off / static-only / Next as Production | Enable Fluid; sole Node `listen()` for Publish |
|
|
647
704
|
| Devices fail only on Preview | Next proxy cannot upgrade `wss` | Expected — pair on Publish URL |
|
|
648
|
-
| Agent
|
|
705
|
+
| Agent asked user to fill `TOMORROWOS_STORE` / `DATABASE_SSL` | Off-protocol | Agent must auto-set those after Neon/Supabase choice |
|
|
706
|
+
| Agent asked Cloudinary key/secret in three chat turns | Off-protocol | Use **one Env popup** with all Cloudinary fields |
|
|
707
|
+
| Agent asked for OpenAI / `sk-` key during setup | Hallucinated “AI pairing” requirement | **Refuse.** TomorrowOS does not need LLM keys. Return to Q2 media choices only |
|
|
708
|
+
| Agent built a login page after user pasted a URL | Misread brand reference as product scope | Remove login; keep starter Control Panel; apply URL only to `brand.json` |
|
|
649
709
|
| Secrets in git | Mistake | Rotate keys; move to Vercel Env Vars |
|
|
650
710
|
| Replit files present | Copied wrong protocol | Delete `.replit*`; use this file |
|
|
651
|
-
| Brand colours wrong from URL | SPA / blocked fetch | Manual Option B for colours + logo |
|
|
652
|
-
| Agent built a login page after user pasted a URL | Misread brand reference as product scope | Remove login; keep starter Control Panel; apply URL only to `brand.json` |
|
|
653
711
|
|
|
654
712
|
---
|
|
655
713
|
|
|
@@ -658,12 +716,12 @@ Tell the user:
|
|
|
658
716
|
| Topic | Replit | Vercel |
|
|
659
717
|
|-------|--------|--------|
|
|
660
718
|
| Secrets | Replit Secrets | Vercel Environment Variables |
|
|
661
|
-
| Deploy config | `.replit`, artifact `kind=web` | `vercel.json` + Fluid; **no** `.replit` |
|
|
719
|
+
| Deploy config | `.replit`, artifact `kind=web` | `vercel.json` + Fluid; **Framework = Other**; **no** `.replit` |
|
|
662
720
|
| `hostingTarget` | `"here"` | `"vercel"` |
|
|
663
721
|
| Database | Supabase (primary) | Supabase **or Neon** (pooled); SQLite demo only |
|
|
664
722
|
| Media | Cloudinary or Replit Object Storage | Cloudinary **or Vercel Blob** |
|
|
665
723
|
| Preview | Replit Preview / Autoscale Node | **Next shell + proxy** when v0 hardwires Next |
|
|
666
|
-
| Publish | Long-lived Node | **Node `TomorrowOS.listen` only** (not Next) |
|
|
724
|
+
| Publish | Long-lived Node | **Node `TomorrowOS.listen` only** (not Next); gate on no `404 NOT_FOUND` |
|
|
667
725
|
| Brand from URL | Not in Replit protocol | **Option A** — infer **`brand.json` only**; never invent login |
|
|
668
726
|
|
|
669
727
|
---
|
|
@@ -673,18 +731,19 @@ Tell the user:
|
|
|
673
731
|
After setup, report:
|
|
674
732
|
|
|
675
733
|
1. **Live URL** (Production Publish)
|
|
676
|
-
2. **
|
|
677
|
-
3. **
|
|
678
|
-
4. **
|
|
679
|
-
5. **
|
|
680
|
-
6. **
|
|
681
|
-
7. **
|
|
682
|
-
8. **
|
|
734
|
+
2. **Publish health:** Framework Preset = Other; Start = Node; `GET /` OK (not 404)
|
|
735
|
+
3. **Preview note:** Next proxy shell used / not needed
|
|
736
|
+
4. **Env Vars set** (names only — never values); note which were auto-set (`TOMORROWOS_STORE`, `DATABASE_SSL`)
|
|
737
|
+
5. **Store:** Supabase pooler / Neon pooled / SQLite (with warning)
|
|
738
|
+
6. **Media:** Cloudinary, Vercel Blob, or local (with warning)
|
|
739
|
+
7. **Brand:** name + `hostingTarget: vercel` (+ note if derived from website URL)
|
|
740
|
+
8. **Runtime split:** Preview = … ; Publish = Node TomorrowOS (confirm **not** Next)
|
|
741
|
+
9. **Open action:** if Database still ERROR, paste the pooler instruction again
|
|
683
742
|
|
|
684
743
|
Protocol complete when:
|
|
685
744
|
|
|
686
745
|
- **Preview** shows the Control Panel at `/` (via Next proxy if required), **and**
|
|
687
|
-
- **Publish**
|
|
746
|
+
- **Publish** returns Control Panel at `/` from pure Node `TomorrowOS.listen` with Fluid (not `404: NOT_FOUND`), **and**
|
|
688
747
|
- Q1–Q3 configuration is saved
|
|
689
748
|
|
|
690
749
|
— 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.26",
|
|
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.26",
|
|
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.26",
|
|
17
17
|
"dotenv": "^17.2.3",
|
|
18
18
|
"tsx": "^4.19.0"
|
|
19
19
|
},
|