Skip to main content

Overview

An extension package is a single Git repository that contains multiple extensions — for example, an integration, a plugin, and a widget that work together. This is the recommended approach when your extensions are tightly coupled (e.g., a Notion integration + Notion plugin + Notion widget).

Manifest format

Create a radarboard-extension.json file at the root of your repo:
radarboard-extension.json
{
  "name": "Notion Extension Package",
  "description": "Full Notion integration for Radarboard",
  "author": {
    "name": "Your Name",
    "url": "https://github.com/yourname"
  },
  "minAppVersion": "1.0.0",
  "extensions": [
    {
      "type": "integration",
      "path": "integrations/notion",
      "name": "@radarboard/integration-notion",
      "required": true
    },
    {
      "type": "plugin",
      "path": "plugins/notion",
      "name": "@radarboard/plugin-notion",
      "required": true
    },
    {
      "type": "widget",
      "path": "widgets/notion",
      "name": "@radarboard/widget-notion",
      "required": false
    }
  ]
}

Fields

FieldTypeDescription
namestringHuman-readable package name
descriptionstringWhat this package provides
authorobject{ name, url } of the author
minAppVersionstringMinimum Radarboard version required
extensionsarrayList of extensions in this repo

Extension entry fields

FieldTypeDescription
type"integration" | "plugin" | "widget"Extension category
pathstringRelative path from repo root to the extension directory
namestringPackage name (must match the name in the extension’s package.json)
requiredbooleanIf true, validation fails when this extension has errors

Repository structure

radarboard-notion/
├── radarboard-extension.json     # Package manifest
├── package.json                  # Root package.json
├── tsconfig.json
├── biome.json
├── integrations/
│   └── notion/
│       ├── package.json          # @radarboard/integration-notion
│       └── src/
│           ├── index.ts          # IntegrationDescriptor
│           ├── types.ts
│           └── api/
│               ├── client.ts
│               └── data-sources.ts
├── plugins/
│   └── notion/
│       ├── package.json          # @radarboard/plugin-notion
│       └── src/
│           ├── index.ts          # PluginDescriptor
│           └── components/
│               └── notion-overlay.tsx
└── widgets/
    └── notion/
        ├── package.json          # @radarboard/widget-notion
        ├── index.ts              # WidgetDescriptor
        └── components/
            ├── notion-compact.tsx
            └── notion-expanded.tsx

Scaffolding

Use the scaffold tool to generate a starter extension repo:
# Integration + plugin + widget
pnpm scaffold:extension-repo notion --integration --plugin --widget

# Just a widget
pnpm scaffold:extension-repo my-charts --widget --out ~/projects

# Integration + widget (no plugin)
pnpm scaffold:extension-repo jira --integration --widget
This generates a complete repo with:
  • radarboard-extension.json manifest
  • package.json files for each extension with correct SDK dependencies
  • Starter TypeScript code with descriptor exports
  • biome.json, tsconfig.json, .gitignore
  • README with installation instructions

Installation

When a user installs from a GitHub URL, Radarboard:
  1. Checks for radarboard-extension.json in the repo
  2. If found, validates each declared extension
  3. Clones the repo and extracts each extension to its correct directory (integrations/, plugins/, widgets/)
  4. Updates radarboard.config.ts with all extensions
  5. Runs pnpm generate:extensions and pnpm install
If no manifest is found, it falls back to single-extension validation (checking package.json name prefix).

Validation

Each extension in the package is validated independently:
  • Package name matches the manifest entry
  • Required SDK dependency is present (integration-sdk, plugin-sdk, or widget-sdk)
  • Export map has a . (default) entry
  • No cross-extension imports (widget importing plugin code, etc.)
  • No forbidden workspace dependencies
Run validation locally before publishing:
# The install UI will validate before installing
# Or use the validation API programmatically:
import { validateExtensionPackage } from "@/lib/extension-installer/validate-package";

Dependency rules

Each extension type has allowed workspace dependencies:
Extension typeAllowed @radarboard/* deps
Integrationintegration-sdk, types, utils
Pluginplugin-sdk, types, utils, ui, widget-engine, embedding-service, llm
Widgetwidget-sdk, widget-engine, types, utils, ui, charts, hooks, assistant-ui