npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@qlop/plugin-sdk

v0.2.1

Published

`@qlop/plugin-sdk` is the supported frontend contract for Qlop plugins.

Readme

@qlop/plugin-sdk

@qlop/plugin-sdk is the supported frontend contract for Qlop plugins.

Plugins compile against this package. They should not import app-private modules from apps/desktop. External plugin repositories should install the published package rather than relying on monorepo workspace linking.

pnpm add @qlop/[email protected] react

Keep the plugin dependency version exact, and keep manifest.json plugin_sdk_version equal to that same exact SDK version. The host currently treats SDK compatibility as an exact match.

@qlop/ui-kit is not part of the external plugin contract. Reusable plugin-facing UI primitives should come from this SDK, not from app-internal UI kit imports.

This README documents the current 0.2.x panel-based plugin model. It is intentionally strict. Some rules described here are architectural requirements that the host will enforce more directly over time. Plugin authors should treat them as binding now rather than waiting for stricter runtime checks later.

What The SDK Owns

  • plugin manifest types
  • panel frontend module types
  • host auth command helpers
  • shared panel shell primitives

What The SDK Does Not Own

  • direct access to app-private frontend modules
  • direct access to raw Tauri internals
  • arbitrary filesystem, shell, or OS automation
  • workflow/catalogue source-of-truth ownership
  • custom authentication storage

Qlop plugins are not mini-desktop apps with unrestricted host access. A plugin is a bounded, host-loaded extension surface used to support org workflows inside the Qlop runtime.

Runtime Model

In the current SDK version, plugins are panel-first.

That means:

  • a plugin ships one compiled frontend ESM bundle
  • that bundle exports a panels map
  • the host loads the bundle
  • the host selects the requested panel component
  • the host renders that panel inside a Qlop-managed panel window

The host owns:

  • plugin discovery
  • manifest validation
  • compatibility checks
  • auth gating
  • panel window creation
  • theme resolution
  • runtime import of the plugin bundle

The plugin owns:

  • panel UI
  • plugin-specific frontend behavior
  • optional backend routes and integration logic

Plugins should assume that every panel runs inside a host-controlled shell, not in a fully independent app runtime.

React Runtime Contract

Qlop owns the React runtime for plugin panels.

That means:

  • the host installs a singleton React instance on globalThis.__QLOP_REACT__
  • plugin bundles must bind to that singleton through SDK shim modules
  • plugin bundles must not assume raw react imports will resolve at runtime
  • plugin bundles should not ship their own independent React copy for panel rendering

When bundling a plugin, alias the React entrypoints to the SDK shims:

pnpm exec esbuild frontend/index.ts \
  --bundle \
  --format=esm \
  --platform=browser \
  --target=es2020 \
  --jsx=automatic \
  --alias:react=@qlop/plugin-sdk/react \
  --alias:react/jsx-runtime=@qlop/plugin-sdk/jsx-runtime \
  --alias:react/jsx-dev-runtime=@qlop/plugin-sdk/jsx-dev-runtime \
  --outfile=dist/index.js

If your bundler supports aliases through configuration files instead of CLI flags, configure those same three mappings there.

Plugin Purpose

Plugins exist to support Qlop workflows, not replace Qlop's workflow authority.

A plugin may:

  • provide a specialized panel for a workflow step
  • query org systems through its backend
  • present authenticated views or tools needed to complete a workflow
  • contribute theme tokens for its own panel presentation

Preferred theme token names follow the host's shadcn-style roles, for example:

  • background, foreground
  • card, card-foreground
  • primary, primary-foreground
  • secondary, secondary-foreground
  • muted, muted-foreground
  • destructive, destructive-foreground
  • border, input, ring

A plugin must not assume it owns:

  • the canonical meaning of a workflow item
  • the global search/index model
  • app-wide navigation policy
  • raw credential storage
  • unrestricted machine automation

Catalogue items and workflow records remain the host's authoritative execution inputs. Plugins are attached capability surfaces around those workflows.

Auth Model

Qlop uses external auth providers. Qlop does not define its own user model.

