@tomorrowos/sdk 0.9.54 → 0.9.56

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/REPLIT_SETUP.md CHANGED
@@ -11,9 +11,9 @@ When a human pastes a prompt like this, **read this file and start Question 1 im
11
11
 
12
12
  ```text
13
13
  Follow @tomorrowos/sdk REPLIT_SETUP.md and set up my TomorrowOS CMS.
14
- Ask only the questions listed in REPLIT_SETUP.md, in order. Do not use LLM_PROMPT.md.
14
+ Ask only the questions listed in REPLIT_SETUP.md, in order. Do not invent a separate questionnaire.
15
15
  Artifact kind must be **web** (never **api**).
16
- Question 1 database options in this order: 1) Supabase (Recommended) 2) Built-in Replit PostgreSQL 3) SQLite.
16
+ Question 1 database options in this order: 1) Supabase (Recommended) 2) Built-in Replit PostgreSQL.
17
17
  If Supabase: show the SUPABASE_URL Secrets input immediately after the choice (same Question 1 — once only).
18
18
  If Cloudinary: show all CLOUDINARY_* Secrets inputs immediately after the choice (same Question 2 — one multi-field dialog).
19
19
  Question 3: one multi-field dialog with all seven branding inputs stacked (all optional; blanks keep brand.json defaults). Never ask branding fields one-by-one with Next.
@@ -26,7 +26,7 @@ Alternate triggers that also activate this protocol:
26
26
  - “Set up TomorrowOS on Replit using REPLIT_SETUP.md”
27
27
  - “Configure my TomorrowOS CMS (Replit wizard)”
28
28
 
29
- When any of these triggers fire, **ignore `LLM_PROMPT.md` entirely** for the Q&A phase.
29
+ When any of these triggers fire, stay on this file for the Q&A phase.
30
30
 
31
31
  **Before Question 1:** ensure the Replit workspace primary artifact **`kind` is `web`** (see **IRON RULE: `web` kind only**). If the project is `api` / API-only or not publishable, **fix artifact kind first** — do not continue the questionnaire on a broken project type.
32
32
 
@@ -62,7 +62,7 @@ Replit Agent must ask **only** the questions defined in **this file**, in **this
62
62
 
63
63
  | Step | Section | When |
64
64
  |------|---------|------|
65
- | 1 | **Question 1** — Database choice (Supabase → Replit Postgres → SQLite) | Always |
65
+ | 1 | **Question 1** — Database choice (Supabase → Replit Postgres) | Always |
66
66
  | 2 | **Question 2** — Media storage (+ Cloudinary Secrets if chosen) | Always (after Q1) |
67
67
  | 3 | **Question 3** — Brand / TomorrowOS app look (**all fields optional**) | Always last, before execution |
68
68
 
@@ -79,11 +79,10 @@ Replit Agent must ask **only** the questions defined in **this file**, in **this
79
79
  **Critical — Question 1 option order (exact):** Present database choices in **this order only**:
80
80
  1. **Supabase Postgres (Recommended)**
81
81
  2. **Built-in Replit PostgreSQL**
82
- 3. **SQLite (local file — fine for testing)**
83
82
 
84
83
  **Critical — Supabase path collected once only:** If the user picks Supabase, collect `SUPABASE_URL` **immediately after the choice**, in **one** step (Secrets UI **or** a single chat paste — not both). After it is saved, **never** ask for the connection string / `SUPABASE_URL` again.
85
84
 
86
- **Critical:** Question 2 is **not complete** when the user says “Cloudinary” or “yes”. You **must immediately** open the **multi-field** Cloudinary Secrets dialog (same Question 2 — **not** “2b”, **not** one key per message) and collect `CLOUDINARY_CLOUD_NAME`, `CLOUDINARY_API_KEY`, and `CLOUDINARY_API_SECRET` **before** Question 3.
85
+ **Critical:** Question 2 is **not complete** when the user says “Cloudinary” or **1**. You **must immediately** open the **multi-field** Cloudinary Secrets dialog (same Question 2 — **not** “2b”, **not** one key per message) and collect `CLOUDINARY_CLOUD_NAME`, `CLOUDINARY_API_KEY`, and `CLOUDINARY_API_SECRET` **before** Question 3.
87
86
 
88
87
  **Critical — Question 3:** All seven branding fields are **optional** and must appear in **one stacked multi-input dialog**. Empty / skip / “use defaults” → keep starter **`brand.json`**. **Never** ask name, then colours, then logo across separate turns.
89
88
 
@@ -91,15 +90,15 @@ If the user volunteers extra info early (e.g. brand name before Q3), **record it
91
90
 
92
91
  If the user says “just set it up, don’t ask questions,” respond:
93
92
 
94
- > I need a few quick answers from REPLIT_SETUP.md (database choice — Supabase recommended, then Replit Postgres, then SQLite — media storage — plus Cloudinary credentials if you choose Cloudinary — and optional branding). It takes about a minute and ensures the CMS deploys correctly on Replit.
93
+ > I need a few quick answers from REPLIT_SETUP.md (database choice — Supabase recommended, then Replit Postgres — media storage — plus Cloudinary credentials if you choose Cloudinary — and optional branding). It takes about a minute and ensures the CMS deploys correctly on Replit.
95
94
 
96
- Then ask **Question 1** — do not switch to `LLM_PROMPT.md`.
95
+ Then ask **Question 1** — stay on this file.
97
96
 
98
97
  ---
99
98
 
100
99
  ## Hard rules
101
100
 
102
- 1. **Ask only REPLIT_SETUP.md questions 1–3** (see **Questionnaire scope**). Never use `LLM_PROMPT.md` on Replit.
101
+ 1. **Ask only REPLIT_SETUP.md questions 1–3** (see **Questionnaire scope**). Do not invent alternate setup questionnaires on Replit.
103
102
  2. **One questionnaire step at a time (Q1 → Q2 → Q3).** Wait for that step to finish before opening the next. **Inside** a step, use **one multi-field dialog** when collecting Secrets or branding — **never** field-by-field “Next” wizards.
104
103
  2a. **Q1 Supabase:** choice → **immediately** `SUPABASE_URL` input (same Question 1).
105
104
  2b. **Q2 Cloudinary:** choice → **immediately** multi-field `CLOUDINARY_*` Secrets dialog (same Question 2).
@@ -404,20 +403,20 @@ Then (and only then) check: API vs Web type → `.replit` ports/`deploymentTarge
404
403
 
