@telemetryos/cli 1.7.4 → 1.8.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.
Files changed (25) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/commands/init.js +11 -0
  3. package/dist/services/generate-application.d.ts +1 -0
  4. package/dist/services/generate-application.js +127 -4
  5. package/dist/utils/validate-project-name.d.ts +19 -0
  6. package/dist/utils/validate-project-name.js +44 -0
  7. package/package.json +2 -2
  8. package/templates/vite-react-typescript/CLAUDE.md +68 -1244
  9. package/templates/vite-react-typescript/_claude/settings.local.json +17 -0
  10. package/templates/vite-react-typescript/_claude/skills/tos-architecture/SKILL.md +313 -0
  11. package/templates/vite-react-typescript/_claude/skills/tos-debugging/SKILL.md +299 -0
  12. package/templates/vite-react-typescript/_claude/skills/tos-media-api/SKILL.md +335 -0
  13. package/templates/vite-react-typescript/_claude/skills/tos-proxy-fetch/SKILL.md +319 -0
  14. package/templates/vite-react-typescript/_claude/skills/tos-render-design/SKILL.md +332 -0
  15. package/templates/vite-react-typescript/_claude/skills/tos-requirements/SKILL.md +252 -0
  16. package/templates/vite-react-typescript/_claude/skills/tos-settings-ui/SKILL.md +636 -0
  17. package/templates/vite-react-typescript/_claude/skills/tos-store-sync/SKILL.md +359 -0
  18. package/templates/vite-react-typescript/_claude/skills/tos-weather-api/SKILL.md +384 -0
  19. package/templates/vite-react-typescript/src/hooks/store.ts +3 -3
  20. package/templates/vite-react-typescript/src/index.css +0 -1
  21. package/templates/vite-react-typescript/src/views/Render.css +2 -1
  22. package/templates/vite-react-typescript/src/views/Render.tsx +2 -3
  23. package/templates/vite-react-typescript/src/views/Settings.tsx +2 -3
  24. package/templates/vite-react-typescript/AGENTS.md +0 -7
  25. /package/templates/vite-react-typescript/{gitignore → _gitignore} +0 -0
