Skip to content

Repository files navigation

Kite Plugin SDK

Build React plugins for Kite with @kite-dev/plugin-sdk. Plugins can add pages and sidebar menus, work with Kubernetes resources and CRDs, and reuse Kite's UI components, authentication, cluster selection, and query cache.

A plugin runs inside Kite and uses the signed-in user's permissions. It can provide a resource browser, a custom detail page, or a dashboard that combines several resource types. The SDK includes TypeScript definitions, a Vite configuration helper, and a CLI for packaging installable plugins.

Quick start

Requirements: Node.js ^20.19.0 || >=22.12.0, a package manager, and a Kite instance matching the plugin's engines.kite version range (default: ^0.16.0).

Create a project:

pnpm create @kite-dev/plugin-sdk my-plugin
cd my-plugin
pnpm install
pnpm run build
pnpm run pack

With npm, use npm create @kite-dev/plugin-sdk my-plugin, then npm install, npm run build, and npm run pack.

The creator prompts for a directory and display name. It generates a TypeScript project with plugin.config.tsx, Vite configuration, a lazy-loaded page, English and Chinese locale files, CSS Modules, and package scripts. To skip prompts:

pnpm create @kite-dev/plugin-sdk my-plugin --yes --display-name "My Plugin"

The resulting archive is named <plugin-id>-<version>.tar.gz. In Kite, open Plugin management from the avatar menu and upload it. Plugins distributed through a configured catalog can also be installed there.

Package entry points

Import Use
@kite-dev/plugin-sdk definePlugin, plugin configuration and manifest types
@kite-dev/plugin-sdk/navigation Plugin links, navigation, route parameters, and plugin context
@kite-dev/plugin-sdk/resources Resource query hooks, writes, workload operations, Node operations, and Pod tools
@kite-dev/plugin-sdk/k8s Kubernetes types grouped by API group and version; type-only imports
@kite-dev/plugin-sdk/hooks Cluster, namespace, user, appearance, favorites, terminal, and page state
@kite-dev/plugin-sdk/ui UI primitives, resource tables, detail pages, events, and editors
@kite-dev/plugin-sdk/observability Cluster overview, resource usage, Pod metrics, and streaming logs
@kite-dev/plugin-sdk/api Authenticated client for existing Kite API endpoints
@kite-dev/plugin-sdk/i18n createPluginI18n() for typed translation keys, localized navigation, and the current language
@kite-dev/plugin-sdk/vite kitePlugin() build configuration
@kite-dev/plugin-sdk/validation Manifest and module validation, public menu group IDs

React, React Router, TanStack Query, React i18next, and SDK runtime exports are shared with Kite through Module Federation. Use hooks and components inside plugin pages rendered by Kite. Keep the existing React root, router, and providers; do not create a separate QueryClient for a plugin.

Project configuration

A typical project contains:

my-plugin/
  package.json
  plugin.config.tsx
  vite.config.ts
  tsconfig.json
  README.md
  src/
    i18n.ts
    locales/
      en.json
      zh.json
    resources.ts
    pages/
      deployments.tsx
      deployment.tsx
    styles.module.css

The build reads plugin identity and package metadata from package.json:

{
  "name": "workload-tools",
  "displayName": "Workload Tools",
  "version": "0.1.0",
  "private": true,
  "type": "module",
  "description": "A Kubernetes workload dashboard",
  "author": "Your Team",
  "license": "Apache-2.0",
  "engines": {
    "kite": "^0.16.0"
  }
}

Keep the dependencies and scripts generated by the creator when editing these fields.

  • name is the plugin ID and part of its URL. Use 1–64 lowercase letters, digits, or hyphens, starting and ending with a letter or digit. Plugin IDs do not use npm scopes.
  • displayName is the readable name shown in Plugin management; it must contain 1–128 characters.
  • version is a semantic version without a leading v. Keep the ID stable and increment the version when distributing changed contents.
  • description, author, homepage, and license are optional. author can be a string or an object with a name field.
  • engines.kite is the supported Kite version range. It defaults to ^0.16.0 when omitted. Set it to the versions your plugin supports; the build validates the range and writes it to plugin.json as requires.kite.

Configure Vite:

// vite.config.ts
import { kitePlugin } from '@kite-dev/plugin-sdk/vite'
import { defineConfig } from 'vite'

export default defineConfig(kitePlugin())