In phase 1:

  • auth connections are defined by the app, not by plugins
  • panels declare auth_strategy
  • the host shows the auth UI before panel render
  • the plugin does not build its own login form
  • Active Directory / LDAP is the first implemented provider

AD / LDAP Contract

AD connections are app-owned and connection-oriented. They require:

  • id
  • strategy: "ad"
  • displayName
  • domain
  • serverUrl
  • optional baseDn

Supported serverUrl forms:

  • ldaps://host:636
  • ldap://host:389

If ldap:// is used, Qlop attempts StartTLS by default.

Qlop binds against LDAP before it marks the session active. Multiple AD sessions may stay active for the current app runtime. Connection metadata, last-used username, and a plugin-scoped last-used connection preference may be persisted by the app for reuse.

Manifest Contract

manifest.json is the canonical contract between a plugin and the Qlop host.

Required fields:

  • id
  • author
  • display_name
  • version
  • min_qlop_version
  • plugin_sdk_version
  • frontend.entry
  • panels
  • permissions

Optional fields:

  • backend.entry
  • theme_path
  • settings
  • actions

The manifest is read and validated before the frontend bundle is imported. A plugin should assume that invalid manifests, invalid version ranges, or missing runtime files can cause the host to mark the plugin unavailable without executing any plugin code.

Panel definitions

A panel definition identifies a stable UI surface within the plugin.

panel_id must be stable across plugin releases unless the panel is intentionally replaced. Changing display names is fine. Reusing a panel_id for a different panel meaning is not.

permissions

permissions is part of the supported manifest contract in the current SDK version, even where host enforcement is still evolving.

Plugin authors must treat permissions as a declaration of intended capability domains. Do not use it as decorative metadata.

The architectural intent is:

  • plugins declare what host capabilities they expect
  • the host validates that declaration
  • the host exposes only approved capability surfaces to plugin code
  • undeclared capabilities should be considered unavailable

Supported permission values in 0.2.x are:

  • host.backend.request
  • host.external.open
  • host.auth.read
  • host.auth.write
  • host.actions.execute
  • host.remote_api.execute

Permissions are enforced by the host when plugin code uses the matching helper families. Keep them conservative and explicit.

Good permission declarations are:

  • stable
  • human-reviewable
  • auditable
  • narrow enough to explain why the plugin needs them

Bad permission declarations are:

  • catch-all names with unclear meaning
  • permissions added “just in case”
  • permissions that do not correspond to real plugin behavior

Backend entry

If a plugin declares backend.entry, backend behavior should remain plugin-owned and domain-specific. The backend is the right place for:

  • LDAP or directory queries
  • org API calls
  • data normalization for a plugin panel
  • workflow-specific external system access

The backend is not the right place to recreate host responsibilities such as plugin discovery, window management, catalogue ownership, or host auth policy.

Plugin settings

Plugins may optionally declare a host-managed settings block:

  • settings.schema_id
  • optional settings.display_name
  • optional settings.description
  • optional settings.defaults

The host owns persistence. Plugins may read or update only their own settings through the SDK helper surface. Settings changes may cause the host to reload plugin runtime state.

Settings fields are manifest-declared form definitions. Current supported field types are:

  • text
  • textarea
  • number
  • boolean
  • select
  • string_list
  • object_list

Plugin actions

Plugins may declare custom workflow actions in manifest.json. A plugin action is metadata plus a backend route. Catalogue workflows invoke it with the built-in plugin_action type.

Rules:

  • actions[].action_id must be unique inside the plugin
  • actions[].display_name must be non-empty
  • actions[].params uses the same schema as plugin settings
  • actions[].execute.method must be POST
  • actions[].execute.path must point to a plugin backend route
  • declaring any action requires backend.entry
  • declaring any action requires host.actions.execute

Manifest example:

{
  "permissions": ["host.backend.request", "host.actions.execute"],
  "backend": { "entry": "backend/routes.py" },
  "actions": [
    {
      "action_id": "post-demo-event",
      "display_name": "Post Demo Event",
      "description": "Send a workflow event to the example plugin backend.",
      "params": {
        "schema_id": "example-plugin.post-demo-event.v1",
        "fields": [
          { "key": "message", "label": "Message", "type": "text", "required": true },
          {
            "key": "severity",
            "label": "Severity",
            "type": "select",
            "options": [
              { "value": "info", "label": "Info" },
              { "value": "warning", "label": "Warning" }
            ]
          }
        ]
      },
      "execute": { "method": "POST", "path": "/actions/post-demo-event" }
    }
  ]
}

Catalogue usage:

{
  "type": "plugin_action",
  "condition": "always",
  "on_error": "stop",
  "params": {
    "plugin_id": "example-plugin",
    "action_id": "post-demo-event",
    "values": {
      "message": "{{prompt.note}}",
      "severity": "info"
    }
  }
}

Manifest Example

{
  "id": "example-ad-plugin",
  "author": "example-org",
  "display_name": "Example AD Plugin",
  "version": "1.0.0",
  "min_qlop_version": "0.1.0",
  "plugin_sdk_version": "0.2.0",
  "frontend": {
    "entry": "dist/index.js"
  },
  "panels": [
    {
      "panel_id": "example-ad-panel",
      "display_name": "Example AD Panel",
      "auth_strategy": "ad"
    }
  ],
  "permissions": [
    "host.auth.read",
    "host.auth.write"
  ]
}

Frontend Module Example

import { definePluginFrontendModule } from '@qlop/plugin-sdk'
import ExampleAdPanel from './ExampleAdPanel'

export default definePluginFrontendModule({
  panels: {
    'example-ad-panel': ExampleAdPanel
  }
})

The exported panels map is the plugin's render contract with the host.

Rules:

  • panel keys must match manifest panel_id values
  • every exported panel must be intentionally declared in the manifest
  • plugin code must not expect the host to discover components by filename or convention
  • the default export must remain a plain registration object, not a side-effectful bootstrap routine

Optional Typed Helpers

The SDK exports a few no-runtime-cost helpers to keep plugin code explicit:

  • definePlugin(...)
  • definePluginFrontendModule(...)
  • definePanel(...)
  • PanelShell

These helpers mainly improve authoring clarity and type inference.

Host Auth Helpers

The SDK also exports host auth commands:

import {
  clearActiveAuthSession,
  getActiveAuthSession,
  listActiveAuthSessions,
  getPanelAuthState,
  listAuthConnections,
  loginToAuthConnection
} from '@qlop/plugin-sdk'

These talk to the Qlop host. They do not bypass host-managed auth.

Use them when a panel needs to inspect current auth status or trigger explicit sign-in flows later.

These helpers are examples of the intended plugin boundary:

  • the plugin asks the host for auth state
  • the host decides how auth is resolved and persisted
  • the plugin does not reach around the host to manage raw credentials itself

For plugin-owned backend routes, use the host-mediated backend helper rather than direct localhost fetches from the panel:

import { requestPluginBackend } from '@qlop/plugin-sdk'

const response = await requestPluginBackend<MyResponse>('my-plugin', {
  method: 'GET',
  path: '/my-route',
  query: { id: '123' }
})

This keeps mounted plugin routes behind the same authenticated sidecar boundary.

For organisation-brokered external API calls, use the host-mediated organisation API helper. This requires host.remote_api.execute, and only works when the local Qlop sidecar is configured with an organisation service:

import { requestOrganisationApi } from '@qlop/plugin-sdk'

const response = await requestOrganisationApi<{ ticketId: string }>({
  externalServiceId: 'service-desk',
  operation: 'create-ticket',
  params: {
    title: 'Printer issue',
    priority: 'normal'
  }
})

Plugins request named operations. They do not send raw API secrets, arbitrary URLs, or arbitrary headers. The organisation service validates params against the operation schema before calling the external API.

For simple external links that should be opened by the host shell, use the SDK opener helper. Only https, http, and mailto URLs are supported:

import { openExternalUrl } from '@qlop/plugin-sdk'

await openExternalUrl('https://www.rundeck.com')

Panel Author Responsibilities

