@telemetryos/cli 1.9.0 → 1.11.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 (41) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/commands/auth.js +8 -15
  3. package/dist/commands/init.js +131 -68
  4. package/dist/commands/publish.d.ts +22 -0
  5. package/dist/commands/publish.js +238 -0
  6. package/dist/index.js +2 -0
  7. package/dist/plugins/math-tools.d.ts +2 -0
  8. package/dist/plugins/math-tools.js +18 -0
  9. package/dist/services/api-client.d.ts +18 -0
  10. package/dist/services/api-client.js +70 -0
  11. package/dist/services/archiver.d.ts +4 -0
  12. package/dist/services/archiver.js +65 -0
  13. package/dist/services/build-poller.d.ts +10 -0
  14. package/dist/services/build-poller.js +63 -0
  15. package/dist/services/cli-config.d.ts +10 -0
  16. package/dist/services/cli-config.js +45 -0
  17. package/dist/services/generate-application.d.ts +2 -1
  18. package/dist/services/generate-application.js +31 -32
  19. package/dist/services/project-config.d.ts +24 -0
  20. package/dist/services/project-config.js +51 -0
  21. package/dist/services/run-server.js +29 -73
  22. package/dist/types/api.d.ts +44 -0
  23. package/dist/types/api.js +1 -0
  24. package/dist/types/applications.d.ts +44 -0
  25. package/dist/types/applications.js +1 -0
  26. package/dist/utils/ansi.d.ts +10 -0
  27. package/dist/utils/ansi.js +10 -0
  28. package/dist/utils/path-utils.d.ts +55 -0
  29. package/dist/utils/path-utils.js +99 -0
  30. package/package.json +4 -2
  31. package/templates/vite-react-typescript/CLAUDE.md +14 -6
  32. package/templates/vite-react-typescript/_claude/skills/tos-architecture/SKILL.md +4 -28
  33. package/templates/vite-react-typescript/_claude/skills/tos-multi-mode/SKILL.md +359 -0
  34. package/templates/vite-react-typescript/_claude/skills/tos-render-design/SKILL.md +304 -12
  35. package/templates/vite-react-typescript/_claude/skills/tos-render-kiosk-design/SKILL.md +384 -0
  36. package/templates/vite-react-typescript/_claude/skills/tos-render-signage-design/SKILL.md +515 -0
  37. package/templates/vite-react-typescript/_claude/skills/tos-render-ui-design/SKILL.md +325 -0
  38. package/templates/vite-react-typescript/_claude/skills/tos-requirements/SKILL.md +405 -125
  39. package/templates/vite-react-typescript/_claude/skills/tos-store-sync/SKILL.md +96 -5
  40. package/templates/vite-react-typescript/_claude/skills/tos-weather-api/SKILL.md +443 -269
  41. package/templates/vite-react-typescript/index.html +1 -1