405
404
  ### Step A — Ask exactly (option order is mandatory)
406
405
 
407
- Present **exactly these three options, in this order** (Supabase first / recommended):
406
+ Present **exactly these two options, in this order** (Supabase first / recommended):
408
407
 
409
408
  > **Database choice**
410
409
  >
411
410
  > 1. **Supabase Postgres (Recommended)** — I have a connection string
412
- > 2. **Built-in Replit PostgreSQL** — no external account needed
413
- > 3. **SQLite** (local file — fine for testing)
411
+ > 2. **Built-in Replit PostgreSQL** — no external account needed
414
412
  >
415
- > Which do you want?
413
+ > Which do you want? Reply **1** or **2**.
416
414
 
417
415
  **Forbidden option orders / labels:**
418
416
 
419
417
  - ❌ Putting Built-in Replit PostgreSQL first or marking it “recommended”
420
- - ❌ Omitting Supabase or SQLite
418
+ - ❌ Omitting Supabase
419
+ - ❌ Inventing SQLite or other database options not listed above
421
420
  - ❌ Asking only for a connection string without showing this choice first (unless `SUPABASE_URL` is already saved and the user is continuing a prior Supabase setup)
422
421
 
423
422
  **Agent auto-sets after the choice** (never ask the user to type these):
@@ -426,7 +425,6 @@ Present **exactly these three options, in this order** (Supabase first / recomme
426
425
  |--------|------------|---------------|
427
426
  | 1 Supabase | `TOMORROWOS_STORE=supabase`, `DATABASE_SSL=true` | `SUPABASE_URL` (**once**) |
428
427
  | 2 Replit PostgreSQL | `TOMORROWOS_STORE=postgres`, `DATABASE_SSL=true` | Use Replit’s built-in **`DATABASE_URL`** (do not ask them to invent a URI) |
429
- | 3 SQLite | `TOMORROWOS_STORE=sqlite` | Confirmation only |
430
428
 
431
429
  ### Step B1 — If **1 Supabase (Recommended)**
432
430
 
@@ -480,14 +478,6 @@ If `SUPABASE_URL` is **already** present in Replit Secrets when this branch star
480
478
  7. **Do not** test the DB connection in Preview — configuration only.
481
479
  8. Proceed to Question 2.
482
480
 
483
- ### Step B3 — If **3 SQLite**
484
-
485
- 1. Set `TOMORROWOS_STORE=sqlite` yourself.
486
- 2. Ensure `data/tomorrowos.db` path via starter / `createTomorrowOSStore({ sqlitePath: join(__dirname, "data", "tomorrowos.db") })`.
487
- 3. Warn briefly: SQLite is fine for testing; pairings may not survive some rebuilds as well as Postgres.
488
- 4. **Do not** ask for any connection string.
489
- 5. Proceed to Question 2.
490
-
491
481
  ### Shared `server.ts` shape (all Q1 paths)
492
482
 
493
483
  ```ts
@@ -501,7 +491,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
501
491
  const brand = JSON.parse(readFileSync(join(__dirname, "brand.json"), "utf8"));
502
492
 
503
493
  const store = createTomorrowOSStore({
504
- // Supabase: SUPABASE_URL. Replit Postgres: DATABASE_URL. SQLite: ignores URL.
494
+ // Supabase: SUPABASE_URL. Replit Postgres: DATABASE_URL. Unused when TOMORROWOS_STORE is postgres/supabase.
505
495
  databaseUrl: process.env.SUPABASE_URL || process.env.DATABASE_URL,
506
496
  sqlitePath: join(__dirname, "data", "tomorrowos.db")
507
497
  });
@@ -527,15 +517,13 @@ tomorrowos.listen({
527
517
 
528
518
  > How should playlist media (images/videos) be stored?
529
519
  >
530
- > **Recommended: Cloudinary** — public HTTPS URLs, survives Replit restarts/redeploys, avoids Object Storage permission issues.
520
+ > **1. Cloudinary (Recommended)** — public HTTPS URLs, survives Replit restarts/redeploys, avoids Object Storage permission issues.
531
521
  >
532
- > Alternatives:
533
- > - **Replit Object Storage** — keep files under `public/uploads` backed by Object Storage (no Cloudinary account).
534
- > - **Local disk only** — `public/uploads` on the Repl filesystem (fine for quick tests; files may disappear on rebuild if not persistent).
522
+ > **2. Replit Object Storage** — keep files under `public/uploads` backed by Object Storage (no Cloudinary account).
535
523
  >
536
- > Do you want me to set up **Cloudinary**? (yes / no)
524
+ > Reply **1** for Cloudinary or **2** for Replit Object Storage.
537
525
 
538
- ### Step B — If YES / Cloudinary (same Question 2 — immediate multi-field Secrets dialog)
526
+ ### Step B — If **1 / Cloudinary** (same Question 2 — immediate multi-field Secrets dialog)
539
527
 
540
528
  **Do this in the same Question 2 flow, immediately when the user chooses Cloudinary.**
541
529
 
@@ -574,7 +562,7 @@ tomorrowos.listen({
574
562
 
575
563
  The SDK auto-detects these env vars and uploads media to Cloudinary. New uploads will return `https://res.cloudinary.com/...` URLs.
576
564
 
577
- ### If NO Replit Object Storage (or local)
565
+ ### If **2 / Replit Object Storage**
578
566
 
579
567
  **Do this:**
580
568
 
@@ -598,7 +586,7 @@ The SDK auto-detects these env vars and uploads media to Cloudinary. New uploads
598
586
  >
599
587
  > **Every field is optional.** Blank / skip / “use defaults” → keep starter `brand.json`. **Do not** re-ask for missing fields.
600
588
 
601
- > **This is the only branding block.** Do not ask separate LLM_PROMPT “target platform”, “use case”, or “hosting” questions. **Do not** ask screen count.
589
+ > **This is the only branding block.** Do not ask separate “target platform”, “use case”, or “hosting” questions outside this branding block. **Do not** ask screen count.
602
590
 
603
591
  ### Preferred UI (Replit form / multi-input dialog)
604
592
 
@@ -715,12 +703,6 @@ DATABASE_SSL=true
715
703
 
716
704
  Use Replit’s **`DATABASE_URL`**. **Do not** ask for `SUPABASE_URL`.
717
705
 
718
- **If Q1 = SQLite:**
719
-
720
- ```env
721
- TOMORROWOS_STORE=sqlite
722
- ```
723
-
724
706
  **Cloudinary (if chosen):**
725
707
 
726
708
  ```env
@@ -782,7 +764,6 @@ Only investigate further if Preview is empty, server crashed, or user reports Pu
782
764
 
783
765
  ### Forbidden questionnaire sources
784
766
 
785
- - **`LLM_PROMPT.md`** — not used on Replit setup; its questions are **replaced** by REPLIT_SETUP Questions 1–3
786
767
  - Standalone “target platform” / “hosting target” / “use case” / **screen count** prompts outside Question 3
787
768
 
788
769
  Do **not** ask the user how to implement:
@@ -806,7 +787,7 @@ Do **not**:
806
787
  - Skip Cloudinary recommendation without stating the persistence tradeoff
807
788
  - **Skip Cloudinary Secrets** after the user chooses Cloudinary — always collect `CLOUDINARY_*` inside Question 2 before Question 3
808
789
  - **Announce “Question 2b”** or a separate follow-up question — show the Secrets form immediately instead
809
- - Claim uploads are “permanent” on Replit local disk without Object Storage or Cloudinary
790
+ - Claim uploads are durable without configuring Cloudinary or Replit Object Storage do not invent a local-disk-only media option
810
791
  - Leave `tsx` only in `devDependencies` if the Deploy pipeline installs production deps only
811
792
  - Use Canvas preview as the primary CMS verification surface
812
793
  - Serve a different app or API-only response at `/` instead of the Control Panel
@@ -822,22 +803,6 @@ Do **not**:
822
803
 
823
804
  ---
824
805
 
825
- ## SQLite path — `server.ts` reference (Q1 option 3)
826
-
827
- ```ts
828
- const store = createTomorrowOSStore({
829
- sqlitePath: join(__dirname, "data", "tomorrowos.db")
830
- });
831
- ```
832
-
833
- With Secrets / `.env`:
834
-
835
- ```env
836
- TOMORROWOS_STORE=sqlite
837
- ```
838
-
839
- ---
840
-
841
806
  ## Failure recovery cheat sheet
842
807
 
843
808
  | Symptom | Likely cause | Fix |
@@ -858,7 +823,9 @@ TOMORROWOS_STORE=sqlite
858
823
 
859
824
  ## Protocol version
860
825
 
861
- `replit-setup/1.11` — aligned with TomorrowOS protocol `1.0` and `@tomorrowos/sdk` store drivers `sqlite` | `supabase` | `postgres` | `memory`.
826
+ `replit-setup/1.12` — aligned with TomorrowOS protocol `1.0` and `@tomorrowos/sdk` store drivers `sqlite` | `supabase` | `postgres` | `memory`.
827
+
828
+ **Changelog 1.12:** Q1 no longer offers SQLite (Supabase or Replit Postgres only). Q2 no longer offers local disk (Cloudinary or Replit Object Storage only).
862
829
 
863
830
  **Changelog 1.11:** Q1 Supabase → **immediate** `SUPABASE_URL` Secrets input; Q2 Cloudinary → **immediate multi-field** `CLOUDINARY_*` dialog; Q3 = **one stacked dialog with all seven** branding inputs (no field-by-field Next).
864
831
 
package/REPLIT_UPGRADE.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  > **This file is a contract for Replit Agent / Replit AI.**
4
4
  > Follow it literally. This protocol **upgrades an existing CMS** to the latest `@tomorrowos/sdk`.
5
- > It is **not** a greenfield setup. Do **not** use `REPLIT_SETUP.md` or `LLM_PROMPT.md` for this flow unless the user explicitly asks for a full re-setup.
5
+ > It is **not** a greenfield setup. Do **not** use `REPLIT_SETUP.md` for this flow unless the user explicitly asks for a full re-setup.
6
6
 
7
7
  ---
8
8
 
@@ -24,7 +24,7 @@ Alternate triggers that also activate this protocol:
24
24
 
25
25
  When any of these triggers fire:
26
26
 
27
- 1. **Ignore `LLM_PROMPT.md` and `REPLIT_SETUP.md` questionnaires** — this is an upgrade, not setup Q&A.
27
+ 1. **Ignore `REPLIT_SETUP.md` questionnaires** — this is an upgrade, not setup Q&A.
28
28
  2. **Do not** reinvent pairing, WebSocket, or playlist servers.
29
29
  3. Preserve project data and custom UI/server wiring.
30
30
 
@@ -1,70 +1,65 @@
1
- # Vercel / v0 閳?Verbatim question bank
1
+ # Vercel / v0 ? Verbatim question bank
2
2
 
3
- > **Protocol:** `vercel-questions/1.0` (pairs with `VERCEL_SETUP.md` 1.9+ / `@tomorrowos/sdk` 0.9.54+)
3
+ > **Protocol:** `vercel-questions/1.1` (pairs with `VERCEL_SETUP.md` 1.10+ / `@tomorrowos/sdk` 0.9.56+)
4
4
  > **For Agents:** When asking the user a setup question, **copy-paste the block below for that step EXACTLY**.
5
- > Do **not** paraphrase, reorder, rename options, or invent alternatives (no S3, no Supabase Storage, no 閳ユ窂lob recommended閳? no 閳ユ竸isable media閳?.
5
+ > Do **not** paraphrase, reorder, rename options, or invent alternatives (no S3, no Supabase Storage, no "Blob recommended", no "disable media").
6
6
  > After each answer, configure per `VERCEL_SETUP.md`, then paste the **next** block.
7
7
 
8
8
  ---
9
9
 
10
- ## Q1 閳?Database (paste this entire block)
10
+ ## Q1 ? Database (paste this entire block)
11
11
 
12
12
  Which database should TomorrowOS use on Vercel?
13
13
 
14
- **1. Supabase Postgres (recommended)** 閳?durable pairing/playlists; use the **Session pooler** URL (`*.pooler.supabase.com:6543`).
14
+ **1. Supabase Postgres (recommended)** ? durable pairing/playlists; use the **Session pooler** URL (`*.pooler.supabase.com:6543`).
15
15
 
16
- **2. Neon Postgres** 閳?serverless Postgres native to Vercel; use Neon閳ユ獨 **pooled** connection string from the Neon dashboard (not the direct un-pooled host for serverless).
16
+ **2. Neon Postgres** ? serverless Postgres native to Vercel; use Neon's **pooled** connection string from the Neon dashboard (not the direct un-pooled host for serverless).
17
17
 
18
- **3. SQLite (`data/tomorrowos.db`)** 閳?**demo / local only on Vercel.** The filesystem is ephemeral; pairings and playlists will not survive redeploys. Only choose this if you understand the limitation.
19
-
20
- Reply with **1**, **2**, or **3** (or 閳ユ藩upabase閳?/ 閳ユ罚eon閳?/ 閳ユ藩QLite閳?.
18
+ Reply with **1** or **2** (or "Supabase" / "Neon").
21
19
 
22
20
  ---
23
21
 
24
- ## After Q1 閳?agent only (do not ask the user)
22
+ ## After Q1 ? agent only (do not ask the user)
25
23
 
26
24
  | Choice | You set silently | User only provides |
27
25
  |--------|------------------|--------------------|
28
26
  | 1 Supabase | `TOMORROWOS_STORE=supabase`, `DATABASE_SSL=true` | `SUPABASE_URL` (pooler) |
29
27
  | 2 Neon | `TOMORROWOS_STORE=postgres`, `DATABASE_SSL=true` | `DATABASE_URL` (pooled) |
30
- | 3 SQLite | `TOMORROWOS_STORE=sqlite` | confirmation |
31
28
 
32
29
  Never show blank Env fields for `TOMORROWOS_STORE` or `DATABASE_SSL`.
33
30
 
34
31
  ---
35
32
 
36
- ## Q2 閳?Media (paste this entire block)
33
+ ## Q2 ? Media (paste this entire block)
37
34
 
38
35
  How should playlist media (images/videos) be stored?
39
36
 
40
- **1. Cloudinary (recommended)** 閳?durable public HTTPS URLs (`https://res.cloudinary.com/...`). Works out of the box with `@tomorrowos/sdk` auto-detection. **Prefer this on Vercel.**
41
-
42
- **2. Vercel Blob** 閳?Vercel-native object storage; durable `https://*.public.blob.vercel-storage.com/...` URLs. Use when you want media on the same Vercel project without a Cloudinary account.
37
+ **1. Cloudinary (recommended)** ? durable public HTTPS URLs (`https://res.cloudinary.com/...`). Works out of the box with `@tomorrowos/sdk` auto-detection. **Prefer this on Vercel.**
43
38
 
44
- **3. Local disk only** 閳?`cms-panel/uploads` or `public/uploads` (OK for quick local tests; **not** for production Vercel fleets 閳?files are ephemeral).
39
+ **2. Vercel Blob** ? Vercel-native object storage; durable `https://*.public.blob.vercel-storage.com/...` URLs. Use when you want media on the same Vercel project without a Cloudinary account.
45
40
 
46
- Reply with **1**, **2**, or **3** (or 閳ユ窅loudinary閳?/ 閳ユ钒ercel Blob閳?/ 閳ユ笓ocal閳?.
41
+ Reply with **1** or **2** (or "Cloudinary" / "Vercel Blob").
47
42
 
48
- **Forbidden replacements (never ask these):** Vercel Blob as recommended #1; Supabase Storage; S3 / R2 / B2 / `MEDIA_S3_*`; 閳ユ笜o media storage閳?/ disable uploads.
43
+ **Forbidden replacements (never ask these):** Vercel Blob as recommended #1; Supabase Storage; S3 / R2 / B2 / `MEDIA_S3_*`; "no media storage" / disable uploads; local disk only.
49
44
 
50
45
  ---
51
46
 
52
- ## Q3 閳?Brand (paste this entire block)
47
+ ## Q3 ? Brand (paste this entire block)
53
48
 
54
- Let閳ユ獨 brand your TomorrowOS experience. Choose **one**:
49
+ Let's brand your TomorrowOS experience. Choose **one**:
55
50
 
56
- **Option A 閳?Website URL (recommended if you have a site)**
51
+ **Option A ? Website URL (recommended if you have a site)**
57
52
  Paste **one public website URL**. I will use it **only** to fill **`brand.json`**: product name, tagline, primary / secondary / **background** / text colours, fonts, and logo.
58
- I will **not** rebuild that website or add login 閳?unless you explicitly ask for login/auth later.
53
+ I will **not** rebuild that website or add login ? unless you explicitly ask for login/auth later.
59
54
 
60
- **Option B 閳?Manual brand fields**
55
+ **Option B ? Manual brand fields**
61
56
  Send as many of these as you can (name + at least one colour + logo is ideal):
62
57
  1. **Product / venue name** (required)
63
58
  2. **Tagline** (optional)
64
59
  3. **Primary colour** (hex, e.g. `#FF8A3D`)
65
- 4. **Background colour** (hex 閳?page / panel background)
66
- 5. **Text colour** (hex, optional 閳?default `#0A0908`)
60
+ 4. **Background colour** (hex ? page / panel background)
61
+ 5. **Text colour** (hex, optional ? default `#0A0908`)
67
62
  6. **Secondary / accent colour** (hex, optional)
68
- 7. **Logo** 閳?upload SVG/PNG into the project, or a public image URL I can fetch into `./assets/`
63
+ 7. **Logo** ? upload SVG/PNG into the project, or a public image URL I can fetch into `./assets/`
69
64
 
70
65
  Reply with a URL **or** the manual fields. Do not send only a name with no colours/logo unless you want defaults (I will confirm defaults before writing).
package/VERCEL_SETUP.md CHANGED
@@ -3,9 +3,9 @@
3
3
  > **This file is a contract for Vercel Agent / AI coding assistants setting up TomorrowOS on Vercel.**
4
4
  > Follow it literally. Ask questions in order. Do not invent secrets. Prefer configuring **Vercel Environment Variables**, `.env` (non-secrets only), `brand.json`, and `server.ts` store wiring over inventing a custom CMS.
5
5
  >
6
- > **On Vercel, this file is the setup questionnaire** (not `REPLIT_SETUP.md`, not `LLM_PROMPT.md`).
6
+ > **On Vercel, this file is the setup questionnaire** (not `REPLIT_SETUP.md`).
7
7
  >
8
- > Protocol id: `vercel-setup/1.9` pairs with `@tomorrowos/sdk` **0.9.54+** (`templates/cms-starter-v0`, Fluid Functions WebSockets).
8
+ > Protocol id: `vercel-setup/1.9` ? pairs with `@tomorrowos/sdk` **0.9.56+** (`templates/cms-starter-v0`, Fluid Functions WebSockets).
9
9
  >
10
10
  > **Questions source of truth:** `VERCEL_QUESTIONS.md` ? Agents must **open that file and paste each Q block verbatim**. Do not paraphrase.
11
11
  > **Copy questions verbatim from `VERCEL_QUESTIONS.md`.** Do not invent alternate Q2 options (no ?Supabase Storage?? no S3, no ?disable media?? no making Blob the recommended option).
@@ -33,20 +33,30 @@ These rules override any platform-generated recommendation that conflicts with t
33
33
  When a human pastes a prompt like this, **read this file and start Question 1 immediately**:
34
34
 
35
35
  ```text
36
- Follow @tomorrowos/sdk VERCEL_SETUP.md and set up my TomorrowOS CMS.
37
- First open VERCEL_QUESTIONS.md. For Q1, Q2, Q3: paste each question block VERBATIM (character-for-character). Do not rephrase options.
38
- Q2 must show Cloudinary (recommended) as option 1 ??never Blob-as-recommended, never S3, never Supabase Storage.
39
- After database choice, YOU set TOMORROWOS_STORE and DATABASE_SSL ??do not ask me to fill those.
40
- Do not use LLM_PROMPT.md or REPLIT_SETUP.md. No OpenAI/LLM API keys.
41
- Apply the NON-NEGOTIABLE VERCEL RUNTIME CONTRACT exactly: one TomorrowOS instance; no public/ Output Directory; Preview Next only under preview/; Publish through the Fluid Function; verify /status JSON, / Control Panel, and WebSocket 101; pair devices only with the Production URL.
36
+ Follow @tomorrowos/sdk (an NPM package) VERCEL_SETUP.md and set up my TomorrowOS CMS from scratch.
37
+
38
+ Open VERCEL_QUESTIONS.md first.
39
+ For Q1, Q2, Q3: paste each question block VERBATIM. Do not rephrase options.
40
+ After Q1 database choice, YOU set TOMORROWOS_STORE and DATABASE_SSL. Do not ask me to fill those.
41
+ After Q2 media storage choice, YOU set TOMORROWOS_MEDIA. Do not ask me to fill those.
42
+ Run: npx @tomorrowos/sdk@0.9.56 init . --hosting v0 --force
43
+ Keep cms-starter-v0 layout.
44
+ Apply the NON-NEGOTIABLE VERCEL RUNTIME CONTRACT exactly:
45
+ one TomorrowOS instance only
46
+ never create a second TomorrowOS in api/ws.ts
47
+ never use public/ as Vercel Output Directory
48
+ Preview Next only under preview/
49
+ Publish must be the Fluid Function
50
+ after deploy verify: GET /status ? JSON, GET / ? Control Panel, WebSocket on / or /api ? 101
51
+ pair devices against the Production URL only
42
52
  ```
43
53
 
44
54
  Alternate triggers:
45
55
 
46
- - ?Set up TomorrowOS on Vercel using VERCEL_SETUP.md??
47
- - ?Configure my TomorrowOS CMS (Vercel wizard)??
56
+ - ?Set up TomorrowOS on Vercel using VERCEL_SETUP.md?
57
+ - ?Configure my TomorrowOS CMS (Vercel wizard)?
48
58
 
49
- When any of these fire, **ignore `REPLIT_SETUP.md` and `LLM_PROMPT.md`** for the Q&A phase (you may skim Replit docs only for shared SDK semantics).
59
+ When any of these fire, **ignore `REPLIT_SETUP.md`** for the Q&A phase (you may skim Replit docs only for shared SDK semantics).
50
60
 
51
61
  ---
52
62
 
@@ -149,8 +159,8 @@ Ask **only** these questions, in **this exact order**. **Copy the ?Ask exactly??
149
159
 
150
160
  | Step | Section | When |
151
161
  |------|---------|------|
152
- | 1 | **Question 1** ??Database (Supabase ??Neon ??SQLite) | Always |
153
- | 2 | **Question 2** ??Media (**Cloudinary recommended** ??Vercel Blob ??local) | Always (after Q1) |
162
+ | 1 | **Question 1** ? Database (Supabase ? Neon) | Always |
163
+ | 2 | **Question 2** ? Media (**Cloudinary recommended** ? Vercel Blob) | Always (after Q1) |
154
164
  | 3 | **Question 3** ??Brand / TomorrowOS look (`brand.json` only) | Always last |
155
165
 
156
166
  **That is the complete list.** Do **not** ask how many screens / devices.
@@ -177,7 +187,7 @@ Ask **only** these questions, in **this exact order**. **Copy the ?Ask exactly??
177
187
 
178
188
  TomorrowOS pairing and Control Panel logic live in `@tomorrowos/sdk`. They do **not** call OpenAI.
179
189
 
180
- If the user **skips** a Question 2 option, do **not** invent a substitute question. Re-state Question 2 choices **verbatim**, or proceed with local uploads + warning ??never pivot to LLM keys or Supabase Storage.
190
+ If the user **skips** a Question 2 option, do **not** invent a substitute question. Re-state Question 2 choices **verbatim** ? never pivot to LLM keys, Supabase Storage, or local disk uploads.
181
191
 
182
192
  If the user says ?just set it up, don?t ask questions,??respond:
183
193
 
@@ -189,10 +199,10 @@ Then ask **Question 1**.
189
199
 
190
200
  ## Hard rules
191
201
 
192
- 1. **Open `VERCEL_QUESTIONS.md` first.** Paste Q1 ??configure ??paste Q2 ??configure ??paste Q3. Never paraphrase question options. Never use `LLM_PROMPT.md` or `REPLIT_SETUP.md` as the questionnaire. **Never** ask for OpenAI or other LLM API keys.
193
- 2. **Question 2 options are fixed:** (1) Cloudinary **(recommended)**, (2) Vercel Blob, (3) local disk. Do not reorder; do not invent Supabase Storage / disable-uploads.
202
+ 1. **Open `VERCEL_QUESTIONS.md` first.** Paste Q1 ??configure ??paste Q2 ??configure ??paste Q3. Never paraphrase question options. Never use `REPLIT_SETUP.md` as the questionnaire. **Never** ask for OpenAI or other LLM API keys.
203
+ 2. **Question 2 options are fixed:** (1) Cloudinary **(recommended)**, (2) Vercel Blob. Do not reorder; do not invent Supabase Storage, local disk, or disable-uploads.
194
204
  3. **Cloudinary:** open **one** Vercel Env popup with **all** Cloudinary fields together. Do **not** quiz one credential at a time.
195
- 4. **`TOMORROWOS_STORE` + `DATABASE_SSL` = agent-owned.** After Q1 choice (and URL if needed), set them yourself in Vercel Env Vars. **Never** include them as blank fields for the customer. Mapping: Supabase ??`TOMORROWOS_STORE=supabase` + `DATABASE_SSL=true`; Neon ??`TOMORROWOS_STORE=postgres` + `DATABASE_SSL=true`; SQLite ??`TOMORROWOS_STORE=sqlite` (no `DATABASE_SSL` required).
205
+ 4. **`TOMORROWOS_STORE` + `DATABASE_SSL` = agent-owned.** After Q1 choice (and URL if needed), set them yourself in Vercel Env Vars. **Never** include them as blank fields for the customer. Mapping: Supabase ? `TOMORROWOS_STORE=supabase` + `DATABASE_SSL=true`; Neon ? `TOMORROWOS_STORE=postgres` + `DATABASE_SSL=true`. **Do not offer SQLite as a questionnaire choice** ? it is ephemeral on Vercel and unsuitable for production.
196
206
  5. **Do not invent** Cloudinary credentials, database URLs, Vercel Blob tokens, brand colours, **or LLM API keys**.
197
207
  6. **Postgres env naming:** prefer **`SUPABASE_URL`** for Supabase; prefer **`DATABASE_URL`** for Neon.
198
208
  7. **Always prefer pooled connection strings** (Supabase Session pooler **6543**; Neon pooled host).
@@ -582,10 +592,11 @@ If Vercel?s UI and this file disagree on bundling, **prefer whatever keeps a sin
582
592
 
583
593
  ---
584
594
 
585
- ## Question 1 ??Database (always)
595
+ ## Question 1 ? Database (always)
586
596
 
587
- > **Do not** ask screen counts. Present all three options; recommend **Supabase** for Vercel fleets.
588
- > **After the user picks a database:** collect the URL if needed, then **you** set `TOMORROWOS_STORE` + `DATABASE_SSL` (see mapping below). The customer must **never** be asked to type those two names or values.
597
+ > **Do not** ask screen counts. Present both options; recommend **Supabase** for Vercel fleets.
598
+ > **After the user picks a database:** collect the URL if needed, then **you** set `TOMORROWOS_STORE` + `DATABASE_SSL` (see mapping below). The customer must **never** be asked to type those two names or values.
599
+ > **Forbidden:** offering SQLite as a user-facing Q1 choice (ephemeral filesystem on Vercel).
589
600
 
590
601
  ### Agent-owned env mapping (mandatory)
591
602
 
@@ -593,23 +604,20 @@ If Vercel?s UI and this file disagree on bundling, **prefer whatever keeps a sin
593
604
  |------------|--------------------|--------------------|
594
605
  | 1 Supabase | `TOMORROWOS_STORE=supabase`, `DATABASE_SSL=true` | `SUPABASE_URL` (pooler string) |
595
606
  | 2 Neon | `TOMORROWOS_STORE=postgres`, `DATABASE_SSL=true` | `DATABASE_URL` (pooled string) |
596
- | 3 SQLite | `TOMORROWOS_STORE=sqlite` | Confirmation only |
597
607
 
598
- If you open an Env configuration UI for Q1, fields visible to the user may include **only** `SUPABASE_URL` or `DATABASE_URL`. **Do not** show blank `TOMORROWOS_STORE` / `DATABASE_SSL` inputs ??write those yourself in the same step.
608
+ If you open an Env configuration UI for Q1, fields visible to the user may include **only** `SUPABASE_URL` or `DATABASE_URL`. **Do not** show blank `TOMORROWOS_STORE` / `DATABASE_SSL` inputs ? write those yourself in the same step.
599
609
 
600
- ### Step A ??Ask storage choice
610
+ ### Step A ? Ask storage choice
601
611
 
602
612
  **Ask exactly:**
603
613
 
604
614
  > Which database should TomorrowOS use on Vercel?
605
615
  >
606
- > **1. Supabase Postgres (recommended)** ??durable pairing/playlists; use the **Session pooler** URL (`*.pooler.supabase.com:6543`).
616
+ > **1. Supabase Postgres (recommended)** ? durable pairing/playlists; use the **Session pooler** URL (`*.pooler.supabase.com:6543`).
607
617
  >
608
- > **2. Neon Postgres** ??serverless Postgres native to Vercel; use Neon?s **pooled** connection string from the Neon dashboard (not the direct un-pooled host for serverless).
618
+ > **2. Neon Postgres** ? serverless Postgres native to Vercel; use Neon's **pooled** connection string from the Neon dashboard (not the direct un-pooled host for serverless).
609
619
  >
610
- > **3. SQLite (`data/tomorrowos.db`)** ??**demo / local only on Vercel.** The filesystem is ephemeral; pairings and playlists will not survive redeploys. Only choose this if you understand the limitation.
611
- >
612
- > Reply with **1**, **2**, or **3** (or ?Supabase??/ ?Neon??/ ?SQLite??.
620
+ > Reply with **1** or **2** (or "Supabase" / "Neon").
613
621
 
614
622
  ### Step B ??If **1 / Supabase**
615
623
 
@@ -653,21 +661,7 @@ If you open an Env configuration UI for Q1, fields visible to the user may inclu
653
661
  2. Wire `createTomorrowOSStore` for Postgres.
654
662
  3. **Do not** commit the real connection string.
655
663
  4. **Do not** ask the user to fill `TOMORROWOS_STORE` or `DATABASE_SSL` in chat or Env UI.
656
- 5. If you see `ENETUNREACH` on `:5432`, switch to Neon?s **pooled** URL.
657
-
658
- ### Step D ??If **3 / SQLite**
659
-
660
- **Warn exactly before continuing:**
661
-
662
- > SQLite on Vercel is **not** suitable for production fleets ??data in `data/tomorrowos.db` is lost when the instance is recycled or redeployed. Continue only for a quick demo.
663
-
664
- Only proceed after the user explicitly confirms.
665
-
666
- **You must then (automatic):**
667
-
668
- 1. Set `TOMORROWOS_STORE=sqlite` yourself (do not ask the user to type it).
669
- 2. Keep `sqlitePath` for `data/tomorrowos.db`.
670
- 3. Warn again in the final summary that they should move to Supabase or Neon for real devices.
664
+ 5. If you see `ENETUNREACH` on `:5432`, switch to Neon's **pooled** URL.
671
665
 
672
666
  **Later in Question 3:** set `cms.hostingTarget` to **`"vercel"`** (all Q1 branches).
673
667
 
@@ -677,23 +671,21 @@ Only proceed after the user explicitly confirms.
677
671
 
678
672
  > **This question is only about media files (images/videos).**
679
673
  > 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.
680
- > **Copy the three options below verbatim.** Wrong examples that must **never** appear: ?Vercel Blob (recommended)?? ?Supabase Storage?? ?No media storage / disable uploads??
674
+ > **Copy the two options below verbatim.** Wrong examples that must **never** appear: "Vercel Blob (recommended)", "Supabase Storage", "No media storage / disable uploads", "local disk only".
681
675
 
682
- ### Step A ??Ask storage choice
676
+ ### Step A ? Ask storage choice
683
677
 
684
- **Ask exactly (wording must match ??Cloudinary is recommended):**
678
+ **Ask exactly (wording must match ? Cloudinary is recommended):**
685
679
 
686
680
  > How should playlist media (images/videos) be stored?
687
681
  >
688
- > **1. Cloudinary (recommended)** ??durable public HTTPS URLs (`https://res.cloudinary.com/...`). Works out of the box with `@tomorrowos/sdk` auto-detection. **Prefer this on Vercel.**
689
- >
690
- > **2. Vercel Blob** ??Vercel-native object storage; durable `https://*.public.blob.vercel-storage.com/...` URLs. Use when you want media on the same Vercel project without a Cloudinary account.
682
+ > **1. Cloudinary (recommended)** ? durable public HTTPS URLs (`https://res.cloudinary.com/...`). Works out of the box with `@tomorrowos/sdk` auto-detection. **Prefer this on Vercel.**
691
683
  >
692
- > **3. Local disk only** ??`cms-panel/uploads` or `public/uploads` (OK for quick local tests; **not** for production Vercel fleets ??files are ephemeral).
684
+ > **2. Vercel Blob** ? Vercel-native object storage; durable `https://*.public.blob.vercel-storage.com/...` URLs. Use when you want media on the same Vercel project without a Cloudinary account.
693
685
  >
694
- > Reply with **1**, **2**, or **3** (or ?Cloudinary??/ ?Vercel Blob??/ ?local??.
686
+ > Reply with **1** or **2** (or "Cloudinary" / "Vercel Blob").
695
687
 
696
- If you catch yourself about to offer Blob-as-recommended, Supabase Storage, or ?no media?? **stop and paste the block above instead**.
688
+ If you catch yourself about to offer Blob-as-recommended, Supabase Storage, local disk, or "no media" ? **stop and paste the block above instead**.
697
689
 
698
690
  ### Step B ??If **1 / Cloudinary** (same Question 2 ??**one Env popup**)
699
691
 
@@ -755,11 +747,6 @@ import { put } from "@vercel/blob";
755
747
  4. Warn: until the project?s Blob bridge is wired, Control Panel **Media** status may show local/ephemeral ??verify uploads return `https://*.blob.vercel-storage.com/...` before calling setup complete.
756
748
  5. Do **not** proceed to Question 3 until `BLOB_READ_WRITE_TOKEN` exists (or Blob is linked and token is confirmed in the Vercel dashboard).
757
749
 
758
- ### Step D ??If **3 / local uploads**
759
-
760
- 1. `mkdir -p public/uploads`
761
- 2. Warn clearly: files may vanish on redeploy; players need stable HTTPS URLs ??Cloudinary or Vercel Blob is strongly preferred for Vercel.
762
-
763
750
  ---
764
751
 
765
752
  ## Question 3 ??Brand (`brand.json` only)
@@ -881,7 +868,7 @@ Then (**Scheme A ??mandatory for Publish**):
881
868
  |------|----------|--------------|
882
869
  | `SUPABASE_URL` | If Q1 = Supabase | User pastes URL |
883
870
  | `DATABASE_URL` | If Q1 = Neon | User pastes URL |
884
- | `TOMORROWOS_STORE` | Yes | **Agent auto-sets only** ??never a blank field for the user (`supabase` / `postgres` / `sqlite`) |
871
+ | `TOMORROWOS_STORE` | Yes | **Agent auto-sets only** ? never a blank field for the user (`supabase` / `postgres`) |
885
872
  | `DATABASE_SSL` | Yes for Postgres | **Agent auto-sets only** ??never a blank field for the user (`true`) |
886
873
  | `CLOUDINARY_*` | If Q2 = Cloudinary | User via **one Env popup** (all fields) |
887
874
  | `BLOB_READ_WRITE_TOKEN` | If Q2 = Vercel Blob | User / Blob link |
@@ -965,7 +952,7 @@ Tell the user:
965
952
  | WebSocket fails on Publish | Fluid off / static-only / Next as Production / no Function WS | Enable Fluid; Scheme A; test 101 upgrade |
966
953
  | Devices fail only on Preview | Next proxy cannot upgrade `wss` | Expected ??pair on Publish URL |
967
954
  | Agent asked user to fill `TOMORROWOS_STORE` / `DATABASE_SSL` | Off-protocol | Agent must auto-set after DB choice; remove those fields from user Env forms |
968
- | Agent offered Blob-as-recommended / Supabase Storage / ?no media??for Q2 | Invented options | Re-ask Q2 verbatim: **1 Cloudinary (recommended)**, 2 Blob, 3 local |
955
+ | Agent offered Blob-as-recommended / Supabase Storage / local disk / "no media" for Q2 | Invented options | Re-ask Q2 verbatim: **1 Cloudinary (recommended)**, 2 Vercel Blob |
969
956
  | Agent asked Cloudinary key/secret in three chat turns | Off-protocol | Use **one Env popup** with all Cloudinary fields |
970
957
  | 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 |
971
958
  | 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` |
@@ -981,7 +968,7 @@ Tell the user:
981
968
  | Secrets | Replit Secrets | Vercel Environment Variables |
982
969
  | Deploy config | `.replit`, artifact `kind=web` | `vercel.json` + Fluid; **Framework = Other**; **no** `.replit` |
983
970
  | `hostingTarget` | `"here"` | `"vercel"` |
984
- | Database | Supabase (primary) | Supabase **or Neon** (pooled); SQLite demo only |
971
+ | Database | Supabase (primary) | Supabase **or Neon** (pooled) |
985
972
  | Media | Cloudinary or Replit Object Storage | Cloudinary **or Vercel Blob** |
986
973
  | Preview | Replit Preview / Autoscale Node | **Next shell + proxy** when v0 hardwires Next |
987
974
  | Publish | Long-lived Node | **Node `TomorrowOS.listen` only** (not Next); gate on no `404 NOT_FOUND` |
@@ -997,8 +984,8 @@ After setup, report:
997
984
  2. **Publish health:** Framework = Other; Output ??`public`; `GET /status` ??JSON; `GET /` OK
998
985
  3. **Preview note:** Next proxy shell used / not needed
999
986
  4. **Env Vars set** (names only ??never values); note which were auto-set (`TOMORROWOS_STORE`, `DATABASE_SSL`)
1000
- 5. **Store:** Supabase pooler / Neon pooled / SQLite (with warning)
1001
- 6. **Media:** Cloudinary, Vercel Blob, or local (with warning)
987
+ 5. **Store:** Supabase pooler or Neon pooled
988
+ 6. **Media:** Cloudinary or Vercel Blob
1002
989
  7. **Brand:** name + `hostingTarget: vercel` (+ note if derived from website URL)
1003
990
  8. **Runtime split:** Preview = ??; Publish = Node TomorrowOS (confirm **not** Next)
1004
991
  9. **Open action:** if Database still ERROR, paste the pooler instruction again
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tomorrowos/sdk",
3
- "version": "0.9.54",
3
+ "version": "0.9.56",
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",
@@ -18,13 +18,10 @@
18
18
  "dist",
19
19
  "templates",
20
20
  "README.md",
21
- "LLM_PROMPT.md",
22
21
  "REPLIT_SETUP.md",
23
- "LOVABLE_SETUP.md",
24
22
  "VERCEL_SETUP.md",
25
23
  "VERCEL_QUESTIONS.md",
26
24
  "REPLIT_UPGRADE.md",
27
- "BUILD_GUARDRAILS.md",
28
25
  "PLAYER_INSTALL.md",
29
26
  "brand.schema.json",
30
27
  "brand.example.json"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "my-cms",
3
- "version": "0.9.54",
3
+ "version": "0.9.56",
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.54",
16
+ "@tomorrowos/sdk": "^0.9.56",
17
17
  "dotenv": "^17.2.3",
18
18
  "tsx": "^4.19.0"
19
19
  },
@@ -2,14 +2,13 @@
2
2
  "policy": {
3
3
  "playlists": [
4
4
  {
5
- "id": "weekday-promo",
6
- "name": "Weekday promo",
5
+ "id": "promo-window",
6
+ "name": "Promo window",
7
7
  "schedule": {
8
- "startDate": "2026-05-01",
9
- "endDate": "2026-05-31",
10
- "daysOfWeek": [1, 2, 3, 4, 5],
11
- "start": "09:00",
12
- "end": "17:00"
8
+ "startDate": "2026-05-05",
9
+ "start": "13:00",
10
+ "endDate": "2026-05-08",
11
+ "end": "14:00"
13
12
  },
14
13
  "items": [
15
14
  {