@tomorrowos/sdk 0.9.58 → 0.9.60
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 +6 -6
- package/REPLIT_SETUP.md +19 -5
- package/REPLIT_UPGRADE.md +18 -5
- package/VERCEL_SETUP.md +3 -3
- package/dist/chunked-upload.d.ts +6 -1
- package/dist/chunked-upload.d.ts.map +1 -1
- package/dist/chunked-upload.js +8 -2
- package/dist/cli.js +4 -4
- package/dist/tomorrowos.d.ts.map +1 -1
- package/dist/tomorrowos.js +23 -10
- package/package.json +1 -1
- package/templates/cms-starter/package.json +2 -3
- package/templates/cms-starter/public/methods.js +50 -3
- package/templates/cms-starter-v0/README.md +2 -2
- package/templates/cms-starter-v0/cms-panel/methods.js +50 -3
- package/templates/cms-starter-v0/package.json +2 -2
package/README.md
CHANGED
|
@@ -98,9 +98,9 @@ Supported hosts: **Replit** and **Vercel / v0**.
|
|
|
98
98
|
1. Follow the host setup doc for this request (`REPLIT_SETUP.md` / `REPLIT_UPGRADE.md`, or `VERCEL_SETUP.md` + `VERCEL_QUESTIONS.md`).
|
|
99
99
|
2. On Vercel: paste Q1–Q3 **verbatim** from `VERCEL_QUESTIONS.md`; after database choice set `TOMORROWOS_STORE` and `DATABASE_SSL` yourself; after media choice set `TOMORROWOS_MEDIA` yourself.
|
|
100
100
|
3. Read `brand.schema.json` for the validated shape of `brand.json`.
|
|
101
|
-
4. Scaffold with the correct template:
|
|
102
|
-
- Replit / self-host style: `npx @tomorrowos/sdk init
|
|
103
|
-
- Vercel / v0: `npx @tomorrowos/sdk init . --hosting v0 --force` (`cms-starter-v0`)
|
|
101
|
+
4. Scaffold with the correct template (**always `@latest`**):
|
|
102
|
+
- Replit / self-host style: `npx @tomorrowos/sdk@latest init .` (default `cms-starter`)
|
|
103
|
+
- Vercel / v0: `npx @tomorrowos/sdk@latest init . --hosting v0 --force` (`cms-starter-v0`)
|
|
104
104
|
5. Apply the user’s answers — brand and use case. Validate `brand.json`. On Vercel, apply the **NON-NEGOTIABLE VERCEL RUNTIME CONTRACT** from `VERCEL_SETUP.md`.
|
|
105
105
|
6. Point the user at `PLAYER_INSTALL.md` and the Control Panel **Download Players** UI once the CMS is running.
|
|
106
106
|
|
|
@@ -115,17 +115,17 @@ Do not write WebSocket transport, pairing wire format, or platform-specific play
|
|
|
115
115
|
## 2. Build it yourself
|
|
116
116
|
|
|
117
117
|
```bash
|
|
118
|
-
npm install @tomorrowos/sdk
|
|
118
|
+
npm install @tomorrowos/sdk@latest
|
|
119
119
|
```
|
|
120
120
|
|
|
121
121
|
Scaffold, install, run:
|
|
122
122
|
|
|
123
123
|
```bash
|
|
124
124
|
# Default template (good for Replit, Railway, Render, Fly.io, self-host)
|
|
125
|
-
npx tomorrowos init my-cms
|
|
125
|
+
npx @tomorrowos/sdk@latest init my-cms
|
|
126
126
|
|
|
127
127
|
# Vercel / v0 Publish (cms-panel + vercel.json + optional Next preview)
|
|
128
|
-
npx tomorrowos init my-cms --hosting v0
|
|
128
|
+
npx @tomorrowos/sdk@latest init my-cms --hosting v0
|
|
129
129
|
|
|
130
130
|
cd my-cms
|
|
131
131
|
npm install
|
package/REPLIT_SETUP.md
CHANGED
|
@@ -18,6 +18,7 @@ If Supabase: show the SUPABASE_URL Secrets input immediately after the choice (s
|
|
|
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.
|
|
20
20
|
After all setup questions are answered, 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.
|
|
21
|
+
Seed with **`npx @tomorrowos/sdk@latest init .`** only (never untagged `npx @tomorrowos/sdk init` — that can install a stale cached version).
|
|
21
22
|
Do not skip steps.
|
|
22
23
|
```
|
|
23
24
|
|
|
@@ -108,7 +109,7 @@ Then ask **Question 1** — stay on this file.
|
|
|
108
109
|
4a. **If Supabase is chosen: never collect `SUPABASE_URL` twice.** One capture right after the choice is enough.
|
|
109
110
|
4b. **Question 3 fields are all optional.** Missing values → keep starter `brand.json` defaults; do not block or re-prompt.
|
|
110
111
|
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.
|
|
111
|
-
5. **
|
|
112
|
+
5. **Always seed with `npx @tomorrowos/sdk@latest init .`** (or merge `templates/cms-starter` from that same `@latest` install). **Never** run untagged `npx @tomorrowos/sdk init` — npx may reuse a stale cache and pin an old SDK. Do not rebuild pairing, WebSocket, or playlist APIs from scratch.
|
|
112
113
|
6. **Never commit secrets** into git. Put credentials only in Replit **Secrets** (and optionally mirror non-secret flags in `.env` that reference env vars).
|
|
113
114
|
7. After Q&A, **configure and start** — do **not** run a long test suite (see **Post-Q&A: minimal verification only**).
|
|
114
115
|
8. **Follow the Runtime & Replit publish rules below exactly.** A project that cannot Deploy / Publish on Replit is incomplete.
|
|
@@ -136,7 +137,7 @@ When scaffolding a **new** Repl:
|
|
|
136
137
|
1. On the Replit homepage, open the artifact-type dropdown and choose **Web app** (not API, not “Backend only”, not Agent & Automation-only).
|
|
137
138
|
2. **Do not** create an “API server” artifact as the only publishable output.
|
|
138
139
|
3. Describe the project as: *“TomorrowOS digital signage CMS — web Control Panel at `/` with WebSocket device pairing.”*
|
|
139
|
-
4. Run `npx @tomorrowos/sdk init
|
|
140
|
+
4. Run `npx @tomorrowos/sdk@latest init .` at the project root (or merge the cms-starter template from that install).
|
|
140
141
|
|
|
141
142
|
**Forbidden setups that cause publish failure:**
|
|
142
143
|
|
|
@@ -255,7 +256,7 @@ Allowed extras (optional): `"build-player": "tomorrowos build --platform tizen"`
|
|
|
255
256
|
"node": ">=20"
|
|
256
257
|
},
|
|
257
258
|
"dependencies": {
|
|
258
|
-
"@tomorrowos/sdk": "^0.9.
|
|
259
|
+
"@tomorrowos/sdk": "^0.9.60",
|
|
259
260
|
"dotenv": "^17.2.3",
|
|
260
261
|
"tsx": "^4.19.0"
|
|
261
262
|
},
|
|
@@ -270,7 +271,7 @@ Allowed extras (optional): `"build-player": "tomorrowos build --platform tizen"`
|
|
|
270
271
|
}
|
|
271
272
|
```
|
|
272
273
|
|
|
273
|
-
|
|
274
|
+
**IRON RULE — always latest SDK:** Scaffold only via `npx @tomorrowos/sdk@latest init .`. After init, confirm `node_modules/@tomorrowos/sdk/package.json` `"version"` matches current npm `latest` (or newer). If older, run `npm install @tomorrowos/sdk@latest` and fix `package.json` to `^<that version>`.
|
|
274
275
|
|
|
275
276
|
### Replit Run command
|
|
276
277
|
|
|
@@ -671,11 +672,21 @@ Run these steps in order. Tick mentally; do not skip.
|
|
|
671
672
|
**If creating a new Repl:** choose **Web app** on the Replit homepage before `init`. Confirm `.replit-artifact/artifact.toml` has **`kind = "web"`** after init.
|
|
672
673
|
|
|
673
674
|
```bash
|
|
675
|
+
# REQUIRED: @latest — untagged `npx @tomorrowos/sdk init` can install a stale cached SDK.
|
|
674
676
|
npx @tomorrowos/sdk@latest init .
|
|
675
677
|
# If the directory is not empty and the user confirms overwrite of starter files only:
|
|
676
678
|
# npx @tomorrowos/sdk@latest init . --force
|
|
677
679
|
```
|
|
678
680
|
|
|
681
|
+
Then verify:
|
|
682
|
+
|
|
683
|
+
```bash
|
|
684
|
+
node -p "require('./node_modules/@tomorrowos/sdk/package.json').version"
|
|
685
|
+
# or: npm ls @tomorrowos/sdk --depth=0
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
If not the current npm latest, run `npm install @tomorrowos/sdk@latest` before continuing.
|
|
689
|
+
|
|
679
690
|
If `init` is inappropriate (existing customised project), merge carefully:
|
|
680
691
|
|
|
681
692
|
- Keep `data/tomorrowos.db` and `public/uploads/`
|
|
@@ -775,6 +786,7 @@ Do **not** ask the user how to implement:
|
|
|
775
786
|
|
|
776
787
|
Do **not**:
|
|
777
788
|
|
|
789
|
+
- Run untagged `npx @tomorrowos/sdk init` (always use `npx @tomorrowos/sdk@latest init .`)
|
|
778
790
|
- Replace `@tomorrowos/sdk` with a hand-rolled server
|
|
779
791
|
- Change the production start command away from `tsx server.ts` / `npm run start`
|
|
780
792
|
- Point Supabase at Replit’s reserved `DATABASE_URL` when `SUPABASE_URL` should be used
|
|
@@ -823,7 +835,9 @@ Do **not**:
|
|
|
823
835
|
|
|
824
836
|
## Protocol version
|
|
825
837
|
|
|
826
|
-
`replit-setup/1.
|
|
838
|
+
`replit-setup/1.13` — aligned with TomorrowOS protocol `1.0` and `@tomorrowos/sdk` store drivers `sqlite` | `supabase` | `postgres` | `memory`.
|
|
839
|
+
|
|
840
|
+
**Changelog 1.13:** Seed / upgrade commands **must** use `@latest` (`npx @tomorrowos/sdk@latest init .`, `npm install @tomorrowos/sdk@latest`). Untagged `npx @tomorrowos/sdk init` is forbidden (stale npx cache pinned old SDKs such as 0.9.51).
|
|
827
841
|
|
|
828
842
|
**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).
|
|
829
843
|
|
package/REPLIT_UPGRADE.md
CHANGED
|
@@ -32,7 +32,7 @@ When any of these triggers fire:
|
|
|
32
32
|
|
|
33
33
|
## Hard rules (non-negotiable)
|
|
34
34
|
|
|
35
|
-
1. **Do NOT** run `npx @tomorrowos/sdk init
|
|
35
|
+
1. **Do NOT** run `npx @tomorrowos/sdk init`, `npx @tomorrowos/sdk@latest init`, or any `init --force` (or any command that re-scaffolds / overwrites the project from the starter). That would wipe customisations. Upgrade = **`npm install @tomorrowos/sdk@latest` only**.
|
|
36
36
|
2. **Do NOT** delete `data/` or `public/uploads/` (or move them aside permanently). Pairings, playlists, and local media must survive the upgrade.
|
|
37
37
|
3. **Do NOT** invent or rotate Secrets (`SUPABASE_URL`, Cloudinary keys, etc.). Keep existing Secrets / `.env`.
|
|
38
38
|
4. **Do NOT** blindly overwrite `public/methods.js`, `public/index.html`, `public/panel.css`, or `server.ts` with template copies. Always **diff → report → merge only with user consent** (or apply surgical patches that preserve custom code).
|
|
@@ -79,10 +79,14 @@ Record the **backup path** for the final report.
|
|
|
79
79
|
|
|
80
80
|
### 3. Install latest SDK
|
|
81
81
|
|
|
82
|
+
**REQUIRED — always `@latest`:**
|
|
83
|
+
|
|
82
84
|
```bash
|
|
83
85
|
npm install @tomorrowos/sdk@latest
|
|
84
86
|
```
|
|
85
87
|
|
|
88
|
+
Do **not** run `npm install @tomorrowos/sdk` without `@latest` (may keep a stale lockfile / range).
|
|
89
|
+
|
|
86
90
|
### 4. Verify `package.json` dependency
|
|
87
91
|
|
|
88
92
|
Confirm `dependencies["@tomorrowos/sdk"]` reflects a newer / latest range (or exact version after install).
|
|
@@ -90,10 +94,17 @@ Confirm `dependencies["@tomorrowos/sdk"]` reflects a newer / latest range (or ex
|
|
|
90
94
|
- If `package.json` did not update, set `"@tomorrowos/sdk": "^<installed version>"` (or `@latest` resolution result) and run:
|
|
91
95
|
|
|
92
96
|
```bash
|
|
93
|
-
npm install
|
|
97
|
+
npm install @tomorrowos/sdk@latest
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
- Confirm `node_modules/@tomorrowos/sdk/package.json` `"version"` is the **current npm latest** (not an older pin such as 0.9.51).
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
node -p "require('./node_modules/@tomorrowos/sdk/package.json').version"
|
|
104
|
+
npm view @tomorrowos/sdk version
|
|
94
105
|
```
|
|
95
106
|
|
|
96
|
-
|
|
107
|
+
If local version < npm latest, re-run Step 3 until they match.
|
|
97
108
|
|
|
98
109
|
Record **new SDK version** for the final report.
|
|
99
110
|
|
|
@@ -176,7 +187,7 @@ Tell the user clearly:
|
|
|
176
187
|
|
|
177
188
|
## What not to do
|
|
178
189
|
|
|
179
|
-
- ❌ `npx @tomorrowos/sdk init` / `init --force`
|
|
190
|
+
- ❌ `npx @tomorrowos/sdk init` / `npx @tomorrowos/sdk@latest init` / `init --force` (upgrade must not re-scaffold)
|
|
180
191
|
- ❌ Delete or empty `data/`, `public/uploads/`, or SQLite DB files
|
|
181
192
|
- ❌ Blind overwrite of customised panel / `server.ts`
|
|
182
193
|
- ❌ Re-run full `REPLIT_SETUP.md` questionnaire during upgrade
|
|
@@ -200,6 +211,8 @@ Tell the user clearly:
|
|
|
200
211
|
|
|
201
212
|
## Protocol version
|
|
202
213
|
|
|
203
|
-
`replit-upgrade/1.
|
|
214
|
+
`replit-upgrade/1.1` — pairs with `@tomorrowos/sdk` packages that ship `templates/cms-starter` and this file.
|
|
215
|
+
|
|
216
|
+
**Changelog 1.1:** Emphasize **`npm install @tomorrowos/sdk@latest` only**; verify against `npm view @tomorrowos/sdk version`; forbid untagged installs that leave stale pins (e.g. 0.9.51).
|
|
204
217
|
|
|
205
218
|
**Changelog 1.0:** Initial upgrade-only Agent contract: install `@latest`, backup, template diff/merge (no init, no data wipe), restart, report versions + smoke.
|
package/VERCEL_SETUP.md
CHANGED
|
@@ -39,7 +39,7 @@ Open VERCEL_QUESTIONS.md first.
|
|
|
39
39
|
For Q1, Q2, Q3: paste each question block VERBATIM. Do not rephrase options.
|
|
40
40
|
After Q1 database choice, YOU set TOMORROWOS_STORE and DATABASE_SSL. Do not ask me to fill those.
|
|
41
41
|
After Q2 media storage choice, YOU set TOMORROWOS_MEDIA. Do not ask me to fill those.
|
|
42
|
-
Run: npx @tomorrowos/sdk@
|
|
42
|
+
Run: npx @tomorrowos/sdk@latest init . --hosting v0 --force
|
|
43
43
|
Keep cms-starter-v0 layout.
|
|
44
44
|
Apply the NON-NEGOTIABLE VERCEL RUNTIME CONTRACT exactly:
|
|
45
45
|
one TomorrowOS instance only
|
|
@@ -206,7 +206,7 @@ Then ask **Question 1**.
|
|
|
206
206
|
5. **Do not invent** Cloudinary credentials, database URLs, Vercel Blob tokens, brand colours, **or LLM API keys**.
|
|
207
207
|
6. **Postgres env naming:** prefer **`SUPABASE_URL`** for Supabase; prefer **`DATABASE_URL`** for Neon.
|
|
208
208
|
7. **Always prefer pooled connection strings** (Supabase Session pooler **6543**; Neon pooled host).
|
|
209
|
-
8. **Prefer `npx @tomorrowos/sdk init --hosting v0`**. For Replit/Railway use
|
|
209
|
+
8. **Prefer `npx @tomorrowos/sdk@latest init --hosting v0`**. For Replit/Railway use `npx @tomorrowos/sdk@latest init .`. Do not rebuild pairing / WebSocket APIs. Untagged `npx @tomorrowos/sdk init` is forbidden (stale cache).
|
|
210
210
|
9. **Never commit secrets.** Use Vercel Environment Variables.
|
|
211
211
|
10. **Skip Replit-only files.**
|
|
212
212
|
11. **SQLite is not a production store on Vercel.**
|
|
@@ -225,7 +225,7 @@ Then ask **Question 1**.
|
|
|
225
225
|
## Scheme A ??Vercel Functions + WebSockets (mandatory for Production)
|
|
226
226
|
|
|
227
227
|
> **Goal:** Preview may use Next + proxy; **Publish** runs TomorrowOS as a **Vercel Function** (`api/index.ts`) on **Fluid**, with WebSocket support per [Vercel WebSockets docs](https://vercel.com/docs/functions/websockets).
|
|
228
|
-
> **Scope:** Layout + `vercel.json` + dashboard. Prefer `npx @tomorrowos/sdk init --hosting v0` (`cms-starter-v0`).
|
|
228
|
+
> **Scope:** Layout + `vercel.json` + dashboard. Prefer `npx @tomorrowos/sdk@latest init --hosting v0` (`cms-starter-v0`).
|
|
229
229
|
> **Do not** break Replit/Railway: those keep using default `init` ??`cms-starter` (root `server.ts` + `server.listen`, no `api/`).
|
|
230
230
|
|
|
231
231
|
### Why Scheme A exists
|
package/dist/chunked-upload.d.ts
CHANGED
|
@@ -11,7 +11,12 @@ export interface ChunkedUploadMeta {
|
|
|
11
11
|
received: number[];
|
|
12
12
|
}
|
|
13
13
|
export declare function isValidUploadId(uploadId: string): boolean;
|
|
14
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Chunk parts live in OS temp — not under staticRoot/public.
|
|
16
|
+
* Replit Object Storage mounts often reject or "Not found" writes to
|
|
17
|
+
* ad-hoc dirs like public/.upload-tmp while still allowing public/uploads.
|
|
18
|
+
*/
|
|
19
|
+
export declare function chunkedUploadRoot(_staticRoot?: string): string;
|
|
15
20
|
export declare function chunkedUploadDir(staticRoot: string, uploadId: string): string;
|
|
16
21
|
export declare function initChunkedUpload(staticRoot: string, options: {
|
|
17
22
|
filename: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chunked-upload.d.ts","sourceRoot":"","sources":["../src/chunked-upload.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"chunked-upload.d.ts","sourceRoot":"","sources":["../src/chunked-upload.ts"],"names":[],"mappings":"AAKA,gFAAgF;AAChF,eAAO,MAAM,wBAAwB,QAAkB,CAAC;AAIxD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAK7E;AAED,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7D,OAAO,CAAC,iBAAiB,CAAC,CA2B5B;AAED,wBAAsB,qBAAqB,CACzC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,iBAAiB,CAAC,CAI5B;AAUD,wBAAsB,sBAAsB,CAC1C,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,iBAAiB,CAAC,CA0B5B;AAED,wBAAsB,qBAAqB,CACzC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,iBAAiB,CAAA;CAAE,CAAC,CAiCvF;AAED,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CAIf;AAED,gEAAgE;AAChE,wBAAsB,0BAA0B,CAC9C,UAAU,EAAE,MAAM,EAClB,QAAQ,SAAqB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAuBf"}
|
package/dist/chunked-upload.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "crypto";
|
|
2
2
|
import fs from "fs/promises";
|
|
3
|
+
import os from "os";
|
|
3
4
|
import path from "path";
|
|
4
5
|
/** Keep each proxy request well under typical host/body limits (Replit 413). */
|
|
5
6
|
export const MEDIA_UPLOAD_CHUNK_BYTES = 1 * 1024 * 1024;
|
|
@@ -7,8 +8,13 @@ const UPLOAD_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3
|
|
|
7
8
|
export function isValidUploadId(uploadId) {
|
|
8
9
|
return UPLOAD_ID_RE.test(String(uploadId || "").trim());
|
|
9
10
|
}
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Chunk parts live in OS temp — not under staticRoot/public.
|
|
13
|
+
* Replit Object Storage mounts often reject or "Not found" writes to
|
|
14
|
+
* ad-hoc dirs like public/.upload-tmp while still allowing public/uploads.
|
|
15
|
+
*/
|
|
16
|
+
export function chunkedUploadRoot(_staticRoot) {
|
|
17
|
+
return path.join(os.tmpdir(), "tomorrowos-upload-tmp");
|
|
12
18
|
}
|
|
13
19
|
export function chunkedUploadDir(staticRoot, uploadId) {
|
|
14
20
|
if (!isValidUploadId(uploadId)) {
|
package/dist/cli.js
CHANGED
|
@@ -201,10 +201,10 @@ Options:
|
|
|
201
201
|
--force With init: copy into a non-empty directory
|
|
202
202
|
|
|
203
203
|
Examples:
|
|
204
|
-
npx @tomorrowos/sdk init
|
|
205
|
-
npx @tomorrowos/sdk init ./my-cms --hosting replit
|
|
206
|
-
npx @tomorrowos/sdk init ./my-cms --hosting v0
|
|
207
|
-
npx @tomorrowos/sdk migrate --from sqlite --from-sqlite ./data/tomorrowos.db --to supabase --to-database-url "$DATABASE_URL"
|
|
204
|
+
npx @tomorrowos/sdk@latest init .
|
|
205
|
+
npx @tomorrowos/sdk@latest init ./my-cms --hosting replit
|
|
206
|
+
npx @tomorrowos/sdk@latest init ./my-cms --hosting v0
|
|
207
|
+
npx @tomorrowos/sdk@latest migrate --from sqlite --from-sqlite ./data/tomorrowos.db --to supabase --to-database-url "$DATABASE_URL"
|
|
208
208
|
`);
|
|
209
209
|
}
|
|
210
210
|
function printMigrateHelp() {
|
package/dist/tomorrowos.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tomorrowos.d.ts","sourceRoot":"","sources":["../src/tomorrowos.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"tomorrowos.d.ts","sourceRoot":"","sources":["../src/tomorrowos.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEtC,OAAO,IAAI,MAAM,MAAM,CAAC;AAaxB,OAAO,EAAE,eAAe,EAAE,KAAK,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAChF,OAAO,KAAK,EACV,gBAAgB,EAKhB,eAAe,EAEhB,MAAM,kBAAkB,CAAC;AAkD1B,YAAY,EACV,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,aAAa,EACd,MAAM,oBAAoB,CAAC;AAE5B,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,eAAe,CAAC;IACvB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,eAAe,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAoED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,2EAA2E;IAC3E,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,kBAAkB,EAAE,OAAO,CAAC;IAC5B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,mEAAmE;IACnE,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,kBAAkB,EAAE,KAAK,CAAC;QACxB,UAAU,EAAE,MAAM,CAAC;QACnB,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,EAAE;YACT,SAAS,CAAC,EAAE,MAAM,CAAC;YACnB,OAAO,CAAC,EAAE,MAAM,CAAC;YACjB,KAAK,CAAC,EAAE,MAAM,CAAC;YACf,GAAG,CAAC,EAAE,MAAM,CAAC;SACd,CAAC;KACH,CAAC,CAAC;IACH,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,gBAAgB,EAAE;QAChB,GAAG,EAAE,MAAM,CAAC;QACZ,UAAU,EAAE,MAAM,CAAC;KACpB,GAAG,IAAI,CAAC;IACT,2DAA2D;IAC3D,UAAU,EAAE,gBAAgB,GAAG,IAAI,CAAC;CACrC;AAoBD,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAoMD,qBAAa,UAAW,SAAQ,YAAY;IAC1C,QAAQ,CAAC,KAAK,EAAE,eAAe,CAAC;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAkB;IACxC,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAC;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmC;IAC3D,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAsC;IACxE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAuC;IAClE,yFAAyF;IACzF,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAA6B;IACnE,OAAO,CAAC,mBAAmB,CAA+C;IAC1E,OAAO,CAAC,UAAU,CAA4B;IAC9C,OAAO,CAAC,GAAG,CAAgC;IAC3C,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,eAAe,CAAgB;IACvC,wFAAwF;IACxF,OAAO,CAAC,eAAe,CAAuB;IAC9C,+DAA+D;IAC/D,OAAO,CAAC,UAAU,CAAuB;gBAE7B,OAAO,EAAE,iBAAiB;IAOtC,yFAAyF;IACnF,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QACxD,MAAM,EAAE,OAAO,CAAC;QAChB,MAAM,CAAC,EAAE,iBAAiB,CAAC,QAAQ,CAAC,CAAC;KACtC,CAAC;YAkBY,iBAAiB;IAY/B,6EAA6E;IACvE,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAmFhE,6EAA6E;IACvE,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC;IAuCvF,OAAO;uBACU,MAAM;sBA5HgC,MAAM;;2BA6HxC,MAAM;sBAzCgC,MAAM;sBAAY,OAAO;;MA0ClF;IAEF,kFAAkF;IAC5E,WAAW,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAgF9C;;;OAGG;IACG,mBAAmB,CACvB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,OAAO,GAClB,OAAO,CAAC;QACT,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,gBAAgB,CAAC;QAC7B,MAAM,EAAE,OAAO,CAAC;KACjB,CAAC;IAqBF;;;OAGG;IACG,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QACrD,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,IAAI,CAAC;QACjB,MAAM,EAAE,OAAO,CAAC;KACjB,CAAC;YAkBY,sBAAsB;IAsB9B,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC;QACjE,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IAyBF,0FAA0F;YAC5E,2BAA2B;YAW3B,0BAA0B;IAYxC,qFAAqF;YACvE,8BAA8B;IAY5C,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,aAAa;IAMrB,OAAO,CAAC,iBAAiB;IAKzB;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,oBAAoB;IAM5B,OAAO,CAAC,yBAAyB;IAUjC,OAAO,CAAC,0BAA0B;IAkBlC,gFAAgF;YAClE,uBAAuB;IAarC,OAAO,CAAC,mBAAmB;IAgB3B,OAAO,CAAC,uBAAuB;IAU/B,OAAO,CAAC,gBAAgB;YAoBV,+BAA+B;YAsC/B,iBAAiB;YASjB,iBAAiB;YAqCjB,kBAAkB;IAUhC,uFAAuF;IACvF,OAAO,CAAC,kBAAkB;YAoBZ,gBAAgB;YAMhB,uBAAuB;IA0CrC,MAAM,CAAC,QAAQ,EAAE,MAAM;oBAGD,CAAC,oBACT,MAAM,WACN,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAAC;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;;IAU5E,OAAO,CAAC,mBAAmB;IA6D3B,6EAA6E;IAC7E,OAAO,CAAC,kBAAkB;IAO1B,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,CAAC,MAAM;YAqE7B,iBAAiB;YAgEjB,qBAAqB;YA6BrB,mBAAmB;YA8FnB,qBAAqB;YAmCrB,sBAAsB;YAoCtB,yBAAyB;YAwDzB,yBAAyB;YA+CzB,uBAAuB;IA6ErC,OAAO,CAAC,oBAAoB;YASd,yBAAyB;YAQzB,4BAA4B;YAa5B,2CAA2C;YAO3C,iCAAiC;YA6BjC,wBAAwB;YAiBxB,cAAc;IA8E5B,OAAO,CAAC,uBAAuB;IAO/B,OAAO,CAAC,mBAAmB;YAOb,yBAAyB;YAwBzB,oBAAoB;YAkDpB,yBAAyB;YAsBzB,uBAAuB;YAcvB,UAAU;YAsbV,gBAAgB;IAqG9B,OAAO,CAAC,iBAAiB;IAWzB,OAAO,CAAC,gBAAgB;CA2KzB"}
|
package/dist/tomorrowos.js
CHANGED
|
@@ -1522,14 +1522,18 @@ export class TomorrowOS extends EventEmitter {
|
|
|
1522
1522
|
async handleHttp(req, res) {
|
|
1523
1523
|
try {
|
|
1524
1524
|
const url = new URL(req.url ?? "/", "http://localhost");
|
|
1525
|
-
const
|
|
1525
|
+
const method = String(req.method || "GET").toUpperCase();
|
|
1526
|
+
let pathname = url.pathname || "/";
|
|
1527
|
+
if (pathname.length > 1 && pathname.endsWith("/")) {
|
|
1528
|
+
pathname = pathname.slice(0, -1);
|
|
1529
|
+
}
|
|
1526
1530
|
/** Same-origin brand for TomorrowOS players (e.g. GET /brand.json + applyBrand). */
|
|
1527
|
-
if (
|
|
1531
|
+
if (method === "GET" && pathname === "/brand.json") {
|
|
1528
1532
|
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
1529
1533
|
res.end(JSON.stringify(this.brand));
|
|
1530
1534
|
return;
|
|
1531
1535
|
}
|
|
1532
|
-
if (
|
|
1536
|
+
if (method === "GET" && pathname === "/status") {
|
|
1533
1537
|
const report = await buildServerStatus({
|
|
1534
1538
|
store: this.store,
|
|
1535
1539
|
staticRoot: this.staticRoot
|
|
@@ -1541,31 +1545,40 @@ export class TomorrowOS extends EventEmitter {
|
|
|
1541
1545
|
});
|
|
1542
1546
|
return;
|
|
1543
1547
|
}
|
|
1544
|
-
if (
|
|
1548
|
+
if (method === "GET" && pathname === "/media/upload-capabilities") {
|
|
1549
|
+
sendJson(res, 200, {
|
|
1550
|
+
status: "success",
|
|
1551
|
+
chunked: Boolean(this.staticRoot),
|
|
1552
|
+
chunkSize: MEDIA_UPLOAD_CHUNK_BYTES,
|
|
1553
|
+
cloudinary: Boolean(resolveCloudinaryConfig())
|
|
1554
|
+
});
|
|
1555
|
+
return;
|
|
1556
|
+
}
|
|
1557
|
+
if (method === "POST" && pathname === "/media/upload") {
|
|
1545
1558
|
await this.handleMediaUpload(req, res, url);
|
|
1546
1559
|
return;
|
|
1547
1560
|
}
|
|
1548
|
-
if (
|
|
1561
|
+
if (method === "GET" && pathname === "/media/upload-sign") {
|
|
1549
1562
|
await this.handleMediaUploadSign(req, res, url);
|
|
1550
1563
|
return;
|
|
1551
1564
|
}
|
|
1552
|
-
if (
|
|
1565
|
+
if (method === "POST" && pathname === "/media/register") {
|
|
1553
1566
|
await this.handleMediaRegister(req, res);
|
|
1554
1567
|
return;
|
|
1555
1568
|
}
|
|
1556
|
-
if (
|
|
1569
|
+
if (method === "POST" && pathname === "/media/upload-init") {
|
|
1557
1570
|
await this.handleMediaUploadInit(req, res);
|
|
1558
1571
|
return;
|
|
1559
1572
|
}
|
|
1560
|
-
if (
|
|
1573
|
+
if (method === "POST" && pathname === "/media/upload-chunk") {
|
|
1561
1574
|
await this.handleMediaUploadChunk(req, res, url);
|
|
1562
1575
|
return;
|
|
1563
1576
|
}
|
|
1564
|
-
if (
|
|
1577
|
+
if (method === "POST" && pathname === "/media/upload-complete") {
|
|
1565
1578
|
await this.handleMediaUploadComplete(req, res);
|
|
1566
1579
|
return;
|
|
1567
1580
|
}
|
|
1568
|
-
if (
|
|
1581
|
+
if (method === "GET" && pathname === "/players/brightsign.zip") {
|
|
1569
1582
|
try {
|
|
1570
1583
|
const cmsOrigin = resolveRequestCmsOrigin(req);
|
|
1571
1584
|
const zipBytes = await buildBrightSignZipWithCmsEndpoint(cmsOrigin);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tomorrowos/sdk",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.60",
|
|
4
4
|
"description": "TomorrowOS CMS server SDK - WebSocket transport, pairing, device commands, optional static CMS UI. Includes CLI (tomorrowos init / build) and starter templates.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "my-cms",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.60",
|
|
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.60",
|
|
17
17
|
"dotenv": "^17.2.3",
|
|
18
18
|
"tsx": "^4.19.0"
|
|
19
19
|
},
|
|
@@ -22,4 +22,3 @@
|
|
|
22
22
|
"typescript": "^5.5.0"
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
|
-
|
|
@@ -1843,6 +1843,16 @@ function xhrSendBinary(url, body, { timeoutMs, onProgress, contentType }) {
|
|
|
1843
1843
|
});
|
|
1844
1844
|
}
|
|
1845
1845
|
|
|
1846
|
+
async function fetchUploadCapabilities() {
|
|
1847
|
+
try {
|
|
1848
|
+
const res = await fetch("/media/upload-capabilities", { cache: "no-store" });
|
|
1849
|
+
if (!res.ok) return null;
|
|
1850
|
+
return await res.json();
|
|
1851
|
+
} catch {
|
|
1852
|
+
return null;
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1846
1856
|
async function uploadViaChunkedProxy(file, onProgress) {
|
|
1847
1857
|
const initRes = await fetch("/media/upload-init", {
|
|
1848
1858
|
method: "POST",
|
|
@@ -1859,12 +1869,17 @@ async function uploadViaChunkedProxy(file, onProgress) {
|
|
|
1859
1869
|
} catch {
|
|
1860
1870
|
initPayload = {};
|
|
1861
1871
|
}
|
|
1872
|
+
if (initRes.status === 404 || /not found/i.test(String(initPayload.error || ""))) {
|
|
1873
|
+
const err = new Error(initPayload.error || "Not found");
|
|
1874
|
+
err.code = "CHUNKED_UNAVAILABLE";
|
|
1875
|
+
throw err;
|
|
1876
|
+
}
|
|
1862
1877
|
if (!initRes.ok || initPayload.status === "failed" || !initPayload.uploadId) {
|
|
1863
1878
|
throw new Error(initPayload.error || `Upload init failed (${initRes.status})`);
|
|
1864
1879
|
}
|
|
1865
1880
|
|
|
1866
1881
|
const uploadId = String(initPayload.uploadId);
|
|
1867
|
-
const chunkSize = Number(initPayload.chunkSize) ||
|
|
1882
|
+
const chunkSize = Number(initPayload.chunkSize) || 1 * 1024 * 1024;
|
|
1868
1883
|
const totalChunks =
|
|
1869
1884
|
Number(initPayload.totalChunks) || Math.ceil(file.size / chunkSize);
|
|
1870
1885
|
let uploadedBytes = 0;
|
|
@@ -1916,6 +1931,24 @@ async function uploadViaChunkedProxy(file, onProgress) {
|
|
|
1916
1931
|
return completePayload;
|
|
1917
1932
|
}
|
|
1918
1933
|
|
|
1934
|
+
async function uploadViaSingleProxy(file, onProgress) {
|
|
1935
|
+
let lastErr = null;
|
|
1936
|
+
for (let attempt = 1; attempt <= UPLOAD_MAX_RETRIES; attempt += 1) {
|
|
1937
|
+
try {
|
|
1938
|
+
return await uploadFileWithProgress(file, { onProgress, attempt });
|
|
1939
|
+
} catch (err) {
|
|
1940
|
+
lastErr = err;
|
|
1941
|
+
const msg = err?.message || String(err);
|
|
1942
|
+
if (/Upload failed \(413\)|USE_CHUNKED|too large for single-request/i.test(msg)) {
|
|
1943
|
+
break;
|
|
1944
|
+
}
|
|
1945
|
+
if (attempt >= UPLOAD_MAX_RETRIES) break;
|
|
1946
|
+
await new Promise((resolve) => setTimeout(resolve, 500 * attempt));
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
throw lastErr || new Error("Upload failed");
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1919
1952
|
async function uploadFile(file, onProgress) {
|
|
1920
1953
|
try {
|
|
1921
1954
|
const direct = await uploadViaCloudinaryDirect(file, onProgress);
|
|
@@ -1928,8 +1961,22 @@ async function uploadFile(file, onProgress) {
|
|
|
1928
1961
|
}
|
|
1929
1962
|
}
|
|
1930
1963
|
|
|
1931
|
-
|
|
1932
|
-
|
|
1964
|
+
const caps = await fetchUploadCapabilities();
|
|
1965
|
+
const chunkedSupported = caps?.chunked === true;
|
|
1966
|
+
|
|
1967
|
+
if (chunkedSupported || file.size > PROXY_SINGLE_MAX_BYTES) {
|
|
1968
|
+
try {
|
|
1969
|
+
return await uploadViaChunkedProxy(file, onProgress);
|
|
1970
|
+
} catch (err) {
|
|
1971
|
+
if (err?.code === "CHUNKED_UNAVAILABLE" || /not found/i.test(err?.message || "")) {
|
|
1972
|
+
// Panel JS is newer than the CMS server — fall back so uploads work again.
|
|
1973
|
+
return uploadViaSingleProxy(file, onProgress);
|
|
1974
|
+
}
|
|
1975
|
+
throw err;
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
|
|
1979
|
+
return uploadViaSingleProxy(file, onProgress);
|
|
1933
1980
|
}
|
|
1934
1981
|
|
|
1935
1982
|
async function addAssetFromFile(file, queueIndex, queueTotal) {
|
|
@@ -13,7 +13,7 @@ This starter is for **Vercel Publish** and **v0**. It follows
|
|
|
13
13
|
## Scaffold
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
npx @tomorrowos/sdk init my-cms --hosting v0
|
|
16
|
+
npx @tomorrowos/sdk@latest init my-cms --hosting v0
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
## Local development
|
|
@@ -46,5 +46,5 @@ TV endpoint: `https://YOUR.vercel.app/` (players also try `/api` on `*.vercel.ap
|
|
|
46
46
|
Use the default starter (unchanged Node listen, no `api/` Function):
|
|
47
47
|
|
|
48
48
|
```bash
|
|
49
|
-
npx @tomorrowos/sdk init my-cms --hosting replit
|
|
49
|
+
npx @tomorrowos/sdk@latest init my-cms --hosting replit
|
|
50
50
|
```
|
|
@@ -1843,6 +1843,16 @@ function xhrSendBinary(url, body, { timeoutMs, onProgress, contentType }) {
|
|
|
1843
1843
|
});
|
|
1844
1844
|
}
|
|
1845
1845
|
|
|
1846
|
+
async function fetchUploadCapabilities() {
|
|
1847
|
+
try {
|
|
1848
|
+
const res = await fetch("/media/upload-capabilities", { cache: "no-store" });
|
|
1849
|
+
if (!res.ok) return null;
|
|
1850
|
+
return await res.json();
|
|
1851
|
+
} catch {
|
|
1852
|
+
return null;
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1846
1856
|
async function uploadViaChunkedProxy(file, onProgress) {
|
|
1847
1857
|
const initRes = await fetch("/media/upload-init", {
|
|
1848
1858
|
method: "POST",
|
|
@@ -1859,12 +1869,17 @@ async function uploadViaChunkedProxy(file, onProgress) {
|
|
|
1859
1869
|
} catch {
|
|
1860
1870
|
initPayload = {};
|
|
1861
1871
|
}
|
|
1872
|
+
if (initRes.status === 404 || /not found/i.test(String(initPayload.error || ""))) {
|
|
1873
|
+
const err = new Error(initPayload.error || "Not found");
|
|
1874
|
+
err.code = "CHUNKED_UNAVAILABLE";
|
|
1875
|
+
throw err;
|
|
1876
|
+
}
|
|
1862
1877
|
if (!initRes.ok || initPayload.status === "failed" || !initPayload.uploadId) {
|
|
1863
1878
|
throw new Error(initPayload.error || `Upload init failed (${initRes.status})`);
|
|
1864
1879
|
}
|
|
1865
1880
|
|
|
1866
1881
|
const uploadId = String(initPayload.uploadId);
|
|
1867
|
-
const chunkSize = Number(initPayload.chunkSize) ||
|
|
1882
|
+
const chunkSize = Number(initPayload.chunkSize) || 1 * 1024 * 1024;
|
|
1868
1883
|
const totalChunks =
|
|
1869
1884
|
Number(initPayload.totalChunks) || Math.ceil(file.size / chunkSize);
|
|
1870
1885
|
let uploadedBytes = 0;
|
|
@@ -1916,6 +1931,24 @@ async function uploadViaChunkedProxy(file, onProgress) {
|
|
|
1916
1931
|
return completePayload;
|
|
1917
1932
|
}
|
|
1918
1933
|
|
|
1934
|
+
async function uploadViaSingleProxy(file, onProgress) {
|
|
1935
|
+
let lastErr = null;
|
|
1936
|
+
for (let attempt = 1; attempt <= UPLOAD_MAX_RETRIES; attempt += 1) {
|
|
1937
|
+
try {
|
|
1938
|
+
return await uploadFileWithProgress(file, { onProgress, attempt });
|
|
1939
|
+
} catch (err) {
|
|
1940
|
+
lastErr = err;
|
|
1941
|
+
const msg = err?.message || String(err);
|
|
1942
|
+
if (/Upload failed \(413\)|USE_CHUNKED|too large for single-request/i.test(msg)) {
|
|
1943
|
+
break;
|
|
1944
|
+
}
|
|
1945
|
+
if (attempt >= UPLOAD_MAX_RETRIES) break;
|
|
1946
|
+
await new Promise((resolve) => setTimeout(resolve, 500 * attempt));
|
|
1947
|
+
}
|
|
1948
|
+
}
|
|
1949
|
+
throw lastErr || new Error("Upload failed");
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1919
1952
|
async function uploadFile(file, onProgress) {
|
|
1920
1953
|
try {
|
|
1921
1954
|
const direct = await uploadViaCloudinaryDirect(file, onProgress);
|
|
@@ -1928,8 +1961,22 @@ async function uploadFile(file, onProgress) {
|
|
|
1928
1961
|
}
|
|
1929
1962
|
}
|
|
1930
1963
|
|
|
1931
|
-
|
|
1932
|
-
|
|
1964
|
+
const caps = await fetchUploadCapabilities();
|
|
1965
|
+
const chunkedSupported = caps?.chunked === true;
|
|
1966
|
+
|
|
1967
|
+
if (chunkedSupported || file.size > PROXY_SINGLE_MAX_BYTES) {
|
|
1968
|
+
try {
|
|
1969
|
+
return await uploadViaChunkedProxy(file, onProgress);
|
|
1970
|
+
} catch (err) {
|
|
1971
|
+
if (err?.code === "CHUNKED_UNAVAILABLE" || /not found/i.test(err?.message || "")) {
|
|
1972
|
+
// Panel JS is newer than the CMS server — fall back so uploads work again.
|
|
1973
|
+
return uploadViaSingleProxy(file, onProgress);
|
|
1974
|
+
}
|
|
1975
|
+
throw err;
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
|
|
1979
|
+
return uploadViaSingleProxy(file, onProgress);
|
|
1933
1980
|
}
|
|
1934
1981
|
|
|
1935
1982
|
async function addAssetFromFile(file, queueIndex, queueTotal) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "my-cms",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.60",
|
|
4
4
|
"description": "TomorrowOS CMS for Vercel / v0 Publish (Fluid Function + WebSockets + optional Next Preview).",
|
|
5
5
|
"private": true,
|
|
6
6
|
"type": "module",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"build-player": "tomorrowos build --platform tizen"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@tomorrowos/sdk": "^0.9.
|
|
20
|
+
"@tomorrowos/sdk": "^0.9.60",
|
|
21
21
|
"dotenv": "^17.2.3",
|
|
22
22
|
"tsx": "^4.19.0"
|
|
23
23
|
},
|