@tomorrowos/sdk 0.9.26 → 0.9.27

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 CHANGED
@@ -73,7 +73,7 @@ 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` (Framework Preset = Other gate on no `404 NOT_FOUND`). Cloudinary uses **one Env popup** (all fields).
76
+ **Publish stays pure Node** `TomorrowOS.listen` (Scheme A: root `server.ts`, Next in `preview/`, `GET /status` must be JSON). Cloudinary uses **one Env popup** (all fields).
77
77
  Neon/Supabase: agent auto-sets `TOMORROWOS_STORE` + `DATABASE_SSL`. Sets `cms.hostingTarget` to `"vercel"`.
78
78
 
79
79
  ### Replit upgrade (existing CMS)
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.5` — pairs with `@tomorrowos/sdk` packages that ship `templates/cms-starter` and this file.
8
+ > Protocol id: `vercel-setup/1.6` — pairs with `@tomorrowos/sdk` packages that ship `templates/cms-starter` and this file.
9
9
 
10
10
  ## User trigger (copy-paste)
11
11
 
@@ -19,7 +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
+ Before claiming Publish works: Framework Preset must be Other/null (not Next.js), startCommand must run TomorrowOS Node, GET /status on the production URL must return JSON (not HTML), and GET / must return the Control Panel (not 404 NOT_FOUND).
23
23
  ```
24
24
 
25
25
  Alternate triggers:
@@ -82,6 +82,29 @@ almost always means **Vercel still thinks this is a Next.js / static deploy**, n
82
82
 
83
83
  **Setup is NOT complete** while Production `GET /` returns `404: NOT_FOUND`. Fix Framework Preset + Start Command and redeploy.
84
84
 
85
+ ### Publish static trap — `public/` served without Node (read this)
86
+
87
+ A common **worse** failure mode than `404: NOT_FOUND`:
88
+
89
+ - `GET /` loads the Control Panel HTML (from static `public/index.html`)
90
+ - `GET /status`, `/devices`, etc. return **HTML** (Vercel 404 page)
91
+ - The panel shows **CMS unreachable** and console:
92
+ `Unexpected token 'T', "The page c"... is not valid JSON`
93
+
94
+ **Root cause:** Vercel deployed `public/` as a **static site** (Output Directory = `public` or Framework=Other default *“public if it exists”*). **No Node process** is running `tsx server.ts`, so API routes never hit `TomorrowOS`.
95
+
96
+ **Fix (Scheme A — see dedicated section below):** force Vercel to run the **root `server.ts` Node entry** with Fluid. `public/` must be served **at runtime** via `staticRoot` in `listen()`, **not** as the build output directory.
97
+
98
+ **Publish gate (mandatory):** after deploy, `GET /status` must return **JSON** starting with `{`, not HTML starting with `<` or `The page`.
99
+
100
+ ```bash
101
+ curl -s https://YOUR-PROJECT.vercel.app/status | head -c 80
102
+ # Expected: {"status":"success",...}
103
+ # Failure: The page could not be found OR <!DOCTYPE html>
104
+ ```
105
+
106
+ **Setup is NOT complete** while `/status` returns HTML or the panel shows the JSON parse error.
107
+
85
108
  ### Preview (v0 / Vercel Agent sandbox) — Next.js shell allowed
86
109
 
87
110
  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.
@@ -150,12 +173,138 @@ Then ask **Question 1**.
150
173
  14. **No inventing CMS login.** A branding URL never means “build auth.” Only add login if the user **explicitly** requests it.
151
174
  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
175
  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).
176
+ 17. **Publish gate:** do not mark setup complete while Production returns `404: NOT_FOUND` **or** `/status` returns HTML (static `public/` trap).
177
+ 18. **Scheme A is doc-driven:** achieve Production Node Publish by **project layout + `vercel.json` + dashboard settings** during setup — **no SDK code changes** required (see **Scheme A** section).
154
178
 
155
179
  **Question order:** Q1 → Q2 → Q3 → execution checklist.
156
180
 
157
181
  ---
158
182
 
183
+ ## Scheme A — Standard Node Publish (mandatory for Production)
184
+
185
+ > **Goal:** Preview may use Next + proxy; **Publish** runs **only** root `server.ts` + `TomorrowOS.listen` on Vercel Fluid.
186
+ > **Scope:** This section is implemented entirely during setup (move files, write `vercel.json`, patch dashboard). **Do not** change `@tomorrowos/sdk` source.
187
+
188
+ ### Why Scheme A exists
189
+
190
+ | Layer | Preview (v0) | Publish (Production) |
191
+ |-------|----------------|----------------------|
192
+ | Host | Next.js shell + HTTP proxy | **Node `server.ts`** only |
193
+ | `public/` | Proxied from TomorrowOS | Served by **`staticRoot`** inside `listen()` — **not** as Vercel Output Directory |
194
+ | WebSocket | Optional / may fail in Preview | Required on same origin (`wss://`) |
195
+ | Next files | Allowed under `preview/` | **Must not** live at project root |
196
+
197
+ ### A1 — Project layout (Agent must enforce)
198
+
199
+ **At project root (Production):**
200
+
201
+ ```
202
+ server.ts ← REQUIRED name at root (or server.mts)
203
+ vercel.json
204
+ package.json
205
+ brand.json
206
+ public/ ← CMS static UI; NOT the Vercel output directory
207
+ ```
208
+
209
+ **Preview only (when v0 needs Next) — move ALL of these under `preview/`:**
210
+
211
+ ```
212
+ preview/
213
+ next.config.mjs
214
+ app/ ← Next proxy routes
215
+ postcss.config.mjs
216
+ next-env.d.ts
217
+ tsconfig.json ← Next/tsconfig if separate from server
218
+ ```
219
+
220
+ **Remove from project root after move:** `next.config.mjs`, `app/`, root Next `tsconfig` plugin config, `postcss.config.mjs`, `next-env.d.ts`.
221
+
222
+ **Do NOT:**
223
+
224
+ - ❌ Leave `next.config.mjs` + `app/` at root while claiming Production is Node-only
225
+ - ❌ Create root `api/index.ts` exporting `http.Server` + `routes` catch-all
226
+ - ❌ Set Output Directory to `public` or `.`
227
+
228
+ **Update `package.json` scripts when Preview uses `preview/`:**
229
+
230
+ ```json
231
+ {
232
+ "scripts": {
233
+ "dev": "tsx watch server.ts",
234
+ "dev:preview": "concurrently -k \"npm:dev:tomorrowos\" \"npm:dev:next\"",
235
+ "dev:tomorrowos": "cross-env TOMORROWOS_INTERNAL_PORT=3001 tsx watch server.ts",
236
+ "dev:next": "cd preview && next dev -p 3000",
237
+ "start": "tsx server.ts",
238
+ "build": "node -e \"process.exit(0)\""
239
+ }
240
+ }
241
+ ```
242
+
243
+ ### A2 — `server.ts` entry (Vercel zero-config Node detection)
244
+
245
+ Vercel detects a **root** `server.ts` / `server.mts` and routes traffic to it when:
246
+
247
+ 1. The file lives at **project root** (not only under `api/`)
248
+ 2. **`listen()` runs at module load** (top-level, not inside an unexported function)
249
+ 3. The file **`export default server`** (the `http.Server` from `tomorrowos.listen`)
250
+
251
+ Use the pattern in **Runtime & Vercel deploy rules** below. On Publish, **do not** set `TOMORROWOS_INTERNAL_PORT` — let Vercel inject `PORT`.
252
+
253
+ ### A3 — `vercel.json` (Production)
254
+
255
+ ```json
256
+ {
257
+ "fluid": true,
258
+ "framework": null,
259
+ "installCommand": "npm install",
260
+ "buildCommand": "npm run build",
261
+ "startCommand": "npm run start",
262
+ "outputDirectory": null
263
+ }
264
+ ```
265
+
266
+ Use `pnpm` / `yarn` equivalents if the project uses them.
267
+
268
+ **Critical:**
269
+
270
+ - `"outputDirectory": null` — **never** `"public"`, `"."`, or `"public if exists"`
271
+ - `"startCommand"` must be explicit when the dashboard keeps defaulting to static
272
+ - `"buildCommand"` must be the no-op `npm run build` — **never** `next build` for Production
273
+
274
+ ### A4 — Vercel Project Settings (dashboard or API — mandatory)
275
+
276
+ `vercel.json` alone is **not enough** if the dashboard still shows Framework = **Next.js** or Output = `public`.
277
+
278
+ | Setting | Required value |
279
+ |---------|----------------|
280
+ | Framework Preset | **Other** / null — **not Next.js** |
281
+ | Install Command | `npm install` (or pnpm/yarn) |
282
+ | Build Command | `npm run build` (no-op exit 0) |
283
+ | **Output Directory** | **empty / null** — **NOT `public`** |
284
+ | **Start Command** | `npm run start` → `tsx server.ts` |
285
+ | Fluid compute | **On** |
286
+
287
+ If Framework Preset still shows **Next.js** after `vercel.json`: **patch via Vercel API / CLI / dashboard**, then redeploy.
288
+
289
+ ### A5 — Publish acceptance (all must pass before “setup complete”)
290
+
291
+ 1. Deploy logs show Node starting / `[TomorrowOS] listening on ...` (not only “Uploading static files”)
292
+ 2. `curl -s https://PROD/status` → JSON (`{"status":...}`)
293
+ 3. `curl -s https://PROD/` → Control Panel HTML
294
+ 4. Control Panel **does not** show `Unexpected token 'T'` / CMS unreachable
295
+ 5. Framework Preset = Other; Fluid on
296
+
297
+ If (2) fails but (3) passes → **static `public/` trap**. Re-apply A3 + A4; confirm `startCommand`; redeploy. **Do not** add `api/` catch-all.
298
+
299
+ ### A6 — What Scheme A does **not** require
300
+
301
+ - ❌ No changes to `@tomorrowos/sdk` package code
302
+ - ❌ No `(req, res)` handler refactor
303
+ - ❌ No `next build` / `next start` on Production
304
+ - ❌ No root `api/` directory for the CMS server
305
+
306
+ ---
307
+
159
308
  ## Runtime & Vercel deploy rules (mandatory)
160
309
 
161
310
  ### Production runtime (Publish)
@@ -300,28 +449,30 @@ Write `vercel.json` so Production cannot be mistaken for Next. Minimum:
300
449
  "framework": null,
301
450
  "installCommand": "npm install",
302
451
  "buildCommand": "npm run build",
452
+ "startCommand": "npm run start",
303
453
  "outputDirectory": null
304
454
  }
