Spaces:
Paused
Paused
| /** | |
| * `definePlugin` — the top-level helper for authoring a Paperclip plugin. | |
| * | |
| * Plugin authors call `definePlugin()` and export the result as the default | |
| * export from their worker entrypoint. The host imports the worker module, | |
| * calls `setup()` with a `PluginContext`, and from that point the plugin | |
| * responds to events, jobs, webhooks, and UI requests through the context. | |
| * | |
| * @see PLUGIN_SPEC.md §14.1 — Example SDK Shape | |
| * | |
| * @example | |
| * ```ts | |
| * // dist/worker.ts | |
| * import { definePlugin } from "@paperclipai/plugin-sdk"; | |
| * | |
| * export default definePlugin({ | |
| * async setup(ctx) { | |
| * ctx.logger.info("Linear sync plugin starting"); | |
| * | |
| * // Subscribe to events | |
| * ctx.events.on("issue.created", async (event) => { | |
| * const config = await ctx.config.get(); | |
| * await ctx.http.fetch(`https://api.linear.app/...`, { | |
| * method: "POST", | |
| * headers: { Authorization: `Bearer ${await ctx.secrets.resolve(config.apiKeyRef as string)}` }, | |
| * body: JSON.stringify({ title: event.payload.title }), | |
| * }); | |
| * }); | |
| * | |
| * // Register a job handler | |
| * ctx.jobs.register("full-sync", async (job) => { | |
| * ctx.logger.info("Running full-sync job", { runId: job.runId }); | |
| * // ... sync logic | |
| * }); | |
| * | |
| * // Register data for the UI | |
| * ctx.data.register("sync-health", async ({ companyId }) => { | |
| * const state = await ctx.state.get({ | |
| * scopeKind: "company", | |
| * scopeId: String(companyId), | |
| * stateKey: "last-sync", | |
| * }); | |
| * return { lastSync: state }; | |
| * }); | |
| * }, | |
| * }); | |
| * ``` | |
| */ | |
| import type { PluginContext } from "./types.js"; | |
| // --------------------------------------------------------------------------- | |
| // Health check result | |
| // --------------------------------------------------------------------------- | |
| /** | |
| * Optional plugin-reported diagnostics returned from the `health()` RPC method. | |
| * | |
| * @see PLUGIN_SPEC.md §13.2 — `health` | |
| */ | |
| export interface PluginHealthDiagnostics { | |
| /** Machine-readable status: `"ok"` | `"degraded"` | `"error"`. */ | |
| status: "ok" | "degraded" | "error"; | |
| /** Human-readable description of the current health state. */ | |
| message?: string; | |
| /** Plugin-reported key-value diagnostics (e.g. connection status, queue depth). */ | |
| details?: Record<string, unknown>; | |
| } | |
| // --------------------------------------------------------------------------- | |
| // Config validation result | |
| // --------------------------------------------------------------------------- | |
| /** | |
| * Result returned from the `validateConfig()` RPC method. | |
| * | |
| * @see PLUGIN_SPEC.md §13.3 — `validateConfig` | |
| */ | |
| export interface PluginConfigValidationResult { | |
| /** Whether the config is valid. */ | |
| ok: boolean; | |
| /** Non-fatal warnings about the config. */ | |
| warnings?: string[]; | |
| /** Validation errors (populated when `ok` is `false`). */ | |
| errors?: string[]; | |
| } | |
| // --------------------------------------------------------------------------- | |
| // Webhook handler input | |
| // --------------------------------------------------------------------------- | |
| /** | |
| * Input received by the plugin worker's `handleWebhook` handler. | |
| * | |
| * @see PLUGIN_SPEC.md §13.7 — `handleWebhook` | |
| */ | |
| export interface PluginWebhookInput { | |
| /** Endpoint key matching the manifest declaration. */ | |
| endpointKey: string; | |
| /** Inbound request headers. */ | |
| headers: Record<string, string | string[]>; | |
| /** Raw request body as a UTF-8 string. */ | |
| rawBody: string; | |
| /** Parsed JSON body (if applicable and parseable). */ | |
| parsedBody?: unknown; | |
| /** Unique request identifier for idempotency checks. */ | |
| requestId: string; | |
| } | |
| // --------------------------------------------------------------------------- | |
| // Plugin definition | |
| // --------------------------------------------------------------------------- | |
| /** | |
| * The plugin definition shape passed to `definePlugin()`. | |
| * | |
| * The only required field is `setup`, which receives the `PluginContext` and | |
| * is where the plugin registers its handlers (events, jobs, data, actions, | |
| * tools, etc.). | |
| * | |
| * All other lifecycle hooks are optional. If a hook is not implemented the | |
| * host applies default behaviour (e.g. restarting the worker on config change | |
| * instead of calling `onConfigChanged`). | |
| * | |
| * @see PLUGIN_SPEC.md §13 — Host-Worker Protocol | |
| */ | |
| export interface PluginDefinition { | |
| /** | |
| * Called once when the plugin worker starts up, after `initialize` completes. | |
| * | |
| * This is where the plugin registers all its handlers: event subscriptions, | |
| * job handlers, data/action handlers, and tool registrations. Registration | |
| * must be synchronous after `setup` resolves — do not register handlers | |
| * inside async callbacks that may resolve after `setup` returns. | |
| * | |
| * @param ctx - The full plugin context provided by the host | |
| */ | |
| setup(ctx: PluginContext): Promise<void>; | |
| /** | |
| * Called when the host wants to know if the plugin is healthy. | |
| * | |
| * The host polls this on a regular interval and surfaces the result in the | |
| * plugin health dashboard. If not implemented, the host infers health from | |
| * worker process liveness. | |
| * | |
| * @see PLUGIN_SPEC.md §13.2 — `health` | |
| */ | |
| onHealth?(): Promise<PluginHealthDiagnostics>; | |
| /** | |
| * Called when the operator updates the plugin's instance configuration at | |
| * runtime, without restarting the worker. | |
| * | |
| * If not implemented, the host restarts the worker to apply the new config. | |
| * | |
| * @param newConfig - The newly resolved configuration | |
| * @see PLUGIN_SPEC.md §13.4 — `configChanged` | |
| */ | |
| onConfigChanged?(newConfig: Record<string, unknown>): Promise<void>; | |
| /** | |
| * Called when the host is about to shut down the plugin worker. | |
| * | |
| * The worker has at most 10 seconds (configurable via plugin config) to | |
| * finish in-flight work and resolve this promise. After the deadline the | |
| * host sends SIGTERM, then SIGKILL. | |
| * | |
| * @see PLUGIN_SPEC.md §12.5 — Graceful Shutdown Policy | |
| */ | |
| onShutdown?(): Promise<void>; | |
| /** | |
| * Called to validate the current plugin configuration. | |
| * | |
| * The host calls this: | |
| * - after the plugin starts (to surface config errors immediately) | |
| * - after the operator saves a new config (to validate before persisting) | |
| * - via the "Test Connection" button in the settings UI | |
| * | |
| * @param config - The configuration to validate | |
| * @see PLUGIN_SPEC.md §13.3 — `validateConfig` | |
| */ | |
| onValidateConfig?(config: Record<string, unknown>): Promise<PluginConfigValidationResult>; | |
| /** | |
| * Called to handle an inbound webhook delivery. | |
| * | |
| * The host routes `POST /api/plugins/:pluginId/webhooks/:endpointKey` to | |
| * this handler. The plugin is responsible for signature verification using | |
| * a resolved secret ref. | |
| * | |
| * If not implemented but webhooks are declared in the manifest, the host | |
| * returns HTTP 501 for webhook deliveries. | |
| * | |
| * @param input - Webhook delivery metadata and payload | |
| * @see PLUGIN_SPEC.md §13.7 — `handleWebhook` | |
| */ | |
| onWebhook?(input: PluginWebhookInput): Promise<void>; | |
| } | |
| // --------------------------------------------------------------------------- | |
| // PaperclipPlugin — the sealed object returned by definePlugin() | |
| // --------------------------------------------------------------------------- | |
| /** | |
| * The sealed plugin object returned by `definePlugin()`. | |
| * | |
| * Plugin authors export this as the default export from their worker | |
| * entrypoint. The host imports it and calls the lifecycle methods. | |
| * | |
| * @see PLUGIN_SPEC.md §14 — SDK Surface | |
| */ | |
| export interface PaperclipPlugin { | |
| /** The original plugin definition passed to `definePlugin()`. */ | |
| readonly definition: PluginDefinition; | |
| } | |
| // --------------------------------------------------------------------------- | |
| // definePlugin — top-level factory | |
| // --------------------------------------------------------------------------- | |
| /** | |
| * Define a Paperclip plugin. | |
| * | |
| * Call this function in your worker entrypoint and export the result as the | |
| * default export. The host will import the module and call lifecycle methods | |
| * on the returned object. | |
| * | |
| * @param definition - Plugin lifecycle handlers | |
| * @returns A sealed `PaperclipPlugin` object for the host to consume | |
| * | |
| * @example | |
| * ```ts | |
| * import { definePlugin } from "@paperclipai/plugin-sdk"; | |
| * | |
| * export default definePlugin({ | |
| * async setup(ctx) { | |
| * ctx.logger.info("Plugin started"); | |
| * ctx.events.on("issue.created", async (event) => { | |
| * // handle event | |
| * }); | |
| * }, | |
| * | |
| * async onHealth() { | |
| * return { status: "ok" }; | |
| * }, | |
| * }); | |
| * ``` | |
| * | |
| * @see PLUGIN_SPEC.md §14.1 — Example SDK Shape | |
| */ | |
| export function definePlugin(definition: PluginDefinition): PaperclipPlugin { | |
| return Object.freeze({ definition }); | |
| } | |