README.md

March 24, 2026 · View on GitHub

Qodalis Web CLI

A web-based terminal for Angular, React, and Vue — extensible, themeable, and packed with built-in developer tools.

npm version npm version npm version npm version Build Status License: MIT

Live Demo · Documentation · npm


Try It Online

FrameworkStackBlitzCodeSandboxGitpod
AngularOpen in StackBlitzOpen in CodeSandboxOpen in Gitpod
ReactOpen in StackBlitzOpen in CodeSandboxOpen in Gitpod
VueOpen in StackBlitzOpen in CodeSandboxOpen in Gitpod
VanillaOpen in StackBlitzOpen in CodeSandboxOpen in Gitpod

Help command

Packages

PackageDescription
@qodalis/cli-coreShared interfaces, models, and types
@qodalis/cliFramework-agnostic terminal engine (50+ built-in commands)
@qodalis/angular-cliAngular wrapper
@qodalis/react-cliReact wrapper
@qodalis/vue-cliVue 3 wrapper

Quick Start

Angular

npm install @qodalis/angular-cli
import { CliModule } from '@qodalis/angular-cli';

@NgModule({
  imports: [CliModule],
})
export class AppModule {}

Add styles to angular.json:

{
  "styles": [
    "node_modules/@qodalis/angular-cli/src/assets/styles.css"
  ]
}
<!-- Full terminal -->
<cli [options]="cliOptions" />

<!-- Collapsible panel -->
<cli-panel />

React

npm install @qodalis/react-cli
import '@qodalis/react-cli/styles.css';
import { Cli } from '@qodalis/react-cli';

function App() {
  return <Cli style={{ width: '100vw', height: '100vh' }} />;
}

Vue

npm install @qodalis/vue-cli
<script setup lang="ts">
import '@qodalis/vue-cli/styles.css';
import { Cli } from '@qodalis/vue-cli';
</script>

<template>
  <Cli :style="{ width: '100vw', height: '100vh' }" />
</template>

Configuration (all frameworks)

Pass options to customize the terminal:

const options = {
  welcomeMessage: {
    message: '-- your custom welcome message --',
    show: 'daily', // 'never', 'once', 'daily', 'always'
  },
  usersModule: {
    enabled: true,
  },
};

Built-in Commands

System

CommandAliasesDescription
helpmanShow all commands or help for a specific command
versionverDisplay CLI version and documentation link
hotkeysshortcuts, keysShow keyboard shortcuts
historyhistBrowse and clear command history
themethemesApply, customize, and save terminal themes (interactive selection with live preview)
feedbacksupportReport bugs or request features on GitHub
pkgpackagesInstall, update, remove, and browse packages

Utilities

CommandAliasesDescription
clearclsClear the terminal (or Ctrl+L)
echoprintPrint text to the terminal
evalcalc, jsEvaluate JavaScript expressions
sleepwaitPause execution for N milliseconds
timedateShow current local and UTC time
uptimeShow session uptime
unamesysinfoSystem and browser information
screendisplayScreen, viewport, and terminal dimensions
openOpen a URL in a new browser tab
alias / unaliasCreate and remove command aliases
yesOutput a string repeatedly
seqsequencePrint number sequences
calcalendarDisplay a monthly calendar

Developer Tools

CommandAliasesDescription
base64b64Encode / decode Base64
jsonFormat, minify, and validate JSON
urlEncode, decode, and parse URLs
jwtDecode and inspect JWT tokens
hashSHA-256, SHA-1, SHA-384, SHA-512 hashes
hexHex encode/decode and base conversion (2-36)
colorcolourConvert between hex, rgb, hsl with preview
randomrandRandom numbers, strings, UUIDs, coin flip, dice
loremlipsumGenerate placeholder text
timestampts, epochConvert between Unix timestamps and dates
convertconvUnit conversion (length, weight, temperature, data)
clipboardcb, pbcopyCopy to / paste from clipboard

Users

CommandAliasesDescription
whoamimeDisplay current user
suswitch-userSwitch user session
adduseruseraddAdd a new user
listusersusersList all users

Package Manager

Install additional commands at runtime from npm — no rebuild needed.

Install packages

pkg browse                     # Browse available packages
pkg add guid                   # Install a package
pkg remove guid                # Remove a package
pkg update                     # Update all packages
pkg update guid@1.0.2          # Pin a specific version
pkg check                      # Check for updates
pkg versions guid              # Show all published versions
pkg source set                 # Interactively select a package source (CDN)
pkg source set unpkg           # Set package source directly