@@ -0,0 +1,44 @@
1
+ export type ApplicationKind = 'git' | 'github' | 'uploaded' | null;
2
+ export type Application = {
3
+ id: string;
4
+ title: string;
5
+ description: string;
6
+ kind: ApplicationKind;
7
+ baseImage: string;
8
+ buildWorkingPath?: string;
9
+ buildScript?: string;
10
+ buildOutputPath?: string;
11
+ buildEnvironmentVariables?: Record<string, string>;
12
+ versions?: ApplicationVersion[];
13
+ createdAt?: string;
14
+ updatedAt?: string;
15
+ };
16
+ export type ApplicationVersion = {
17
+ name?: string;
18
+ version?: string;
19
+ applicationId: string;
20
+ buildId: string;
21
+ applicationSpecifier: string;
22
+ publishedAt: string;
23
+ };
24
+ export type ApplicationBuild = {
25
+ id: string;
26
+ applicationId: string;
27
+ index: number;
28
+ state: 'pending' | 'building' | 'success' | 'failure' | 'failed' | 'cancelled';
29
+ logs: string[];
30
+ scheduledAt?: string;
31
+ startedAt?: string;
32
+ finishedAt?: string;
33
+ };
34
+ export type CreateApplicationRequest = {
35
+ kind: 'uploaded';
36
+ title: string;
37
+ description?: string;
38
+ baseImage: string;
39
+ baseImageRegistryAuth: string;
40
+ buildWorkingPath: string;
41
+ buildScript: string;
42
+ buildOutputPath: string;
43
+ buildEnvironmentVariables?: Record<string, string>;
44
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,44 @@
1
+ export type ApplicationKind = 'git' | 'github' | 'uploaded' | null;
2
+ export type Application = {
3
+ id: string;
4
+ title: string;
5
+ description: string;
6
+ kind: ApplicationKind;
7
+ baseImage: string;
8
+ buildWorkingPath?: string;
9
+ buildScript?: string;
10
+ buildOutputPath?: string;
11
+ buildEnvironmentVariables?: Record<string, string>;
12
+ versions?: ApplicationVersion[];
13
+ createdAt?: string;
14
+ updatedAt?: string;
15
+ };
16
+ export type ApplicationVersion = {
17
+ name?: string;
18
+ version?: string;
19
+ applicationId: string;
20
+ buildId: string;
21
+ applicationSpecifier: string;
22
+ publishedAt: string;
23
+ };
24
+ export type ApplicationBuild = {
25
+ id: string;
26
+ applicationId: string;
27
+ index: number;
28
+ state: 'pending' | 'building' | 'success' | 'failure' | 'failed' | 'cancelled';
29
+ logs: string[];
30
+ scheduledAt?: string;
31
+ startedAt?: string;
32
+ finishedAt?: string;
33
+ };
34
+ export type CreateApplicationRequest = {
35
+ kind: 'uploaded';
36
+ title: string;
37
+ description?: string;
38
+ baseImage: string;
39
+ baseImageRegistryAuth: string;
40
+ buildWorkingPath: string;
41
+ buildScript: string;
42
+ buildOutputPath: string;
43
+ buildEnvironmentVariables?: Record<string, string>;
44
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,10 @@
1
+ export declare const ansi: {
2
+ readonly white: "\u001B[37m";
3
+ readonly yellow: "\u001B[33m";
4
+ readonly green: "\u001B[32m";
5
+ readonly cyan: "\u001B[36m";
6
+ readonly red: "\u001B[31m";
7
+ readonly bold: "\u001B[1m";
8
+ readonly dim: "\u001B[2m";
9
+ readonly reset: "\u001B[0m";
10
+ };
@@ -0,0 +1,10 @@
1
+ export const ansi = {
2
+ white: '\u001b[37m',
3
+ yellow: '\u001b[33m',
4
+ green: '\u001b[32m',
5
+ cyan: '\u001b[36m',
6
+ red: '\u001b[31m',
7
+ bold: '\u001b[1m',
8
+ dim: '\u001b[2m',
9
+ reset: '\u001b[0m',
10
+ };
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Converts a string to kebab-case
3
+ *
4
+ * @param str - The string to convert
5
+ * @returns The kebab-cased string
6
+ *
7
+ * @example
8
+ * toKebabCase('MyApp') // 'my-app'
9
+ * toKebabCase('my_app') // 'my-app'
10
+ * toKebabCase('My App!') // 'my-app'
11
+ * toKebabCase('my--app') // 'my-app'
12
+ */
13
+ export declare function toKebabCase(str: string): string;
14
+ /**
15
+ * Derives a project name from a given path
16
+ *
17
+ * @param projectPath - The path to derive the name from
18
+ * @param currentWorkingDirectory - The current working directory (defaults to process.cwd())
19
+ * @returns The derived project name in kebab-case
20
+ *
21
+ * @example
22
+ * deriveProjectName('my-project') // 'my-project'
23
+ * deriveProjectName('apps/MyApp') // 'my-app'
24
+ * deriveProjectName('./', '/Users/test/MyProject') // 'my-project'
25
+ * deriveProjectName('../parent') // 'parent'
26
+ * deriveProjectName('/absolute/path/to/app') // 'app'
27
+ */
28
+ export declare function deriveProjectName(projectPath: string, currentWorkingDirectory?: string): string;
29
+ /**
30
+ * Resolves a project path and derives the name
31
+ *
32
+ * @param projectPath - The path to resolve
33
+ * @param currentWorkingDirectory - The current working directory (defaults to process.cwd())
34
+ * @returns An object containing the resolved path and derived name
35
+ *
36
+ * @example
37
+ * resolveProjectPathAndName('apps/MyApp')
38
+ * // { resolvedPath: '/Users/user/cwd/apps/MyApp', derivedName: 'my-app' }
39
+ */
40
+ export declare function resolveProjectPathAndName(projectPath: string, currentWorkingDirectory?: string): {
41
+ resolvedPath: string;
42
+ derivedName: string;
43
+ };
44
+ /**
45
+ * Validates a project name according to npm package name requirements
46
+ *
47
+ * @param name - The project name to validate
48
+ * @returns true if valid, or an error message string if invalid
49
+ *
50
+ * @example
51
+ * validateProjectName('my-app') // true
52
+ * validateProjectName('') // 'Project name cannot be empty'
53
+ * validateProjectName('MyApp') // 'Project name must contain only lowercase letters, numbers, and hyphens'
54
+ */
55
+ export declare function validateProjectName(name: string): true | string;
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Utility functions for path handling and project name derivation
3
+ */
4
+ import path from 'node:path';
5
+ /**
6
+ * Converts a string to kebab-case
7
+ *
8
+ * @param str - The string to convert
9
+ * @returns The kebab-cased string
10
+ *
11
+ * @example
12
+ * toKebabCase('MyApp') // 'my-app'
13
+ * toKebabCase('my_app') // 'my-app'
14
+ * toKebabCase('My App!') // 'my-app'
15
+ * toKebabCase('my--app') // 'my-app'
16
+ */
17
+ export function toKebabCase(str) {
18
+ return (str
19
+ .trim()
20
+ // Remove special characters except spaces, underscores, and hyphens
21
+ .replace(/[^a-zA-Z0-9\s_-]/g, '')
22
+ // Replace spaces and underscores with hyphens
23
+ .replace(/[\s_]+/g, '-')
24
+ // Insert hyphen before uppercase letters preceded by lowercase
25
+ .replace(/([a-z])([A-Z])/g, '$1-$2')
26
+ // Convert to lowercase
27
+ .toLowerCase()
28
+ // Replace multiple consecutive hyphens with single hyphen
29
+ .replace(/-+/g, '-')
30
+ // Remove leading/trailing hyphens
31
+ .replace(/^-+|-+$/g, ''));
32
+ }
33
+ /**
34
+ * Derives a project name from a given path
35
+ *
36
+ * @param projectPath - The path to derive the name from
37
+ * @param currentWorkingDirectory - The current working directory (defaults to process.cwd())
38
+ * @returns The derived project name in kebab-case
39
+ *
40
+ * @example
41
+ * deriveProjectName('my-project') // 'my-project'
42
+ * deriveProjectName('apps/MyApp') // 'my-app'
43
+ * deriveProjectName('./', '/Users/test/MyProject') // 'my-project'
44
+ * deriveProjectName('../parent') // 'parent'
45
+ * deriveProjectName('/absolute/path/to/app') // 'app'
46
+ */
47
+ export function deriveProjectName(projectPath, currentWorkingDirectory = process.cwd()) {
48
+ // Resolve the path to handle relative paths
49
+ const resolvedPath = path.resolve(currentWorkingDirectory, projectPath);
50
+ // Get the last segment of the path
51
+ const basename = path.basename(resolvedPath);
52
+ // Convert to kebab-case
53
+ return toKebabCase(basename);
54
+ }
55
+ /**
56
+ * Resolves a project path and derives the name
57
+ *
58
+ * @param projectPath - The path to resolve
59
+ * @param currentWorkingDirectory - The current working directory (defaults to process.cwd())
60
+ * @returns An object containing the resolved path and derived name
61
+ *
62
+ * @example
63
+ * resolveProjectPathAndName('apps/MyApp')
64
+ * // { resolvedPath: '/Users/user/cwd/apps/MyApp', derivedName: 'my-app' }
65
+ */
66
+ export function resolveProjectPathAndName(projectPath, currentWorkingDirectory = process.cwd()) {
67
+ const resolvedPath = path.resolve(currentWorkingDirectory, projectPath);
68
+ const derivedName = deriveProjectName(projectPath, currentWorkingDirectory);
69
+ return { resolvedPath, derivedName };
70
+ }
71
+ /**
72
+ * Validates a project name according to npm package name requirements
73
+ *
74
+ * @param name - The project name to validate
75
+ * @returns true if valid, or an error message string if invalid
76
+ *
77
+ * @example
78
+ * validateProjectName('my-app') // true
79
+ * validateProjectName('') // 'Project name cannot be empty'
80
+ * validateProjectName('MyApp') // 'Project name must contain only lowercase letters, numbers, and hyphens'
81
+ */
82
+ export function validateProjectName(name) {
83
+ if (!name || name.length === 0) {
84
+ return 'Project name cannot be empty';
85
+ }
86
+ if (name.length > 214) {
87
+ return 'Project name must be 214 characters or less';
88
+ }
89
+ if (name.startsWith('.') || name.startsWith('_')) {
90
+ return 'Project name cannot start with . or _';
91
+ }
92
+ if (!/^[a-z0-9-]+$/.test(name)) {
93
+ return 'Project name must contain only lowercase letters, numbers, and hyphens';
94
+ }
95
+ if (name.startsWith('-') || name.endsWith('-')) {
96
+ return 'Project name cannot start or end with a hyphen';
97
+ }
98
+ return true;
99
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@telemetryos/cli",
3
- "version": "1.9.0",
3
+ "version": "1.11.0",
4
4
  "description": "The official TelemetryOS application CLI package. Use it to build applications that run on the TelemetryOS platform",
5
5
  "type": "module",
6
6
  "bin": {
@@ -25,11 +25,13 @@
25
25
  "license": "",
26
26
  "repository": "github:TelemetryTV/Application-API",
27
27
  "dependencies": {
28
- "@telemetryos/development-application-host-ui": "^1.9.0",
28
+ "@telemetryos/development-application-host-ui": "^1.11.0",
29
29
  "@types/serve-handler": "^6.1.4",
30
30
  "commander": "^14.0.0",
31
+ "ignore": "^6.0.2",
31
32
  "inquirer": "^12.9.6",
32
33
  "serve-handler": "^6.1.6",
34
+ "tar": "^7.4.3",
33
35
  "zod": "^4.1.12"
34
36
  },
35
37
  "devDependencies": {
@@ -13,8 +13,11 @@ tos serve # Start dev server (or: npm run dev)
13
13
  **IMPORTANT:** Always run `npm run build` after making changes to check for TypeScript errors. Do not rely solely on the dev server.
14
14
 
15
15
  **Development Host:** http://localhost:2026
16
- - Settings: http://localhost:2026/settings
17
- - Render: http://localhost:2026/render
16
+ Both the render and settings mounts points are visible in the development host.
17
+ The Render mount point is presented in a resizable pane.
18
+ The Settings mount point shows in the right sidebar.
19
+
20
+ **The development host is already running!** The user has already started it and the agent doesn't need to run it
18
21
 
19
22
  ## Architecture
20
23
 
@@ -99,15 +102,20 @@ const response = await proxy().fetch('https://api.example.com/data')
99
102
 
100
103
  **IMPORTANT:** You MUST invoke the relevant skill BEFORE writing code for these tasks:
101
104
 
105
+ **For Render views:** ALWAYS read `tos-render-ui-design` first, then the appropriate specialized skill.
106
+
102
107
  | Task | Required Skill | Why |
103
108
  |------|----------------|-----|
104
- | Building Render views | `tos-render-design` | Digital signage constraints, UI scaling, no hover/scroll |
109
+ | Starting new project | `tos-requirements` | Gather requirements before coding MUST USE |
110
+ | Building ANY Render view | `tos-render-ui-design` | UI scaling foundation - ALWAYS read first |
111
+ | Building digital signage | `tos-render-signage-design` | Display-only patterns (no interaction) |
112
+ | Building interactive kiosk | `tos-render-kiosk-design` | Touch interaction, idle timeout, navigation |
105
113
  | Adding ANY Settings UI | `tos-settings-ui` | SDK components are required - raw HTML won't work |
106
- | Calling external APIs | `tos-proxy-fetch` | Proxy patterns prevent CORS errors |
107
114
  | Adding store keys | `tos-store-sync` | Hook patterns ensure Settings↔Render sync |
108
- | Weather integration | `tos-weather-api` | API-specific patterns and credentials |
115
+ | Building multi-mode apps | `tos-multi-mode` | Entity-scoped data, mode switching, namespace patterns |
116
+ | Calling external APIs | `tos-proxy-fetch` | Proxy patterns prevent CORS errors |
109
117
  | Media library access | `tos-media-api` | SDK media methods and types |
110
- | Starting new project | `tos-requirements` | Gather requirements before coding |
118
+ | Weather integration | `tos-weather-api` | API-specific patterns and credentials |
111
119
  | Debugging issues | `tos-debugging` | Common errors and fixes |
112
120
 
113
121
  **Never write Render layouts, Settings components, or proxy.fetch code without invoking the skill first.**
@@ -232,15 +232,11 @@ export function App() {
232
232
 
233
233
  ### Local Development
234
234
 
235
- ```bash
236
- # Start dev server
237
- tos serve
238
- # Or: npm run dev
235
+ **Development Host:** http://localhost:2026
236
+ Both the render and settings mounts points are visible in the development host.
237
+ The Render mount point is presented in a resizable pane.
238
+ The Settings mount point shows in the right sidebar.
239
239
 
240
- # Access locally:
241
- # Settings: http://localhost:3000/settings
242
- # Render: http://localhost:3000/render
243
- ```
244
240
 
245
241
  ### Build & Deploy
246
242
 
@@ -255,26 +251,6 @@ git add . && git commit -m "Update" && git push
255
251
 
256
252
  ## Common Patterns
257
253
 
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
254
  ### Handle Missing Config
279
255
 
280
256
  ```typescript