@gainsight-hub/hubforge
v0.9.0
Published
DCH development tools for widgets, connectors, and pages
Readme
@gainsight-hub/hubforge
CLI for building and previewing widgets for the Gainsight CC Widget Catalog.
Prerequisites
- Node.js >= 18
- A CC Widget Catalog account with developer access
Installation
npm install -g @gainsight-hub/hubforgeVerify:
hf --versionBoth hf and hubforge are available as command aliases.
Quick Start
# 1. Scaffold a new widget
hf create
# 2. Start the dev server
hf preview
# 3. In your CC instance, click "Edit Layout" → "Dev Mode" → enter the port and token → click ConnectCommands
hf create
Scaffold a new widget interactively.
hf create [options]| Flag | Description | Default |
| ------------------------- | ---------------------------------------------------------------- | -------- |
| --name <slug> | Widget identifier — lowercase, alphanumeric, hyphens/underscores | prompted |
| --framework <framework> | One of: react, vue, angular, vanilla, html | prompted |
| --category <name> | Widget category shown in CC Widget Catalog | prompted |
Example (interactive):
? Widget name (e.g. my-dashboard-widget) › revenue-overview
? Framework › React
? Category › AnalyticsCreates widgets/revenue-overview/ and registers the widget in widget_registry.json (auto-created if missing).
Example (non-interactive):
hf create --name revenue-overview --framework react --category Analyticshf preview
Start a local dev server and pair it with CC Widget Catalog.
hf preview [options]| Flag | Description | Default |
| ----------------- | -------------------------------------------------- | ------- |
| --port <number> | Port for the HubForge server — overrides HF_PORT | 5173 |
| --verbose | Show detailed dev server startup diagnostics | — |
| Environment variable | Description | Default |
| -------------------- | ----------------------------------------------------- | ------- |
| HF_PORT | Port for the HubForge server (overridden by --port) | 5173 |
| HF_TIMEOUT | Seconds to wait for each widget's dev server to start | 60 |
Run from your project root — the directory containing widget_registry.json. Running from a subdirectory will fail.
Each widget must have a dev script in its package.json to appear in the preview selector. Widgets without one are skipped.
HubForge auto-detects the package manager for each widget (bun, yarn, pnpm, or npm) based on the lockfile present.
Example output:
╭────────────────────────────────────────────────────────────────────╮
│ │
│ Widget Dev Preview │
│ │
│ Server: http://localhost:5173 │
│ Token: A3KP-8XZQ │
│ │
│ Open CC Widget Catalog, enter the port and token above. │
│ Press Ctrl+C to end session. │
│ │
╰────────────────────────────────────────────────────────────────────╯The token is a single-session secret — it changes every time you run hf preview.
Pairing with CC Widget Catalog
When hf preview is running:
- Open your CC instance and click Edit Layout
- Click Dev Mode in the widget sidebar
- A Connect Dev Server dialog appears with Port and Token fields
- Enter the port (default
5173) and paste the token shown in the terminal - Click Connect — your local widgets appear in the catalog
Project Structure
my-project/
├── widget_registry.json # Widget registry (managed by hf create)
└── widgets/
└── my-widget/
├── package.json # Must include "dev" script for preview
├── src/
├── dist/ # Output of build script
└── thumbnail.pngwidget_registry.json
HubForge reads widget_registry.json from the project root to discover widgets. hf create manages this file automatically.
JS widget entry (React, Vue, Angular, Vanilla):
{
"type": "revenue-overview",
"title": "Revenue Overview",
"version": "1.0.0",
"category": "Analytics",
"imageSrc": "widgets/revenue-overview/thumbnail.png",
"widgetsLibrary": true,
"containers": ["Full width"],
"settings": {
"configurable": true,
"editable": true,
"removable": true,
"shared": false,
"movable": false
},
"source": {
"path": "widgets/revenue-overview/dist",
"entry": "index.html"
},
"configuration": {
"properties": [
{
"name": "title",
"type": "text",
"label": "Title",
"defaultValue": "Revenue Overview",
"rules": { "required": true }
},
{
"name": "description",
"type": "text",
"label": "Description",
"defaultValue": ""
}
]
},
"defaultConfig": {
"title": "Revenue Overview",
"description": ""
}
}Static HTML widget entry (no build step):
{
"type": "static-banner",
"title": "Static Banner",
"version": "1.0.0",
"category": "My Widgets",
"imageSrc": "widgets/static-banner/thumbnail.png",
"widgetsLibrary": true,
"containers": ["Full width"],
"settings": {
"configurable": true,
"editable": true,
"removable": true,
"shared": false,
"movable": false
},
"source": {
"path": "widgets/static-banner/dist",
"entry": "content.html"
}
}Supported Frameworks
| --framework | Description |
| ------------- | --------------------------------------------------- |
| react | React with Vite |
| vue | Vue 3 with Vite |
| angular | Angular with zoneless change detection |
| vanilla | Vanilla JS/TS with Vite |
| html | Static HTML — no build step, no dev server required |
Environment Variables
| Variable | Affects | Default | Description |
| ------------ | ------------ | ------- | ------------------------------------------------------------- |
| HF_PORT | hf preview | 5173 | HTTP server port |
| HF_TIMEOUT | hf preview | 60 | Seconds to wait for a widget's dev server to become reachable |
Troubleshooting
Port already in use
Use --port or HF_PORT to pick a different port:
hf preview --port 5200Widget not appearing in the preview selector
The widget's package.json must have a dev script. Check with cat widgets/<name>/package.json.
Dev server times out
Increase the timeout: HF_TIMEOUT=120 hf preview. Also verify the widget's dev script works on its own by running it directly from widgets/<name>/ with your package manager (e.g. npm run dev, bun run dev).
"widget_registry.json not found"
Run hf commands from the project root — the directory that contains widget_registry.json.
Pairing token not accepted
The token is tied to the current session. Restart hf preview and reconnect via Edit Layout → Dev Mode with the new token.