Available Packages

Utility Plugins

PackageCommandDescription
@qodalis/cli-data-explorerdata-explorerInteractive query console for SQL, MongoDB, Redis, Elasticsearch
@qodalis/cli-guidguidGenerate and validate UUIDs
@qodalis/cli-regexregexRegular expression testing
@qodalis/cli-qrqrQR code generation
@qodalis/cli-speed-testspeed-testInternet speed test
@qodalis/cli-curlcurlHTTP requests (GET, POST, PUT, DELETE)
@qodalis/cli-password-generatorgenerate-passwordPassword generation
@qodalis/cli-stringstringString manipulation (case, trim, reverse, slug, wc, etc.)
@qodalis/cli-todotodoTask management
@qodalis/cli-browser-storagelocal-storage, cookiesBrowser storage operations
@qodalis/cli-text-to-imagetext-to-imageGenerate images from text
@qodalis/cli-filesls, cat, nano, mkdir, touch, rmVirtual filesystem
@qodalis/cli-yesnoyesnoInteractive yes/no confirmation prompts
@qodalis/cli-server-logsserver logsLive server log streaming with level filtering
@qodalis/cli-server-jobsserver jobsManage server-side background jobs
@qodalis/cli-userswhoami, adduser, loginUser management and authentication
@qodalis/cli-chartchartRender bar, line, and sparkline charts
@qodalis/cli-croncronSchedule and manage recurring commands
@qodalis/cli-csvcsvParse, filter, sort, and convert CSV data
@qodalis/cli-markdownmdRender Markdown content in the terminal
@qodalis/cli-encodebase64, url, hex, htmlEncode/decode Base64, URL, hex, HTML entities
@qodalis/cli-scpscpSecure copy — transfer files between local and remote
@qodalis/cli-wgetwgetDownload files from URLs
@qodalis/cli-stopwatchstopwatchInteractive stopwatch and countdown timer

Game Plugins

PackageCommandDescription
@qodalis/cli-snakesnakeClassic Snake game
@qodalis/cli-tetristetrisTetris
@qodalis/cli-204820482048 sliding puzzle
@qodalis/cli-minesweeperminesweeperMinesweeper
@qodalis/cli-wordlewordleWordle word game
@qodalis/cli-sudokusudokuSudoku puzzle

Language Packs

PackageLanguage
@qodalis/cli-lang-esSpanish
@qodalis/cli-lang-frFrench
@qodalis/cli-lang-deGerman
@qodalis/cli-lang-itItalian
@qodalis/cli-lang-ptPortuguese
@qodalis/cli-lang-roRomanian
@qodalis/cli-lang-ruRussian
@qodalis/cli-lang-zhChinese
@qodalis/cli-lang-jaJapanese
@qodalis/cli-lang-koKorean

Any npm package with UMD support can also be loaded:

pkg add lodash
eval _.map([1, 2, 3], n => n * 2)

Server Integration

Connect the frontend terminal to a backend server for server-side command execution, interactive shell sessions, filesystem access, and event streaming. Three official backend implementations are available:

ServerPackagePortTech Stack
.NETQodalis.Cli8046ASP.NET Core, .NET 8
Node.js@qodalis/cli-server8047Express, TypeScript, node-pty
Pythonqodalis-cli-server8048FastAPI, uvicorn

All three implement the same API surface:

EndpointMethodPurpose
/api/cli/versionsGETVersion discovery
/api/v1/cli/commandsGETList registered command processors
/api/v1/cli/executePOSTExecute a CLI command
/api/v1/cli/execute/streamPOSTExecute with SSE streaming output
/ws/v1/cli/eventsWSServer-push event streaming
/ws/v1/cli/shellWSInteractive PTY shell session
/api/cli/fs/*VariousFilesystem operations (ls, cat, upload, download, mkdir, rm)

Connecting to a Server

import { CliOptions } from '@qodalis/cli-core';

const options: CliOptions = {
  servers: [
    { name: 'node', url: 'http://localhost:8047' },
    { name: 'dotnet', url: 'http://localhost:8046' },
    { name: 'python', url: 'http://localhost:8048' },
  ],
};

// Angular: <cli [options]="options" />
// React:   <Cli options={options} />
// Vue:     <Cli :options="options" />

SSE Streaming Execution

The frontend automatically uses SSE streaming when the server supports it, with transparent fallback to the legacy POST endpoint. Streaming processors emit output chunks as they execute rather than buffering the full response.

Resilience

The frontend includes built-in resilience features:

  • WebSocket auto-reconnect with exponential backoff (up to 5 attempts, max 30s delay)
  • Periodic health checks for disconnected servers (30s interval)
  • HTTP retry on transient fetch failures (1 retry with 1s delay)
  • Configurable request timeout per server (default 30s)

Server Plugins

PluginServersDescription
Background Jobs.NET, Node.js, PythonScheduled and recurring job execution with cron/interval support
Admin Dashboard.NET, Node.jsReact-based web UI for server management — commands, jobs, plugins, logs, filesystem, events, and an embedded terminal
AWS Cloud.NET, PythonS3, EC2, Lambda, DynamoDB, IAM, ECS, SQS, SNS, CloudWatch with multi-profile credential management
Data Explorer.NET, Node.js, PythonSQL, MongoDB, Redis, Elasticsearch, and custom data source providers

Docker

Run all three servers with Docker Compose from the workspace root:

docker compose up --build

Creating a CLI Plugin

Quick Start

Scaffold a complete plugin project with a single command:

npx @qodalis/create-cli-plugin

The interactive prompts will ask for:

PromptDescriptionExample
Plugin nameLowercase, no spaces, no cli- prefix (added automatically)weather
DescriptionShort description of the pluginWeather forecasts
Processor class namePascalCase name for the command processor classWeather

You can also skip the prompts by passing arguments directly:

npx @qodalis/create-cli-plugin --name weather --description "Weather forecasts" --processor-name Weather

Or install globally:

npm install -g @qodalis/create-cli-plugin
create-cli-plugin

Standalone vs Monorepo Mode

The tool auto-detects whether you're inside the web-cli monorepo:

ModeDetectionOutput directoryPackage manager
StandaloneAny directory outside web-cli./qodalis-cli-<name>/npm or pnpm (auto-detected)
MonorepoInside web-cli workspacepackages/plugins/<name>/pnpm (workspace)

In monorepo mode, the tool also:

  • Creates project.json (Nx build + test targets)
  • Creates tsconfig.spec.json (Karma/Jasmine test config)
  • Updates tsconfig.base.json with the path alias @qodalis/cli-<name> -> dist/<name>

Generated Project Structure

qodalis-cli-weather/                    # or packages/plugins/weather/ in monorepo
  package.json                          # npm package with CJS/ESM/UMD exports
  tsup.config.ts                        # Build config (library + IIFE bundles)
  tsconfig.json                         # TypeScript config
  README.md                             # Auto-generated README
  src/
    public-api.ts                       # Public exports + ICliModule declaration
    cli-entrypoint.ts                   # IIFE entrypoint for browser runtime loading
    lib/
      version.ts                        # LIBRARY_VERSION + API_VERSION constants
      index.ts                          # Barrel re-exports
      processors/
        cli-weather-command-processor.ts # Command processor (your main logic goes here)
    tests/
      index.spec.ts                     # Jasmine test scaffold

Step-by-Step Workflow

1. Scaffold the plugin

npx @qodalis/create-cli-plugin --name weather

2. Implement your command processor

Edit src/lib/processors/cli-weather-command-processor.ts:

import {
    CliProcessCommand,
    DefaultLibraryAuthor,
    ICliCommandProcessor,
    ICliExecutionContext,
} from '@qodalis/cli-core';
import { LIBRARY_VERSION } from '../version';

export class CliWeatherCommandProcessor implements ICliCommandProcessor {
    command = 'weather';
    description = 'Weather forecasts';
    author = DefaultLibraryAuthor;
    version = LIBRARY_VERSION;

    // Sub-commands
    processors?: ICliCommandProcessor[] = [
        {
            command: 'forecast',
            description: 'Get weather forecast for a city',
            parameters: [
                {
                    name: 'city',
                    aliases: ['c'],
                    description: 'City name',
                    required: true,
                    type: 'string',
                },
                {
                    name: 'days',
                    aliases: ['d'],
                    description: 'Number of days',
                    required: false,
                    type: 'number',
                    defaultValue: '3',
                },
            ],
            processCommand: async (command, context) => {
                const city = command.args['city'];
                const days = parseInt(command.args['days'] ?? '3');
                context.writer.writeln(`Forecast for ${city} (${days} days):`);
                context.writer.writeSuccess('Sunny, 25°C');
            },
        },
        {
            command: 'current',
            description: 'Get current weather',
            acceptsRawInput: true,    // command.value = text after 'current'
            valueRequired: true,       // error if no value provided
            processCommand: async (command, context) => {
                context.writer.writeln(`Current weather in ${command.value}: Sunny, 25°C`);
            },
        },
    ];

    // Default handler (runs when user types just 'weather')
    async processCommand(command: CliProcessCommand, context: ICliExecutionContext): Promise<void> {
        context.executor.showHelp(command, context);
    }
}

3. Build the plugin

Standalone:

cd qodalis-cli-weather
npm run build          # or: npx tsup

Monorepo:

pnpm nx build weather

Build output goes to dist/weather/ with three bundles:

  • public-api.js (CJS) + public-api.mjs (ESM) + public-api.d.ts (types)
  • umd/index.js (IIFE — self-contained browser bundle for runtime pkg add)

4. Test the plugin

Standalone: Add your preferred test runner.

Monorepo:

pnpm nx test weather

5. Publish to npm

cd dist/weather         # or: cd qodalis-cli-weather/dist
npm publish --access public

Users can then install your plugin at runtime in any Qodalis CLI terminal:

pkg add @qodalis/cli-weather
weather forecast --city "New York"

The ICliModule Export

Every plugin exports an ICliModule object in public-api.ts. This is how frameworks register your plugin:

import { ICliModule } from '@qodalis/cli-core';
import { CliWeatherCommandProcessor } from './lib/processors/cli-weather-command-processor';
import { API_VERSION } from './lib/version';

export const weatherModule: ICliModule = {
    apiVersion: API_VERSION,         // must be >= 2
    name: '@qodalis/cli-weather',
    processors: [new CliWeatherCommandProcessor()],
};

ICliModule also supports optional lifecycle hooks and configuration:

export const weatherModule: ICliModule = {
    apiVersion: 2,
    name: '@qodalis/cli-weather',
    processors: [new CliWeatherCommandProcessor()],
    dependencies: ['@qodalis/cli-curl'],     // boot other modules first
    priority: 0,                              // boot order (lower = first)
    configure(config) { /* ... */ return this; },
    onInit(context) { /* before processors initialize */ },
    onAfterBoot(context) { /* after all modules boot */ },
    onDestroy(context) { /* teardown */ },
};

