Widget CLI
The package is @glasshome/widget-cli; the binary is glasshome-widget. Scaffolded projects pin it in devDependencies behind a "widget": "glasshome-widget" script alias, so commands below read bun widget <command> (project-local, kept current with bun update). Elsewhere use bunx @glasshome/widget-cli@latest <command>; without @latest, bunx can reuse a stale cached version.
Version requirements
Hub enforces a minimum CLI version, currently 0.5.0, served from /api/widgets/cli-version. CLIs 0.4.14 and newer fetch it on login and publish and hard-stop below the floor with an actionable message instead of a cryptic OAuth or HTTP error; older CLIs lack the check, so the floor only protects clients that can read it.
On a “widget CLI is no longer supported” error, update:
# one-off, no install:
bunx @glasshome/widget-cli@latest publish
# or update the project dev dependency:
bun add -D @glasshome/widget-cli@latest
Two auth flows
connect authenticates against your local Dash instance for live testing; login against GlassHome Hub for publishing. Tokens are stored separately and are not interchangeable.
Commands
Scaffolds a widget project: package.json, vite.config.ts, tsconfig, and a starter widget whose details it prompts for (no extra add call needed). Run it outside an existing project.
Prompts for a name and description, then creates src/{name}/index.tsx and src/{name}/manifest.json.
Builds each widget into a self-contained JS bundle in dist/ and generates dist/registry.json with metadata for all widgets.
Checks manifests, bundle sizes, and registry consistency. Pass a name to check one widget, omit for all.
Prints the project header, local registry summary, and per-widget metadata + bundle size.
Live testing against a running Dash instance; widgets hot-reload as you edit. Full sequence in Local Testing Workflow. Auth is an OAuth device-code flow against the local Dash instance; approve in the browser and the CLI stores a host-scoped bearer token. Distinct from login, which targets GlassHome Hub.
Validates, prompts for scope/widget/version (or takes flags), then builds and uploads via a presigned R2 URL. Runs login automatically if no token is found. Scopes come from GET /api/widgets/scopes: your personal scope (your username) plus any org scopes you can publish to. See Publishing Scopes.
version in the widget’s manifest.@my-team).Authenticates with GlassHome Hub via OAuth PKCE (not local Dash): opens a browser, listens on http://127.0.0.1:9274/callback (120 s timeout), stores the token locally. publish runs it automatically when no token is present.
Moves the project to a newer @glasshome/widget-sdk and syncs the sdkVersion in every manifest.json to what is then installed. In a standalone project: bun adds the SDK at latest (or --to <version>) into whichever section already declares it, which also pulls in the @glasshome/ui it peers, then syncs the manifests and re-runs bun widget validate. In a monorepo workspace (a package.json with a workspaces field at or above the project): reads the SDK version from the workspace package, runs bun install from the root, syncs every manifest, then re-runs bun widget validate.
Rewrites widgets’ raw-Zod config (z.object, widgetFields) to the defineConfig + field.* API. Assistive, not lossy: fields it cannot translate stay raw Zod and are reported as manual TODOs; validation is never dropped.
Global flag
Use --dir <path> with any project-scoped command (add, build, validate, info, connect, publish, upgrade, migrate) to point at a different widget project directory. create ignores it; it scaffolds into a new directory under the current working directory.
Local Testing Workflow
What connect does, in order:
- Builds all widgets.
- Runs the OAuth device-code flow against the Dash URL (browser opens to the Dash approval page).
- Uploads each bundle to the Dash API under the
localscope. - Registers the widgets via the Dash internal tRPC endpoint and enables dev mode.
- Watches
src/; 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.