kitePlugin() returns a Vite UserConfig and reads plugin.config.tsx from the project root. To use another entry, pass kitePlugin({ entry: './src/plugin.tsx' }).

The build generates dist/plugin.json from package metadata and the route/menu configuration. It also records the Kite version requirement, Federation entry, and stylesheet paths. You do not need to write this file yourself.

Kite uses requires.kite as the plugin compatibility check. Package dependency versions control local installation and development. Kite supplies shared runtime modules without an additional package-version check.

Routes and menus

Export a definePlugin(...) configuration with routes and menus:

// plugin.config.tsx
import { lazy } from 'react'
import { definePlugin } from '@kite-dev/plugin-sdk'

import { label, translations } from './src/i18n'

const DeploymentsPage = lazy(() => import('./src/pages/deployments'))
const DeploymentPage = lazy(() => import('./src/pages/deployment'))

export default definePlugin({
  i18n: translations,
  routes: [
    {
      id: 'deployments',
      path: '',
      title: label('navigation.workloads'),
      element: <DeploymentsPage />,
    },
    {
      id: 'deployment',
      path: 'deployments/:namespace/:name',
      title: label('navigation.deployment'),
      element: <DeploymentPage />,
    },
  ],
  menus: [
    {
      id: 'workloads',
      parent: 'core:workloads',
      label: label('navigation.workloads'),
      route: 'deployments',
      icon: 'IconBox',
      order: 50,
    },
  ],
})

The label and translations exports come from the local setup described in Localization.

element accepts a React node, including JSX with props or a composition of components. definePlugin infers route IDs from inline declarations and checks menus.route against them. If you extract the route array into a variable, use as const on that array to preserve its literal IDs.

Route paths are relative to /plugins/<plugin-id>. For this example:

Route URL
deployments /plugins/workload-tools
deployment /plugins/workload-tools/deployments/default/demo

An empty path is the plugin home page. Paths support named parameters, optional parameters, and a trailing *. Each route renders a complete page. Route IDs and menu IDs must be unique within their respective lists, begin with a letter or digit, and contain only letters, digits, underscores, or hyphens.

Menu placement

A menu with route links to one of the plugin's routes. Choose a route that can open without required parameters. A menu without route is a group.

parent value Placement
Omitted Top-level sidebar entry or group
core:workloads Inside Kite's Workloads group
workload-tools:tools Inside this plugin's menu group with ID tools

Public host groups are core:application, core:workloads, core:traffic, core:storage, core:config, core:security, and core:other. They are also exported as coreMenuGroupIds from /validation.

A plugin menu parent must be one of these host groups or a group declared by the same plugin. Parent relationships must not contain cycles. order sets the default position; user sidebar preferences take precedence. icon accepts a host icon name such as IconBox or IconPackage. Unknown names use the host's default icon.

Configuration and lazy loading

The build executes the configuration in Node.js to obtain route and menu metadata. The same file becomes the browser entry. Keep the metadata identical in both environments. Imported constants, expressions, and helper functions are supported; route and menu declarations must not depend on browser globals or user state.

Import page modules with React.lazy(() => import(...)) and place their CSS and browser dependencies in those modules. Each lazy module must provide a default component export. The build does not execute the lazy import callback. A static component import is also valid when its entire dependency chain can execute in Node.js.

Kite registers navigation from plugin.json. It loads the plugin's JavaScript and styles when a plugin page is first opened, then renders the selected route inside its existing providers, Suspense boundary, and plugin error boundary.

Navigation

Use route IDs to link between plugin pages:

import { PluginLink, usePluginNavigate } from '@kite-dev/plugin-sdk/navigation'
import { Button } from '@kite-dev/plugin-sdk/ui'

export function DeploymentActions() {
  const navigate = usePluginNavigate()

  return (
    <>
      <PluginLink
        route="deployment"
        params={{ namespace: 'default', name: 'demo' }}
        search="?tab=yaml"
      >
        Open deployment
      </PluginLink>
      <Button onClick={() => navigate('deployments')}>All deployments</Button>
    </>
  )
}

PluginLink accepts normal React Router link props except to, plus route, params, search, and hash. usePluginNavigate() returns navigate(routeId, params?, options?); options include React Router navigation options, search, and hash.