305
455
  ```
306
456
 
307
- Use `pnpm` / `yarn` variants of install/build if that is the project’s package manager.
457
+ Use `pnpm` / `yarn` variants of install/build/start if that is the project’s package manager.
308
458
 
309
- **Also force Project Settings (dashboard or Vercel API) — this is mandatory:**
459
+ **Also force Project Settings (dashboard or Vercel API) — this is mandatory (see Scheme A4):**
310
460
 
311
461
  | Setting | Required value |
312
462
  |---------|----------------|
313
463
  | Framework Preset | **Other** / null — **not Next.js** |
314
464
  | Install Command | `npm install` (or pnpm/yarn) |
315
465
  | Build Command | `npm run build` (no-op exit 0) |
316
- | Output Directory | **empty** |
317
- | Start Command | `npm run start` → `tsx server.ts` (or `.mts`) |
466
+ | Output Directory | **empty / null** — **NOT `public`** |
467
+ | Start Command | `npm run start` → `tsx server.ts` (or `.mts`) — **explicit in `vercel.json` too** |
318
468
  | Fluid compute | **On** |
319
469
 
320
470
  If `next` / `next.config.*` exist for Preview and the dashboard still auto-selects Next.js:
321
471
 
322
472
  1. Patch Framework Preset to **Other** via API/CLI/dashboard.
323
473
  2. Redeploy.
324
- 3. Hit Production `GET /` — must be Control Panel HTML, not `404: NOT_FOUND`.
474
+ 3. Hit Production `GET /status` — must be **JSON**, not HTML.
475
+ 4. Hit Production `GET /` — must be Control Panel HTML, not `404: NOT_FOUND`.
325
476
 
326
477
  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.
327
478
 
@@ -611,16 +762,15 @@ npx @tomorrowos/sdk@latest init .
611
762
  # npx @tomorrowos/sdk@latest init . --force
612
763
  ```