The IIFE Entrypoint

src/cli-entrypoint.ts enables runtime loading via pkg add. It calls bootCliModule() which registers the module with the global window.__cliModuleRegistry:

import { bootCliModule, ICliModule } from '@qodalis/cli-core';
import { CliWeatherCommandProcessor } from './lib/processors/cli-weather-command-processor';
import { API_VERSION } from './lib/version';

const module: ICliModule = {
    apiVersion: API_VERSION,
    name: '@qodalis/cli-weather',
    processors: [new CliWeatherCommandProcessor()],
};

bootCliModule(module);

Execution Context API

The ICliExecutionContext passed to processCommand provides:

PropertyDescription
context.writerTerminal output — writeln(), writeInfo(), writeSuccess(), writeError(), wrapInColor()
context.readerUser input — readLine(), readPassword(), readConfirm(), readSelect(), readMultiSelect(), readNumber()
context.executorCommand execution — showHelp(command, context)
context.clipboardClipboard — write(), read()
context.statePersistent key-value store
context.progressBarProgress bar widget
context.spinnerSpinner widget
context.terminalRaw xterm.js Terminal instance
context.onAbortSubject<void> for Ctrl+C cancellation
context.enterFullScreenMode()Switch to full-screen TUI mode
context.createInterval() / context.createTimeout()Managed timers (auto-cleaned on abort)

Registering Plugins in Your App

Angular (via providers)

import { CliModule, resolveCliModuleProvider } from '@qodalis/angular-cli';
import { weatherModule } from '@qodalis/cli-weather';

@NgModule({
  imports: [CliModule],
  providers: [resolveCliModuleProvider(weatherModule)],
})
export class AppModule {}

Angular (via template input)

import { weatherModule } from '@qodalis/cli-weather';

export class AppComponent {
  modules = [weatherModule];
}
<cli [modules]="modules" />

React

import { CliConfigProvider, Cli } from '@qodalis/react-cli';
import { weatherModule } from '@qodalis/cli-weather';

function App() {
  return (
    <CliConfigProvider modules={[weatherModule]}>
      <Cli />
    </CliConfigProvider>
  );
}

Vue