Parameters are encoded and the plugin prefix is added automatically. Kite's deployment base path is handled by its router. Use useParams() in detail pages; /navigation also exports useSearchParams, useLocation, and Outlet. For lower-level URL generation, usePlugin() returns the plugin ID and route table for resolvePluginRoute(context, routeId, params?).

Resource queries

Identify a resource by API group and plural resource name:

// src/resources.ts
import type { AppsV1 } from '@kite-dev/plugin-sdk/k8s'
import type {
  KubernetesResource,
  ResourceReference,
} from '@kite-dev/plugin-sdk/resources'

export const deploymentRef = {
  group: 'apps',
  resource: 'deployments',
} satisfies ResourceReference

export type Deployment = AppsV1.Deployment & KubernetesResource

useResources<T>(ref, options?) returns UseQueryResult<T[], Error>. useResource<T>(ref, name, options?) returns UseQueryResult<T, Error>. Both use the current cluster and plugin namespace unless you pass explicit cluster or namespace options. Their query cache keys include cluster and resource scope.

Query option Purpose
cluster Query a specific accessible cluster
namespace Override the plugin namespace
enabled Enable or disable the query
staleTime Cache freshness in milliseconds
refreshInterval Polling interval in milliseconds; 0 disables polling
labelSelector Filter resource labels
fieldSelector, reduce Built-in resource list options

Lists accept _all for all namespaces or a comma-separated namespace selection. A single-resource query needs the object's actual namespace. Use scope: 'Cluster' for cluster-scoped custom resources; Kite recognizes the scope of its built-in resources.

Custom resources

CRDs use the same query hooks and a plugin-defined resource type:

import {
  useResources,
  type KubernetesResource,
} from '@kite-dev/plugin-sdk/resources'

interface Certificate extends KubernetesResource {
  spec: { secretName: string; dnsNames?: string[] }
}

export function CertificateCount() {
  const certificates = useResources<Certificate>(
    { group: 'cert-manager.io', resource: 'certificates' },
    { namespace: '_all' }
  )

  if (certificates.isLoading) return <p>Loading certificates…</p>
  if (certificates.error)
    return <p role="alert">{certificates.error.message}</p>
  return <p>{certificates.data?.length ?? 0} certificates</p>
}

Kite selects the CRD API version. Custom resource lists support labelSelector; they do not support fieldSelector. The hooks return resource arrays and do not expose server pagination. Poll with refreshInterval when periodic updates are needed.

Resource UI

Import components and their props from /ui. All resource components are optional; you can use the hooks with your own page layout.

Resource tables

This page uses the resource reference above and the routes from plugin.config.tsx:

// src/pages/deployments.tsx
import { useNamespace } from '@kite-dev/plugin-sdk/hooks'
import { PluginLink } from '@kite-dev/plugin-sdk/navigation'
import { useResources } from '@kite-dev/plugin-sdk/resources'
import { ResourceTable, type ColumnDef } from '@kite-dev/plugin-sdk/ui'

import { deploymentRef, type Deployment } from '../resources'

const columns: ColumnDef<Deployment, unknown>[] = [
  {
    id: 'name',
    header: 'Name',
    accessorFn: (item) => item.metadata.name,
    cell: ({ row }) => (
      <PluginLink
        route="deployment"
        params={{
          namespace: row.original.metadata.namespace!,
          name: row.original.metadata.name,
        }}
      >
        {row.original.metadata.name}
      </PluginLink>
    ),
  },
  {
    id: 'namespace',
    header: 'Namespace',
    accessorFn: (item) => item.metadata.namespace,
  },
]

export default function DeploymentsPage() {
  const { namespace, setNamespace } = useNamespace()
  const query = useResources<Deployment>(deploymentRef, { namespace })

  return (
    <ResourceTable<Deployment>
      id="deployments"
      resourceName="Deployments"
      data={query.data}
      columns={columns}
      isLoading={query.isLoading}
      error={query.error}
      onRefresh={query.refetch}
      namespace={{ value: namespace, onChange: setNamespace }}
    />
  )
}

ResourceTable provides search, sorting, client-side pagination, row counts, column visibility, and refresh controls. The plugin supplies the data and query state. Set id to a stable identifier unique within the plugin; resourceName is the display label. Search, column visibility, and page size are saved per cluster and table.

