@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,348 @@
|
|
|
1
|
+
# Build Guardrails for TomorrowOS CMS Projects
|
|
2
|
+
|
|
3
|
+
> **These are not suggestions. Every CMS generated from `@tomorrowos/sdk` must include each component in this document. The SDK assumes they exist. If any are missing, pairing will fail, content will not deploy, and the LLM that generated the CMS has produced broken code.**
|
|
4
|
+
|
|
5
|
+
This document exists because an LLM given a blank canvas will produce a CMS that looks right and doesn't work. This file is the fence that stops that. Read each section. Include each component. Do not skip any.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1. Server-side WebSocket host
|
|
10
|
+
|
|
11
|
+
**File:** `server.ts` (or `server.js`)
|
|
12
|
+
|
|
13
|
+
**Must contain:**
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { TomorrowOS } from '@tomorrowos/sdk';
|
|
17
|
+
import brand from './brand.json';
|
|
18
|
+
|
|
19
|
+
const tomorrowos = new TomorrowOS({ brand });
|
|
20
|
+
|
|
21
|
+
tomorrowos.listen({
|
|
22
|
+
port: Number(process.env.PORT) || 3000,
|
|
23
|
+
host: '0.0.0.0',
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Why this matters:**
|
|
28
|
+
|
|
29
|
+
- `TomorrowOS` is a class from the SDK that wraps WebSocket handling, pairing, command envelopes, lifecycle tracking, and event publishing. Do not reimplement any of this.
|
|
30
|
+
- `tomorrowos.listen()` binds a WebSocket server that every player connects to. Without this call, no screen can ever pair.
|
|
31
|
+
- `host: '0.0.0.0'` is required for Replit / Claude Code / containerised hosting. Do not use `localhost` or `127.0.0.1` in the starter.
|
|
32
|
+
|
|
33
|
+
**Must not contain:**
|
|
34
|
+
|
|
35
|
+
- Raw `ws` or `socket.io` imports — the SDK handles WebSocket transport
|
|
36
|
+
- Manual pairing code generation — the SDK provides `tomorrowos.pairing.createCode()`
|
|
37
|
+
- Manual JWT signing for device tokens — the SDK mints and verifies these
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 2. Pairing page
|
|
42
|
+
|
|
43
|
+
**Route:** `/pair`
|
|
44
|
+
|
|
45
|
+
**Must include:**
|
|
46
|
+
|
|
47
|
+
- An input field accepting a 6-digit numeric code (input pattern `\d{6}`)
|
|
48
|
+
- A submit button that calls `tomorrowos.pairing.verify(code)` via the SDK
|
|
49
|
+
- Success state showing the newly-paired device's name and ID
|
|
50
|
+
- Error state for expired, invalid, or already-paired codes
|
|
51
|
+
- A link back to the main screens list
|
|
52
|
+
|
|
53
|
+
**Example flow:**
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
import { useTomorrowOS } from '@tomorrowos/sdk/react';
|
|
57
|
+
|
|
58
|
+
function PairPage() {
|
|
59
|
+
const { pairing } = useTomorrowOS();
|
|
60
|
+
const [code, setCode] = useState('');
|
|
61
|
+
const [result, setResult] = useState(null);
|
|
62
|
+
|
|
63
|
+
async function handleSubmit() {
|
|
64
|
+
try {
|
|
65
|
+
const device = await pairing.verify(code);
|
|
66
|
+
setResult({ success: true, device });
|
|
67
|
+
} catch (err) {
|
|
68
|
+
setResult({ success: false, error: err.message });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return (/* UI here */);
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Why mandatory:**
|
|
77
|
+
|
|
78
|
+
Without a pairing page, a device showing an activation code on its screen has no way to tell the CMS "I am device X, please accept me." The CMS will never gain a paired device. Every CMS needs this page regardless of use case.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## 3. Screens list page
|
|
83
|
+
|
|
84
|
+
**Route:** `/` or `/screens`
|
|
85
|
+
|
|
86
|
+
**Must include:**
|
|
87
|
+
|
|
88
|
+
- Live list of paired devices from `tomorrowos.devices.list()`
|
|
89
|
+
- Online / offline indicator per device (driven by `device.heartbeat` events)
|
|
90
|
+
- Last-seen timestamp per device (from heartbeat stream)
|
|
91
|
+
- Device name, model, and platform per row
|
|
92
|
+
- Click-through to `/screens/[deviceId]`
|
|
93
|
+
|
|
94
|
+
**Real-time updates:**
|
|
95
|
+
|
|
96
|
+
The SDK emits `device.online`, `device.offline`, and `device.heartbeat` events. Subscribe to these via `tomorrowos.on('device.online', handler)` to keep the list live without polling.
|
|
97
|
+
|
|
98
|
+
**Why mandatory:**
|
|
99
|
+
|
|
100
|
+
This is the operator's primary workspace. Without it, the CMS has paired devices it cannot show.
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## 4. Per-device control panel
|
|
105
|
+
|
|
106
|
+
**Route:** `/screens/[deviceId]`
|
|
107
|
+
|
|
108
|
+
**Must include the following controls, wired to SDK methods:**
|
|
109
|
+
|
|
110
|
+
### Device info panel (top of page)
|
|
111
|
+
|
|
112
|
+
Display output of `tomorrowos.device(deviceId).info.get()`:
|
|
113
|
+
|
|
114
|
+
- Device name
|
|
115
|
+
- Platform and platform version
|
|
116
|
+
- Hardware model and serial
|
|
117
|
+
- Current resolution
|
|
118
|
+
- Paired since date
|
|
119
|
+
|
|
120
|
+
### Control buttons
|
|
121
|
+
|
|
122
|
+
Each button calls the corresponding SDK method via `tomorrowos.device(deviceId).sendCommand()`:
|
|
123
|
+
|
|
124
|
+
| Label | SDK call | Notes |
|
|
125
|
+
|------------------|--------------------------------------------------|------------------------------------------------|
|
|
126
|
+
| Reboot | `device.power.reboot()` | Confirm dialog before sending |
|
|
127
|
+
| Deploy content | Opens URL input → `device.content.setPolicy()` | Wrap URL as single-item policy; see below |
|
|
128
|
+
| Clear content | `device.content.clear()` | Confirm dialog |
|
|
129
|
+
| Screenshot | `device.display.screenshot()` | Only show button if capability supports it |
|
|
130
|
+
|
|
131
|
+
### Current content indicator
|
|
132
|
+
|
|
133
|
+
Poll `tomorrowos.device(deviceId).content.current()` every 10 seconds, or subscribe to `content.started` / `content.finished` events for push updates.
|
|
134
|
+
|
|
135
|
+
### Deploy content — correct shape
|
|
136
|
+
|
|
137
|
+
When the user enters a URL to deploy, the SDK call must wrap it as a content policy:
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
await device.sendCommand('device.content.setPolicy', {
|
|
141
|
+
policy: {
|
|
142
|
+
revision: Date.now(), // monotonic
|
|
143
|
+
playlists: [{
|
|
144
|
+
playlistId: crypto.randomUUID(),
|
|
145
|
+
priority: 10,
|
|
146
|
+
items: [{
|
|
147
|
+
contentId: crypto.randomUUID(),
|
|
148
|
+
url: userProvidedUrl,
|
|
149
|
+
durationSec: 30,
|
|
150
|
+
transition: 'cut',
|
|
151
|
+
}],
|
|
152
|
+
loop: true,
|
|
153
|
+
schedule: { startDate: null, endDate: null },
|
|
154
|
+
}],
|
|
155
|
+
priorityResolution: 'highest_wins',
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Do not call** `device.content.deploy(url)` — this method does not exist in V1. Content is declarative policy, not imperative deploy.
|
|
161
|
+
|
|
162
|
+
**Why mandatory:**
|
|
163
|
+
|
|
164
|
+
Without the control panel, the CMS is a pretty dashboard with no function. Every TomorrowOS demo, test, and user showcase depends on this page working.
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## 5. Brand application via `brand.json`
|
|
169
|
+
|
|
170
|
+
**Brand data MUST flow through `brand.json`**, never hard-coded in components.
|
|
171
|
+
|
|
172
|
+
### Read brand at startup
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
import brand from './brand.json';
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Apply brand via CSS custom properties
|
|
179
|
+
|
|
180
|
+
Generate a `brand.css` file (or equivalent) on server start that derives CSS variables from `brand.json`:
|
|
181
|
+
|
|
182
|
+
```css
|
|
183
|
+
:root {
|
|
184
|
+
--color-primary: #FF8A3D; /* from brand.primaryColor */
|
|
185
|
+
--color-background: #FAFAF9; /* from brand.backgroundColor */
|
|
186
|
+
--color-text: #0A0908; /* from brand.textColor */
|
|
187
|
+
--font-sans: 'Inter', sans-serif; /* from brand.fontFamily */
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Use the `useBrand()` hook in React components
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
import { useBrand } from '@tomorrowos/sdk/react';
|
|
195
|
+
|
|
196
|
+
function Header() {
|
|
197
|
+
const brand = useBrand();
|
|
198
|
+
return (
|
|
199
|
+
<header>
|
|
200
|
+
<img src={brand.logoPath} alt={brand.name} />
|
|
201
|
+
<h1>{brand.name} Signage</h1>
|
|
202
|
+
</header>
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**Must not:**
|
|
208
|
+
|
|
209
|
+
- Hard-code the brand name, logo path, or colours in any component
|
|
210
|
+
- Use inline `style={{ color: '#FF8A3D' }}` — always use the CSS variable
|
|
211
|
+
- Reference the TomorrowOS amber or the starter's placeholder colour in production components
|
|
212
|
+
|
|
213
|
+
**Why mandatory:**
|
|
214
|
+
|
|
215
|
+
Hard-coded branding means the user has to edit every file to change a colour. `brand.json`-driven branding means one file change propagates everywhere — including into the device's activation screen (baked into the `.wgt` at build time by `tomorrowos build`).
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## 6. Command envelope
|
|
220
|
+
|
|
221
|
+
**Every mutating command must go through `sdk.sendCommand(method, params)`.**
|
|
222
|
+
|
|
223
|
+
The SDK wraps your call with the standard command envelope:
|
|
224
|
+
|
|
225
|
+
```json
|
|
226
|
+
{
|
|
227
|
+
"commandId": "uuid-generated",
|
|
228
|
+
"method": "device.power.reboot",
|
|
229
|
+
"params": {},
|
|
230
|
+
"issuedAt": "2026-05-14T10:30:00Z",
|
|
231
|
+
"ttlSec": 300,
|
|
232
|
+
"idempotencyKey": "uuid-generated",
|
|
233
|
+
"requiresAck": true
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
**Must not:**
|
|
238
|
+
|
|
239
|
+
- Write to the WebSocket directly. If you find yourself typing `ws.send()` or `socket.emit()` in your CMS code, stop — you are bypassing the SDK and your commands will be rejected by the player.
|
|
240
|
+
- Construct your own commandId or idempotency logic. The SDK does this.
|
|
241
|
+
- Assume a command is complete when `sendCommand` resolves. `sendCommand` returns when the command is `accepted`. Listen for `command.verified` or `command.failed` events to know the final outcome.
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## 7. No platform-specific branching in CMS code
|
|
246
|
+
|
|
247
|
+
The CMS must be platform-agnostic. The SDK + capability declarations abstract every platform difference.
|
|
248
|
+
|
|
249
|
+
**Do not write:**
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
// WRONG
|
|
253
|
+
if (device.platform === 'tizen') {
|
|
254
|
+
await device.sendCommand('device.power.reboot');
|
|
255
|
+
} else if (device.platform === 'brightsign') {
|
|
256
|
+
await device.sendCommand('device.power.restart');
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
**Write:**
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
// CORRECT
|
|
264
|
+
await device.sendCommand('device.power.reboot');
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
If the device doesn't support reboot, the SDK returns a `CAPABILITY_NOT_SUPPORTED` error. Handle that error — do not pre-branch on platform.
|
|
268
|
+
|
|
269
|
+
**Do not:**
|
|
270
|
+
|
|
271
|
+
- Check `device.platform` or `device.class` to change which methods you call
|
|
272
|
+
- Call raw native APIs (there are none exposed to the CMS — only SDK methods)
|
|
273
|
+
- Import any `@tomorrowos/player-*` packages into the CMS — those are player-side, not CMS-side
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## 8. Event subscription for real-time UI
|
|
278
|
+
|
|
279
|
+
Subscribe to events for live dashboards. Do not poll unless explicitly required.
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
tomorrowos.on('device.online', (event) => {
|
|
283
|
+
// update UI
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
tomorrowos.on('device.heartbeat', (event) => {
|
|
287
|
+
// update last-seen
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
tomorrowos.on('command.verified', (event) => {
|
|
291
|
+
// mark command as successful in UI
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
tomorrowos.on('command.failed', (event) => {
|
|
295
|
+
// show error to operator
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
tomorrowos.on('content.error', (event) => {
|
|
299
|
+
// alert operator of content playback failure
|
|
300
|
+
});
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
---
|
|
304
|
+
|
|
305
|
+
## Optional but strongly recommended
|
|
306
|
+
|
|
307
|
+
The following are not required but improve the CMS substantially. Include them if the user is at expectedScreens > 10 or if the use case warrants it.
|
|
308
|
+
|
|
309
|
+
### Build player button
|
|
310
|
+
|
|
311
|
+
A button in the CMS admin area that runs `npx tomorrowos build --platform tizen` (or other) and links the user to the generated `.wgt` file in `dist/`. Saves the user a terminal step.
|
|
312
|
+
|
|
313
|
+
### Settings page for `brand.json`
|
|
314
|
+
|
|
315
|
+
A `/settings` page that lets the user edit brand fields in-UI and writes back to `brand.json`. On save, the CMS reloads the brand.
|
|
316
|
+
|
|
317
|
+
### Fleet broadcast
|
|
318
|
+
|
|
319
|
+
A bulk-command UI that uses `tomorrowos.fleet.broadcast(method, params, targets)` to send the same command to many devices at once. Essential at > 50 screens.
|
|
320
|
+
|
|
321
|
+
### Proof-of-play log
|
|
322
|
+
|
|
323
|
+
A `/playback` page showing which content played on which screen at what time. Reads from `tomorrowos.device(id).proofOfPlay.getLog()`.
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## Summary — the "did I build this right?" checklist
|
|
328
|
+
|
|
329
|
+
Before shipping a generated CMS, verify:
|
|
330
|
+
|
|
331
|
+
- `server.ts` imports `TomorrowOS` from `@tomorrowos/sdk` and calls `listen()` — ☐
|
|
332
|
+
- `/pair` page exists and calls `pairing.verify(code)` — ☐
|
|
333
|
+
- `/screens` page shows live device list with online/offline status — ☐
|
|
334
|
+
- `/screens/[id]` page has reboot, deploy, clear, current-content at minimum — ☐
|
|
335
|
+
- Deploy uses `device.content.setPolicy` with a wrapped policy object, not `content.deploy` — ☐
|
|
336
|
+
- `brand.json` exists at project root and drives all UI branding via CSS variables — ☐
|
|
337
|
+
- No hard-coded colours, logos, or brand names in components — ☐
|
|
338
|
+
- All mutating commands use `sdk.sendCommand()` — no raw WebSocket writes — ☐
|
|
339
|
+
- No platform branching in CMS code — ☐
|
|
340
|
+
- Event subscriptions for `device.online`, `device.offline`, `device.heartbeat` active — ☐
|
|
341
|
+
|
|
342
|
+
If every box is checked, the CMS will work on first connection. If any box is unchecked, pairing or control will fail in ways the user cannot diagnose.
|
|
343
|
+
|
|
344
|
+
---
|
|
345
|
+
|
|
346
|
+
## Protocol compliance
|
|
347
|
+
|
|
348
|
+
This guardrails document is version `1.0`, aligned with TomorrowOS wire protocol `1.0`. Future SDK versions may add or strengthen guardrails; they will not relax them without a major-version bump.
|
package/LLM_PROMPT.md
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# Elicitation Protocol for TomorrowOS CMS Projects
|
|
2
|
+
|
|
3
|
+
> **This file is the contract between TomorrowOS and any LLM generating code using this package. You must follow it literally. Do not skip questions, do not infer answers, do not start writing code until the protocol is complete.**
|
|
4
|
+
|
|
5
|
+
You are generating a digital signage CMS using `@tomorrowos/sdk`. The user has asked you to build something like "a CMS for my restaurant" or "a signage dashboard for my retail network". Before you write any code, you must gather five pieces of information from the user.
|
|
6
|
+
|
|
7
|
+
Ask each question in order. If the user answers multiple at once, record those and skip ahead. If the user hasn't given you an answer to a specific field, do not invent one — ask the question explicitly.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Question 1 — Target platform
|
|
12
|
+
|
|
13
|
+
**Ask exactly:**
|
|
14
|
+
|
|
15
|
+
> "Which screen platform will you manage? TomorrowOS V1 fully supports Samsung Tizen (commercial signage displays) and BrightSign (media players). Which would you like to target?"
|
|
16
|
+
|
|
17
|
+
**Valid answers:** `tizen`, `brightsign`, or `both`
|
|
18
|
+
|
|
19
|
+
**If the user names a platform not in V1** (webOS, Android, ChromeOS, Windows), respond:
|
|
20
|
+
|
|
21
|
+
> "That platform is planned for a later release. For V1, the supported options are Tizen and BrightSign. Which would you like to use for now?"
|
|
22
|
+
|
|
23
|
+
**Store answer as:** `targetPlatforms` array in `brand.json`
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Question 2 — Brand identity
|
|
28
|
+
|
|
29
|
+
**Ask exactly:**
|
|
30
|
+
|
|
31
|
+
> "What's the name of the product or company this CMS is for? I'll also need:
|
|
32
|
+
>
|
|
33
|
+
> - A logo file (SVG preferred, PNG acceptable — or a URL I can fetch)
|
|
34
|
+
> - A primary brand colour (hex format, e.g. `#FF8A3D`)
|
|
35
|
+
> - Optional: a secondary/accent colour"
|
|
36
|
+
|
|
37
|
+
**Required fields:**
|
|
38
|
+
|
|
39
|
+
- `brand.name` — string
|
|
40
|
+
- `brand.primaryColor` — hex string matching `^#[0-9A-Fa-f]{6}$`
|
|
41
|
+
- `brand.logoPath` — relative path to logo file the user will save to the project
|
|
42
|
+
|
|
43
|
+
**If the user does not provide a logo**, proceed with a placeholder comment in the generated code noting that the logo must be added before production use. Do not fabricate a logo.
|
|
44
|
+
|
|
45
|
+
**If the user provides a brand name but no colour**, ask:
|
|
46
|
+
|
|
47
|
+
> "What's the primary colour for your brand? I need it as a hex code like `#FF8A3D`."
|
|
48
|
+
|
|
49
|
+
Store answers in `brand.json`.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Question 3 — Use case
|
|
54
|
+
|
|
55
|
+
**Ask exactly:**
|
|
56
|
+
|
|
57
|
+
> "What's the primary use case for this CMS? For example: restaurant menu boards, retail promotions, corporate lobby displays, hospital wayfinding, transit information."
|
|
58
|
+
|
|
59
|
+
**Use the answer to:**
|
|
60
|
+
|
|
61
|
+
- Tailor the example content shown in the starter template (e.g. menu items vs retail promo templates)
|
|
62
|
+
- Set the dashboard's default greeting and page titles
|
|
63
|
+
- Suggest sensible default playlist names
|
|
64
|
+
|
|
65
|
+
**Do NOT use the answer to:**
|
|
66
|
+
|
|
67
|
+
- Change the SDK method signatures
|
|
68
|
+
- Add or remove required components from `BUILD_GUARDRAILS.md`
|
|
69
|
+
- Skip any mandatory pages
|
|
70
|
+
|
|
71
|
+
The SDK contract is identical regardless of use case. Use case only affects content and copy.
|
|
72
|
+
|
|
73
|
+
**Store answer as:** `cms.useCase` in `brand.json` (one of: `restaurant`, `retail`, `corporate`, `wayfinding`, `transit`, `other`)
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Question 4 — Hosting target
|
|
78
|
+
|
|
79
|
+
**Ask exactly:**
|
|
80
|
+
|
|
81
|
+
> "Where will the CMS server run? Options:
|
|
82
|
+
>
|
|
83
|
+
> - `here` — you're in Replit, Claude Code, or another hosted AI workspace (I'll configure it automatically)
|
|
84
|
+
> - `vercel` — deploy to Vercel
|
|
85
|
+
> - `railway` — deploy to Railway
|
|
86
|
+
> - `self-hosted` — you'll run it on your own infrastructure"
|
|
87
|
+
|
|
88
|
+
**If the user says `here`:**
|
|
89
|
+
|
|
90
|
+
- Use environment variable `process.env.PORT` for the server port
|
|
91
|
+
- Configure the WebSocket handler to bind to `0.0.0.0`
|
|
92
|
+
- Include a comment in `server.ts` noting that Replit/Claude Code handles the hosting and HTTPS termination
|
|
93
|
+
- Do NOT commit any secrets or keys to the generated code
|
|
94
|
+
|
|
95
|
+
**If the user says `vercel`:**
|
|
96
|
+
|
|
97
|
+
- Note that Vercel free tier has WebSocket limitations; recommend Railway for production
|
|
98
|
+
- Generate `vercel.json` with appropriate WebSocket routing
|
|
99
|
+
|
|
100
|
+
**If the user says `railway`:**
|
|
101
|
+
|
|
102
|
+
- Generate `railway.toml` with port configuration
|
|
103
|
+
- Include Dockerfile if appropriate
|
|
104
|
+
|
|
105
|
+
**Store answer as:** `cms.hostingTarget` in `brand.json`
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## Question 5 — Expected number of screens
|
|
110
|
+
|
|
111
|
+
**Ask exactly:**
|
|
112
|
+
|
|
113
|
+
> "How many screens will you manage initially? (1-10 is fine for testing. For larger deployments I'll add pagination and grouping features.)"
|
|
114
|
+
|
|
115
|
+
**If answer is 1-10:** generate simple list views without pagination.
|
|
116
|
+
|
|
117
|
+
**If answer is 11-100:** include pagination on the screens list page (page size 20).
|
|
118
|
+
|
|
119
|
+
**If answer is 100+:** include pagination, grouping, and tag filtering on the screens list. Note to user that fleet-level features (bulk commands, group broadcast) are recommended.
|
|
120
|
+
|
|
121
|
+
**Store answer as:** `cms.expectedScreens` in `brand.json`
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## After all five answers collected
|
|
126
|
+
|
|
127
|
+
Follow this sequence strictly:
|
|
128
|
+
|
|
129
|
+
1. **Write `brand.json`** at the project root using the answers. Validate against `brand.schema.json`.
|
|
130
|
+
2. **Run `npx tomorrowos init [dir]`** or copy `templates/cms-starter/` from the SDK package into the user's project as the seed.
|
|
131
|
+
3. **Customise the starter:**
|
|
132
|
+
- Replace placeholder brand name, colour, logo references
|
|
133
|
+
- Rename example content to match the use case
|
|
134
|
+
- Remove any platform-specific notes for platforms the user didn't select
|
|
135
|
+
4. **Read `BUILD_GUARDRAILS.md`** and verify every mandatory component is present in the generated code. Do not skip any.
|
|
136
|
+
5. **Run `npm install`** and confirm dependencies resolve.
|
|
137
|
+
6. **Start the dev server** (`npm run dev`) and confirm it runs without errors.
|
|
138
|
+
7. **Show the user `PLAYER_INSTALL.md`** and explain what to do next to get a screen paired and controllable.
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## What not to ask
|
|
143
|
+
|
|
144
|
+
Do not ask the user:
|
|
145
|
+
|
|
146
|
+
- How WebSocket connections should be handled (the SDK handles this)
|
|
147
|
+
- How to implement pairing (the SDK handles this)
|
|
148
|
+
- How to format command messages (the SDK handles this)
|
|
149
|
+
- Which WebSocket library to use (the SDK bundles this)
|
|
150
|
+
- What TCP port to use (use `process.env.PORT` or `3000`)
|
|
151
|
+
- Whether to use TypeScript or JavaScript (default to TypeScript; offer JS only if user explicitly asks)
|
|
152
|
+
|
|
153
|
+
These are implementation details the SDK controls. The user cares about their brand, their platform, their use case. Everything else is the SDK's job.
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## When to re-prompt
|
|
158
|
+
|
|
159
|
+
If the user's initial prompt already contains answers to some questions (e.g. "build me a CMS for my Acme Burger restaurant menu boards on Samsung Tizen"), record those answers and only ask the remaining questions. Do not re-ask what the user has already told you.
|
|
160
|
+
|
|
161
|
+
If the user says "just build it, don't ask me questions," respond:
|
|
162
|
+
|
|
163
|
+
> "I'd love to, but I need five quick answers to make sure the CMS you get actually works with your screens and looks like your brand. It'll take about sixty seconds. Ready?"
|
|
164
|
+
|
|
165
|
+
Do not proceed without the answers. The SDK will not function without at minimum: `targetPlatforms`, `brand.name`, `brand.primaryColor`, and `cms.hostingTarget`.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Protocol version
|
|
170
|
+
|
|
171
|
+
This elicitation protocol is version `1.0`, aligned with TomorrowOS wire protocol `1.0`. Future SDK versions may add questions; they will not remove existing ones without a major-version bump.
|