# Milestone 11: Developer CLI Tool **Status**: Planning **Goal**: Command-line tool for app development workflow. --- ## Overview The CLI tool (`mosis`) streamlines the developer workflow: project creation, building, testing, signing, and publishing. --- ## Commands Overview ``` mosis ├── init Create new app project ├── validate Validate manifest and assets ├── build Create .mosis package ├── sign Sign package with developer key ├── run Run in local designer/emulator ├── test Run automated tests ├── login Authenticate with portal ├── logout Clear authentication ├── publish Upload and submit for review ├── status Check review status ├── keys │ ├── generate Generate signing keypair │ ├── list List registered keys │ └── register Upload public key to portal └── config ├── get Get config value └── set Set config value ``` --- ## Command Details ### `mosis init` Create a new app project with boilerplate. ```bash $ mosis init ? App name: My Calculator ? Package ID: com.myname.calculator ? Description: A simple calculator app ? Author name: John Doe ? Author email: john@example.com Creating project structure... ✓ Created manifest.json ✓ Created assets/main.rml ✓ Created assets/styles/theme.rcss ✓ Created assets/scripts/app.lua ✓ Created icons/ (placeholder icons) Project created! Next steps: cd my-calculator mosis run # Preview in designer mosis build # Create package mosis publish # Submit to store ``` #### Generated Structure ``` my-calculator/ ├── manifest.json ├── assets/ │ ├── main.rml │ ├── styles/ │ │ └── theme.rcss │ └── scripts/ │ └── app.lua ├── icons/ │ ├── icon-32.png │ ├── icon-64.png │ └── icon-128.png └── .mosisignore # Files to exclude from package ``` --- ### `mosis validate` Validate project without building. ```bash $ mosis validate Validating manifest.json... ✓ Required fields present ✓ Package ID format valid ✓ Version format valid Validating assets... ✓ Entry point exists: assets/main.rml ✓ All RML files valid (3 files) ✓ All RCSS files valid (2 files) ✓ All Lua files valid (4 files) Validating icons... ✓ icon-32.png (32x32) ✓ icon-64.png (64x64) ✓ icon-128.png (128x128) Checking permissions... ✓ Permissions declared: storage, network All validations passed! ``` --- ### `mosis build` Create a .mosis package. ```bash $ mosis build Reading manifest.json... Package: com.myname.calculator v1.0.0 (1) Collecting files... ✓ manifest.json ✓ assets/main.rml ✓ assets/styles/theme.rcss ✓ assets/scripts/app.lua ✓ icons/icon-32.png ✓ icons/icon-64.png ✓ icons/icon-128.png Creating package... ✓ Package created: dist/com.myname.calculator-1.0.0.mosis (45.2 KB) ⚠ Package is unsigned. Run 'mosis sign' before publishing. ``` #### Options ```bash mosis build [options] Options: -o, --output Output path (default: dist/) --no-compress Skip compression --include-source Include .lua source maps ``` --- ### `mosis sign` Sign a package with developer key. ```bash $ mosis sign dist/com.myname.calculator-1.0.0.mosis Using key: ~/.mosis/signing_key.pem Fingerprint: SHA256:abc123... Generating file hashes... Signing MANIFEST.MF... ✓ Package signed: dist/com.myname.calculator-1.0.0.mosis Signature details: Algorithm: Ed25519 Key fingerprint: SHA256:abc123... Files signed: 7 ``` #### Options ```bash mosis sign [options] Options: -k, --key Path to private key (default: ~/.mosis/signing_key.pem) --verify Verify existing signature ``` --- ### `mosis run` Launch in desktop designer for testing. ```bash $ mosis run Starting Mosis Designer... Loading: assets/main.rml Designer running at http://localhost:8080 Press Ctrl+C to stop [Hot reload enabled - changes auto-refresh] ``` #### Options ```bash mosis run [options] Options: --entry Override entry point --port Designer port (default: 8080) --no-hot-reload Disable hot reload --device Emulate device (phone, tablet) ``` --- ### `mosis login` Authenticate with developer portal. ```bash $ mosis login Opening browser for authentication... Waiting for authorization... ✓ Logged in as john@example.com API key stored in ~/.mosis/credentials ``` #### Options ```bash mosis login [options] Options: --api-key Use API key instead of browser auth --portal Portal URL (default: https://portal.mosis.dev) ``` --- ### `mosis publish` Upload and submit for review. ```bash $ mosis publish Checking authentication... ✓ Logged in as john@example.com Building package... ✓ Package created: dist/com.myname.calculator-1.0.0.mosis Signing package... ✓ Package signed Uploading... ████████████████████████████████ 100% Submitting for review... ✓ Version 1.0.0 submitted Review status: In Review Estimated review time: 24 hours Track status: mosis status ``` #### Options ```bash mosis publish [options] Options: --package Use existing package --notes Release notes --notes-file Release notes from file --draft Upload without submitting for review ``` --- ### `mosis status` Check app/version status. ```bash $ mosis status App: My Calculator (com.myname.calculator) Versions: v1.0.0 (1) Published Jan 10, 2024 1,234 downloads v1.1.0 (2) In Review Jan 15, 2024 Submitted 2h ago Latest review: Status: In Review Submitted: Jan 15, 2024 10:30 AM Estimated completion: Jan 16, 2024 Run 'mosis status --watch' to monitor in real-time. ``` --- ### `mosis keys generate` Generate Ed25519 signing keypair. ```bash $ mosis keys generate Generating Ed25519 keypair... Private key saved to: ~/.mosis/signing_key.pem Public key saved to: ~/.mosis/signing_key.pub Fingerprint: SHA256:abc123def456... ⚠ IMPORTANT: Keep your private key secure! - Never share or commit signing_key.pem - Back it up securely - If compromised, revoke immediately Next step: Register your public key mosis keys register ``` --- ### `mosis keys register` Upload public key to portal. ```bash $ mosis keys register Reading public key from ~/.mosis/signing_key.pub Fingerprint: SHA256:abc123def456... ? Key name: MacBook Pro 2024 Uploading to portal... ✓ Key registered successfully Your signing key is now active. Packages signed with this key will be accepted for review. ``` --- ## Configuration ### Config File Location ``` ~/.mosis/ ├── config.json # CLI configuration ├── credentials # Auth tokens (encrypted) ├── signing_key.pem # Private key └── signing_key.pub # Public key ``` ### Config Options ```json { "portal_url": "https://portal.mosis.dev", "api_url": "https://api.mosis.dev", "designer_path": "/usr/local/bin/mosis-designer", "default_author": { "name": "John Doe", "email": "john@example.com" } } ``` --- ## Implementation ### Tech Stack Options #### Option A: Go ```go // Using cobra for CLI framework package main import ( "github.com/spf13/cobra" ) func main() { rootCmd := &cobra.Command{ Use: "mosis", Short: "Mosis app development CLI", } rootCmd.AddCommand(initCmd()) rootCmd.AddCommand(buildCmd()) rootCmd.AddCommand(signCmd()) rootCmd.AddCommand(publishCmd()) // ... rootCmd.Execute() } ``` **Pros**: Single binary, fast, cross-platform **Cons**: More code to write #### Option B: Node.js (oclif) ```typescript // Using oclif framework import { Command } from '@oclif/core' export default class Build extends Command { static description = 'Build .mosis package' async run() { const manifest = await this.readManifest() const files = await this.collectFiles() const package = await this.createPackage(files) this.log(`✓ Package created: ${package.path}`) } } ``` **Pros**: Fast development, npm distribution **Cons**: Requires Node.js runtime #### Option C: Rust (clap) ```rust use clap::{Parser, Subcommand}; #[derive(Parser)] #[command(name = "mosis")] struct Cli { #[command(subcommand)] command: Commands, } #[derive(Subcommand)] enum Commands { Init { name: Option }, Build { output: Option }, Sign { package: PathBuf }, Publish, } fn main() { let cli = Cli::parse(); match cli.command { Commands::Init { name } => init::run(name), Commands::Build { output } => build::run(output), // ... } } ``` **Pros**: Single binary, very fast **Cons**: Slower development --- ## Distribution ### npm (Node.js version) ```bash npm install -g @mosis/cli ``` ### Homebrew (macOS) ```bash brew tap mosis/tap brew install mosis ``` ### Direct Download ```bash # Linux/macOS curl -fsSL https://mosis.dev/install.sh | sh # Windows irm https://mosis.dev/install.ps1 | iex ``` ### Package Managers | Platform | Package Manager | Command | |----------|-----------------|---------| | macOS | Homebrew | `brew install mosis` | | Windows | Scoop | `scoop install mosis` | | Linux | apt (deb) | `apt install mosis` | | Any | npm | `npm install -g @mosis/cli` | --- ## Error Handling ### User-Friendly Errors ```bash $ mosis build Error: manifest.json not found Are you in a Mosis project directory? Run 'mosis init' to create a new project. ``` ### Verbose Mode ```bash $ mosis build --verbose [DEBUG] Reading manifest from ./manifest.json [DEBUG] Manifest parsed: {id: "com.example.app", ...} [DEBUG] Collecting files from ./assets [DEBUG] Found 7 files [DEBUG] Creating ZIP archive [DEBUG] Writing to dist/com.example.app-1.0.0.mosis [DEBUG] Package size: 45234 bytes ✓ Package created ``` --- ## CI/CD Integration ### GitHub Actions ```yaml name: Build and Publish on: push: tags: - 'v*' jobs: publish: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Setup Mosis CLI run: npm install -g @mosis/cli - name: Build and Sign env: MOSIS_SIGNING_KEY: ${{ secrets.MOSIS_SIGNING_KEY }} run: | echo "$MOSIS_SIGNING_KEY" > signing_key.pem mosis build mosis sign dist/*.mosis --key signing_key.pem - name: Publish env: MOSIS_API_KEY: ${{ secrets.MOSIS_API_KEY }} run: mosis publish --api-key "$MOSIS_API_KEY" ``` --- ## Deliverables - [ ] CLI framework selection - [ ] `init` command - [ ] `validate` command - [ ] `build` command - [ ] `sign` command - [ ] `run` command (designer integration) - [ ] `login/logout` commands - [ ] `publish` command - [ ] `status` command - [ ] `keys` subcommands - [ ] Configuration management - [ ] Distribution packages - [ ] CI/CD examples --- ## Open Questions 1. Should CLI auto-update itself? 2. Offline mode for build/sign? 3. Plugin system for custom commands? 4. IDE integrations (VS Code extension)? --- ## References - [Cobra CLI Framework](https://cobra.dev/) - [oclif Framework](https://oclif.io/) - [Clap for Rust](https://docs.rs/clap/)