Optional props include searchQueryFilter(item, query), defaultHiddenColumns, extraToolbars, emptyState, and onCreateClick. Passing namespace: { value, onChange } displays the namespace selector. Passing both refreshInterval and onRefreshIntervalChange displays the interval selector; pass the same interval to your resource hook to control polling.

Resource detail pages

ResourceDetailShell<T> provides loading/error states, an overview tab, a YAML tab, refresh, and optional resource actions:

// src/pages/deployment.tsx
import { useParams, usePluginNavigate } from '@kite-dev/plugin-sdk/navigation'
import { updateResource, useResource } from '@kite-dev/plugin-sdk/resources'
import { ResourceDetailShell, ResourceOverview } from '@kite-dev/plugin-sdk/ui'

import { deploymentRef, type Deployment } from '../resources'

export default function DeploymentPage() {
  const { namespace = '', name = '' } = useParams()
  const navigate = usePluginNavigate()
  const query = useResource<Deployment>(deploymentRef, name, { namespace })

  return (
    <ResourceDetailShell<Deployment>
      resource={deploymentRef}
      resourceLabel="Deployment"
      name={name}
      namespace={namespace}
      data={query.data}
      isLoading={query.isLoading}
      error={query.error}
      onRefresh={query.refetch}
      onSaveYaml={async (value) => {
        await updateResource(deploymentRef, name, value, { namespace })
        await query.refetch()
      }}
      showDelete
      onDeleted={() => {
        void navigate('deployments')
      }}
      overview={({ resource }) => (
        <ResourceOverview
          resource={deploymentRef}
          name={name}
          namespace={namespace}
          metadata={resource.metadata}
          fields={[
            { label: 'Desired replicas', value: resource.spec?.replicas ?? 0 },
          ]}
        />
      )}
    />
  )
}

onSaveYaml receives the parsed resource object, not a YAML string. Without it, the YAML tab is read-only. Delete and clone actions are disabled by default for plugins; enable them with showDelete and showClone. onDeleted runs after deletion. The describe action can be controlled with showDescribe.

Use preYamlTabs or extraTabs to add tabs with { value, label, content }. Overview and tab content can be React nodes or callbacks receiving the current resource, YAML state, saving state, and refresh callback. headerActions, titleIcon, and yamlToolbar customize the surrounding controls.

ResourceOverview displays metadata, custom fields, events, and the host's related-resource card. It also accepts children. For CRDs, supply your own relatedResources content or pass relatedResources={null} to skip the built-in relationship lookup. ResourceEvents provides a standalone events table with resource, name, and optional namespace props.

UI primitives and editors

The /ui entry includes Button, Badge, Input, Label, the Card family, and the Dialog, Select, and Tabs families. Their component props are exported or available through React's ComponentProps.

Namespace and YAML controls use these props:

import { useState } from 'react'
import { useNamespace } from '@kite-dev/plugin-sdk/hooks'
import { NamespaceSelector, YamlEditor } from '@kite-dev/plugin-sdk/ui'

export function ResourceEditor() {
  const { namespace, setNamespace } = useNamespace()
  const [yaml, setYaml] = useState('')

  return (
    <>
      <NamespaceSelector
        selectedNamespace={namespace}
        handleNamespaceChange={setNamespace}
        showAll={false}
        multiple={false}
      />
      <YamlEditor
        value={yaml}
        onChange={(value) => setYaml(value ?? '')}
        height="400px"
      />
    </>
  )
}

NamespaceSelector also accepts disabled, triggerClassName, and modal. YamlEditor accepts disabled; its change callback receives string | undefined.

Resource writes and operations

The /resources entry provides Promise-based writes:

Function Behavior
applyResource(yaml, namespace?) Create or update resources from YAML using the current cluster
updateResource(ref, name, body, options?) Replace a built-in or custom resource with a complete object
patchResource<T>(ref, name, body, options?) Submit DeepPartial<T> to a supported built-in PATCH endpoint
deleteResource(ref, name, options?) Delete a built-in or custom resource

For updateResource, patchResource, and deleteResource, options accept namespace, cluster, and signal. Deletion also accepts force and wait. Specify the actual namespace for namespaced writes.

applyResource uses Kite's create/update workflow. An explicit namespace overrides namespace fields on namespaced YAML objects; cluster-scoped objects ignore it. Its ApplyResourceResponse describes the affected resource or resources. For custom resources, use updateResource or applyResource; generic CRD PATCH and Server-Side Apply are not provided by these helpers.

