@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/README.md +327 -123
- package/REPLIT_SETUP.md +24 -57
- package/REPLIT_UPGRADE.md +2 -2
- package/VERCEL_QUESTIONS.md +21 -26
- package/VERCEL_SETUP.md +49 -62
- package/package.json +1 -4
- package/templates/cms-starter/package.json +2 -2
- package/templates/cms-starter/policy.example.json +6 -7
- package/templates/cms-starter-v0/package.json +2 -2
- package/templates/cms-starter-v0/policy.example.json +6 -7
- package/BUILD_GUARDRAILS.md +0 -350
- package/LOVABLE_SETUP.md +0 -462
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
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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** —
|
|
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**).
|
|
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
|
|
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
|
|
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.
|
|
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
|
-
> **
|
|
520
|
+
> **1. Cloudinary (Recommended)** — public HTTPS URLs, survives Replit restarts/redeploys, avoids Object Storage permission issues.
|
|
531
521
|
>
|
|
532
|
-
>
|
|
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
|
-
>
|
|
524
|
+
> Reply **1** for Cloudinary or **2** for Replit Object Storage.
|
|
537
525
|
|
|
538
|
-
### Step B — If
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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`
|
|
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 `
|
|
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
|
|
package/VERCEL_QUESTIONS.md
CHANGED
|
@@ -1,70 +1,65 @@
|
|
|
1
|
-
# Vercel / v0
|
|
1
|
+
# Vercel / v0 ? Verbatim question bank
|
|
2
2
|
|
|
3
|
-
> **Protocol:** `vercel-questions/1.
|
|
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
|
|
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
|
|
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)**
|
|
14
|
+
**1. Supabase Postgres (recommended)** ? durable pairing/playlists; use the **Session pooler** URL (`*.pooler.supabase.com:6543`).
|
|
15
15
|
|
|
16
|
-
**2. Neon Postgres**
|
|
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
|
-
|
|
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
|
|
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
|
|
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)**
|
|
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
|
-
**
|
|
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
|
|
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_*`;
|
|
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
|
|
47
|
+
## Q3 ? Brand (paste this entire block)
|
|
53
48
|
|
|
54
|
-
Let
|
|
49
|
+
Let's brand your TomorrowOS experience. Choose **one**:
|
|
55
50
|
|
|
56
|
-
**Option A
|
|
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
|
|
53
|
+
I will **not** rebuild that website or add login ? unless you explicitly ask for login/auth later.
|
|
59
54
|
|
|
60
|
-
**Option B
|
|
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
|
|
66
|
-
5. **Text colour** (hex, optional
|
|
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**
|
|
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
|
|
6
|
+
> **On Vercel, this file is the setup questionnaire** (not `REPLIT_SETUP.md`).
|
|
7
7
|
>
|
|
8
|
-
> Protocol id: `vercel-setup/1.9`
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
|
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**
|
|
153
|
-
| 2 | **Question 2**
|
|
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
|
|
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 `
|
|
193
|
-
2. **Question 2 options are fixed:** (1) Cloudinary **(recommended)**, (2) Vercel Blob
|
|
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
|
|
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
|
|
595
|
+
## Question 1 ? Database (always)
|
|
586
596
|
|
|
587
|
-
> **Do not** ask screen counts. Present
|
|
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
|
|
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
|
|
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)**
|
|
616
|
+
> **1. Supabase Postgres (recommended)** ? durable pairing/playlists; use the **Session pooler** URL (`*.pooler.supabase.com:6543`).
|
|
607
617
|
>
|
|
608
|
-
> **2. Neon Postgres**
|
|
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
|
-
>
|
|
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
|
|
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
|
|
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
|
|
676
|
+
### Step A ? Ask storage choice
|
|
683
677
|
|
|
684
|
-
**Ask exactly (wording must match
|
|
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)**
|
|
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
|
-
> **
|
|
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
|
|
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
|
|
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**
|
|
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 /
|
|
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)
|
|
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
|
|
1001
|
-
6. **Media:** Cloudinary
|
|
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.
|
|
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.
|
|
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.
|
|
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": "
|
|
6
|
-
"name": "
|
|
5
|
+
"id": "promo-window",
|
|
6
|
+
"name": "Promo window",
|
|
7
7
|
"schedule": {
|
|
8
|
-
"startDate": "2026-05-
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
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
|
{
|