Plugin Manifest

The manifest declares your plugin to Pomavo: its identity, the widgets it embeds, the settings it collects, optional theme, and the resources it requests.

What the Manifest Is

The manifest is the contract between your plugin and the host. It declares who the plugin is, what UI it contributes, what configuration it needs, and what Pomavo resources it wants access to. The host stores the manifest when the plugin is published and re-validates it on every update.

TypeScripttypescript
interface PluginManifest {
  id: string;                       // Stable unique id, e.g. "github-integration"
  name: string;                     // Display name
  version: string;                  // Semver, e.g. "1.2.0"
  type?: 'theme' | 'ui' | 'general';
  author: { name: string; url?: string };
  description: string;
  icon?: string;                    // URL or path to an icon
  categories?: string[];            // e.g. ['integration', 'developer-tools']
  lifecycle?: { url: string };      // POST endpoint for install/uninstall/update
  setup?: { url: string; description?: string };
  widgets?: PluginWidget[];
  parameters?: PluginParameter[];
  theme?: ThemeContribution;        // Only for theme plugins
  resources?: {
    automations?: number[];
    integrations?: { id: number; requestedPermissions: string[] }[];
  };
}

id, name, version, author, and description are required. Everything else is optional and depends on what your plugin does. A pure integration may have only a lifecycle.url; a UI plugin adds widgets; a theme plugin adds theme.

Widgets

Each widget is an embeddable UI panel. The host loads its url into a sandboxed iframe wherever the widget is placed, such as a panel on the ticket detail page.

TypeScripttypescript
interface PluginWidget {
  id: string;            // Unique within the plugin, e.g. "prs"
  title: string;         // Shown in the widget header
  url: string;           // Absolute URL the host embeds in the iframe
  defaultHeight?: number;// Initial iframe height in px (before auto-resize)
}
JSONjson
"widgets": [
  { "id": "prs", "title": "Pull Requests", "url": "https://my-plugin.example.com/widgets/prs", "defaultHeight": 320 },
  { "id": "commits", "title": "Commits", "url": "https://my-plugin.example.com/widgets/commits" }
]

Parameters

Parameters are the settings the host collects from an admin when the plugin is installed or configured. Their values are persisted by the host and handed back to your widgets and backend as settings.

TypeScripttypescript
interface PluginParameter {
  key: string;                      // Settings key
  label: string;                    // Field label in the settings form
  type: 'string' | 'secret' | 'boolean' | 'number' | 'select';
  required?: boolean;
  default?: string;
  placeholder?: string;
  description?: string;
  options?: string[];               // For type: 'select'
}
JSONjson
"parameters": [
  { "key": "org", "label": "GitHub Org", "type": "string", "required": true },
  { "key": "token", "label": "Access Token", "type": "secret", "required": true },
  { "key": "autoSync", "label": "Auto-sync commits", "type": "boolean", "default": "true" }
]

Use type: 'secret' for tokens and passwords so the host masks the value in the settings UI. Never bake credentials into the manifest itself - collect them as parameters at install time.

Lifecycle & Setup URLs

  • lifecycle.url - the endpoint Pomavo POSTs install/uninstall/update events to. See Lifecycle & Auth.
  • setup.url - an optional page the host links to so an admin can complete configuration that can't be expressed as simple parameters, such as an OAuth handshake.
JSONjson
"lifecycle": { "url": "https://my-plugin.example.com/lifecycle" },
"setup": { "url": "https://my-plugin.example.com/setup", "description": "Connect your GitHub account" }

Resources

A plugin can declare the Pomavo resources it wants to bring in or access:

  • automations - ids of automation templates the plugin contributes.
  • integrations - integration ids together with the permissions the plugin requests. The host surfaces these so the installing admin can review what access is being granted.
JSONjson
"resources": {
  "integrations": [
    { "id": 12, "requestedPermissions": ["tickets:read", "tickets:write", "comments:write"] }
  ]
}

Theme Plugins

A plugin with type: 'theme' or the theme category contributes one or more color themes instead of widgets. The host's theme loader registers these for users to select.

TypeScripttypescript
theme?: {
  light: Record<string, string | string[]>;
  dark: Record<string, string | string[]>;
  font?: { sans?: string; mono?: string };
  defaultVariantName?: string;
  variants?: {
    id: string;
    name: string;
    light: Record<string, string | string[]>;
    dark: Record<string, string | string[]>;
    font?: { sans?: string; mono?: string };
  }[];
}

Each light / dark map is a set of CSS custom-property values using the same tokens documented under Themes. Provide multiple named variants to ship a family of related themes in one plugin.

A Complete Example

JSONjson
{
  "id": "github-integration",
  "name": "GitHub",
  "version": "1.0.0",
  "type": "ui",
  "icon": "https://gh-plugin.example.com/icon.png",
  "author": { "name": "Pomavo", "url": "https://pomavo.dev" },
  "description": "Link commits, branches, and pull requests to tickets.",
  "categories": ["integration", "developer-tools"],
  "lifecycle": { "url": "https://gh-plugin.example.com/lifecycle" },
  "setup": { "url": "https://gh-plugin.example.com/setup", "description": "Connect your GitHub account" },
  "parameters": [
    { "key": "org", "label": "GitHub Org", "type": "string", "required": true, "placeholder": "my-company" },
    { "key": "token", "label": "Access Token", "type": "secret", "required": true, "description": "A fine-grained token with repo read access." },
    { "key": "autoSync", "label": "Auto-sync commits", "type": "boolean", "default": "true" },
    { "key": "environment", "label": "Environment", "type": "select", "options": ["production", "staging"], "default": "production" }
  ],
  "widgets": [
    { "id": "prs", "title": "Pull Requests", "url": "https://gh-plugin.example.com/widgets/prs", "defaultHeight": 320 },
    { "id": "commits", "title": "Commits", "url": "https://gh-plugin.example.com/widgets/commits" }
  ],
  "resources": {
    "automations": [7, 11],
    "integrations": [
      { "id": 12, "requestedPermissions": ["tickets:read", "tickets:write", "comments:write"] }
    ]
  }
}

Next Steps