Writes do not automatically refresh queries. Handle pending/error state in your page and call refetch() after success, or invalidate the appropriate queries with TanStack Query.

Additional exports are grouped below. Their request and response types are available from the same entry.

Area Functions
Events and inspection useResourceEvents, useDescribe, useResourceHistory, useRelatedResources
Workloads scaleDeployment, restartWorkload, useWorkloadRevisions, rollbackWorkload
Nodes drainNode, cordonNode, uncordonNode, taintNode, untaintNode
Pod debugging resizePod, debugPod, copyDebugPod
Pod files usePodFiles, podDownloadFile, podPreviewFile, podUploadFile
Configuration helpers useTemplates, useImageTags

Inspection hooks accept a resource reference, name, and query options. useResourceHistory additionally supports page and pageSize; it returns Kite's recorded operations, not every historical Kubernetes object version. Related-resource discovery covers supported built-in resources. Query CRD relationships with useResources and match owner references or your resource's fields.

scaleDeployment operates on Deployments. restartWorkload accepts Deployments and StatefulSets. Revision queries and rollback also support DaemonSets. Workload, Node, and Pod operations use the current cluster.

Pod file functions receive namespace, Pod name, container, and path; upload additionally takes a browser File. debugPod creates an ephemeral container, while copyDebugPod creates a debug Pod copy. resizePod accepts Partial<CoreV1.Pod>.

Cluster and application hooks

Import these hooks from /hooks:

Hook Result or action
useCluster() currentCluster and setCurrentCluster
useClusters(options?) Query result containing accessible clusters; enabled by default
useNamespace() Plugin namespace selection and setNamespace
useAuth() user, isLoading, and capabilities
useTheme() theme, actualTheme, and setTheme for light/dark/system mode
useFavorites() Favorites plus add, remove, toggle, lookup, and refresh functions
useTerminal() Open, close, minimize, and toggle the global terminal panel
usePageTitle(title) Set the page title
useIsMobile() Whether Kite is using its mobile layout
useInterval(callback, delay) Run a callback at an interval in milliseconds
useVersionInfo() Query Kite version information

currentCluster can be null before a cluster is selected. useClusters({ enabled: false }) disables its query. Each ClusterInfo contains name, isDefault, and optional version and error; it contains no cluster credentials.

useAuth exposes user information and feature capabilities, with user helpers such as isAdmin(). Authentication remains managed by Kite. Appearance, favorites, and terminal actions affect the host application. useTerminal controls the global panel rather than creating a Pod terminal session.

Kubernetes types

Use the type-only /k8s entry for built-in Kubernetes resource definitions:

import type { AppsV1, CoreV1, MetaV1 } from '@kite-dev/plugin-sdk/k8s'

type Deployment = AppsV1.Deployment
type Pod = CoreV1.Pod
type ObjectMeta = MetaV1.ObjectMeta

Types are grouped by API group and version, including AutoscalingV1, AutoscalingV2, NetworkingV1, and RbacV1. The entry exposes the modules provided by the SDK's kubernetes-types dependency. Use import type; this entry has no runtime module. Define your own interfaces for CRDs, optionally extending KubernetesResource from /resources.

Metrics and logs

The /observability entry provides:

  • useOverview(options?) for cluster summary data.
  • useResourceUsageHistory(duration, options?) for resource usage history.
  • usePodMetrics(namespace, podName, duration, options?) for Pod time-series metrics.
  • useLogsWebSocket(namespace, podName, options?) for streaming logs.
import { useState } from 'react'
import { useLogsWebSocket } from '@kite-dev/plugin-sdk/observability'

export function PodLogs() {
  const [lines, setLines] = useState<string[]>([])
  const stream = useLogsWebSocket('default', 'demo', {
    container: 'app',
    tailLines: 100,
    onNewLog: (line) => setLines((previous) => [...previous.slice(-999), line]),
    onClear: () => setLines([]),
  })

  return (
    <>
      {stream.error && <p role="alert">{stream.error.message}</p>}
      <pre>{lines.join('\n')}</pre>
    </>
  )
}

These hooks use the current cluster. Metrics queries keep cluster-specific cache entries. Log streaming is enabled by default; options include enabled, container, tailLines, timestamps, previous, sinceSeconds, and labelSelector. The result includes loading and connection state, errors, download speed, refetch, stopStreaming, and clearLogs. The page controls how many log lines it retains.