<script setup lang="ts">
import { CliConfigProvider, Cli } from '@qodalis/vue-cli';
import { weatherModule } from '@qodalis/cli-weather';
</script>

<template>
  <CliConfigProvider :modules="[weatherModule]">
    <Cli />
  </CliConfigProvider>
</template>

Data Explorer

The @qodalis/cli-data-explorer plugin adds an interactive, full-screen REPL for querying data sources (SQL, MongoDB, Redis, Elasticsearch, and custom providers) directly from the terminal.

Setup

Install and register the plugin, then configure backend servers:

import { dataExplorerModule } from '@qodalis/cli-data-explorer';

// Add to your modules array
const modules = [dataExplorerModule, /* ... other modules */];

// Configure at least one server with data explorer providers
const options: CliOptions = {
  servers: [
    { name: 'node', url: 'http://localhost:8047' },
    { name: 'dotnet', url: 'http://localhost:8046' },
    { name: 'python', url: 'http://localhost:8048' },
  ],
};

Or install at runtime without a rebuild:

pkg add data-explorer

Usage

data-explorer

Select a server and data source, then enter the full-screen REPL:

data-explorer> SELECT * FROM users WHERE active = true;

3 rows (12ms)
┌────┬───────┬──────────────┐
│ id │ name  │ email        │
├────┼───────┼──────────────┤
│  1 │ Alice │ alice@ex.com │
│  2 │ Bob   │ bob@ex.com   │
│  3 │ Carol │ carol@ex.com │
└────┴───────┴──────────────┘

data-explorer> \format json
Output format set to: json

data-explorer> db.users.find({"age": {"$gt": 25}})
[
  { "_id": "abc", "name": "Alice", "age": 30 },
  { "_id": "def", "name": "Bob", "age": 28 }
]

REPL Commands

CommandDescription
\format <table|json|csv|raw>Switch output format
\schemaShow database schema (tables, columns, types)
\templatesList available query templates
\use <name>Load a template query
\historyShow query history
\clearClear screen
\helpShow all commands
\quit / \qExit

Use Up/Down arrows for history navigation, Escape or Ctrl+C to exit.

Extending with Inline Commands

For quick one-off commands without creating a full plugin, implement ICliCommandProcessor directly in your app:

import {
  ICliCommandProcessor,
  CliProcessCommand,
  ICliExecutionContext,
} from '@qodalis/cli-core';

export class GreetCommandProcessor implements ICliCommandProcessor {
  command = 'greet';
  description = 'Greet someone by name';
  acceptsRawInput = true;
  valueRequired = true;

  async processCommand(
    command: CliProcessCommand,
    context: ICliExecutionContext,
  ): Promise<void> {
    context.writer.writeln(`Hello, ${command.value}!`);
  }
}

Register it with your framework (Angular: resolveCommandProcessorProvider(GreetCommandProcessor), React/Vue: pass via processors prop), or wrap it in an inline ICliModule:

const myModule: ICliModule = {
  apiVersion: 2,
  name: 'my-app-commands',
  processors: [new GreetCommandProcessor()],
};
~$ greet World
Hello, World!

Using the Engine Directly

For advanced use cases or non-framework environments, use CliEngine directly:

import { CliEngine } from '@qodalis/cli';

const engine = new CliEngine(document.getElementById('terminal')!, {
  welcomeMessage: { message: 'Welcome!', show: 'always' },
});

engine.registerProcessors([new GreetCommandProcessor()]);
await engine.start();

Features

  • Multi-framework — Angular, React, Vue, or vanilla JS
  • 38 official plugins — 24 utility, 6 games, 10 language packs
  • Command chaining with &&, ||, |, and >> operators
  • Command history with arrow key navigation
  • Tab-like completions and keyboard shortcuts (Ctrl+C, Ctrl+L, Escape)
  • 29 built-in themes with custom color support
  • Tabs & split panes — multiple terminals in tabs with horizontal splitting
  • User sessions with multi-user support
  • State persistence across sessions
  • Interactive prompts with live preview (select menus with real-time feedback)
  • Full-screen mode API for rich TUI commands (games, editors, pagers)
  • Progress bars, spinners, and text animations
  • Runtime package installation from npm
  • Server integration with SSE streaming, auto-reconnect, and health checks
  • Admin dashboard for server management (React SPA)
  • Data explorer for SQL, MongoDB, Redis, and Elasticsearch

Contributing

  1. Fork this repository
  2. Create a branch for your feature or bugfix
  3. Submit a pull request

License

MIT