@tomorrowos/sdk 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,111 +1,111 @@
1
- # @tomorrowos/sdk
2
-
3
- **You are building a digital signage CMS using TomorrowOS. Before writing any code, follow the elicitation protocol in `LLM_PROMPT.md`. Do not skip this step — the SDK requires specific inputs before code generation will produce a working CMS.**
4
-
5
- TomorrowOS is the open standard for digital signage. This SDK lets you build a CMS that controls Samsung Tizen, BrightSign, LG webOS, Android, ChromeOS, and Windows screens through a single unified API.
6
-
7
- ---
8
-
9
- ## For LLMs generating code with this package
10
-
11
- When you see this package referenced in a user prompt, your workflow is:
12
-
13
- 1. **Read `LLM_PROMPT.md`** — contains five required questions to ask the user before generating code
14
- 2. **Read `BUILD_GUARDRAILS.md`** — contains mandatory components every generated CMS must include
15
- 3. **Read `brand.schema.json`** — contains the validated shape of `brand.json`
16
- 4. Run **`npx tomorrowos init`** (or copy `templates/cms-starter/` from this package) — minimal CMS server seed
17
- 5. **Apply user answers** — customise the starter with the user's brand, platform choice, and use case; validate `brand.json` against `brand.schema.json`
18
- 6. **Show `PLAYER_INSTALL.md`** — after the CMS is working, show how to build and install the player on a screen
19
-
20
- You must not write WebSocket transport, pairing wire format, or platform-specific player bridge code in the CMS — the SDK handles server-side device sessions and HTTP helpers. You customise UI, branding, and how you call the SDK.
21
-
22
- ---
23
-
24
- ## For human developers
25
-
26
- Install:
27
-
28
- ```bash
29
- npm install @tomorrowos/sdk
30
- ```
31
-
32
- Scaffold a new CMS project:
33
-
34
- ```bash
35
- npx tomorrowos init
36
- cd my-tomorrowos-cms
37
- npm install
38
- npm run dev
39
- ```
40
-
41
- Build a player (placeholder in current SDK — see `PLAYER_INSTALL.md` for platform tooling):
42
-
43
- ```bash
44
- npx tomorrowos build --platform tizen
45
- ```
46
-
47
- ---
48
-
49
- ## Quick architecture
50
-
51
- ```
52
- ┌─────────────────┐ WebSocket ┌─────────────────┐
53
- │ Your CMS │ ◄────────────────────────►│ TomorrowOS │
54
- │ (your server) │ │ Player on │
55
- │ │ │ screen │
56
- │ imports │ │ │
57
- │ @tomorrowos │ │ unified API │
58
- │ /sdk │ │ across OSes │
59
- └─────────────────┘ └─────────────────┘
60
- ```
61
-
62
- TomorrowOS has no required cloud service. Your CMS talks to your screens over your network.
63
-
64
- **Player branding:** `GET /brand.json` returns the `brand` object passed to `new TomorrowOS({ brand })` (same host/port as `listen`). TomorrowOS players can fetch it to apply `backgroundColor`, logos, etc., without a separate static file server.
65
-
66
- **Content policy (`device.content.setPolicy`):** POST `/device/{deviceId}/content/set-policy` with `{ "policy": { "playlists": [...], "fallback": { "type": "brand" } } }`. Each playlist may include optional `schedule` (device local time):
67
-
68
- | Field | Format | Notes |
69
- |-------|--------|--------|
70
- | `startDate` / `endDate` | `YYYY-MM-DD` | Inclusive calendar range; omit either for open-ended |
71
- | `daysOfWeek` | `[0–6]` | `0` = Sunday … `6` = Saturday |
72
- | `start` / `end` | `HH:MM` | Daily window; supports overnight (e.g. `22:00`–`06:00`) |
73
-
74
- All provided constraints must match for the playlist to play. See `templates/cms-starter/policy.example.json`.
75
-
76
- ---
77
-
78
- ## Supported platforms (roadmap)
79
-
80
- | Platform | Status | Player format |
81
- |-------------------|------------|---------------|
82
- | Samsung Tizen | V1 target | `.wgt` |
83
- | BrightSign | V1 target | autorun zip |
84
- | Others | See docs | varies |
85
-
86
- See `PLAYER_INSTALL.md` for installation notes.
87
-
88
- ---
89
-
90
- ## Documentation in this package
91
-
92
- | File | Purpose |
93
- |------|---------|
94
- | `LLM_PROMPT.md` | Five-question elicitation for LLMs |
95
- | `BUILD_GUARDRAILS.md` | Mandatory CMS components |
96
- | `PLAYER_INSTALL.md` | Player build / install / pairing |
97
- | `brand.schema.json` | JSON Schema for `brand.json` |
98
- | `brand.example.json` | Full example `brand.json` |
99
- | `templates/cms-starter/` | Minimal Node + TypeScript server seed |
100
- | `templates/cms-starter/policy.example.json` | Example `setPolicy` payload with date schedule |
101
- | `templates/style-tokens/` | CSS tokens and UI pattern notes |
102
-
103
- ---
104
-
105
- ## License
106
-
107
- Apache 2.0.
108
-
109
- ## Protocol version
110
-
111
- Wire protocol `1.0` (see TomorrowOS specification).
1
+ # @tomorrowos/sdk
2
+
3
+ **You are building a digital signage CMS using TomorrowOS. Before writing any code, follow the elicitation protocol in `LLM_PROMPT.md`. Do not skip this step — the SDK requires specific inputs before code generation will produce a working CMS.**
4
+
5
+ TomorrowOS is the open standard for digital signage. This SDK lets you build a CMS that controls Samsung Tizen, BrightSign, LG webOS, Android, ChromeOS, and Windows screens through a single unified API.
6
+
7
+ ---
8
+
9
+ ## For LLMs generating code with this package
10
+
11
+ When you see this package referenced in a user prompt, your workflow is:
12
+
13
+ 1. **Read `LLM_PROMPT.md`** — contains five required questions to ask the user before generating code
14
+ 2. **Read `BUILD_GUARDRAILS.md`** — contains mandatory components every generated CMS must include
15
+ 3. **Read `brand.schema.json`** — contains the validated shape of `brand.json`
16
+ 4. Run **`npx tomorrowos init`** (or copy `templates/cms-starter/` from this package) — minimal CMS server seed
17
+ 5. **Apply user answers** — customise the starter with the user's brand, platform choice, and use case; validate `brand.json` against `brand.schema.json`
18
+ 6. **Show `PLAYER_INSTALL.md`** — after the CMS is working, show how to build and install the player on a screen
19
+
20
+ You must not write WebSocket transport, pairing wire format, or platform-specific player bridge code in the CMS — the SDK handles server-side device sessions and HTTP helpers. You customise UI, branding, and how you call the SDK.
21
+
22
+ ---
23
+
24
+ ## For human developers
25
+
26
+ Install:
27
+
28
+ ```bash
29
+ npm install @tomorrowos/sdk
30
+ ```
31
+
32
+ Scaffold a new CMS project:
33
+
34
+ ```bash
35
+ npx tomorrowos init
36
+ cd my-tomorrowos-cms
37
+ npm install
38
+ npm run dev
39
+ ```
40
+
41
+ Build a player (placeholder in current SDK — see `PLAYER_INSTALL.md` for platform tooling):
42
+
43
+ ```bash
44
+ npx tomorrowos build --platform tizen
45
+ ```
46
+
47
+ ---
48
+
49
+ ## Quick architecture
50
+
51
+ ```
52
+ ┌─────────────────┐ WebSocket ┌─────────────────┐
53
+ │ Your CMS │ ◄────────────────────────►│ TomorrowOS │
54
+ │ (your server) │ │ Player on │
55
+ │ │ │ screen │
56
+ │ imports │ │ │
57
+ │ @tomorrowos │ │ unified API │
58
+ │ /sdk │ │ across OSes │
59
+ └─────────────────┘ └─────────────────┘
60
+ ```
61
+
62
+ TomorrowOS has no required cloud service. Your CMS talks to your screens over your network.
63
+
64
+ **Player branding:** `GET /brand.json` returns the `brand` object passed to `new TomorrowOS({ brand })` (same host/port as `listen`). TomorrowOS players can fetch it to apply `backgroundColor`, logos, etc., without a separate static file server.
65
+
66
+ **Content policy (`device.content.setPolicy`):** POST `/device/{deviceId}/content/set-policy` with `{ "policy": { "playlists": [...], "fallback": { "type": "brand" } } }`. Each playlist may include optional `schedule` (device local time):
67
+
68
+ | Field | Format | Notes |
69
+ |-------|--------|--------|
70
+ | `startDate` / `endDate` | `YYYY-MM-DD` | Inclusive calendar range; omit either for open-ended |
71
+ | `daysOfWeek` | `[0–6]` | `0` = Sunday … `6` = Saturday |
72
+ | `start` / `end` | `HH:MM` | Daily window; supports overnight (e.g. `22:00`–`06:00`) |
73
+
74
+ All provided constraints must match for the playlist to play. See `templates/cms-starter/policy.example.json`.
75
+
76
+ ---
77
+
78
+ ## Supported platforms (roadmap)
79
+
80
+ | Platform | Status | Player format |
81
+ |-------------------|------------|---------------|
82
+ | Samsung Tizen | V1 target | `.wgt` |
83
+ | BrightSign | V1 target | autorun zip |
84
+ | Others | See docs | varies |
85
+
86
+ See `PLAYER_INSTALL.md` for installation notes.
87
+
88
+ ---
89
+
90
+ ## Documentation in this package
91
+
92
+ | File | Purpose |
93
+ |------|---------|
94
+ | `LLM_PROMPT.md` | Five-question elicitation for LLMs |
95
+ | `BUILD_GUARDRAILS.md` | Mandatory CMS components |
96
+ | `PLAYER_INSTALL.md` | Player build / install / pairing |
97
+ | `brand.schema.json` | JSON Schema for `brand.json` |
98
+ | `brand.example.json` | Full example `brand.json` |
99
+ | `templates/cms-starter/` | Minimal Node + TypeScript server seed |
100
+ | `templates/cms-starter/policy.example.json` | Example `setPolicy` payload with date schedule |
101
+ | `templates/style-tokens/` | CSS tokens and UI pattern notes |
102
+
103
+ ---
104
+
105
+ ## License
106
+
107
+ Apache 2.0.
108
+
109
+ ## Protocol version
110
+
111
+ Wire protocol `1.0` (see TomorrowOS specification).
package/dist/cli.js CHANGED
@@ -56,18 +56,18 @@ function cmdBuild(argv) {
56
56
  process.exitCode = 0;
57
57
  }