Calling other Kite APIs

apiClient from /api provides get, post, put, patch, delete, and request. It uses Kite authentication, the current cluster, and the host's deployment base path.

Pass API-relative paths: apiClient.get('/pods/_all') calls /api/v1/pods/_all under the Kite deployment. Do not include /api/v1 again. The typed convenience methods return parsed data and reject unsuccessful responses. request returns a raw Response, so callers check its status themselves. Options accept RequestInit fields and retryOnUnauthorized.

Use useQuery, useMutation, and useQueryClient directly from @tanstack/react-query for custom queries. Include plugin identity, cluster, namespace, and relevant parameters in your query keys. For a query bound to a captured cluster, use the cluster path explicitly:

import { apiClient } from '@kite-dev/plugin-sdk/api'
import { useCluster } from '@kite-dev/plugin-sdk/hooks'
import type { CoreV1 } from '@kite-dev/plugin-sdk/k8s'
import { useQuery } from '@tanstack/react-query'

export function usePluginPodList() {
  const { currentCluster } = useCluster()

  return useQuery({
    queryKey: ['workload-tools', 'pods', currentCluster, '_all'],
    enabled: !!currentCluster,
    queryFn: ({ signal }) =>
      apiClient.get<CoreV1.PodList>(
        `/_clusters/${encodeURIComponent(currentCluster!)}/pods/_all`,
        { signal }
      ),
  })
}

Use existing Kite endpoints; frontend plugins do not register backend APIs.

Localization

Keep translated text in src/locales/en.json and src/locales/zh.json, using the same keys in both files.

src/locales/en.json:

{
  "navigation": {
    "workloads": "Workload Tools",
    "deployment": "Deployment"
  },
  "actions": {
    "refresh": "Refresh {{name}}"
  }
}

src/locales/zh.json:

{
  "navigation": {
    "workloads": "工作负载工具",
    "deployment": "部署"
  },
  "actions": {
    "refresh": "刷新 {{name}}"
  }
}

Bind these dictionaries once in src/i18n.ts:

import { createPluginI18n } from '@kite-dev/plugin-sdk/i18n'

import en from './locales/en.json'
import zh from './locales/zh.json'

export const {
  resources: translations,
  label,
  useTranslation,
} = createPluginI18n({ en, zh })

Set i18n: translations in definePlugin(...). Use label('navigation.workloads') for route titles and menu labels. label() returns all supported translations as data; it does not select a language. The SDK includes these navigation labels in plugin.json, so Kite can display and translate the sidebar before loading the plugin's JavaScript.

Import useTranslation from the plugin's local i18n.ts inside pages:

import { Button } from '@kite-dev/plugin-sdk/ui'

import { useTranslation } from '../i18n'

export function RefreshButton({
  name,
  onClick,
}: {
  name: string
  onClick: () => void
}) {
  const { t } = useTranslation()
  return <Button onClick={onClick}>{t('actions.refresh', { name })}</Button>
}

label() and t() provide TypeScript completion for the dictionary's keys. t() supports i18next interpolation and pluralization. The hook also returns language for date and number formatting. Translations are isolated to the plugin and update when the user changes Kite's language. Page translations load with the plugin; no manual namespace registration is required.

Styling

Host UI components include their styles. For custom layouts, use CSS Modules and Kite's CSS variables:

/* src/styles.module.css */
.panel {
  display: grid;
  gap: 1rem;
  padding: 1rem;
  color: var(--card-foreground);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 0.5rem;
}

.secondaryText {
  color: var(--muted-foreground);
}

Available host variables include --background, --foreground, --card, --card-foreground, --primary, --primary-foreground, --muted, --muted-foreground, and --border. They follow Kite's appearance. Import CSS from the page module that uses it.

Kite's Tailwind build does not scan separately built plugin source files. Additional utility classes need to be generated by the plugin's own build. Scope plugin styles and avoid global resets or Tailwind preflight that would affect the host.

Building and packaging plugins

Generated projects include these scripts:

Command Result
pnpm run type-check Check TypeScript
pnpm run lint Check JavaScript, TypeScript, and React Hooks
pnpm run lint:fix Apply ESLint fixes
pnpm run format Format source files with Prettier
pnpm run format:check Check formatting without changing files
pnpm run build Check types and build production assets into dist/
pnpm run dev Run vite build --watch
pnpm run pack Package the current dist/ directory