@@ -0,0 +1,17 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Skill(tos-architecture)",
5
+ "Skill(tos-debugging)",
6
+ "Skill(tos-media-api)",
7
+ "Skill(tos-proxy-fetch)",
8
+ "Skill(tos-render-design)",
9
+ "Skill(tos-requirements)",
10
+ "Skill(tos-settings-ui)",
11
+ "Skill(tos-store-sync)",
12
+ "Skill(tos-weather-api)",
13
+ "Bash(npm run build:*)",
14
+ "Bash(npm run dev:*)"
15
+ ]
16
+ }
17
+ }
@@ -0,0 +1,313 @@
1
+ ---
2
+ name: tos-architecture
3
+ description: Understand TelemetryOS two-mount-point architecture (Render vs Settings). Use when debugging mount-point issues or understanding how Settings and Render communicate.
4
+ ---
5
+
6
+ # TelemetryOS Architecture
7
+
8
+ TelemetryOS applications run on digital signage devices (TVs, kiosks, displays). Understanding the architecture is key to building effective apps.
9
+
10
+ ## Two Mount Points
11
+
12
+ Every standard TOS app has two entry points:
13
+
14
+ ### /render - Device Display
15
+
16
+ ```
17
+ ┌─────────────────────────────────┐
18
+ │ Physical Device │
19
+ │ (TV, Kiosk, Digital Sign) │
20
+ │ │
21
+ │ ┌───────────────────────────┐ │
22
+ │ │ Chrome Browser │ │
23
+ │ │ ┌─────────────────────┐ │ │
24
+ │ │ │ Your App │ │ │
25
+ │ │ │ /render route │ │ │
26
+ │ │ │ │ │ │
27
+ │ │ │ Shows content to │ │ │
28
+ │ │ │ end users/viewers │ │ │
29
+ │ │ └─────────────────────┘ │ │
30
+ │ └───────────────────────────┘ │
31
+ └─────────────────────────────────┘
32
+ ```
33
+
34
+ **Characteristics:**
35
+ - Runs on physical device (TelemetryOS player)
36
+ - Chrome browser in iframe sandbox
37
+ - Has access to `store().device` (device-local storage)
38
+ - Displays content to viewers/customers
39
+ - Full screen, optimized for viewing from distance
40
+ - Can access device hardware info
41
+
42
+ ### /settings - Admin Configuration
43
+
44
+ ```
45
+ ┌─────────────────────────────────┐
46
+ │ Admin's Browser │
47
+ │ (Desktop/Laptop/Tablet) │
48
+ │ │
49
+ │ ┌───────────────────────────┐ │
50
+ │ │ TelemetryOS Studio │ │
51
+ │ │ ┌─────────────────────┐ │ │
52
+ │ │ │ Your App │ │ │
53
+ │ │ │ /settings route │ │ │
54
+ │ │ │ │ │ │
55
+ │ │ │ Configuration UI │ │ │
56
+ │ │ │ for administrators │ │ │
57
+ │ │ └─────────────────────┘ │ │
58
+ │ └───────────────────────────┘ │
59
+ └─────────────────────────────────┘
60
+ ```
61
+
62
+ **Characteristics:**
63
+ - Runs in Studio admin portal (user's browser)
64
+ - NO access to `store().device` (throws error!)
65
+ - Admin-facing UI for configuration
66
+ - Side panel in Studio editor
67
+ - Form-based controls for settings
68
+
69
+ ## Communication Flow
70
+
71
+ ```
72
+ ┌─────────────┐ ┌─────────────────┐ ┌─────────────┐
73
+ │ Settings │ ───▶ │ store(). │ ───▶ │ Render │
74
+ │ /settings │ WRITE │ instance │ READ │ /render │
75
+ │ │ │ │ +SUB │ │
76
+ │ Admin sets │ │ { city: 'NYC' │ │ Displays │
77
+ │ city='NYC' │ │ units: 'F' } │ │ NYC weather│
78
+ └─────────────┘ └─────────────────┘ └─────────────┘
79
+ ```
80
+
81
+ 1. **Settings writes** to instance store via hooks
82
+ 2. **Render subscribes** to instance store via same hooks
83
+ 3. Changes sync automatically (real-time)
84
+ 4. Same hooks work in both mount points
85
+
86
+ ## Project Structure
87
+
88
+ ```
89
+ src/
90
+ ├── index.tsx # Entry point - configure SDK here
91
+ ├── App.tsx # Router - directs to correct view
92
+ ├── views/
93
+ │ ├── Settings.tsx # /settings mount point
94
+ │ └── Render.tsx # /render mount point
95
+ ├── hooks/
96
+ │ └── store.ts # Shared store hooks (used in both views)
97
+ ├── components/ # Reusable UI components
98
+ ├── types/ # TypeScript interfaces
99
+ └── utils/ # Helper functions
100
+ ```
101
+
102
+ ## Routing
103
+
104
+ ```typescript
105
+ // App.tsx
106
+ import Settings from './views/Settings'
107
+ import Render from './views/Render'
108
+
109
+ export function App() {
110
+ const path = window.location.pathname
111
+
112
+ if (path === '/settings') return <Settings />
113
+ if (path === '/render') return <Render />
114
+
115
+ return <div>Invalid mount point: {path}</div>
116
+ }
117
+ ```
118
+
119
+ Or with React Router:
120
+
121
+ ```typescript
122
+ import { createBrowserRouter, RouterProvider } from 'react-router'
123
+ import Settings from './views/Settings'
124
+ import Render from './views/Render'
125
+
126
+ const router = createBrowserRouter([
127
+ { path: '/render', element: <Render /> },
128
+ { path: '/settings', element: <Settings /> },
129
+ ])
130
+
131
+ export function App() {
132
+ return <RouterProvider router={router} />
133
+ }
134
+ ```
135
+
136
+ ## Runtime Environment
137
+
138
+ ### Both Mount Points
139
+
140
+ - **Browser:** Modern Chrome (platform-controlled version)
141
+ - **Execution:** Iframe sandbox
142
+ - **Runtime:** Client-side only (no SSR, no Node.js)
143
+ - **APIs:** Full browser APIs (Fetch, WebSocket, Canvas, WebGL)
144
+ - **SDK:** `@telemetryos/sdk` for platform communication
145
+
146
+ ### Render Only
147
+
148
+ - `store().device` - Device-local storage
149
+ - `devices().getInformation()` - Hardware info
150
+ - Physical display output
151
+ - Background worker communication
152
+
153
+ ### Settings Only
154
+
155
+ - SettingsUI components (`@telemetryos/sdk/react`)
156
+ - Runs in Studio portal
157
+ - Form-based interaction
158
+ - Preview pane integration
159
+
160
+ ## telemetry.config.json
161
+
162
+ ```json
163
+ {
164
+ "name": "my-app",
165
+ "version": "1.0.0",
166
+ "mountPoints": {
167
+ "render": "/render",
168
+ "settings": "/settings"
169
+ },
170
+ "devServer": {
171
+ "runCommand": "vite --port 3000",
172
+ "url": "http://localhost:3000"
173
+ }
174
+ }
175
+ ```
176
+
177
+ ### Mount Point Configuration
178
+
179
+ | Mount Point | Purpose | Route |
180
+ |-------------|---------|-------|
181
+ | render | Device display | /render |
182
+ | settings | Admin config UI | /settings |
183
+
184
+ ### Optional: Workers
185
+
186
+ ```json
187
+ {
188
+ "backgroundWorkers": {
189
+ "sync": "workers/sync.js"
190
+ },
191
+ "serverWorkers": {
192
+ "api": "workers/api.js"
193
+ }
194
+ }
195
+ ```
196
+
197
+ ## Store Scopes
198
+
199
+ ```
200
+ ┌────────────────────────────────────────────────────────────────┐
201
+ │ Account │
202
+ │ ┌──────────────────────────────────────────────────────────┐ │
203
+ │ │ store().application │ │
204
+ │ │ Shared across ALL instances of this app │ │
205
+ │ │ (API keys, account-wide settings) │ │
206
+ │ └──────────────────────────────────────────────────────────┘ │
207
+ │ │
208
+ │ ┌─────────────────────┐ ┌─────────────────────┐ │
209
+ │ │ App Instance 1 │ │ App Instance 2 │ │
210
+ │ │ instance store │ │ instance store │ │
211
+ │ │ (Settings↔Render) │ │ (Settings↔Render) │ │
212
+ │ └─────────────────────┘ └─────────────────────┘ │
213
+ └────────────────────────────────────────────────────────────────┘
214
+
215
+ ┌────────────────────────────────────────────────────────────────┐
216
+ │ Physical Device │
217
+ │ ┌──────────────────────────────────────────────────────────┐ │
218
+ │ │ store().device │ │
219
+ │ │ Local to this device only (Render only!) │ │
220
+ │ │ (Cache, calibration, device-specific data) │ │
221
+ │ └──────────────────────────────────────────────────────────┘ │
222
+ └────────────────────────────────────────────────────────────────┘
223
+
224
+ ┌────────────────────────────────────────────────────────────────┐
225
+ │ store().shared('namespace') │
226
+ │ Inter-app communication (any app can read/write) │
227
+ │ (Weather data sharing, event broadcasting) │
228
+ └────────────────────────────────────────────────────────────────┘
229
+ ```
230
+
231
+ ## Development Workflow
232
+
233
+ ### Local Development
234
+
235
+ ```bash
236
+ # Start dev server
237
+ tos serve
238
+ # Or: npm run dev
239
+
240
+ # Access locally:
241
+ # Settings: http://localhost:3000/settings
242
+ # Render: http://localhost:3000/render
243
+ ```
244
+
245
+ ### Build & Deploy
246
+
247
+ ```bash
248
+ # Build production
249
+ npm run build
250
+
251
+ # Deploy via Git
252
+ git add . && git commit -m "Update" && git push
253
+ # GitHub integration auto-deploys
254
+ ```
255
+
256
+ ## Common Patterns
257
+
258
+ ### Check Mount Point
259
+
260
+ ```typescript
261
+ const isSettings = window.location.pathname === '/settings'
262
+ const isRender = window.location.pathname === '/render'
263
+ ```
264
+
265
+ ### Conditional Features
266
+
267
+ ```typescript
268
+ // In a shared component
269
+ const isRender = window.location.pathname === '/render'
270
+
271
+ // Only fetch external data in Render
272
+ useEffect(() => {
273
+ if (!isRender) return
274
+ fetchExternalData()
275
+ }, [isRender])
276
+ ```
277
+
278
+ ### Handle Missing Config
279
+
280
+ ```typescript
281
+ // In Render.tsx
282
+ const [isLoading, city] = useCityStoreState()
283
+
284
+ if (isLoading) return <div>Loading...</div>
285
+ if (!city) return <div>Please configure city in Settings</div>
286
+
287
+ return <WeatherDisplay city={city} />
288
+ ```
289
+
290
+ ## Debugging
291
+
292
+ ### Check Current Path
293
+
294
+ ```typescript
295
+ console.log('Current path:', window.location.pathname)
296
+ console.log('Is Settings:', window.location.pathname === '/settings')
297
+ console.log('Is Render:', window.location.pathname === '/render')
298
+ ```
299
+
300
+ ### Verify SDK Configuration
301
+
302
+ ```typescript
303
+ // In index.tsx - name must match telemetry.config.json
304
+ configure('my-app')
305
+ ```
306
+
307
+ ### Check Store Values
308
+
309
+ ```typescript
310
+ // Temporary debug
311
+ const value = await store().instance.get('myKey')
312
+ console.log('Store value:', value)
313
+ ```
@@ -0,0 +1,299 @@
1
+ ---
2
+ name: tos-debugging
3
+ description: Debug common TelemetryOS application issues and errors. Use when encountering SDK errors, sync issues, or render problems.
4
+ ---
5
+
6
+ # TelemetryOS Debugging Guide
7
+
8
+ Common errors, their causes, and solutions.
9
+
10
+ ## Error Reference
11
+
12
+ ### "SDK not configured"
13
+
14
+ **Cause:** SDK methods called before `configure()`.
15
+
16
+ **Solution:**
17
+ ```typescript
18
+ // index.tsx - MUST call configure before React renders
19
+ import { configure } from '@telemetryos/sdk'
20
+
21
+ configure('my-app') // Name must match telemetry.config.json
22
+
23
+ ReactDOM.createRoot(document.getElementById('root')!).render(<App />)
24
+ ```
25
+
26
+ **Check:**
27
+ - Is `configure()` called in index.tsx?
28
+ - Is it called BEFORE ReactDOM.createRoot()?
29
+ - Does the name match `telemetry.config.json`?
30
+
31
+ ---
32
+
33
+ ### "device storage not available"
34
+
35
+ **Cause:** Using `store().device` in Settings view.
36
+
37
+ **Why:** Settings runs in Studio (admin's browser), not on devices. Device storage is only available on physical TelemetryOS devices.
38
+
39
+ **Solution:**
40
+ ```typescript
41
+ // WRONG - in Settings.tsx
42
+ const [, value, setValue] = useMyState() // using createUseDeviceStoreState
43
+
44
+ // CORRECT - use instance scope
45
+ const [, value, setValue] = useMyState() // using createUseInstanceStoreState
46
+ ```
47
+
48
+ **Scope guide:**
49
+ - `createUseInstanceStoreState` - Settings ↔ Render communication
50
+ - `createUseApplicationStoreState` - Shared across all instances
51
+ - `createUseDeviceStoreState` - Render ONLY (device-local)
52
+
53
+ ---
54
+
55
+ ### CORS Error
56
+
57
+ **Symptom:** Browser console shows:
58
+ ```
59
+ Access to fetch at 'https://api.example.com' from origin 'http://localhost:3000'
60
+ has been blocked by CORS policy
61
+ ```
62
+
63
+ **Cause:** External API doesn't include CORS headers.
64
+
65
+ **Solution:**
66
+ ```typescript
67
+ // WRONG - blocked by CORS
68
+ const response = await fetch('https://api.example.com/data')
69
+
70
+ // CORRECT - use proxy
71
+ import { proxy } from '@telemetryos/sdk'
72
+ const response = await proxy().fetch('https://api.example.com/data')
73
+ ```
74
+
75
+ ---
76
+
77
+ ### "Request timeout"
78
+
79
+ **Cause:** SDK operation exceeded 30-second timeout.
80
+
81
+ **Solution:**
82
+ ```typescript
83
+ try {
84
+ const response = await proxy().fetch(url)
85
+ // ...
86
+ } catch (err) {
87
+ if (err instanceof Error && err.message.includes('timeout')) {
88
+ // Handle timeout - maybe retry or show error
89
+ console.error('Request timed out')
90
+ }
91
+ }
92
+ ```
93
+
94
+ **Prevention:**
95
+ - Check if API endpoint is slow or unreachable
96
+ - Add loading states to prevent user confusion
97
+ - Consider retry logic for intermittent issues
98
+
99
+ ---
100
+
101
+ ### Render Not Updating
102
+
103
+ **Symptoms:**
104
+ - Settings changes don't appear in Render
105
+ - Render shows stale data
106
+ - Preview in Studio doesn't update
107
+
108
+ **Causes & Solutions:**
109
+
110
+ **1. Missing store hook in Render:**
111
+ ```typescript
112
+ // Render.tsx - MUST use same hook as Settings
113
+ import { useCityState } from '../hooks/store'
114
+
115
+ const [isLoading, city] = useCityState()
116
+ ```
117
+
118
+ **2. Not using instance scope:**
119
+ ```typescript
120
+ // WRONG - different scopes don't sync
121
+ // Settings: createUseApplicationStoreState
122
+ // Render: createUseInstanceStoreState
123
+
124
+ // CORRECT - same scope
125
+ // Both use: createUseInstanceStoreState
126
+ ```
127
+
128
+ **3. Missing subscription (low-level API):**
129
+ ```typescript
130
+ // If using manual subscription
131
+ useEffect(() => {
132
+ const handler = (value) => setValue(value)
133
+ store().instance.subscribe('key', handler)
134
+
135
+ return () => store().instance.unsubscribe('key', handler) // IMPORTANT!
136
+ }, [])
137
+ ```
138
+
139
+ ---
140
+
141
+ ### Memory Leak Warning
142
+
143
+ **Symptom:** React warning about memory leak or state update on unmounted component.
144
+
145
+ **Cause:** Not cleaning up store subscriptions.
146
+
147
+ **Solution - Use SDK hooks (recommended):**
148
+ ```typescript
149
+ // Automatic cleanup
150
+ const [isLoading, value, setValue] = useMyState()
151
+ ```
152
+
153
+ **Solution - Manual subscription:**
154
+ ```typescript
155
+ useEffect(() => {
156
+ const handler = (value) => setValue(value)
157
+ store().instance.subscribe('key', handler)
158
+
159
+ // MUST return cleanup function
160
+ return () => {
161
+ store().instance.unsubscribe('key', handler)
162
+ }
163
+ }, [])
164
+ ```
165
+
166
+ ---
167
+
168
+ ### Blank Screen
169
+
170
+ **Check these in order:**
171
+
172
+ 1. **Browser console for errors**
173
+ - Open DevTools (F12)
174
+ - Check Console tab for red errors
175
+
176
+ 2. **Correct route**
177
+ ```
178
+ http://localhost:3000/render ← for Render view
179
+ http://localhost:3000/settings ← for Settings view
180
+ ```
181
+
182
+ 3. **App.tsx routing**
183
+ ```typescript
184
+ if (path === '/settings') return <Settings />
185
+ if (path === '/render') return <Render />
186
+ ```
187
+
188
+ 4. **SDK configured**
189
+ ```typescript
190
+ // index.tsx
191
+ configure('app-name') // Before React renders
192
+ ```
193
+
194
+ 5. **telemetry.config.json matches**
195
+ ```json
196
+ {
197
+ "name": "app-name", // Must match configure()
198
+ "mountPoints": {
199
+ "render": "/render",
200
+ "settings": "/settings"
201
+ }
202
+ }
203
+ ```
204
+
205
+ ---
206
+
207
+ ### TypeScript Errors
208
+
209
+ **"Cannot find module '@telemetryos/sdk'"**
210
+ ```bash
211
+ npm install @telemetryos/sdk
212
+ ```
213
+
214
+ **"Property does not exist on type"**
215
+ ```typescript
216
+ // Add type parameter to store operations
217
+ const value = await store().instance.get<MyType>('key')
218
+
219
+ // Or define interfaces
220
+ interface Config {
221
+ city: string
222
+ units: 'imperial' | 'metric'
223
+ }
224
+ ```
225
+
226
+ **"Type 'string | undefined' is not assignable"**
227
+ ```typescript
228
+ // Handle undefined from store
229
+ const config = await store().instance.get<Config>('config')
230
+ if (config) {
231
+ // config is now Config, not Config | undefined
232
+ }
233
+ ```
234
+
235
+ ---
236
+
237
+ ## Debugging Techniques
238
+
239
+ ### Console Logging
240
+
241
+ ```typescript
242
+ // Check current path
243
+ console.log('Path:', window.location.pathname)
244
+
245
+ // Check store values
246
+ useEffect(() => {
247
+ store().instance.get('myKey').then(value => {
248
+ console.log('Store value:', value)
249
+ })
250
+ }, [])
251
+
252
+ // Check hook values
253
+ const [isLoading, value] = useMyState()
254
+ console.log('isLoading:', isLoading, 'value:', value)
255
+ ```
256
+
257
+ ### Network Tab
258
+
259
+ 1. Open DevTools → Network tab
260
+ 2. Look for failed requests (red)
261
+ 3. Check request/response details
262
+ 4. Verify API endpoints and headers
263
+
264
+ ### React DevTools
265
+
266
+ 1. Install React DevTools browser extension
267
+ 2. Inspect component props and state
268
+ 3. Check hook values
269
+ 4. Trace re-renders
270
+
271
+ ### Local Development URLs
272
+
273
+ ```
274
+ Settings: http://localhost:3000/settings
275
+ Render: http://localhost:3000/render
276
+ ```
277
+
278
+ ---
279
+
280
+ ## Quick Fixes Checklist
281
+
282
+ - [ ] `configure()` called before React in index.tsx
283
+ - [ ] App name matches telemetry.config.json
284
+ - [ ] Using `createUseInstanceStoreState` (not device) in Settings
285
+ - [ ] Same store hooks in Settings and Render
286
+ - [ ] `disabled={isLoading}` on form inputs
287
+ - [ ] Error handling with try/catch
288
+ - [ ] Cleanup in useEffect return
289
+ - [ ] CORS issues → use `proxy().fetch()`
290
+
291
+ ---
292
+
293
+ ## Getting Help
294
+
295
+ 1. **Check browser console** - Most errors show here first
296
+ 2. **Check network tab** - API failures visible here
297
+ 3. **Read error message** - SDK errors are descriptive
298
+ 4. **Simplify** - Remove code until error disappears, then add back
299
+ 5. **Compare to template** - Check working template code