---
title: "Widget CLI"
description: "All CLI commands for creating, building, testing, and publishing GlassHome widgets."
canonical: https://glasshome.app/docs/widget-cli
section: "CLI & Publishing"
updated: 2026-06-09
---
# Widget CLI

The package is `@glasshome/widget-cli` and its binary is `glasshome-widget`. Scaffolded projects pin `@glasshome/widget-cli` in `devDependencies` and add a `widget` script alias in `package.json` (e.g. `"widget": "glasshome-widget"`), which is why every command below is shown as `bun widget <command>`; the alias runs the project-local version, kept current with `bun update`. Outside a scaffolded project use `bunx @glasshome/widget-cli@latest <command>` instead. The `@latest` tag matters: a bare `bunx @glasshome/widget-cli` can reuse a stale cached version.

## Version requirements

Hub enforces a minimum CLI version. The current floor is **0.4.11**, served from `/api/widgets/cli-version`. CLIs at or above **0.4.14** fetch this endpoint on `login` and `publish`, and hard-stop below the floor with an actionable message instead of a cryptic OAuth or HTTP error. CLIs older than 0.4.14 lack the check, so the floor only protects clients that can read it.

If you see a "widget CLI is no longer supported" error, update:

```bash
# one-off, no install:
bunx @glasshome/widget-cli@latest publish

# or update the project dev dependency:
bun add -D @glasshome/widget-cli@latest
```

## create

```bash
bunx @glasshome/widget-cli@latest create
# or, equivalent, runs `create` by default when not inside a project:
bunx @glasshome/widget-cli@latest
```

Scaffolds a new widget project with `package.json`, `vite.config.ts`, `tsconfig`, and a starter widget (`create` already prompts for the first widget's details, no extra `add` call needed). Run this outside of an existing project.

## add

```bash
bun widget add
```

Adds a new widget to your project. Prompts for a name and description, then creates `src/{name}/index.tsx` and `src/{name}/manifest.json`.

## build

```bash
bun widget build
```

Builds all widgets into per-widget, self-contained JS bundles in `dist/`. Also generates `dist/registry.json` with metadata for all widgets.

## validate

```bash
bun widget validate [name]
```

Checks manifests, bundle sizes, and registry consistency. Pass a widget name to validate just one, or omit for all.

## info

```bash
bun widget info [name]
```

Prints the project header, the local registry summary, and per-widget metadata + bundle size. Useful for sanity-checking before publishing.

## connect

```bash
bun widget connect <url> [--re-auth]
```

Connects to a running Dash instance for live testing. Builds all widgets, uploads each bundle to the dashboard API under the `local` scope, registers them with the dashboard, and watches for changes. Widgets hot-reload as you edit. No local server is started (see [Local Testing Workflow](#local-testing-workflow)).

Auth: `connect` uses the **OAuth device-code flow** against the local Dash instance. A browser window opens to the Dash approval page; approve it, and the CLI stores a host-scoped bearer token. This is distinct from `login`, which authenticates against GlassHome Hub.

Flags:

- `--re-auth`: discard stored credentials and force a fresh device-authorization flow.

## publish

```bash
bun widget publish [hub-url] [--name <widget>] [--bump keep|patch|minor|major] [--scope <scope>]
```

Validates first, prompts for scope/widget/version (or takes them from flags), then builds and uploads via a presigned R2 URL. Auto-runs `login` if no token is found, so the explicit `login` step is optional.

Flags:

- `--name <widget>`: publish only the named widget instead of prompting.
- `--bump keep|patch|minor|major`: pre-pick the version bump (updates the `version` field in `src/<name>/manifest.json`).
- `--scope <scope>`: pre-pick the publishing scope (e.g. `@my-team`).

## login

```bash
bun widget login [hub-url]
```

Authenticates with **GlassHome Hub** via OAuth PKCE (not the local Dash instance). Opens a browser, listens on `http://127.0.0.1:9274/callback` for the callback (`localhost` is also accepted; 120 s timeout), stores the resulting token locally. `publish` runs this automatically if no token is present.

## upgrade

```bash
bun widget upgrade
```

Syncs the `sdkVersion` field in every `manifest.json` to the SDK version actually installed in your project. The SDK (`@glasshome/widget-sdk`) is published to npm. How the upgrade works depends on your project type:

**Standalone project:** Update the `@glasshome/widget-sdk` range in `peerDependencies` (or `devDependencies`) in your `package.json`, run `bun install` to fetch the new version from npm, then run `bun widget upgrade` to sync the manifests and validate compatibility.

**Monorepo workspace:** The CLI reads the SDK version from the workspace package and runs `bun install` from the monorepo root to sync, then updates all manifest files.

In both cases `bun widget upgrade` re-runs `bun widget validate` afterwards to catch any breaking API changes.

## Local Testing Workflow

The `connect` command is how you test widgets against a running Dash instance. Here's what it does:

- Builds all widgets in your project.
- Runs an **OAuth device-code flow against the Dash URL** (browser opens to the Dash approval page).
- Uploads each bundle to the Dash API under the `local` scope.
- Registers the widgets via the Dash internal tRPC endpoint and enables dev mode.
- Watches `src/` for changes; on save, rebuilds and re-uploads the affected widget.
- On `Ctrl+C`, unregisters the widgets and exits.

No local HTTP server is started; Dash talks to its own API for the bundle bytes.

> **Global flag:** Use `--dir <path>` with any project-scoped command (`add`, `build`, `validate`, `info`, `connect`, `publish`, `upgrade`) to point at a different widget project directory. `create` ignores it; it scaffolds into a new directory under the current working directory.