The build produces plugin.json, Federation metadata and entry files, JavaScript chunks, styles, and an optional README. A root README.md is copied to dist/README.md and included in the archive. Use complete repository URLs for links to files that are not packaged with the README.

The pack command does not run a build. Build first, then package:

pnpm run build
pnpm exec kite-plugin pack

To choose the input directory and archive path:

pnpm exec kite-plugin pack dist workload-tools-0.1.0.tar.gz

The archive contains the contents of dist/ at its root, without an enclosing directory. The CLI validates the manifest and package files and prints the archive path and SHA-256 digest. Keep the output archive outside the input directory.

Use Plugin management to install the archive. To distribute through a catalog, publish the archive and its metadata through your catalog's tooling; the catalog can also make the packaged README available for preview.

During development, watch mode rebuilds assets as source files change. Plugin pages run in Kite; the watch command does not serve a standalone application. Repackage and install a new plugin version to update an installed build. Restart the watcher after changing the plugin ID or version.

SDK development

The SDK can be built from its own repository without a Kite source checkout:

pnpm install --frozen-lockfile
pnpm run lint
pnpm run format:check
pnpm run type-check
pnpm run build
pnpm pack

ESLint covers SDK source and the project creator. Prettier uses the same formatting conventions as Kite. Use pnpm run lint:fix and pnpm run format to apply fixes. Generated output and dependency directories are excluded.

The CI workflow runs on pull requests, pushes to main, and manual dispatches using Node.js 24. It checks linting and formatting, builds the SDK, verifies the creator CLI, and checks both npm package contents without publishing them.

The SDK and creator share one version and a pnpm workspace. The creator uses workspace:* to link the local SDK during development; pnpm pack converts it to that exact SDK version in the published package. Install dependencies from the repository root with pnpm install --frozen-lockfile.

The separate publish.yml workflow publishes both packages when a v* Git tag is pushed. Stable versions publish to npm's latest tag; prerelease versions publish to beta. A GitHub Release is not required. The workflow checks versions, linting, formatting, the SDK build, and the creator CLI before publishing the SDK followed by the creator. Already published versions are skipped when rerunning the workflow. npm may take a few minutes to make newly published packages available.

With dependencies installed and all changes committed, prepare a release by specifying the new version:

./scripts/release.sh 0.0.4
git push --atomic origin HEAD v0.0.4

The release script updates both package versions and the shared lockfile, creates a release v<version> commit, and adds an annotated v<version> tag. Use a version such as 0.0.4-beta.0 for a prerelease. The script prepares the release locally and prints the push command; pushing the tag triggers npm publishing.

Configure an npm Trusted Publisher for each package, @kite-dev/plugin-sdk and @kite-dev/create-plugin-sdk, with organization kite-org, repository plugin-sdk, and workflow filename publish.yml. Leave the environment empty and allow direct npm publish. The workflow authenticates through OIDC without an npm token.

pnpm pack runs the build and creates kite-dev-plugin-sdk-<version>.tgz, containing the compiled modules, type declarations, CLI, README, and license. Pack the creator with pnpm --dir create-plugin-sdk pack so its workspace dependency is converted to a registry version.

To use a local SDK checkout from a sibling plugin project:

# In the SDK checkout
pnpm run build

# In the sibling plugin project
pnpm add @kite-dev/plugin-sdk@file:../kite-plugin-sdk
pnpm run build
pnpm run pack

After changing SDK source, rebuild it and refresh the consuming project's local dependency. Verify the plugin against the Kite versions declared in its engines.kite range. Use a published SDK version in plugins intended for distribution.

The project creator is maintained in create-plugin-sdk/ and published as @kite-dev/create-plugin-sdk. See its README for local creator development.

Validation API

Build and packaging commands validate manifests automatically. Tooling can also import these functions from /validation:

  • validateManifest(input) validates metadata, versions, asset paths, routes, and menus and narrows the value to PluginManifest.
  • validateNavigation(pluginId, input) validates route and menu metadata.
  • validateModule(manifest, input) verifies that a loaded definition matches the manifest's navigation and declares an element for every route.
  • coreMenuGroupIds lists the public host menu groups.

The TypeScript declarations in src/ provide the complete argument, option, and response types for each public module.

License

Apache-2.0.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages