Project Structure
CLI commands, templates, generated file structure, and build configuration
Project Structure
The tos CLI (included with the TelemetryOS Developer App) scaffolds a complete, production-ready application in seconds. The generated project follows TelemetryOS conventions — mount points are wired up, the SDK is pre-configured, and the development environment is ready to run.
Creating a Project
tos init my-weather-appThis creates a my-weather-app/ directory with the full project structure, installs dependencies, and initializes a Git repository. The project is ready to run immediately:
cd my-weather-app
tos devGenerated Structure
my-weather-app/
├── src/
│ ├── render/ # Display component shown on devices
│ │ ├── index.tsx
│ │ └── App.tsx
│ ├── settings/ # Configuration UI shown in Studio sidebar
│ │ ├── index.tsx
│ │ └── Settings.tsx
│ └── shared/ # Shared code and types
├── public/ # Static assets
├── telemetry.config.json # TelemetryOS platform configuration
├── vite.config.ts # Build configuration
├── tsconfig.json # TypeScript configuration
├── package.json # Dependencies and scripts
└── .gitignore # Git ignore rules
The src/render/ directory contains the display component that runs on devices. The src/settings/ directory contains the configuration interface that appears in the Studio sidebar. Both components come pre-configured with SDK imports and example storage patterns so you can see the settings-to-render communication working immediately.
Configuration
The telemetry.config.json file tells the platform where to find each component and how to run the development server:
{
"name": "my-weather-app",
"version": "2026.1.0",
"mountPoints": {
"render": "/render",
"settings": "/settings"
},
"devServer": {
"runCommand": "vite --port $PORT",
"url": "http://localhost:$PORT",
"readyPattern": "VITE.*ready in"
}
}The mountPoints paths correspond to the directory structure under src/. The $PORT variable is replaced at runtime by the Developer App with an available port. See Configuration for the complete reference.
Build Configuration
The generated vite.config.ts is pre-configured with multiple entry points:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
build: {
rollupOptions: {
input: {
render: '/render/index.html',
settings: '/settings/index.html'
}
}
}
});CLI Commands
tos init
Create a new application from a template.
tos init [project-path] [options]| Option | Description |
|---|---|
-d, --description <string> | Application description |
-a, --author <string> | Application author |
-v, --version <string> | Application version (default: CalVer e.g. 2026.4.0) |
-t, --template <string> | Template to use (default: vite-react-typescript) |
-y, --yes | Skip prompts and use defaults |
To scaffold into the current directory, use . as the path:
tos init .When run without --yes, the CLI interactively prompts for the project name, description, author, version, template, and whether to include Claude Code skills and settings.
tos dev
Open a project in the Developer App.
tos dev [project-path]tos serve is an alias for tos dev.
tos auth
Authenticate with TelemetryOS by saving your API token. Required before tos publish.
tos auth [options]| Option | Description |
|---|---|
-t, --token <string> | Provide the API token directly (skip interactive prompt) |
-f, --force | Replace existing token without confirmation |
Tokens are obtained from Studio > Settings > API Tokens.
tos publish
Publish an application to TelemetryOS. Archives the project, uploads it, and streams build logs.
tos publish [project-path] [options]| Option | Description |
|---|---|
-i, --interactive | Interactively prompt for all build configuration |
--name <name> | Application name (overrides config) |
--base-image <image> | Docker base image for build (default: node:20) |
--build-script <script> | Build commands (default: npm install && npm run build) |
--build-output <path> | Build output directory (default: /dist) |
--working-path <path> | Working directory in container (default: /) |
Build configuration options only apply when creating a new application. For existing applications, these are ignored.
tos version
Bump the project version (CalVer patch increment).
tos version [project-path]tos archive
Archive a project as a .tar.gz file, respecting .gitignore rules.
tos archive [project-path] [options]| Option | Description |
|---|---|
-o, --output <path> | Output directory for the archive |
tos migrate
Detect and apply project migrations (SDK updates, config schema changes).
tos migrate [project-path] [options]| Option | Description |
|---|---|
-y, --yes | Skip confirmation prompt |
tos mcp
Connect AI agents to the Developer App via MCP (Model Context Protocol). Acts as a stdio-to-HTTP proxy when the Developer App is running, or starts a lightweight fallback server when it is not.
tos mcp [project-path]tos claude-code
Apply or update Claude Code skills and settings in a project.
tos claude-code [project-path]Templates
Specify a template with the --template flag:
tos init my-app --template vite-react-typescript-webvite-react-typescript (default) — React with TypeScript and Vite. Full type safety with SDK type definitions, hot module replacement, and the fastest build times. Includes render and settings mount points.
vite-react-typescript-web — Same as the default template, plus a Web mount point for browser-accessible interfaces (staff dashboards, public kiosks, etc.).
Adding Components
Workers
To add a background worker, create the worker file and update the configuration:
// src/worker/index.ts
import { configure, store } from '@telemetryos/sdk';
configure();
async function syncData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
await store().application.set('latest-data', data);
} catch (error) {
console.error('Sync failed:', error);
}
setTimeout(syncData, 60000);
}
syncData();{
"backgroundWorkers": {
"sync": "workers/sync.js"
}
}Containers
To add a Docker container for backend services, add the container definition to telemetry.config.json:
{
"containers": {
"api-server": { "image": "myapp/api:latest", "port": 3000 }
}
}The container key becomes a hostname accessible from your application code: fetch('http://api-server:3000/data'). Containers only run on physical devices, not in Studio or the local development server.
Updated about 1 hour ago