613
764
 
614
- Then:
765
+ Then (**Scheme A — mandatory for Publish**):
615
766
 
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.
767
+ 1. Keep **`server.ts`** at **project root** with top-level `listen()` + `export default server`.
768
+ 2. If Preview needs Next: **move** `next.config.*`, `app/`, Next `tsconfig` / `postcss` / `next-env.d.ts` into **`preview/`** remove them from root so Production is not mistaken for Next/static.
769
+ 3. Write `vercel.json` per **Scheme A3** (`fluid`, `framework: null`, `startCommand`, `outputDirectory: null`).
770
+ 4. **Force Project Settings** per **Scheme A4** (Framework = Other, Output Directory empty, Start = `npm run start`, Fluid on). Patch dashboard via API if it still says Next.js.
771
+ 5. Do **not** add root `api/` catch-all for raw `http.Server`.
772
+ 6. Do **not** add `.replit*`.
773
+ 7. If Q2 = Vercel Blob: install `@vercel/blob` and wire the upload bridge.
624
774
 
625
775
  ### B. Environment Variables (Vercel dashboard + local)
626
776
 
@@ -658,15 +808,17 @@ vercel --prod
658
808
  # or git push → Vercel Git integration
659
809
  ```
660
810
 
661
- **Publish acceptance checklist (all must pass):**
811
+ **Publish acceptance checklist (all must pass — Scheme A5):**
662
812
 
663
813
  1. Framework Preset = **Other** / null (dashboard confirms — not Next.js)
664
- 2. Start Command = Node `TomorrowOS.listen` (`npm run start`)
665
- 3. Fluid compute on
666
- 4. Env Vars present for Production
667
- 5. Production `GET /` returns Control Panel HTML — **not** `404: NOT_FOUND`
814
+ 2. Output Directory = **empty** (not `public`)
815
+ 3. Start Command = Node `TomorrowOS.listen` (`npm run start` / `tsx server.ts`)
816
+ 4. Fluid compute on
817
+ 5. Env Vars present for Production
818
+ 6. Production `GET /status` returns **JSON** — **not** HTML (`Unexpected token 'T'` means this failed)
819
+ 7. Production `GET /` returns Control Panel HTML — **not** `404: NOT_FOUND`
668
820
 
669
- If step 5 fails: do **not** invent `api/` + `routes`. Patch Framework Preset + Start Command, redeploy, re-check `/`.
821
+ If step 6 fails but step 7 passes: **static `public/` trap** — Vercel is not running Node. Re-apply Scheme A3–A4; do **not** invent `api/` + `routes`.
670
822
 
671
823
  Tell the user:
672
824
 
@@ -678,11 +830,12 @@ Tell the user:
678
830
 
679
831
  **Do:**
680
832
 
681
- 1. Preview: Control Panel HTML at `/` (via Next proxy if applicable)
682
- 2. Publish: Control Panel HTML at `/` from **Node** TomorrowOS (block on `404: NOT_FOUND`)
683
- 3. Server status: Media OK when Cloudinary or Blob configured
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
685
- 5. Fluid compute on for Production
833
+ 1. Preview: Control Panel HTML at `/` (via Next proxy in `preview/` if applicable)
834
+ 2. Publish: `GET /status` JSON; `GET /` → Control Panel from **Node** TomorrowOS
835
+ 3. Publish: panel does **not** show `Unexpected token 'T'` / CMS unreachable
836
+ 4. Server status: Media OK when Cloudinary or Blob configured
837
+ 5. 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
838
+ 6. Fluid compute on for Production
686
839
 
687
840
  **Do not block** on long WebSocket/device pairing tests unless the user asks. Prefer pairing against **Publish**, not Preview.
688
841
 
@@ -692,8 +845,10 @@ Tell the user:
692
845
 
693
846
  | Symptom | Likely cause | Fix |
694
847
  |---------|--------------|-----|
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 |
848
+ | Publish `404: NOT_FOUND` | Framework Preset still **Next.js** / no Node start | Scheme A: Framework = **Other**, Start = `npm run start`, Output empty, Fluid on; redeploy |
849
+ | Panel loads but **CMS unreachable** / `Unexpected token 'T', "The page c"...` | **`public/` deployed as static site** no Node running | Scheme A: `outputDirectory: null`, explicit `startCommand`, move Next to `preview/`; verify `GET /status` returns JSON |
850
+ | `vercel inspect` shows Output = `public if it exists` | Framework=Other static default | Force Output Directory **empty** in dashboard + `outputDirectory: null`; ensure root `server.ts` + `startCommand` |
851
+ | Build runs `next build` / missing routes-manifest | Next auto-detected from root `next.config` / `app/` | Move Next to `preview/`; Framework null/Other; `build` = no-op |
697
852
  | Preview blank but logs show TomorrowOS listening | v0 Next intercepts public port | Add Next reverse proxy → internal TomorrowOS port |
698
853
  | Preview works, Publish broken / Next-only | Production still on Next | Switch Production entry to `tsx server.ts` / `export default server` |
699
854
  | `ENETUNREACH` / IPv6 / `:5432` | Direct Postgres URL | Use **pooled** URL (Supabase `:6543` or Neon pooler) |
@@ -731,7 +886,7 @@ Tell the user:
731
886
  After setup, report:
732
887
 
733
888
  1. **Live URL** (Production Publish)
734
- 2. **Publish health:** Framework Preset = Other; Start = Node; `GET /` OK (not 404)
889
+ 2. **Publish health:** Framework = Other; Output `public`; `GET /status` → JSON; `GET /` OK
735
890
  3. **Preview note:** Next proxy shell used / not needed
736
891
  4. **Env Vars set** (names only — never values); note which were auto-set (`TOMORROWOS_STORE`, `DATABASE_SSL`)
737
892
  5. **Store:** Supabase pooler / Neon pooled / SQLite (with warning)
@@ -743,7 +898,7 @@ After setup, report:
743
898
  Protocol complete when:
744
899
 
745
900
  - **Preview** shows the Control Panel at `/` (via Next proxy if required), **and**
746
- - **Publish** returns Control Panel at `/` from pure Node `TomorrowOS.listen` with Fluid (not `404: NOT_FOUND`), **and**
901
+ - **Publish** returns Control Panel at `/` **and** `/status` JSON from pure Node `TomorrowOS.listen` with Fluid (not static `public/`, not `404: NOT_FOUND`, not `Unexpected token 'T'`), **and**
747
902
  - Q1–Q3 configuration is saved
748
903
 
749
904
  — 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.26",
3
+ "version": "0.9.27",
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.26",
3
+ "version": "0.9.27",
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.26",
16
+ "@tomorrowos/sdk": "^0.9.27",
17
17
  "dotenv": "^17.2.3",
18
18
  "tsx": "^4.19.0"
19
19
  },