import { pgTable, uuid, text, integer, timestamp, jsonb, index, uniqueIndex, } from "drizzle-orm/pg-core"; import type { PluginCategory, PluginStatus, PaperclipPluginManifestV1 } from "@paperclipai/shared"; /** * `plugins` table — stores one row per installed plugin. * * Each plugin is uniquely identified by `plugin_key` (derived from * the manifest `id`). The full manifest is persisted as JSONB in * `manifest_json` so the host can reconstruct capability and UI * slot information without loading the plugin package. * * @see PLUGIN_SPEC.md §21.3 */ export const plugins = pgTable( "plugins", { id: uuid("id").primaryKey().defaultRandom(), pluginKey: text("plugin_key").notNull(), packageName: text("package_name").notNull(), version: text("version").notNull(), apiVersion: integer("api_version").notNull().default(1), categories: jsonb("categories").$type().notNull().default([]), manifestJson: jsonb("manifest_json").$type().notNull(), status: text("status").$type().notNull().default("installed"), installOrder: integer("install_order"), /** Resolved package path for local-path installs; used to find worker entrypoint. */ packagePath: text("package_path"), lastError: text("last_error"), installedAt: timestamp("installed_at", { withTimezone: true }).notNull().defaultNow(), updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow(), }, (table) => ({ pluginKeyIdx: uniqueIndex("plugins_plugin_key_idx").on(table.pluginKey), statusIdx: index("plugins_status_idx").on(table.status), }), );