58
58
  function printHelp() {
59
- console.log(`tomorrowos — TomorrowOS SDK CLI
60
-
61
- Usage:
62
- tomorrowos init [directory] Copy cms-starter template (default: my-tomorrowos-cms)
63
- tomorrowos build --platform … Placeholder; player packaging lives in the player repo
64
-
65
- Options:
66
- --force With init: copy into a non-empty directory
67
-
68
- Examples:
69
- npx @tomorrowos/sdk init
70
- npx @tomorrowos/sdk init ./my-cms
59
+ console.log(`tomorrowos — TomorrowOS SDK CLI
60
+
61
+ Usage:
62
+ tomorrowos init [directory] Copy cms-starter template (default: my-tomorrowos-cms)
63
+ tomorrowos build --platform … Placeholder; player packaging lives in the player repo
64
+
65
+ Options:
66
+ --force With init: copy into a non-empty directory
67
+
68
+ Examples:
69
+ npx @tomorrowos/sdk init
70
+ npx @tomorrowos/sdk init ./my-cms
71
71
  `);
72
72
  }
73
73
  function main() {
@@ -40,7 +40,7 @@ export interface DeviceListItem {
40
40
  lastPolicyPushAt: string | null;
41
41
  screenOnlineActive: boolean;
42
42
  screenOnlineLabel: string;
43
- /** ISO time when the TV WebSocket session last became active (not CMS browser). */
43
+ /** Same as lastBootAt while connected; used for uptime display. */
44
44
  screenOnlineSince: string | null;
45
45
  }
46
46
  export declare class TomorrowOS extends EventEmitter {
@@ -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":"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"}
@@ -153,14 +153,14 @@ export class TomorrowOS extends EventEmitter {
153
153
  const connected = this.isDeviceConnected(deviceId);
154
154
  let screenOnlineActive = false;
155
155
  let screenOnlineLabel = "Not active";
156
- if (connected && record.lastOnlineAt) {
157
- const since = new Date(record.lastOnlineAt).getTime();
158
- if (!Number.isNaN(since)) {
156
+ if (connected && record.lastBootAt) {
157
+ const bootMs = new Date(record.lastBootAt).getTime();
158
+ if (!Number.isNaN(bootMs)) {
159
159
  screenOnlineActive = true;
160
- screenOnlineLabel = formatDurationMs(now - since);
160
+ screenOnlineLabel = formatDurationMs(now - bootMs);
161
161
  }
162
162
  }
163
- const screenOnlineSince = connected && record.lastOnlineAt ? record.lastOnlineAt : null;
163
+ const screenOnlineSince = connected && record.lastBootAt ? record.lastBootAt : null;
164
164
  return {
165
165
  deviceId,
166
166
  connected,
package/package.json CHANGED
@@ -1,43 +1,43 @@
1
- {
2
- "name": "@tomorrowos/sdk",
3
- "version": "0.2.2",
4
- "description": "TomorrowOS CMS server SDK — WebSocket transport, pairing, device commands, optional static CMS UI. Includes CLI (tomorrowos init / build) and starter templates.",
5
- "type": "module",
6
- "main": "./dist/index.js",
7
- "types": "./dist/index.d.ts",
8
- "exports": {
9
- ".": {
10
- "types": "./dist/index.d.ts",
11
- "import": "./dist/index.js"
12
- }
13
- },
14
- "bin": {
15
- "tomorrowos": "./dist/cli.js"
16
- },
17
- "files": [
18
- "dist",
19
- "templates",
20
- "README.md",
21
- "LLM_PROMPT.md",
22
- "BUILD_GUARDRAILS.md",
23
- "PLAYER_INSTALL.md",
24
- "brand.schema.json",
25
- "brand.example.json"
26
- ],
27
- "scripts": {
28
- "build": "tsc",
29
- "prepublishOnly": "npm run build"
30
- },
31
- "engines": {
32
- "node": ">=18"
33
- },
34
- "dependencies": {
35
- "ws": "^8.18.0"
36
- },
37
- "devDependencies": {
38
- "@types/node": "^20.19.41",
39
- "@types/ws": "^8.5.10",
40
- "typescript": "^5.5.0"
41
- },
42
- "license": "Apache-2.0"
43
- }
1
+ {
2
+ "name": "@tomorrowos/sdk",
3
+ "version": "0.2.3",
4
+ "description": "TomorrowOS CMS server SDK — WebSocket transport, pairing, device commands, optional static CMS UI. Includes CLI (tomorrowos init / build) and starter templates.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "bin": {
15
+ "tomorrowos": "./dist/cli.js"
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "templates",
20
+ "README.md",
21
+ "LLM_PROMPT.md",
22
+ "BUILD_GUARDRAILS.md",
23
+ "PLAYER_INSTALL.md",
24
+ "brand.schema.json",
25
+ "brand.example.json"
26
+ ],
27
+ "scripts": {
28
+ "build": "tsc",
29
+ "prepublishOnly": "npm run build"
30
+ },
31
+ "engines": {
32
+ "node": ">=18"
33
+ },
34
+ "dependencies": {
35
+ "ws": "^8.18.0"
36
+ },
37
+ "devDependencies": {
38
+ "@types/node": "^20.19.41",
39
+ "@types/ws": "^8.5.10",
40
+ "typescript": "^5.5.0"
41
+ },
42
+ "license": "Apache-2.0"
43
+ }
@@ -1,20 +1,20 @@
1
- {
2
- "name": "my-cms",
3
- "version": "0.2.2",
4
- "description": "CMS server on @tomorrowos/sdk. Add your UI (React, static files, etc.) alongside this server.",
5
- "private": true,
6
- "type": "module",
7
- "scripts": {
8
- "dev": "tsx watch server.ts",
9
- "start": "tsx server.ts",
10
- "build-player": "tomorrowos build --platform tizen"
11
- },
12
- "dependencies": {
13
- "@tomorrowos/sdk": "^0.2.2"
14
- },
15
- "devDependencies": {
16
- "@types/node": "^20.0.0",
17
- "tsx": "^4.19.0",
18
- "typescript": "^5.5.0"
19
- }
20
- }
1
+ {
2
+ "name": "my-cms",
3
+ "version": "0.2.3",
4
+ "description": "CMS server on @tomorrowos/sdk. Add your UI (React, static files, etc.) alongside this server.",
5
+ "private": true,
6
+ "type": "module",
7
+ "scripts": {
8
+ "dev": "tsx watch server.ts",
9
+ "start": "tsx server.ts",
10
+ "build-player": "tomorrowos build --platform tizen"
11
+ },
12
+ "dependencies": {
13
+ "@tomorrowos/sdk": "^0.2.3"
14
+ },
15
+ "devDependencies": {
16
+ "@types/node": "^20.0.0",
17
+ "tsx": "^4.19.0",
18
+ "typescript": "^5.5.0"
19
+ }
20
+ }
@@ -1,30 +1,30 @@
1
- {
2
- "policy": {
3
- "playlists": [
4
- {
5
- "id": "weekday-promo",
6
- "name": "Weekday promo",
7
- "schedule": {
8
- "startDate": "2026-05-01",
9
- "endDate": "2026-05-31",
10
- "daysOfWeek": [1, 2, 3, 4, 5],
11
- "start": "09:00",
12
- "end": "17:00"
13
- },
14
- "items": [
15
- {
16
- "url": "https://example.com/hero.jpg",
17
- "type": "image",
18
- "durationMs": 10000
19
- },
20
- {
21
- "url": "https://example.com/spot.mp4",
22
- "type": "video",
23
- "durationMs": 30000
24
- }
25
- ]
26
- }
27
- ],
28
- "fallback": { "type": "brand" }
29
- }
30
- }
1
+ {
2
+ "policy": {
3
+ "playlists": [
4
+ {
5
+ "id": "weekday-promo",
6
+ "name": "Weekday promo",
7
+ "schedule": {
8
+ "startDate": "2026-05-01",
9
+ "endDate": "2026-05-31",
10
+ "daysOfWeek": [1, 2, 3, 4, 5],
11
+ "start": "09:00",
12
+ "end": "17:00"
13
+ },
14
+ "items": [
15
+ {
16
+ "url": "https://example.com/hero.jpg",
17
+ "type": "image",
18
+ "durationMs": 10000
19
+ },
20
+ {
21
+ "url": "https://example.com/spot.mp4",
22
+ "type": "video",
23
+ "durationMs": 30000
24
+ }
25
+ ]
26
+ }
27
+ ],
28
+ "fallback": { "type": "brand" }
29
+ }
30
+ }
@@ -30,11 +30,13 @@
30
30
  </div>
31
31
  </section>
32
32
 
33
- <section class="card">
33
+ <section class="card" id="cmsUrlSection">
34
34
  <h2>CMS URL for screens</h2>
35
35
  <p class="hint">
36
- HTTP(S) base URL your TVs use to download uploads (same host as <code>ws://</code>, not
37
- <code>localhost</code>).
36
+ <strong>Local development only.</strong> Fill this in when you run the CMS on your PC and
37
+ TVs are on the same LAN (e.g. <code>http://192.168.1.105:3000</code> — same machine as your
38
+ <code>ws://</code> address, not <code>localhost</code>). On hosted CMS (Replit, etc.) you
39
+ normally do <strong>not</strong> need to change this.
38
40
  </p>
39
41
  <div class="row">
40
42
  <input
@@ -48,14 +48,14 @@ function formatDateTimeSeconds(iso) {
48
48
  });
49
49
  }
50
50
 
51
- /** TV online duration since last WebSocket active (device.lastOnlineAt), not this browser tab. */
51
+ /** TV uptime: now minus last boot time (device.lastBootAt). Not active when app is offline. */
52
52
  function formatDeviceOnlineLabel(device) {
53
53
  if (!device.connected) return "Not active";
54
- const sinceIso = device.screenOnlineSince || device.lastOnlineAt;
55
- if (!sinceIso) return "Not active";
56
- const since = new Date(sinceIso).getTime();
57
- if (Number.isNaN(since)) return "Not active";
58
- return formatDurationMs(Date.now() - since);
54
+ const bootIso = device.lastBootAt;
55
+ if (!bootIso) return "Not active";
56
+ const bootMs = new Date(bootIso).getTime();
57
+ if (Number.isNaN(bootMs)) return "Not active";
58
+ return formatDurationMs(Date.now() - bootMs);
59
59
  }
60
60
 
61
61
  function updateDeviceUptimeLabels() {
@@ -137,7 +137,7 @@ function renderDeviceCards() {
137
137
  if (label === "Device online") {
138
138
  dd.className = "device-online-time";
139
139
  dd.title =
140
- "Time since this TV's WebSocket session became active (updates every minute)";
140
+ "Current time minus this TV last boot time (refreshes every 30 seconds)";
141
141
  }
142
142
  dd.textContent = value;
143
143
  row.appendChild(dt);
@@ -205,7 +205,7 @@ function startDevicePolling() {
205
205
  void fetchDevices();
206
206
  }, 8000);
207
207
 
208
- deviceUptimeTimer = setInterval(updateDeviceUptimeLabels, 60_000);
208
+ deviceUptimeTimer = setInterval(updateDeviceUptimeLabels, 30_000);
209
209
  }
210
210
 
211
211
  function showResult(data) {
@@ -267,7 +267,7 @@ function absoluteMediaUrl(path) {
267
267
  const base = getMediaBaseOrigin();
268
268
  if (!base) {
269
269
  throw new Error(
270
- "Set CMS URL for screens (use your PC LAN IP or Replit https URL, not localhost)."
270
+ "Local CMS only: set CMS URL for screens (your PC LAN IP, e.g. http://192.168.1.105:3000 not localhost)."
271
271
  );
272
272
  }
273
273
  return `${base}${p.startsWith("/") ? p : `/${p}`}`;
@@ -558,7 +558,7 @@ async function publishToDevice(deviceId) {
558
558
  const mediaBase = getMediaBaseOrigin();
559
559
  if (!mediaBase) {
560
560
  alert(
561
- "Set CMS URL for screens first (e.g. http://192.168.1.105:3000 — same machine as the TV ws:// address)."
561
+ "Local CMS only: set CMS URL for screens first (e.g. http://192.168.1.105:3000 — your PC LAN IP, not localhost)."
562
562
  );
563
563
  return;
564
564
  }