@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.
@@ -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,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -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();
@@ -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,2 @@
1
+ export { TomorrowOS } from "./tomorrowos.js";
2
+ export { MemoryStore } from "./store/memory-store.js";
@@ -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"}