@tomorrowos/sdk 0.9.25 → 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 +156 -113
- 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
|
|
|
@@ -19,6 +19,7 @@ If any step contradicts Vercel, adapt to settings Vercel can accept (document th
|
|
|
19
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.
|
|
20
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.
|
|
21
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).
|
|
22
23
|
```
|
|
23
24
|
|
|
24
25
|
Alternate triggers:
|
|
@@ -44,6 +45,7 @@ TomorrowOS CMS is **one Node process** that serves:
|
|
|
44
45
|
| Required | Forbidden |
|
|
45
46
|
|----------|-----------|
|
|
46
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`) |
|
|
47
49
|
| **Fluid compute** enabled (needed for WebSockets on Vercel) | Classic short-lived serverless with no WebSocket / no Fluid |
|
|
48
50
|
| Durable store = **Postgres** (Supabase or Neon, pooled URL) | **SQLite / `data/*.db` as production store** on Vercel (ephemeral filesystem) |
|
|
49
51
|
| Media = **Cloudinary** or **Vercel Blob** (durable HTTPS URLs) | Relying on local `public/uploads` alone for production fleets |
|
|
@@ -52,6 +54,34 @@ TomorrowOS CMS is **one Node process** that serves:
|
|
|
52
54
|
|
|
53
55
|
**Publish / Production Deploy** must run **`TomorrowOS.listen(...)`** directly. Do **not** put Next.js in front of Production traffic.
|
|
54
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
|
+
|
|
55
85
|
### Preview (v0 / Vercel Agent sandbox) — Next.js shell allowed
|
|
56
86
|
|
|
57
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.
|
|
@@ -87,6 +117,7 @@ Ask **only** these questions, in **this exact order**:
|
|
|
87
117
|
|
|
88
118
|
- ❌ OpenAI / Anthropic / Gemini / any LLM API key (`sk-…`, etc.)
|
|
89
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**
|
|
90
121
|
- ❌ Any secret not listed in Questions 1–3 (DB URL, media credentials, brand)
|
|
91
122
|
|
|
92
123
|
TomorrowOS pairing and Control Panel logic live in `@tomorrowos/sdk`. They do **not** call OpenAI.
|
|
@@ -104,9 +135,9 @@ Then ask **Question 1**.
|
|
|
104
135
|
## Hard rules
|
|
105
136
|
|
|
106
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.
|
|
107
|
-
2. **
|
|
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.
|
|
108
139
|
3. **Do not invent** Cloudinary credentials, database URLs, Vercel Blob tokens, brand colours, **or LLM API keys**.
|
|
109
|
-
4. **Postgres env naming:** prefer **`SUPABASE_URL`** when using Supabase; prefer **`DATABASE_URL`**
|
|
140
|
+
4. **Postgres env naming:** prefer **`SUPABASE_URL`** when using Supabase; prefer **`DATABASE_URL`** when using Neon. `createTomorrowOSStore` accepts `SUPABASE_URL` then `DATABASE_URL`.
|
|
110
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).
|
|
111
142
|
6. **Prefer `npx @tomorrowos/sdk init`** (or `templates/cms-starter`) as the seed. Do not rebuild pairing / WebSocket / playlist APIs.
|
|
112
143
|
7. **Never commit secrets.** Use Vercel Environment Variables (+ optional non-secret flags in committed `.env.example`).
|
|
@@ -118,6 +149,8 @@ Then ask **Question 1**.
|
|
|
118
149
|
13. After Q&A: **configure → install → run/deploy → minimal verify** (Control Panel at `/`). Do not run a long test suite.
|
|
119
150
|
14. **No inventing CMS login.** A branding URL never means “build auth.” Only add login if the user **explicitly** requests it.
|
|
120
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).
|
|
121
154
|
|
|
122
155
|
**Question order:** Q1 → Q2 → Q3 → execution checklist.
|
|
123
156
|
|
|
@@ -127,11 +160,11 @@ Then ask **Question 1**.
|
|
|
127
160
|
|
|
128
161
|
### Production runtime (Publish)
|
|
129
162
|
|
|
130
|
-
- 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`).
|
|
131
164
|
- Keep **`@tomorrowos/sdk`** and put **`tsx` in `dependencies`** (not only `devDependencies`) if production runs via `tsx`.
|
|
132
165
|
- Node **20+**.
|
|
133
166
|
- **Do not** replace the SDK with a hand-rolled server that drops WebSocket upgrades.
|
|
134
|
-
- **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(...)`.
|
|
135
168
|
|
|
136
169
|
### Expected `package.json` scripts
|
|
137
170
|
|
|
@@ -143,7 +176,7 @@ Then ask **Question 1**.
|
|
|
143
176
|
"dev:tomorrowos": "cross-env PORT=3001 TOMORROWOS_INTERNAL_PORT=3001 tsx watch server.ts",
|
|
144
177
|
"dev:next": "next dev -p 3000",
|
|
145
178
|
"start": "tsx server.ts",
|
|
146
|
-
"build": "
|
|
179
|
+
"build": "node -e \"process.exit(0)\""
|
|
147
180
|
}
|
|
148
181
|
}
|
|
149
182
|
```
|
|
@@ -151,6 +184,7 @@ Then ask **Question 1**.
|
|
|
151
184
|
Adapt script names / process runners as needed. **Invariant:**
|
|
152
185
|
|
|
153
186
|
- **`npm start` / Production** = TomorrowOS Node only.
|
|
187
|
+
- **`build`** = no-op exit 0 (never `next build` for Production).
|
|
154
188
|
- **Preview / v0** = Next on public port + TomorrowOS on internal port + proxy.
|
|
155
189
|
|
|
156
190
|
### Minimal dependencies (Production)
|
|
@@ -172,7 +206,8 @@ Adapt script names / process runners as needed. **Invariant:**
|
|
|
172
206
|
},
|
|
173
207
|
"scripts": {
|
|
174
208
|
"dev": "tsx watch server.ts",
|
|
175
|
-
"start": "tsx server.ts"
|
|
209
|
+
"start": "tsx server.ts",
|
|
210
|
+
"build": "node -e \"process.exit(0)\""
|
|
176
211
|
}
|
|
177
212
|
}
|
|
178
213
|
```
|
|
@@ -185,6 +220,8 @@ For Preview shell only, add as needed: `next`, `react`, `react-dom`, and a proce
|
|
|
185
220
|
|
|
186
221
|
### `server.ts` pattern (Vercel-adapted — Production)
|
|
187
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
|
+
|
|
188
225
|
```ts
|
|
189
226
|
import "dotenv/config";
|
|
190
227
|
import { readFileSync } from "fs";
|
|
@@ -230,12 +267,12 @@ Load env with `dotenv` for local/`vercel dev`. On production Deploy, Vercel inje
|
|
|
230
267
|
|
|
231
268
|
Detect Preview / v0 (Next already present, or Preview blank while TomorrowOS logs “listening”). Then:
|
|
232
269
|
|
|
233
|
-
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.*`.
|
|
234
271
|
2. TomorrowOS listens on **`127.0.0.1:TOMORROWOS_INTERNAL_PORT`** (default **3001**).
|
|
235
272
|
3. Add a **catch-all** Next route (or middleware) that proxies HTTP to TomorrowOS:
|
|
236
273
|
|
|
237
274
|
```ts
|
|
238
|
-
// Example: app
|
|
275
|
+
// Example: preview app proxy handler
|
|
239
276
|
// Forward method, headers, and body to http://127.0.0.1:3001/<path>
|
|
240
277
|
// Return the upstream status, headers, and body to the browser.
|
|
241
278
|
```
|
|
@@ -250,24 +287,41 @@ Preferred UX: proxy **`/`** itself (rewrites) so the Preview URL shows the Contr
|
|
|
250
287
|
- Making Next the Production CMS or the only server after Publish.
|
|
251
288
|
- Deleting TomorrowOS `server.ts` because Preview needed Next.
|
|
252
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”.
|
|
253
292
|
|
|
254
|
-
### `vercel.json` (
|
|
293
|
+
### `vercel.json` (Production — explicit Node, not Next)
|
|
255
294
|
|
|
256
|
-
|
|
295
|
+
Write `vercel.json` so Production cannot be mistaken for Next. Minimum:
|
|
257
296
|
|
|
258
297
|
```json
|
|
259
298
|
{
|
|
260
|
-
"fluid": true
|
|
299
|
+
"fluid": true,
|
|
300
|
+
"framework": null,
|
|
301
|
+
"installCommand": "npm install",
|
|
302
|
+
"buildCommand": "npm run build",
|
|
303
|
+
"outputDirectory": null
|
|
261
304
|
}
|
|
262
305
|
```
|
|
263
306
|
|
|
264
|
-
|
|
307
|
+
Use `pnpm` / `yarn` variants of install/build if that is the project’s package manager.
|
|
265
308
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
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:
|
|
321
|
+
|
|
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`.
|
|
271
325
|
|
|
272
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.
|
|
273
327
|
|
|
@@ -282,6 +336,7 @@ If Vercel’s UI and this file disagree on bundling, **prefer whatever keeps a s
|
|
|
282
336
|
|
|
283
337
|
- ❌ `.replit`, `.replit-artifact`
|
|
284
338
|
- ❌ Next.js as the **Production** CMS (App Router replacing TomorrowOS)
|
|
339
|
+
- ❌ Root `api/` catch-all exporting raw `http.Server` to “fix” Publish 404
|
|
285
340
|
- ❌ Separate “API project” without the Control Panel
|
|
286
341
|
- ❌ Committing database passwords, Cloudinary secrets, or Blob tokens
|
|
287
342
|
|
|
@@ -307,52 +362,47 @@ If Vercel’s UI and this file disagree on bundling, **prefer whatever keeps a s
|
|
|
307
362
|
|
|
308
363
|
### Step B — If **1 / Supabase**
|
|
309
364
|
|
|
310
|
-
**Ask exactly:**
|
|
365
|
+
**Ask exactly (connection string only):**
|
|
311
366
|
|
|
312
|
-
> 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`**.
|
|
313
368
|
>
|
|
314
369
|
> In Supabase: **Project Settings → Database → Connection string → Connection pooling** (Session mode).
|
|
315
370
|
> Preferred shape:
|
|
316
371
|
> `postgresql://postgres.[PROJECT]:[PASSWORD]@aws-0-[REGION].pooler.supabase.com:6543/postgres`
|
|
317
372
|
>
|
|
318
|
-
> **Do not** use the direct host `db.*.supabase.co:5432` for Vercel
|
|
319
|
-
>
|
|
320
|
-
> Confirm SSL is required (usually **yes** for Supabase).
|
|
373
|
+
> **Do not** use the direct host `db.*.supabase.co:5432` for Vercel.
|
|
321
374
|
|
|
322
|
-
**You must then:**
|
|
375
|
+
**You must then (automatic — do NOT ask the user to type these):**
|
|
323
376
|
|
|
324
|
-
1. Set Vercel Env Vars
|
|
325
|
-
- `SUPABASE_URL=<pooler string>`
|
|
326
|
-
- `TOMORROWOS_STORE=supabase`
|
|
327
|
-
- `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)
|
|
328
381
|
2. Wire `server.ts` as in **Runtime & Vercel deploy rules**.
|
|
329
|
-
3. Optional committed `.env.example` with **placeholders only
|
|
382
|
+
3. Optional committed `.env.example` with **placeholders only**.
|
|
330
383
|
4. **Do not** commit the real connection string.
|
|
331
|
-
5. **
|
|
384
|
+
5. **Do not** open a second form asking for `TOMORROWOS_STORE` or `DATABASE_SSL`.
|
|
332
385
|
|
|
333
386
|
### Step C — If **2 / Neon**
|
|
334
387
|
|
|
335
|
-
**Ask exactly:**
|
|
388
|
+
**Ask exactly (connection string only):**
|
|
336
389
|
|
|
337
|
-
> 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`**.
|
|
338
391
|
>
|
|
339
|
-
> In Neon: **Dashboard → Connection details → Pooled connection
|
|
392
|
+
> In Neon: **Dashboard → Connection details → Pooled connection**.
|
|
340
393
|
> Typical shape:
|
|
341
|
-
> `postgresql://[user]:[password]@[endpoint]
|
|
342
|
-
>
|
|
343
|
-
> **Do not** use the direct (non-pooled) connection string for Vercel serverless unless Neon docs explicitly say otherwise for your plan.
|
|
344
|
-
>
|
|
345
|
-
> Confirm SSL (`sslmode=require`) is on (usually **yes**).
|
|
394
|
+
> `postgresql://[user]:[password]@[endpoint]-pooler.[region].aws.neon.tech/[dbname]?sslmode=require`
|
|
346
395
|
|
|
347
|
-
**You must then:**
|
|
396
|
+
**You must then (automatic — do NOT ask the user to type these):**
|
|
348
397
|
|
|
349
398
|
1. Set Vercel Env Vars:
|
|
350
|
-
- `DATABASE_URL=<pooled
|
|
351
|
-
- `TOMORROWOS_STORE=postgres`
|
|
352
|
-
- `DATABASE_SSL=true`
|
|
399
|
+
- `DATABASE_URL=<user pooled string>`
|
|
400
|
+
- `TOMORROWOS_STORE=postgres` ← **agent sets this**
|
|
401
|
+
- `DATABASE_SSL=true` ← **agent sets this**
|
|
353
402
|
2. Wire `server.ts` — `createTomorrowOSStore` uses `PostgresStore` for `postgres` / `supabase` drivers.
|
|
354
403
|
3. **Do not** commit the real connection string.
|
|
355
|
-
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.
|
|
356
406
|
|
|
357
407
|
### Step D — If **3 / SQLite**
|
|
358
408
|
|
|
@@ -362,9 +412,9 @@ If Vercel’s UI and this file disagree on bundling, **prefer whatever keeps a s
|
|
|
362
412
|
|
|
363
413
|
Only proceed after the user explicitly confirms.
|
|
364
414
|
|
|
365
|
-
**You must then:**
|
|
415
|
+
**You must then (automatic):**
|
|
366
416
|
|
|
367
|
-
1. Set `TOMORROWOS_STORE=sqlite` (
|
|
417
|
+
1. Set `TOMORROWOS_STORE=sqlite` yourself (do not ask the user to type it).
|
|
368
418
|
2. Keep `sqlitePath` in `server.ts` for `data/tomorrowos.db`.
|
|
369
419
|
3. Warn again in the final summary that they should move to Supabase or Neon for real devices.
|
|
370
420
|
|
|
@@ -391,46 +441,33 @@ Only proceed after the user explicitly confirms.
|
|
|
391
441
|
>
|
|
392
442
|
> Reply with **1**, **2**, or **3** (or “Cloudinary” / “Vercel Blob” / “local”).
|
|
393
443
|
|
|
394
|
-
### Step B — If **1 / Cloudinary** (same Question 2 —
|
|
395
|
-
|
|
396
|
-
> **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.
|
|
444
|
+
### Step B — If **1 / Cloudinary** (same Question 2 — **one Env popup**)
|
|
397
445
|
|
|
398
|
-
|
|
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.
|
|
399
447
|
|
|
400
|
-
**
|
|
401
|
-
|
|
402
|
-
> Paste your Cloudinary **Cloud name** only (`CLOUDINARY_CLOUD_NAME`).
|
|
403
|
-
> Find it at [cloudinary.com/console](https://cloudinary.com/console) → Dashboard.
|
|
448
|
+
**Preferred:** Vercel Env Vars UI (or Agent secrets form) with these fields together:
|
|
404
449
|
|
|
405
|
-
|
|
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`) |
|
|
406
456
|
|
|
407
|
-
|
|
457
|
+
**If a multi-field popup is unavailable, ask once in a single message** (still not three turns):
|
|
408
458
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
>
|
|
412
|
-
>
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
#### B3 — API secret (ask alone — after B2 is saved)
|
|
417
|
-
|
|
418
|
-
**Ask exactly:**
|
|
419
|
-
|
|
420
|
-
> Paste your Cloudinary **API Secret** only (`CLOUDINARY_API_SECRET`).
|
|
421
|
-
> This is the last Cloudinary credential.
|
|
422
|
-
|
|
423
|
-
Save as Vercel Env Var `CLOUDINARY_API_SECRET`.
|
|
424
|
-
|
|
425
|
-
Optional fourth message (only if useful):
|
|
426
|
-
|
|
427
|
-
> Optional: Cloudinary folder name (`CLOUDINARY_FOLDER`, e.g. `tomorrowos`). Press Enter to skip.
|
|
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`
|
|
428
465
|
|
|
429
466
|
**You must then:**
|
|
430
467
|
|
|
431
|
-
1.
|
|
468
|
+
1. Save all three required Env Vars for Production + Preview + Development as appropriate.
|
|
432
469
|
2. **Do not** invent placeholders.
|
|
433
|
-
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.
|
|
434
471
|
4. **Do not** run a formal Cloudinary upload test unless the user asks.
|
|
435
472
|
|
|
436
473
|
The SDK auto-detects these env vars.
|
|
@@ -448,7 +485,7 @@ The SDK auto-detects these env vars.
|
|
|
448
485
|
|
|
449
486
|
**You must then:**
|
|
450
487
|
|
|
451
|
-
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.
|
|
452
489
|
2. `npm install @vercel/blob` (add to `dependencies`).
|
|
453
490
|
3. Wire uploads so media returns **absolute HTTPS Blob URLs** stored in `uploaded_assets` (players need stable public URLs).
|
|
454
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).
|
|
@@ -576,26 +613,26 @@ npx @tomorrowos/sdk@latest init .
|
|
|
576
613
|
|
|
577
614
|
Then:
|
|
578
615
|
|
|
579
|
-
1. Keep TomorrowOS `server.ts` as the **Production** app (**export `listen()` HTTP server**).
|
|
580
|
-
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.
|
|
581
|
-
3.
|
|
582
|
-
4.
|
|
583
|
-
5.
|
|
584
|
-
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.
|
|
585
624
|
|
|
586
625
|
### B. Environment Variables (Vercel dashboard + local)
|
|
587
626
|
|
|
588
|
-
| Name | Required |
|
|
589
|
-
|
|
590
|
-
| `SUPABASE_URL` | If Q1 = Supabase |
|
|
591
|
-
| `DATABASE_URL` | If Q1 = Neon |
|
|
592
|
-
| `TOMORROWOS_STORE` | Yes | `supabase
|
|
593
|
-
| `DATABASE_SSL` | Yes
|
|
594
|
-
| `
|
|
595
|
-
| `
|
|
596
|
-
| `
|
|
597
|
-
| `BLOB_READ_WRITE_TOKEN` | If Q2 = Vercel Blob | From Vercel Blob store |
|
|
598
|
-
| `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`) |
|
|
599
636
|
| `PORT` | Optional | Vercel injects for Production |
|
|
600
637
|
|
|
601
638
|
Mirror non-secrets in `.env` for local `npm run dev` if helpful; never commit secrets.
|
|
@@ -621,12 +658,15 @@ vercel --prod
|
|
|
621
658
|
# or git push → Vercel Git integration
|
|
622
659
|
```
|
|
623
660
|
|
|
624
|
-
|
|
661
|
+
**Publish acceptance checklist (all must pass):**
|
|
625
662
|
|
|
626
|
-
1.
|
|
627
|
-
2.
|
|
663
|
+
1. Framework Preset = **Other** / null (dashboard confirms — not Next.js)
|
|
664
|
+
2. Start Command = Node `TomorrowOS.listen` (`npm run start`)
|
|
628
665
|
3. Fluid compute on
|
|
629
|
-
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 `/`.
|
|
630
670
|
|
|
631
671
|
Tell the user:
|
|
632
672
|
|
|
@@ -639,7 +679,7 @@ Tell the user:
|
|
|
639
679
|
**Do:**
|
|
640
680
|
|
|
641
681
|
1. Preview: Control Panel HTML at `/` (via Next proxy if applicable)
|
|
642
|
-
2. Publish: Control Panel HTML at `/` from **Node** TomorrowOS
|
|
682
|
+
2. Publish: Control Panel HTML at `/` from **Node** TomorrowOS (block on `404: NOT_FOUND`)
|
|
643
683
|
3. Server status: Media OK when Cloudinary or Blob configured
|
|
644
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
|
|
645
685
|
5. Fluid compute on for Production
|
|
@@ -652,6 +692,8 @@ Tell the user:
|
|
|
652
692
|
|
|
653
693
|
| Symptom | Likely cause | Fix |
|
|
654
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 |
|
|
655
697
|
| Preview blank but logs show TomorrowOS listening | v0 Next intercepts public port | Add Next reverse proxy → internal TomorrowOS port |
|
|
656
698
|
| Preview works, Publish broken / Next-only | Production still on Next | Switch Production entry to `tsx server.ts` / `export default server` |
|
|
657
699
|
| `ENETUNREACH` / IPv6 / `:5432` | Direct Postgres URL | Use **pooled** URL (Supabase `:6543` or Neon pooler) |
|
|
@@ -660,12 +702,12 @@ Tell the user:
|
|
|
660
702
|
| Blob uploads 401 / missing token | Blob not linked | Vercel Storage → Blob + `BLOB_READ_WRITE_TOKEN` |
|
|
661
703
|
| WebSocket fails on Publish | Fluid off / static-only / Next as Production | Enable Fluid; sole Node `listen()` for Publish |
|
|
662
704
|
| Devices fail only on Preview | Next proxy cannot upgrade `wss` | Expected — pair on Publish URL |
|
|
663
|
-
| Agent
|
|
664
|
-
|
|
|
665
|
-
| Replit files present | Copied wrong protocol | Delete `.replit*`; use this file |
|
|
666
|
-
| Brand colours wrong from URL | SPA / blocked fetch | Manual Option B for colours + logo |
|
|
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 |
|
|
667
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 |
|
|
668
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` |
|
|
709
|
+
| Secrets in git | Mistake | Rotate keys; move to Vercel Env Vars |
|
|
710
|
+
| Replit files present | Copied wrong protocol | Delete `.replit*`; use this file |
|
|
669
711
|
|
|
670
712
|
---
|
|
671
713
|
|
|
@@ -674,12 +716,12 @@ Tell the user:
|
|
|
674
716
|
| Topic | Replit | Vercel |
|
|
675
717
|
|-------|--------|--------|
|
|
676
718
|
| Secrets | Replit Secrets | Vercel Environment Variables |
|
|
677
|
-
| 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` |
|
|
678
720
|
| `hostingTarget` | `"here"` | `"vercel"` |
|
|
679
721
|
| Database | Supabase (primary) | Supabase **or Neon** (pooled); SQLite demo only |
|
|
680
722
|
| Media | Cloudinary or Replit Object Storage | Cloudinary **or Vercel Blob** |
|
|
681
723
|
| Preview | Replit Preview / Autoscale Node | **Next shell + proxy** when v0 hardwires Next |
|
|
682
|
-
| 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` |
|
|
683
725
|
| Brand from URL | Not in Replit protocol | **Option A** — infer **`brand.json` only**; never invent login |
|
|
684
726
|
|
|
685
727
|
---
|
|
@@ -689,18 +731,19 @@ Tell the user:
|
|
|
689
731
|
After setup, report:
|
|
690
732
|
|
|
691
733
|
1. **Live URL** (Production Publish)
|
|
692
|
-
2. **
|
|
693
|
-
3. **
|
|
694
|
-
4. **
|
|
695
|
-
5. **
|
|
696
|
-
6. **
|
|
697
|
-
7. **
|
|
698
|
-
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
|
|
699
742
|
|
|
700
743
|
Protocol complete when:
|
|
701
744
|
|
|
702
745
|
- **Preview** shows the Control Panel at `/` (via Next proxy if required), **and**
|
|
703
|
-
- **Publish**
|
|
746
|
+
- **Publish** returns Control Panel at `/` from pure Node `TomorrowOS.listen` with Fluid (not `404: NOT_FOUND`), **and**
|
|
704
747
|
- Q1–Q3 configuration is saved
|
|
705
748
|
|
|
706
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
|
},
|