@tomorrowos/sdk 0.9.40 → 0.9.42

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
@@ -12,9 +12,11 @@ When a human pastes a prompt like this, **read this file and start Question 1 im
12
12
  ```text
13
13
  Follow @tomorrowos/sdk REPLIT_SETUP.md and set up my TomorrowOS CMS.
14
14
  Ask only the questions listed in REPLIT_SETUP.md, in order. Do not use LLM_PROMPT.md.
15
- Artifact kind must be **web** (never **api**). Collect SUPABASE_URL once only — never ask for the connection string twice.
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.
17
+ If Supabase: collect SUPABASE_URL once only — never ask for the connection string twice.
16
18
  Question 3 branding fields are all optional; missing values keep starter brand.json defaults.
17
- After all setup questions are answered (including Cloudinary Secrets if you chose Cloudinary), configure and start — minimal verification only (Web app type + Preview at /). **Never test Supabase connection** — save `SUPABASE_URL` and move on. No Supabase or WebSocket tests.
19
+ After all setup questions are answered (including Cloudinary Secrets if you chose Cloudinary), configure and start — minimal verification only (Web app type + Preview at /). **Never test database connection** in Preview configure and move on. No DB or WebSocket tests.
18
20
  Do not skip steps.
19
21
  ```
20
22
 
@@ -59,13 +61,18 @@ Replit Agent must ask **only** the questions defined in **this file**, in **this
59
61
 
60
62
  | Step | Section | When |
61
63
  |------|---------|------|
62
- | 1 | **Question 1** — Supabase connection string (**once**) | Always |
64
+ | 1 | **Question 1** — Database choice (Supabase Replit Postgres → SQLite) | Always |
63
65
  | 2 | **Question 2** — Media storage (+ Cloudinary Secrets if chosen) | Always (after Q1) |
64
66
  | 3 | **Question 3** — Brand / TomorrowOS app look (**all fields optional**) | Always last, before execution |
65
67
 
66
68
  **That is the complete list.** There are no other setup questions. **Do not** ask how many screens / devices. There is **no** “Question 2b” — Cloudinary credentials are collected **inside Question 2**, immediately after the user chooses Cloudinary.
67
69
 
68
- **Critical — Question 1 (Supabase) collected once only:** Asking for the Postgres connection string **is** selecting Supabase. Collect `SUPABASE_URL` in **one** step (Secrets UI **or** chat paste — not both). After it is saved, **never** ask for the connection string / `SUPABASE_URL` again (not in Q2, Q3, execution, Publish, or “confirm Secrets”).
70
+ **Critical — Question 1 option order (exact):** Present database choices in **this order only**:
71
+ 1. **Supabase Postgres (Recommended)**
72
+ 2. **Built-in Replit PostgreSQL**
73
+ 3. **SQLite (local file — fine for testing)**
74
+
75
+ **Critical — Supabase path collected once only:** If the user picks Supabase, collect `SUPABASE_URL` in **one** step (Secrets UI **or** chat paste — not both). After it is saved, **never** ask for the connection string / `SUPABASE_URL` again (not in Q2, Q3, execution, Publish, or “confirm Secrets”).
69
76
 
70
77
  **Critical:** Question 2 is **not complete** when the user says “Cloudinary” or “yes”. You **must** immediately show the Cloudinary Secrets input (same step — do **not** label a new question) and collect `CLOUDINARY_CLOUD_NAME`, `CLOUDINARY_API_KEY`, and `CLOUDINARY_API_SECRET` **before** Question 3. Do **not** skip to branding with placeholder or invented credentials.
71
78
 
@@ -75,7 +82,7 @@ If the user volunteers extra info early (e.g. brand name before Q3), **record it
75
82
 
76
83
  If the user says “just set it up, don’t ask questions,” respond:
77
84
 