Every panel should be written with the following assumptions:

  • the host may delay panel render until auth is complete
  • the host may refuse to load the plugin before render if compatibility checks fail
  • the host owns the surrounding window and shell
  • the host may reload or invalidate plugin modules across app sessions
  • the panel should fail locally without destabilizing the rest of the app

Panels should keep their frontend responsibilities focused on:

  • rendering workflow-specific UI
  • collecting user input
  • presenting data returned by the plugin backend or host contract
  • invoking supported host helper functions through the SDK

Panels should not:

  • import from apps/desktop
  • assume direct filesystem access
  • assume direct shell execution
  • construct raw local file URLs to plugin assets outside the declared bundle contract
  • treat the panel window as an independent application root
  • mutate host-global state except through supported contracts
  • call backend routes for a different plugin id
  • rely on undeclared permissions continuing to work

Authoring Standard

Prefer function components for panel implementations.

Use PanelShell for new panels:

import { PanelShell } from '@qlop/plugin-sdk'

export default function ExamplePanel() {
  return (
    <PanelShell panelId="example-panel" title="Example Panel">
      <p className="text-sm text-muted-foreground">Example plugin panel scaffold.</p>
    </PanelShell>
  )
}

BasePanel remains available for compatibility, but it is now legacy authoring surface for new plugins.

Workflow-Safe Plugin Design

Qlop is workflow-oriented. Plugin design should reinforce that.

A good plugin panel:

  • makes an approved workflow easier to complete
  • keeps system-specific knowledge inside the plugin
  • keeps workflow identity and authority in the host/catalogue layer
  • allows operators to replace or update the workflow target without rewriting the panel contract

A weak plugin panel:

  • hardcodes org workflow meaning entirely inside the plugin
  • assumes it is the sole source of truth for where users should go next
  • bypasses host-managed metadata, auth, or policy

In practice, this means the plugin should usually answer:

  • how to present or fetch workflow-specific data
  • how to talk to one external system

It should not try to answer:

  • what the org's canonical workflow is
  • whether the user is allowed to run that workflow at all
  • how the host should index or rank that workflow globally

Plugin-Owned Runtime Resources

In the current model, runtime resource ownership is panel-scoped.

That means:

  • panel windows are plugin-owned runtime surfaces
  • plugin-specific backend activity is part of plugin runtime ownership
  • any future process tracking should apply to plugin/panel-owned runtime work only

General catalogue actions such as opening a file, opening a folder, opening a URL, or copying text do not become plugin-owned runtime resources just because a workflow item triggered them.

Workflow items may also declare a host-managed request_auth action before later sensitive actions such as run_exe or run_command.

Rules:

  • request_auth is workflow-chain scoped, not plugin-panel scoped
  • request_auth: { strategy: "local" } keeps later sensitive actions on the current OS user
  • request_auth: { strategy: "ad" } requests a chain-local Active Directory credential for later sensitive actions
  • request_auth does not change the host's global active auth session for panels

AD-Backed Panel Responsibilities

For AD-backed panels specifically:

  • declare auth_strategy: "ad" in manifest.json
  • assume the host may block panel render until auth is complete
  • keep LDAP query logic in the plugin backend, not in the frontend panel

Do not:

  • store passwords in plugin state
  • build a separate login form for the same auth profile
  • assume direct access to raw saved credentials

Compatibility Discipline

Plugin authors should treat compatibility fields as real contracts:

  • bump version when plugin behavior changes
  • keep panel_id stable unless intentionally replacing a panel
  • update min_qlop_version only when the plugin truly requires newer host behavior
  • update plugin_sdk_version only when the plugin is rebuilt against a newer SDK contract

Do not rely on accidental compatibility with older hosts.

Failure Expectations

A plugin must be safe to reject.

The host may refuse to load or render a plugin because of:

  • invalid manifest shape
  • missing frontend bundle
  • incompatible host version
  • incompatible SDK version
  • missing auth configuration
  • missing requested panel export

Plugin code should be written so these failures are diagnosable and contained. The rest of Qlop must remain stable if one plugin fails.

Example

See: