@tomorrowos/sdk 0.1.0
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/BUILD_GUARDRAILS.md +348 -0
- package/LLM_PROMPT.md +171 -0
- package/PLAYER_INSTALL.md +211 -0
- package/README.md +98 -0
- package/brand.example.json +34 -0
- package/brand.schema.json +154 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +75 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/store/memory-store.d.ts +15 -0
- package/dist/store/memory-store.d.ts.map +1 -0
- package/dist/store/memory-store.js +23 -0
- package/dist/store/types.d.ts +24 -0
- package/dist/store/types.d.ts.map +1 -0
- package/dist/store/types.js +5 -0
- package/dist/tomorrowos.d.ts +62 -0
- package/dist/tomorrowos.d.ts.map +1 -0
- package/dist/tomorrowos.js +371 -0
- package/package.json +43 -0
- package/templates/cms-starter/assets/logo.svg +4 -0
- package/templates/cms-starter/brand.json +24 -0
- package/templates/cms-starter/package.json +20 -0
- package/templates/cms-starter/server.ts +47 -0
- package/templates/cms-starter/tsconfig.json +12 -0
- package/templates/style-tokens/components.md +282 -0
- package/templates/style-tokens/tokens.css +109 -0
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# Player Installation — Getting TomorrowOS on your screens
|
|
2
|
+
|
|
3
|
+
Your CMS is running. Now you need to get a TomorrowOS player onto your physical screen so the two can talk.
|
|
4
|
+
|
|
5
|
+
This document covers:
|
|
6
|
+
|
|
7
|
+
1. How to build a player for your platform
|
|
8
|
+
2. How to install it on a screen
|
|
9
|
+
3. How to pair the screen with your CMS
|
|
10
|
+
4. Troubleshooting
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Prerequisites
|
|
15
|
+
|
|
16
|
+
- You have a running TomorrowOS CMS (either locally, on Replit, Vercel, Railway, or self-hosted)
|
|
17
|
+
- The CMS has a publicly reachable URL (or tunneled via ngrok for local testing)
|
|
18
|
+
- `brand.json` at the project root is configured with your target platforms
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Samsung Tizen
|
|
23
|
+
|
|
24
|
+
### Build the player
|
|
25
|
+
|
|
26
|
+
From your project root:
|
|
27
|
+
|
|
28
|
+
npx tomorrowos build --platform tizen
|
|
29
|
+
|
|
30
|
+
This reads `brand.json`, applies your branding to the activation screen, and outputs:
|
|
31
|
+
|
|
32
|
+
dist/player-tizen.wgt
|
|
33
|
+
|
|
34
|
+
The `.wgt` is a signed Tizen web package ready to install on any compatible Samsung commercial display.
|
|
35
|
+
|
|
36
|
+
### Install on the screen
|
|
37
|
+
|
|
38
|
+
You have three options, depending on your setup:
|
|
39
|
+
|
|
40
|
+
**Option A — URL Launcher (easiest for testing)**
|
|
41
|
+
|
|
42
|
+
1. On the Samsung display, press the remote's menu button
|
|
43
|
+
2. Go to `Menu → System → Ext. Device Manager → URL Launcher Settings`
|
|
44
|
+
3. Enter the URL where your `.wgt` is hosted (your CMS serves this at `/player/tizen.wgt` once built)
|
|
45
|
+
4. Save and exit
|
|
46
|
+
5. The display reboots and launches TomorrowOS on boot
|
|
47
|
+
|
|
48
|
+
**Option B — Tizen Studio Device Manager (for bulk deployment)**
|
|
49
|
+
|
|
50
|
+
1. Install Tizen Studio from samsung.com
|
|
51
|
+
2. Put the display into Developer Mode (Menu → Support → Developer Mode → enter your PC's IP address)
|
|
52
|
+
3. In Tizen Studio, open Device Manager
|
|
53
|
+
4. Connect to your display by IP
|
|
54
|
+
5. Right-click → Install App → select `dist/player-tizen.wgt`
|
|
55
|
+
6. App installs and auto-launches
|
|
56
|
+
|
|
57
|
+
**Option C — USB Sideload (no network needed)**
|
|
58
|
+
|
|
59
|
+
1. Copy `dist/player-tizen.wgt` to a USB stick
|
|
60
|
+
2. Insert USB into display
|
|
61
|
+
3. On the remote, open Home → Apps → USB Sideload
|
|
62
|
+
4. Select the `.wgt` file and install
|
|
63
|
+
|
|
64
|
+
### Supported Tizen versions
|
|
65
|
+
|
|
66
|
+
V1 supports:
|
|
67
|
+
|
|
68
|
+
- SSSP 5 and newer commercial displays
|
|
69
|
+
- TEP 6.5 and newer commercial displays
|
|
70
|
+
|
|
71
|
+
If you have an older SSSP 4 display, you will need a firmware update or the older-generation player build (contact support).
|
|
72
|
+
|
|
73
|
+
### Activation
|
|
74
|
+
|
|
75
|
+
Once the player is running, the Samsung display shows the TomorrowOS activation screen branded with your logo and colours. Displayed on the screen:
|
|
76
|
+
|
|
77
|
+
- Your logo (top)
|
|
78
|
+
- A 6-digit activation code
|
|
79
|
+
- "Enter this code at [your CMS URL]"
|
|
80
|
+
- Device ID (small text, bottom)
|
|
81
|
+
|
|
82
|
+
On your CMS, go to the **Pair** page, enter the 6-digit code, and press Submit. Within a few seconds the display will transition from the activation screen to your content.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## BrightSign
|
|
87
|
+
|
|
88
|
+
### Build the player
|
|
89
|
+
|
|
90
|
+
npx tomorrowos build --platform brightsign
|
|
91
|
+
|
|
92
|
+
Outputs:
|
|
93
|
+
|
|
94
|
+
dist/player-brightsign.zip
|
|
95
|
+
|
|
96
|
+
### Install on the screen
|
|
97
|
+
|
|
98
|
+
1. Copy the **contents** of `player-brightsign.zip` (not the zip itself) onto a microSD card or USB stick
|
|
99
|
+
2. The root of the card should contain `autorun.brs`, the player files, and the `brand/` assets
|
|
100
|
+
3. Insert the card into the BrightSign device
|
|
101
|
+
4. Power on — BrightSign boots into TomorrowOS automatically
|
|
102
|
+
|
|
103
|
+
### Supported BrightSign versions
|
|
104
|
+
|
|
105
|
+
- BrightSign OS 9.0 and newer
|
|
106
|
+
- BrightSign OS 8.x: not supported in V1 (known local DWS API differences; support planned for V1.1)
|
|
107
|
+
|
|
108
|
+
### Activation
|
|
109
|
+
|
|
110
|
+
Same as Tizen — the BrightSign displays an activation screen with your branding and a 6-digit code. Enter the code in your CMS's Pair page.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## LG webOS Signage (V1.1)
|
|
115
|
+
|
|
116
|
+
Support planned for V1.1 release. Build command will be:
|
|
117
|
+
|
|
118
|
+
npx tomorrowos build --platform webos
|
|
119
|
+
|
|
120
|
+
Output: `dist/player-webos.ipk` installable via webOS Signage Manager or sideload.
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Android Managed (V1.1)
|
|
125
|
+
|
|
126
|
+
Support planned for V1.1 release. Build command will be:
|
|
127
|
+
|
|
128
|
+
npx tomorrowos build --platform android-managed
|
|
129
|
+
|
|
130
|
+
Output: `dist/player-android.apk` with device-owner privileges required at enrolment.
|
|
131
|
+
|
|
132
|
+
Generic (unmanaged) Android boxes are supported in V1.x with capability-limited functionality (see TomorrowOS protocol documentation).
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## ChromeOS Managed Kiosk (V1.x)
|
|
137
|
+
|
|
138
|
+
Support planned for V1.x release. Deployment will be via Google Admin Console as a managed kiosk web app.
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## Windows (V1.x)
|
|
143
|
+
|
|
144
|
+
Support planned for V1.x release. Distribution as MSI installer via Group Policy or Intune.
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Troubleshooting
|
|
149
|
+
|
|
150
|
+
### Activation screen doesn't appear
|
|
151
|
+
|
|
152
|
+
**Check:**
|
|
153
|
+
|
|
154
|
+
- Display is powered on and has network connectivity
|
|
155
|
+
- `brand.json` has `cms_endpoint` set to your publicly reachable CMS URL (it should be `wss://` for production, `ws://` only for local testing on the same network)
|
|
156
|
+
- You rebuilt the player after changing `brand.json` — the CMS endpoint is baked into the `.wgt` at build time
|
|
157
|
+
|
|
158
|
+
### Activation code appears but pairing fails
|
|
159
|
+
|
|
160
|
+
**Check:**
|
|
161
|
+
|
|
162
|
+
- The code has not expired — codes are valid for 15 minutes; generate a new one by restarting the player
|
|
163
|
+
- The CMS server is actually reachable from the display's network — test by opening the CMS URL in the display's browser (if available)
|
|
164
|
+
- Firewall rules — the display needs to reach your CMS on WebSocket port 443 (or whatever you configured)
|
|
165
|
+
|
|
166
|
+
### Replit workspaces go to sleep
|
|
167
|
+
|
|
168
|
+
Replit's free tier sleeps inactive workspaces. Your screen will show "reconnecting..." when the workspace is asleep, and reconnect automatically when someone wakes it.
|
|
169
|
+
|
|
170
|
+
**For production:** upgrade to Replit Hacker (Always-On) or move to Railway / Vercel / self-hosted.
|
|
171
|
+
|
|
172
|
+
### Samsung display stuck on black screen after URL Launcher
|
|
173
|
+
|
|
174
|
+
Check that your `.wgt` is reachable at the URL you set. Open the URL in any browser — it should download the `.wgt` file. If not, the display can't fetch it. Common causes:
|
|
175
|
+
|
|
176
|
+
- URL is http:// but server redirects to https:// (Samsung doesn't follow the redirect)
|
|
177
|
+
- URL requires auth (host the `.wgt` publicly)
|
|
178
|
+
- File is named `.zip` instead of `.wgt` (must be `.wgt`)
|
|
179
|
+
|
|
180
|
+
### BrightSign won't boot into the player
|
|
181
|
+
|
|
182
|
+
Check `autorun.brs` is at the root of the SD card, not in a subfolder. BrightSign only runs `autorun.brs` from the root.
|
|
183
|
+
|
|
184
|
+
### "Pairing already completed" error
|
|
185
|
+
|
|
186
|
+
A device can only be paired once. If you want to re-pair (e.g. moving a screen to a new CMS), first unpair it from the current CMS (`tomorrowos.pairing.revoke(deviceId)` from your CMS server), then generate a new activation code on the device by power-cycling or running "Unpair" from the device's Settings menu (if enabled in `brand.json`).
|
|
187
|
+
|
|
188
|
+
### Pairing code rolls past 15 minutes before I can enter it
|
|
189
|
+
|
|
190
|
+
Power-cycle the device (or call `player.restart()` if available) to generate a fresh code.
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## Getting help
|
|
195
|
+
|
|
196
|
+
- Package issues — file a GitHub issue at `github.com/TomorrowOS/sdk`
|
|
197
|
+
- Samsung-specific issues — the TomorrowOS Samsung bridge is Apache 2.0 at `github.com/TomorrowOS/player-tizen`
|
|
198
|
+
- BrightSign-specific issues — `github.com/TomorrowOS/player-brightsign`
|
|
199
|
+
- Commercial support — SL-X provides supported, SLA-backed deployments; see `sl-x.com`
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## Glossary
|
|
204
|
+
|
|
205
|
+
- `.wgt` — Tizen web package (a signed zip containing your branded player for Samsung displays)
|
|
206
|
+
- **Activation screen** — the branded full-screen UI a new player shows while it waits to be paired
|
|
207
|
+
- **Pairing code** — six-digit number shown on the screen, entered in the CMS to complete device registration
|
|
208
|
+
- **SSSP** — Samsung Smart Signage Platform (older Tizen signage firmware generation)
|
|
209
|
+
- **TEP** — Tizen Enterprise Platform (newer Samsung signage firmware generation)
|
|
210
|
+
- **DWS** — BrightSign's Device Web Services (local HTTPS REST API)
|
|
211
|
+
- **URL Launcher** — Samsung's built-in mechanism for loading a web app from a URL on startup
|
package/README.md
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
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
|
+
---
|
|
65
|
+
|
|
66
|
+
## Supported platforms (roadmap)
|
|
67
|
+
|
|
68
|
+
| Platform | Status | Player format |
|
|
69
|
+
|-------------------|------------|---------------|
|
|
70
|
+
| Samsung Tizen | V1 target | `.wgt` |
|
|
71
|
+
| BrightSign | V1 target | autorun zip |
|
|
72
|
+
| Others | See docs | varies |
|
|
73
|
+
|
|
74
|
+
See `PLAYER_INSTALL.md` for installation notes.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Documentation in this package
|
|
79
|
+
|
|
80
|
+
| File | Purpose |
|
|
81
|
+
|------|---------|
|
|
82
|
+
| `LLM_PROMPT.md` | Five-question elicitation for LLMs |
|
|
83
|
+
| `BUILD_GUARDRAILS.md` | Mandatory CMS components |
|
|
84
|
+
| `PLAYER_INSTALL.md` | Player build / install / pairing |
|
|
85
|
+
| `brand.schema.json` | JSON Schema for `brand.json` |
|
|
86
|
+
| `brand.example.json` | Full example `brand.json` |
|
|
87
|
+
| `templates/cms-starter/` | Minimal Node + TypeScript server seed |
|
|
88
|
+
| `templates/style-tokens/` | CSS tokens and UI pattern notes |
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## License
|
|
93
|
+
|
|
94
|
+
Apache 2.0.
|
|
95
|
+
|
|
96
|
+
## Protocol version
|
|
97
|
+
|
|
98
|
+
Wire protocol `1.0` (see TomorrowOS specification).
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Acme Burger",
|
|
3
|
+
"tagline": "Menu boards across all locations",
|
|
4
|
+
"targetPlatforms": ["tizen", "brightsign"],
|
|
5
|
+
"primaryColor": "#E63946",
|
|
6
|
+
"secondaryColor": "#F1FAEE",
|
|
7
|
+
"backgroundColor": "#FAFAF9",
|
|
8
|
+
"textColor": "#0A0908",
|
|
9
|
+
"logoPath": "./assets/acme-logo.svg",
|
|
10
|
+
"logoPathOnDark": "./assets/acme-logo-white.svg",
|
|
11
|
+
"fontFamily": "Inter",
|
|
12
|
+
"supportUrl": "https://acmeburger.com/signage-support",
|
|
13
|
+
"supportEmail": "it@acmeburger.com",
|
|
14
|
+
"cmsEndpoint": "wss://acme-signage.replit.app",
|
|
15
|
+
"activationScreen": {
|
|
16
|
+
"headline": "Connect this menu board",
|
|
17
|
+
"instructions": "Open the Acme Signage dashboard and enter the code below to activate this screen.",
|
|
18
|
+
"showDeviceInfo": true,
|
|
19
|
+
"showNetworkStatus": true,
|
|
20
|
+
"theme": "light"
|
|
21
|
+
},
|
|
22
|
+
"cms": {
|
|
23
|
+
"useCase": "restaurant",
|
|
24
|
+
"hostingTarget": "here",
|
|
25
|
+
"expectedScreens": 12,
|
|
26
|
+
"features": {
|
|
27
|
+
"bulkCommands": true,
|
|
28
|
+
"proofOfPlay": false,
|
|
29
|
+
"contentScheduling": true,
|
|
30
|
+
"userManagement": false
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"protocolVersion": "1.0"
|
|
34
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"title": "TomorrowOS brand.json",
|
|
4
|
+
"description": "The single source of truth for branding across the CMS UI and the device activation screen. One file, read by both the CMS server at runtime and the CLI at player-build time.",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["name", "targetPlatforms", "primaryColor", "logoPath", "cms"],
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"properties": {
|
|
9
|
+
"name": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"minLength": 1,
|
|
12
|
+
"maxLength": 60,
|
|
13
|
+
"description": "Display name of the CMS / product / company. Appears in the CMS header and on the activation screen."
|
|
14
|
+
},
|
|
15
|
+
"tagline": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"maxLength": 120,
|
|
18
|
+
"description": "Optional short tagline shown under the name on the activation screen."
|
|
19
|
+
},
|
|
20
|
+
"targetPlatforms": {
|
|
21
|
+
"type": "array",
|
|
22
|
+
"minItems": 1,
|
|
23
|
+
"uniqueItems": true,
|
|
24
|
+
"items": {
|
|
25
|
+
"enum": ["tizen", "brightsign", "webos", "android_managed", "android_generic", "chromeos_managed", "windows_player"]
|
|
26
|
+
},
|
|
27
|
+
"description": "Which platforms the CLI should be able to build players for. V1 supports tizen and brightsign only."
|
|
28
|
+
},
|
|
29
|
+
"primaryColor": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"pattern": "^#[0-9A-Fa-f]{6}$",
|
|
32
|
+
"description": "Main brand colour. Used for buttons, highlights, and the activation screen accent. Must be a hex string."
|
|
33
|
+
},
|
|
34
|
+
"secondaryColor": {
|
|
35
|
+
"type": "string",
|
|
36
|
+
"pattern": "^#[0-9A-Fa-f]{6}$",
|
|
37
|
+
"description": "Optional secondary brand colour."
|
|
38
|
+
},
|
|
39
|
+
"backgroundColor": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"pattern": "^#[0-9A-Fa-f]{6}$",
|
|
42
|
+
"default": "#FAFAF9",
|
|
43
|
+
"description": "Page background colour in the CMS. Defaults to near-white."
|
|
44
|
+
},
|
|
45
|
+
"textColor": {
|
|
46
|
+
"type": "string",
|
|
47
|
+
"pattern": "^#[0-9A-Fa-f]{6}$",
|
|
48
|
+
"default": "#0A0908",
|
|
49
|
+
"description": "Default text colour. Defaults to near-black."
|
|
50
|
+
},
|
|
51
|
+
"logoPath": {
|
|
52
|
+
"type": "string",
|
|
53
|
+
"description": "Relative path from project root to the primary logo file. SVG preferred; PNG accepted. Used in the CMS header and baked into the activation screen at build time."
|
|
54
|
+
},
|
|
55
|
+
"logoPathOnDark": {
|
|
56
|
+
"type": "string",
|
|
57
|
+
"description": "Optional alternate logo for use on dark backgrounds (e.g. the activation screen when backgroundColor is dark)."
|
|
58
|
+
},
|
|
59
|
+
"fontFamily": {
|
|
60
|
+
"type": "string",
|
|
61
|
+
"default": "Inter",
|
|
62
|
+
"description": "Font family name. The CMS will attempt to load this from Google Fonts unless overridden by fontUrl."
|
|
63
|
+
},
|
|
64
|
+
"fontUrl": {
|
|
65
|
+
"type": "string",
|
|
66
|
+
"format": "uri",
|
|
67
|
+
"description": "Optional full URL to a self-hosted font file or stylesheet. Overrides fontFamily default."
|
|
68
|
+
},
|
|
69
|
+
"supportUrl": {
|
|
70
|
+
"type": "string",
|
|
71
|
+
"format": "uri",
|
|
72
|
+
"description": "URL shown on the activation screen and in the CMS footer for support contact."
|
|
73
|
+
},
|
|
74
|
+
"supportEmail": {
|
|
75
|
+
"type": "string",
|
|
76
|
+
"format": "email",
|
|
77
|
+
"description": "Support email address shown on the activation screen."
|
|
78
|
+
},
|
|
79
|
+
"cmsEndpoint": {
|
|
80
|
+
"type": "string",
|
|
81
|
+
"description": "WebSocket URL the player will connect to. Should be wss:// in production, ws:// only for local testing. Baked into the player at build time. If omitted, the CLI will prompt during build."
|
|
82
|
+
},
|
|
83
|
+
"activationScreen": {
|
|
84
|
+
"type": "object",
|
|
85
|
+
"additionalProperties": false,
|
|
86
|
+
"properties": {
|
|
87
|
+
"headline": {
|
|
88
|
+
"type": "string",
|
|
89
|
+
"default": "Connect this screen",
|
|
90
|
+
"description": "Large headline text on the activation screen."
|
|
91
|
+
},
|
|
92
|
+
"instructions": {
|
|
93
|
+
"type": "string",
|
|
94
|
+
"default": "Enter the code below in your dashboard to activate this screen.",
|
|
95
|
+
"description": "Supporting instructions shown under the headline."
|
|
96
|
+
},
|
|
97
|
+
"showDeviceInfo": {
|
|
98
|
+
"type": "boolean",
|
|
99
|
+
"default": true,
|
|
100
|
+
"description": "Whether to show device model, serial, and installation ID in small text at the bottom of the activation screen. Useful for support."
|
|
101
|
+
},
|
|
102
|
+
"showNetworkStatus": {
|
|
103
|
+
"type": "boolean",
|
|
104
|
+
"default": true,
|
|
105
|
+
"description": "Whether to show a small network-connected indicator (green dot) on the activation screen."
|
|
106
|
+
},
|
|
107
|
+
"backgroundImagePath": {
|
|
108
|
+
"type": "string",
|
|
109
|
+
"description": "Optional path to a background image for the activation screen. Replaces the solid backgroundColor if set."
|
|
110
|
+
},
|
|
111
|
+
"theme": {
|
|
112
|
+
"enum": ["light", "dark", "auto"],
|
|
113
|
+
"default": "light",
|
|
114
|
+
"description": "Activation screen theme. Affects which logo (logoPath vs logoPathOnDark) is used."
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
"cms": {
|
|
119
|
+
"type": "object",
|
|
120
|
+
"required": ["useCase", "hostingTarget", "expectedScreens"],
|
|
121
|
+
"additionalProperties": false,
|
|
122
|
+
"properties": {
|
|
123
|
+
"useCase": {
|
|
124
|
+
"enum": ["restaurant", "retail", "corporate", "wayfinding", "transit", "education", "healthcare", "other"],
|
|
125
|
+
"description": "Primary deployment context. Affects example content and default copy in the generated CMS."
|
|
126
|
+
},
|
|
127
|
+
"hostingTarget": {
|
|
128
|
+
"enum": ["here", "vercel", "railway", "self-hosted"],
|
|
129
|
+
"description": "Where the CMS server is deployed. Affects configuration in the starter template."
|
|
130
|
+
},
|
|
131
|
+
"expectedScreens": {
|
|
132
|
+
"type": "integer",
|
|
133
|
+
"minimum": 1,
|
|
134
|
+
"description": "Approximate number of screens at launch. Affects whether pagination, grouping, and fleet features are included in the generated CMS."
|
|
135
|
+
},
|
|
136
|
+
"features": {
|
|
137
|
+
"type": "object",
|
|
138
|
+
"additionalProperties": false,
|
|
139
|
+
"properties": {
|
|
140
|
+
"bulkCommands": { "type": "boolean", "default": false, "description": "Include fleet.broadcast UI for bulk operations." },
|
|
141
|
+
"proofOfPlay": { "type": "boolean", "default": false, "description": "Include proof-of-play reporting page." },
|
|
142
|
+
"contentScheduling": { "type": "boolean", "default": true, "description": "Include scheduling UI for time-based content." },
|
|
143
|
+
"userManagement": { "type": "boolean", "default": false, "description": "Include multi-user / role management." }
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
"protocolVersion": {
|
|
149
|
+
"type": "string",
|
|
150
|
+
"const": "1.0",
|
|
151
|
+
"description": "TomorrowOS wire protocol version this brand.json targets. Set automatically by npx tomorrowos init; do not edit."
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
function packageRoot() {
|
|
6
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
return path.resolve(here, "..");
|
|
8
|
+
}
|
|
9
|
+
function copyStarter(destDir, force) {
|
|
10
|
+
const src = path.join(packageRoot(), "templates", "cms-starter");
|
|
11
|
+
if (!fs.existsSync(src)) {
|
|
12
|
+
console.error("[tomorrowos] Starter template not found. Re-install @tomorrowos/sdk (templates should ship with the package).");
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
const resolved = path.resolve(destDir);
|
|
16
|
+
if (fs.existsSync(resolved)) {
|
|
17
|
+
const entries = fs.readdirSync(resolved);
|
|
18
|
+
if (entries.length > 0 && !force) {
|
|
19
|
+
console.error(`[tomorrowos] Target directory is not empty: ${resolved}\nUse --force to copy anyway.`);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
fs.mkdirSync(resolved, { recursive: true });
|
|
25
|
+
}
|
|
26
|
+
fs.cpSync(src, resolved, { recursive: true });
|
|
27
|
+
console.log(`[tomorrowos] Created CMS project at ${resolved}`);
|
|
28
|
+
console.log("Next: cd there, run npm install, then npm run dev");
|
|
29
|
+
}
|
|
30
|
+
function cmdBuild(argv) {
|
|
31
|
+
const platformIdx = argv.indexOf("--platform");
|
|
32
|
+
const platform = platformIdx >= 0 && argv[platformIdx + 1] ? argv[platformIdx + 1] : "tizen";
|
|
33
|
+
console.log(`[tomorrowos] Player packaging for "${platform}" is not bundled in @tomorrowos/sdk yet.`);
|
|
34
|
+
console.log("Use your TomorrowOS player repository (assemble Web app + config), then run the platform SDK (e.g. Tizen CLI tizen package) from the build output directory.");
|
|
35
|
+
process.exitCode = 0;
|
|
36
|
+
}
|
|
37
|
+
function printHelp() {
|
|
38
|
+
console.log(`tomorrowos — TomorrowOS SDK CLI
|
|
39
|
+
|
|
40
|
+
Usage:
|
|
41
|
+
tomorrowos init [directory] Copy cms-starter template (default: my-tomorrowos-cms)
|
|
42
|
+
tomorrowos build --platform … Placeholder; player packaging lives in the player repo
|
|
43
|
+
|
|
44
|
+
Options:
|
|
45
|
+
--force With init: copy into a non-empty directory
|
|
46
|
+
|
|
47
|
+
Examples:
|
|
48
|
+
npx @tomorrowos/sdk init
|
|
49
|
+
npx @tomorrowos/sdk init ./my-cms
|
|
50
|
+
`);
|
|
51
|
+
}
|
|
52
|
+
function main() {
|
|
53
|
+
const argv = process.argv.slice(2);
|
|
54
|
+
if (argv.length === 0 || argv[0] === "-h" || argv[0] === "--help") {
|
|
55
|
+
printHelp();
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const cmd = argv[0];
|
|
59
|
+
const rest = argv.slice(1);
|
|
60
|
+
if (cmd === "init") {
|
|
61
|
+
const force = rest.includes("--force");
|
|
62
|
+
const pos = rest.filter((a) => !a.startsWith("-"));
|
|
63
|
+
const target = pos[0] ?? "my-tomorrowos-cms";
|
|
64
|
+
copyStarter(target, force);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (cmd === "build") {
|
|
68
|
+
cmdBuild(rest);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
console.error(`[tomorrowos] Unknown command: ${cmd}`);
|
|
72
|
+
printHelp();
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
main();
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { TomorrowOS } from "./tomorrowos.js";
|
|
2
|
+
export type { ListenOptions, TomorrowOSBrand, TomorrowOSOptions } from "./tomorrowos.js";
|
|
3
|
+
export type { PairedDeviceRecord, PendingCodeRecord, TomorrowOSStore } from "./store/types.js";
|
|
4
|
+
export { MemoryStore } from "./store/memory-store.js";
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzF,YAAY,EACV,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,EAChB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { PairedDeviceRecord, PendingCodeRecord, TomorrowOSStore } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Default store for development / single-node demos.
|
|
4
|
+
* Data is lost on process restart — not for multi-instance production.
|
|
5
|
+
*/
|
|
6
|
+
export declare class MemoryStore implements TomorrowOSStore {
|
|
7
|
+
private readonly pendingCodes;
|
|
8
|
+
private readonly pairedDevices;
|
|
9
|
+
setPendingCode(code: string, record: PendingCodeRecord): Promise<void>;
|
|
10
|
+
getPendingCode(code: string): Promise<PendingCodeRecord | undefined>;
|
|
11
|
+
deletePendingCode(code: string): Promise<void>;
|
|
12
|
+
setPairedDevice(deviceId: string, record: PairedDeviceRecord): Promise<void>;
|
|
13
|
+
getPairedDevice(deviceId: string): Promise<PairedDeviceRecord | undefined>;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=memory-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-store.d.ts","sourceRoot":"","sources":["../../src/store/memory-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,EAChB,MAAM,YAAY,CAAC;AAEpB;;;GAGG;AACH,qBAAa,WAAY,YAAW,eAAe;IACjD,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAwC;IACrE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAyC;IAEjE,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAItE,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC;IAIpE,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI9C,eAAe,CACnB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,IAAI,CAAC;IAIV,eAAe,CACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC;CAG3C"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default store for development / single-node demos.
|
|
3
|
+
* Data is lost on process restart — not for multi-instance production.
|
|
4
|
+
*/
|
|
5
|
+
export class MemoryStore {
|
|
6
|
+
pendingCodes = new Map();
|
|
7
|
+
pairedDevices = new Map();
|
|
8
|
+
async setPendingCode(code, record) {
|
|
9
|
+
this.pendingCodes.set(code, record);
|
|
10
|
+
}
|
|
11
|
+
async getPendingCode(code) {
|
|
12
|
+
return this.pendingCodes.get(code);
|
|
13
|
+
}
|
|
14
|
+
async deletePendingCode(code) {
|
|
15
|
+
this.pendingCodes.delete(code);
|
|
16
|
+
}
|
|
17
|
+
async setPairedDevice(deviceId, record) {
|
|
18
|
+
this.pairedDevices.set(deviceId, record);
|
|
19
|
+
}
|
|
20
|
+
async getPairedDevice(deviceId) {
|
|
21
|
+
return this.pairedDevices.get(deviceId);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistent (or replaceable) data for pairing — scheme C.
|
|
3
|
+
* Live WebSocket handles stay in-memory on the TomorrowOS instance.
|
|
4
|
+
*/
|
|
5
|
+
export interface PendingCodeRecord {
|
|
6
|
+
deviceId: string;
|
|
7
|
+
createdAt: number;
|
|
8
|
+
}
|
|
9
|
+
export interface PairedDeviceRecord {
|
|
10
|
+
pairingToken: string;
|
|
11
|
+
pairedAt: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Implement with Postgres, Redis, etc. Defaults to MemoryStore (Map).
|
|
15
|
+
* All methods async so DB backends do not need sync adapters.
|
|
16
|
+
*/
|
|
17
|
+
export interface TomorrowOSStore {
|
|
18
|
+
setPendingCode(code: string, record: PendingCodeRecord): Promise<void>;
|
|
19
|
+
getPendingCode(code: string): Promise<PendingCodeRecord | undefined>;
|
|
20
|
+
deletePendingCode(code: string): Promise<void>;
|
|
21
|
+
setPairedDevice(deviceId: string, record: PairedDeviceRecord): Promise<void>;
|
|
22
|
+
getPairedDevice(deviceId: string): Promise<PairedDeviceRecord | undefined>;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/store/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,SAAS,CAAC,CAAC;IACrE,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CAAC;CAC5E"}
|