78
- > I need a few quick answers from REPLIT_SETUP.md (Supabase connection string once, 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.
85
+ > 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.
79
86
 
80
87
  Then ask **Question 1** — do not switch to `LLM_PROMPT.md`.
81
88
 
@@ -86,14 +93,15 @@ Then ask **Question 1** — do not switch to `LLM_PROMPT.md`.
86
93
  1. **Ask only REPLIT_SETUP.md questions 1–3** (see **Questionnaire scope**). Never use `LLM_PROMPT.md` on Replit.
87
94
  2. **One question at a time.** Wait for the user’s answer before asking the next question (unless they already answered several in one message). **Exception:** If the user chooses Cloudinary in Question 2, **stay on Question 2** and immediately show the Secrets form — do **not** open a separate “2b” step or announce a new question number.
88
95
  3. **Do not invent** Cloudinary credentials, Supabase URLs, or brand colours. **Do not ask** screen / device count.
89
- 4. **Do not use Replit’s reserved `DATABASE_URL` Secret for Supabase.** On Replit, `DATABASE_URL` is often pre-claimed. Always use **`SUPABASE_URL`** for TomorrowOS Supabase.
90
- 4a. **Never collect `SUPABASE_URL` twice.** One Question 1 capture is enough for the whole wizard and execution checklist.
96
+ 4. **Do not use Replit’s reserved `DATABASE_URL` Secret for Supabase.** On Replit, `DATABASE_URL` is often the **built-in Replit PostgreSQL** URL. For Supabase always use **`SUPABASE_URL`**. For built-in Replit Postgres, use **`DATABASE_URL`** with `TOMORROWOS_STORE=postgres`.
97
+ 4a. **If Supabase is chosen: never collect `SUPABASE_URL` twice.** One capture after the choice is enough for the whole wizard and execution checklist.
91
98
  4b. **Question 3 fields are all optional.** Missing values → keep starter `brand.json` defaults; do not block or re-prompt.
99
+ 4c. **`TOMORROWOS_STORE` + `DATABASE_SSL` are agent-owned.** After the Q1 choice (and URL if needed), set them yourself. Never ask the user to type those names/values.
92
100
  5. **Prefer `npx @tomorrowos/sdk init`** (or the package’s `templates/cms-starter`) as the project seed. Do not rebuild pairing, WebSocket, or playlist APIs from scratch.
93
101
  6. **Never commit secrets** into git. Put credentials only in Replit **Secrets** (and optionally mirror non-secret flags in `.env` that reference env vars).
94
102
  7. After Q&A, **configure and start** — do **not** run a long test suite (see **Post-Q&A: minimal verification only**).
95
103
  8. **Follow the Runtime & Replit publish rules below exactly.** A project that cannot Deploy / Publish on Replit is incomplete.
96
- 9. **Supabase: configure only — never test.** Save `SUPABASE_URL` + `TOMORROWOS_STORE=supabase` and proceed. **Do not** ping Postgres, run `psql`, start the server solely to verify DB connectivity, or treat log errors like `ENOTFOUND` / `database connection failure` in **Replit dev / Preview** as a failed setup. Supabase often works only after **Publish** (production networking), not in the dev shell.
104
+ 9. **Database: configure only — never test in Preview.** Save store Secrets / flags and proceed. **Do not** ping Postgres, run `psql`, or treat `ENOTFOUND` / `database connection failure` in **Replit dev / Preview** as a failed setup (especially Supabase).
97
105
  10. **Artifact kind = `web` only.** **`kind = "api"` is forbidden** for the TomorrowOS CMS artifact. See **IRON RULE** above. Setup is incomplete if Publish would still see an `api` artifact.
98
106
 
99
107
  **Question order (always):**
@@ -336,17 +344,17 @@ Replace `YOUR-REPLIT-DOMAIN` with the actual published hostname (e.g. `my-cms.re
336
344
 
337
345
  After Questions 1–3, **configure → `npm install` → `npm run start` → done.**
338
346
 
339
- ### Supabase: never test in Replit dev (mandatory)
347
+ ### Database / Postgres: never test in Replit dev (mandatory)
340
348
 
341
- Setup always uses Supabase: **save credentials + wire `server.ts` + start** — **nothing else**.
349
+ After Q1, **save credentials / flags + wire `server.ts` + start** — **nothing else**.
342
350
 
343
351
  **Forbidden after Q&A (and after Question 1):**
344
352
 
345
- - ❌ “Testing Supabase connection…”
353
+ - ❌ “Testing Supabase / Postgres connection…”
346
354
  - ❌ `psql`, `pg`, Prisma `db pull`, or any one-off DB probe script
347
355
  - ❌ Restarting / debugging the server **because** logs show `database connection failure`, `getaddrinfo ENOTFOUND`, or `db.*.supabase.co can't be resolved`
348
356
  - ❌ Telling the user their Supabase URL is wrong **only** because dev Preview cannot resolve the host
349
- - ❌ Blocking setup completion on Supabase connectivity
357
+ - ❌ Blocking setup completion on database connectivity
350
358
 
351
359
  **Expected in Replit dev:** Supabase host may **not resolve** in the dev environment. That is **normal**. The Control Panel at `/` can still load. Mark setup **complete** when Web app type is correct and Preview shows the Control Panel — **even if** Supabase errors appear in logs.
352
360
 
@@ -362,7 +370,7 @@ Setup always uses Supabase: **save credentials + wire `server.ts` + start** —
362
370
 
363
371
  ### Do NOT run after setup (unless user asks or Publish fails)
364
372
 
365
- - ❌ **Supabase connection test** — **never**, including after `npm run start` (no `psql`, no DB ping, no “verifying database…”, no retry loops for `ENOTFOUND`)
373
+ - ❌ **Database connection test** — **never**, including after `npm run start` (no `psql`, no DB ping, no “verifying database…”, no retry loops for `ENOTFOUND`)
366
374
  - ❌ **Cloudinary upload test**
367
375
  - ❌ **`curl` / `fetch` on `/brand.json`** as a formal gate
368
376
  - ❌ **Browser WebSocket console snippet** as a formal gate
@@ -378,41 +386,94 @@ Then (and only then) check: API vs Web type → `.replit` ports/`deploymentTarge
378
386
 
379
387
  ---
380
388
 
381
- ## Question 1 — Supabase connection (always, **once**)
389
+ ## Question 1 — Database choice
390
+
391
+ > **This is the first setup question.** **Do not** ask how many screens / devices.
392
+
393
+ ### Step A — Ask exactly (option order is mandatory)
394
+
395
+ Present **exactly these three options, in this order** (Supabase first / recommended):
396
+
397
+ > **Database choice**
398
+ >
399
+ > 1. **Supabase Postgres (Recommended)** — I have a connection string
400
+ > 2. **Built-in Replit PostgreSQL** — no external account needed
401
+ > 3. **SQLite** (local file — fine for testing)
402
+ >
403
+ > Which do you want?
382
404
 
383
- > **This is the first setup question.** Always configure Supabase. **Do not** ask how many screens / devices.
405
+ **Forbidden option orders / labels:**
384
406
 
385
- ### Collect `SUPABASE_URL` exactly once (IRON RULE)
407
+ - Putting Built-in Replit PostgreSQL first or marking it “recommended”
408
+ - ❌ Omitting Supabase or SQLite
409
+ - ❌ 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)
386
410
 
387
- Pasting / saving the connection string **is** choosing Supabase. Treat that as the full Question 1 answer.
411
+ **Agent auto-sets after the choice** (never ask the user to type these):
412
+
413
+ | Choice | Agent sets | User provides |
414
+ |--------|------------|---------------|
415
+ | 1 Supabase | `TOMORROWOS_STORE=supabase`, `DATABASE_SSL=true` | `SUPABASE_URL` (**once**) |
416
+ | 2 Replit PostgreSQL | `TOMORROWOS_STORE=postgres`, `DATABASE_SSL=true` | Use Replit’s built-in **`DATABASE_URL`** (do not ask them to invent a URI) |
417
+ | 3 SQLite | `TOMORROWOS_STORE=sqlite` | Confirmation only |
418
+
419
+ ### Step B1 — If **1 Supabase (Recommended)**
420
+
421
+ Collect `SUPABASE_URL` **exactly once** (IRON RULE).
388
422
 
389
423
  | Allowed (pick **one**) | Forbidden |
390
424
  |------------------------|-----------|
391
- | Open the Replit **Secrets** UI for `SUPABASE_URL` **or** ask the user to paste the string in chat | Asking “Do you want Supabase?” and then asking for the connection string again |
425
+ | Open the Replit **Secrets** UI for `SUPABASE_URL` **or** ask the user to paste the string in chat | Asking for the connection string again after Secrets was already filled |
392
426
  | Save Secret once, then move on | Opening Secrets **and** also asking them to paste the same URI in chat |
393
427
  | Reuse the saved Secret in execution | Asking again in Q2 / Q3 / “confirm your Supabase Secret” / Publish / checklist |
394
428
 
395
- If `SUPABASE_URL` is **already** present in Replit Secrets when Question 1 starts, **do not** ask for it again — confirm you will use the existing Secret and proceed to Question 2 (unless the user explicitly wants to replace it).
429
+ If `SUPABASE_URL` is **already** present in Replit Secrets when this branch starts, **do not** ask for it again — confirm you will use the existing Secret and proceed to Question 2 (unless the user explicitly wants to replace it).
396
430
 
397
- **Preferred on Replit:** open an **input / Secrets** UI where the user pastes the connection string into a field for:
431
+ **Preferred on Replit:** open an **input / Secrets** UI for:
398
432
 
399
433
  - `SUPABASE_URL`
400
434
 
401
- **Only if** the Secrets UI is unavailable, ask exactly in chat (copy wording; do not substitute LLM_PROMPT questions). **Do not** also open a second Secrets prompt for the same value:
435
+ **Only if** the Secrets UI is unavailable, ask exactly in chat. **Do not** also open a second Secrets prompt for the same value:
402
436
 
403
- > Paste your Supabase Postgres connection string below. I will store it as the Replit Secret **`SUPABASE_URL`** (not `DATABASE_URL` — that name is often reserved by Replit).
437
+ > Paste your Supabase Postgres connection string below. I will store it as the Replit Secret **`SUPABASE_URL`** (not `DATABASE_URL` — that name is for built-in Replit PostgreSQL).
404
438
  >
405
439
  > Example shape: `postgresql://postgres.[PROJECT]:[PASSWORD]@aws-0-[REGION].pooler.supabase.com:6543/postgres`
406
440
  >
407
- > SSL is assumed **yes** for Supabase unless you say otherwise — you do not need to answer a separate SSL question if you already pasted the URI.
441
+ > SSL is assumed **yes** for Supabase unless you say otherwise.
408
442
 
409
443
  **You must then:**
410
444
 
411
445
  1. Create / update Replit Secret: `SUPABASE_URL=<user value>` (**once**).
412
- 2. Create / update Replit Secret or `.env` flags only (not a second paste of the URI):
446
+ 2. Set flags yourself (not a second paste of the URI):
413
447
  - `TOMORROWOS_STORE=supabase`
414
- - `DATABASE_SSL=true` (unless the user explicitly says SSL is off)
415
- 3. Update `server.ts` so the store uses Supabase via `SUPABASE_URL`:
448
+ - `DATABASE_SSL=true`
449
+ 3. Wire `server.ts` with `createTomorrowOSStore({ databaseUrl: process.env.SUPABASE_URL || process.env.DATABASE_URL, sqlitePath: ... })`.
450
+ 4. **Do not** write the password into committed files.
451
+ 5. **Do not** test the connection — configuration only.
452
+ 6. **Do not** proceed to Question 2 until `SUPABASE_URL` is saved (or was already present and reused).
453
+ 7. **After this branch completes, never re-request `SUPABASE_URL`.**
454
+
455
+ ### Step B2 — If **2 Built-in Replit PostgreSQL**
456
+
457
+ 1. Ensure Replit’s built-in Postgres is enabled for the Repl (create/attach if the platform UI requires it).
458
+ 2. Use the platform-provided **`DATABASE_URL`** Secret — **do not** ask the user to paste a Supabase-style URI.
459
+ 3. Set yourself:
460
+ - `TOMORROWOS_STORE=postgres`
461
+ - `DATABASE_SSL=true` (unless Replit docs for this DB say otherwise)
462
+ 4. Wire `server.ts` with `databaseUrl: process.env.DATABASE_URL || process.env.SUPABASE_URL`.
463
+ 5. **Do not** set `TOMORROWOS_STORE=supabase` for this path.
464
+ 6. **Do not** ask for `SUPABASE_URL`.
465
+ 7. **Do not** test the DB connection in Preview — configuration only.
466
+ 8. Proceed to Question 2.
467
+
468
+ ### Step B3 — If **3 SQLite**
469
+
470
+ 1. Set `TOMORROWOS_STORE=sqlite` yourself.
471
+ 2. Ensure `data/tomorrowos.db` path via starter / `createTomorrowOSStore({ sqlitePath: join(__dirname, "data", "tomorrowos.db") })`.
472
+ 3. Warn briefly: SQLite is fine for testing; pairings may not survive some rebuilds as well as Postgres.
473
+ 4. **Do not** ask for any connection string.
474
+ 5. Proceed to Question 2.
475
+
476
+ ### Shared `server.ts` shape (all Q1 paths)
416
477
 
417
478
  ```ts
418
479
  import "dotenv/config";
@@ -425,7 +486,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
425
486
  const brand = JSON.parse(readFileSync(join(__dirname, "brand.json"), "utf8"));
426
487
 
427
488
  const store = createTomorrowOSStore({
428
- // Prefer SUPABASE_URL on Replit. Do NOT rely on Replit's reserved DATABASE_URL.
489
+ // Supabase: SUPABASE_URL. Replit Postgres: DATABASE_URL. SQLite: ignores URL.
429
490
  databaseUrl: process.env.SUPABASE_URL || process.env.DATABASE_URL,
430
491
  sqlitePath: join(__dirname, "data", "tomorrowos.db")
431
492
  });
@@ -439,15 +500,6 @@ tomorrowos.listen({
439
500
  });
440
501
  ```
441
502
 
442
- 4. Ensure `.env` / Secrets include `TOMORROWOS_STORE=supabase` and `DATABASE_SSL=true`. The connection string itself lives **only** in the Secret already saved in step 1 — **do not** ask the user to paste it into `.env` or chat again.
443
-
444
- 5. **Do not** write the real password into committed files.
445
- 6. **Do not** test the Supabase connection after saving the Secret — **configuration only**. No probe, no `npm run start` just to verify DB, no fixing “connection failure” in dev. Saving `SUPABASE_URL` completes Question 1.
446
- 7. **Do not** proceed to Question 2 until `SUPABASE_URL` is saved (or was already present and reused).
447
- 8. **After Question 1 completes, never re-request `SUPABASE_URL`.** Execution checklist step B must **reuse** the existing Secret — wire env / `server.ts` silently.
448
-
449
- If the user refuses Supabase, warn that Replit fleets need a durable Postgres store, then offer SQLite only after they explicitly confirm.
450
-
451
503
  **Store in `brand.json` later (Question 3):** `cms.hostingTarget`: `"here"`. Do **not** ask for `expectedScreens`; leave a default (e.g. `5`) when writing `brand.json`.
452
504
 
453
505
  ---
@@ -598,16 +650,31 @@ If `init` is inappropriate (existing customised project), merge carefully:
598
650
 
599
651
  ### B. Apply store + media Secrets
600
652
 
601
- **Supabase (always from Question 1 — reuse; do not ask again):**
653
+ Apply **only** the path chosen in Question 1 — do not re-ask.
602
654
 
603
- Use the **`SUPABASE_URL` already saved** in Question 1. Wire flags only:
655
+ **If Q1 = Supabase (reuse; do not ask again):**
604
656
 
605
657
  ```env
606
658
  TOMORROWOS_STORE=supabase
607
659
  DATABASE_SSL=true
608
660
  ```
609
661
 
610
- **Do not** prompt the user to paste the Postgres connection string a second time. **Do not** open another Secrets form for `SUPABASE_URL` unless the Secret is missing (should not happen if Q1 completed).
662
+ Use the **`SUPABASE_URL` already saved**. **Do not** prompt to paste it again.
663
+
664
+ **If Q1 = Built-in Replit PostgreSQL:**
665
+
666
+ ```env
667
+ TOMORROWOS_STORE=postgres
668
+ DATABASE_SSL=true
669
+ ```
670
+
671
+ Use Replit’s **`DATABASE_URL`**. **Do not** ask for `SUPABASE_URL`.
672
+
673
+ **If Q1 = SQLite:**
674
+
675
+ ```env
676
+ TOMORROWOS_STORE=sqlite
677
+ ```
611
678
 
612
679
  **Cloudinary (if chosen):**
613
680
 
@@ -620,7 +687,7 @@ CLOUDINARY_API_SECRET=...
620
687
  ### C. Wire `server.ts`
621
688
 
622
689
  - Always: `host: "0.0.0.0"`, `port: Number(process.env.PORT) || 3000`, `staticRoot: public`
623
- - Supabase path: pass `databaseUrl: process.env.SUPABASE_URL || process.env.DATABASE_URL` into `createTomorrowOSStore`
690
+ - Store: `createTomorrowOSStore({ databaseUrl: process.env.SUPABASE_URL || process.env.DATABASE_URL, sqlitePath: ... })` driver comes from `TOMORROWOS_STORE`
624
691
  - Keep TomorrowOS event handlers from the starter (paired / online policy push)
625
692
 
626
693
  ### D. Runtime files for Replit publish
@@ -685,7 +752,9 @@ Do **not**:
685
752
  - Replace `@tomorrowos/sdk` with a hand-rolled server
686
753
  - Change the production start command away from `tsx server.ts` / `npm run start`
687
754
  - Point Supabase at Replit’s reserved `DATABASE_URL` when `SUPABASE_URL` should be used
755
+ - List Q1 databases with **Replit PostgreSQL first** or mark it recommended instead of Supabase
688
756
  - **Ask for `SUPABASE_URL` / the Postgres connection string more than once** (Secrets UI + chat, or again during execution / Publish)
757
+ - Ask the user to type `TOMORROWOS_STORE` or `DATABASE_SSL`
689
758
  - Skip Cloudinary recommendation without stating the persistence tradeoff
690
759
  - **Skip Cloudinary Secrets** after the user chooses Cloudinary — always collect `CLOUDINARY_*` inside Question 2 before Question 3
691
760
  - **Announce “Question 2b”** or a separate follow-up question — show the Secrets form immediately instead
@@ -699,13 +768,13 @@ Do **not**:
699
768
  - **Require** product name, colours, or logo in Question 3 — all branding fields are optional; missing → starter `brand.json` defaults
700
769
  - **Re-ask** Question 3 for blank name/colour instead of keeping defaults
701
770
  - Omit `[[ports]]` / `deploymentTarget` from `.replit` when setting up for Publish
702
- - Run **Supabase connection tests**, **WebSocket console tests**, or other post-setup smoke tests unless Preview fails or the user asks
771
+ - Run **database connection tests**, **WebSocket console tests**, or other post-setup smoke tests unless Preview fails or the user asks
703
772
  - **Debug or “fix” Supabase** because dev logs show `getaddrinfo ENOTFOUND` or `database connection failure` — save config and finish setup instead
704
- - Ask how many screens / devices (≤5 vs >5) — always start with Supabase
773
+ - Ask how many screens / devices (≤5 vs >5)
705
774
 
706
775
  ---
707
776
 
708
- ## SQLite fallback — `server.ts` reference (only if user refuses Supabase)
777
+ ## SQLite path — `server.ts` reference (Q1 option 3)
709
778
 
710
779
  ```ts
711
780
  const store = createTomorrowOSStore({
@@ -741,7 +810,9 @@ TOMORROWOS_STORE=sqlite
741
810
 
742
811
  ## Protocol version
743
812
 
744
- `replit-setup/1.9` — aligned with TomorrowOS protocol `1.0` and `@tomorrowos/sdk` store drivers `sqlite` | `supabase` | `postgres` | `memory`.
813
+ `replit-setup/1.10` — aligned with TomorrowOS protocol `1.0` and `@tomorrowos/sdk` store drivers `sqlite` | `supabase` | `postgres` | `memory`.
814
+
815
+ **Changelog 1.10:** Question 1 is a **database choice** with fixed order: **1) Supabase (Recommended) → 2) Built-in Replit PostgreSQL → 3) SQLite**. Supabase still collects `SUPABASE_URL` once only.
745
816
 
746
817
  **Changelog 1.9:** Question 1 collects `SUPABASE_URL` **once only** (no second Secrets/chat prompt in execution). Question 3 branding fields are **all optional**; missing values keep starter `brand.json` defaults.
747
818
 
@@ -1,27 +1,27 @@
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.40+)
3
+ > **Protocol:** `vercel-questions/1.0` (pairs with `VERCEL_SETUP.md` 1.9+ / `@tomorrowos/sdk` 0.9.42+)
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 “Blob recommended”, no “disable 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’s **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.
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
19
 
20
- Reply with **1**, **2**, or **3** (or “Supabase / “Neon / “SQLite”).
20
+ Reply with **1**, **2**, or **3** (or “Supabase�?/ “Neon�?/ “SQLite�?.
21
21
 
22
22
  ---
23
23
 
24
- ## After Q1 agent only (do not ask the user)
24
+ ## After Q1 �?agent only (do not ask the user)
25
25
 
26
26
  | Choice | You set silently | User only provides |
27
27
  |--------|------------------|--------------------|
@@ -33,38 +33,38 @@ Never show blank Env fields for `TOMORROWOS_STORE` or `DATABASE_SSL`.
33
33
 
34
34
  ---
35
35
 
36
- ## Q2 Media (paste this entire block)
36
+ ## Q2 �?Media (paste this entire block)
37
37
 
38
38
  How should playlist media (images/videos) be stored?
39
39
 
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.**
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
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.
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.
43
43
 
44
- **3. Local disk only** — `cms-panel/uploads` or `public/uploads` (OK for quick local tests; **not** for production Vercel fleets files are ephemeral).
44
+ **3. Local disk only** �?`cms-panel/uploads` or `public/uploads` (OK for quick local tests; **not** for production Vercel fleets �?files are ephemeral).
45
45
 
46
- Reply with **1**, **2**, or **3** (or “Cloudinary / “Vercel Blob / “local”).
46
+ Reply with **1**, **2**, or **3** (or “Cloudinary�?/ “Vercel Blob�?/ “local�?.
47
47
 
48
- **Forbidden replacements (never ask these):** Vercel Blob as recommended #1; Supabase Storage; S3 / R2 / B2 / `MEDIA_S3_*`; “no media storage / disable uploads.
48
+ **Forbidden replacements (never ask these):** Vercel Blob as recommended #1; Supabase Storage; S3 / R2 / B2 / `MEDIA_S3_*`; “no media storage�?/ disable uploads.
49
49
 
50
50
  ---
51
51
 
52
- ## Q3 Brand (paste this entire block)
52
+ ## Q3 �?Brand (paste this entire block)
53
53
 
54
54
  Let’s brand your TomorrowOS experience. Choose **one**:
55
55
 
56
- **Option A Website URL (recommended if you have a site)**
56
+ **Option A �?Website URL (recommended if you have a site)**
57
57
  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.
58
+ I will **not** rebuild that website or add login �?unless you explicitly ask for login/auth later.
59
59
 
60
- **Option B Manual brand fields**
60
+ **Option B �?Manual brand fields**
61
61
  Send as many of these as you can (name + at least one colour + logo is ideal):
62
62
  1. **Product / venue name** (required)
63
63
  2. **Tagline** (optional)
64
64
  3. **Primary colour** (hex, e.g. `#FF8A3D`)
65
- 4. **Background colour** (hex page / panel background)
66
- 5. **Text colour** (hex, optional default `#0A0908`)
65
+ 4. **Background colour** (hex �?page / panel background)
66
+ 5. **Text colour** (hex, optional �?default `#0A0908`)
67
67
  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/`
68
+ 7. **Logo** �?upload SVG/PNG into the project, or a public image URL I can fetch into `./assets/`
69
69
 
70
70
  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).