Add kitchen sink plugin example
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
# @paperclipai/plugin-kitchen-sink-example
|
||||
|
||||
Kitchen Sink is the first-party reference plugin that demonstrates nearly the full currently implemented Paperclip plugin surface in one package.
|
||||
|
||||
It is intentionally broad:
|
||||
|
||||
- full plugin page
|
||||
- dashboard widget
|
||||
- project and issue surfaces
|
||||
- comment surfaces
|
||||
- sidebar surfaces
|
||||
- settings page
|
||||
- worker bridge data/actions
|
||||
- events, jobs, webhooks, tools, streams
|
||||
- state, entities, assets, metrics, activity
|
||||
- local workspace and process demos
|
||||
|
||||
This plugin is for local development, contributor onboarding, and runtime regression testing. It is not meant as a production plugin template to ship unchanged.
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
pnpm --filter @paperclipai/plugin-kitchen-sink-example build
|
||||
pnpm paperclipai plugin install ./packages/plugins/examples/plugin-kitchen-sink-example
|
||||
```
|
||||
|
||||
Or install it from the Paperclip plugin manager as a bundled example once this repo is built.
|
||||
|
||||
## Notes
|
||||
|
||||
- Local workspace and process demos are trusted-only and default to safe, curated commands.
|
||||
- The plugin settings page lets you toggle optional demo surfaces and local runtime behavior.
|
||||
- Some SDK-defined host surfaces still depend on the Paperclip host wiring them visibly; this package aims to exercise the currently mounted ones and make the rest obvious.
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "@paperclipai/plugin-kitchen-sink-example",
|
||||
"version": "0.1.0",
|
||||
"description": "Reference plugin that demonstrates the full Paperclip plugin surface area in one package",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"paperclipPlugin": {
|
||||
"manifest": "./dist/manifest.js",
|
||||
"worker": "./dist/worker.js",
|
||||
"ui": "./dist/ui/"
|
||||
},
|
||||
"scripts": {
|
||||
"prebuild": "node ../../../../scripts/ensure-plugin-build-deps.mjs",
|
||||
"build": "tsc && node ./scripts/build-ui.mjs",
|
||||
"clean": "rm -rf dist",
|
||||
"typecheck": "pnpm --filter @paperclipai/plugin-sdk build && tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@paperclipai/plugin-sdk": "workspace:*",
|
||||
"@paperclipai/shared": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"esbuild": "^0.27.3",
|
||||
"@types/node": "^24.6.0",
|
||||
"@types/react": "^19.0.8",
|
||||
"@types/react-dom": "^19.0.3",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"typescript": "^5.7.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=18"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import esbuild from "esbuild";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const packageRoot = path.resolve(__dirname, "..");
|
||||
|
||||
await esbuild.build({
|
||||
entryPoints: [path.join(packageRoot, "src/ui/index.tsx")],
|
||||
outfile: path.join(packageRoot, "dist/ui/index.js"),
|
||||
bundle: true,
|
||||
format: "esm",
|
||||
platform: "browser",
|
||||
target: ["es2022"],
|
||||
sourcemap: true,
|
||||
external: [
|
||||
"react",
|
||||
"react-dom",
|
||||
"react/jsx-runtime",
|
||||
"@paperclipai/plugin-sdk/ui",
|
||||
],
|
||||
logLevel: "info",
|
||||
});
|
||||
@@ -0,0 +1,112 @@
|
||||
import type { PluginLauncherRegistration } from "@paperclipai/plugin-sdk";
|
||||
|
||||
export const PLUGIN_ID = "paperclip-kitchen-sink-example";
|
||||
export const PLUGIN_VERSION = "0.1.0";
|
||||
|
||||
export const SLOT_IDS = {
|
||||
page: "kitchen-sink-page",
|
||||
settingsPage: "kitchen-sink-settings-page",
|
||||
dashboardWidget: "kitchen-sink-dashboard-widget",
|
||||
sidebar: "kitchen-sink-sidebar-link",
|
||||
sidebarPanel: "kitchen-sink-sidebar-panel",
|
||||
projectSidebarItem: "kitchen-sink-project-link",
|
||||
projectTab: "kitchen-sink-project-tab",
|
||||
issueTab: "kitchen-sink-issue-tab",
|
||||
taskDetailView: "kitchen-sink-task-detail",
|
||||
toolbarButton: "kitchen-sink-toolbar-action",
|
||||
contextMenuItem: "kitchen-sink-context-action",
|
||||
commentAnnotation: "kitchen-sink-comment-annotation",
|
||||
commentContextMenuItem: "kitchen-sink-comment-action",
|
||||
} as const;
|
||||
|
||||
export const EXPORT_NAMES = {
|
||||
page: "KitchenSinkPage",
|
||||
settingsPage: "KitchenSinkSettingsPage",
|
||||
dashboardWidget: "KitchenSinkDashboardWidget",
|
||||
sidebar: "KitchenSinkSidebarLink",
|
||||
sidebarPanel: "KitchenSinkSidebarPanel",
|
||||
projectSidebarItem: "KitchenSinkProjectSidebarItem",
|
||||
projectTab: "KitchenSinkProjectTab",
|
||||
issueTab: "KitchenSinkIssueTab",
|
||||
taskDetailView: "KitchenSinkTaskDetailView",
|
||||
toolbarButton: "KitchenSinkToolbarButton",
|
||||
contextMenuItem: "KitchenSinkContextMenuItem",
|
||||
commentAnnotation: "KitchenSinkCommentAnnotation",
|
||||
commentContextMenuItem: "KitchenSinkCommentContextMenuItem",
|
||||
launcherModal: "KitchenSinkLauncherModal",
|
||||
} as const;
|
||||
|
||||
export const JOB_KEYS = {
|
||||
heartbeat: "demo-heartbeat",
|
||||
} as const;
|
||||
|
||||
export const WEBHOOK_KEYS = {
|
||||
demo: "demo-ingest",
|
||||
} as const;
|
||||
|
||||
export const TOOL_NAMES = {
|
||||
echo: "echo",
|
||||
companySummary: "company-summary",
|
||||
createIssue: "create-issue",
|
||||
} as const;
|
||||
|
||||
export const STREAM_CHANNELS = {
|
||||
progress: "progress",
|
||||
agentChat: "agent-chat",
|
||||
} as const;
|
||||
|
||||
export const SAFE_COMMANDS = [
|
||||
{
|
||||
key: "pwd",
|
||||
label: "Print workspace path",
|
||||
command: "pwd",
|
||||
args: [] as string[],
|
||||
description: "Prints the current workspace directory.",
|
||||
},
|
||||
{
|
||||
key: "ls",
|
||||
label: "List workspace files",
|
||||
command: "ls",
|
||||
args: ["-la"] as string[],
|
||||
description: "Lists files in the selected workspace.",
|
||||
},
|
||||
{
|
||||
key: "git-status",
|
||||
label: "Git status",
|
||||
command: "git",
|
||||
args: ["status", "--short", "--branch"] as string[],
|
||||
description: "Shows git status for the selected workspace.",
|
||||
},
|
||||
] as const;
|
||||
|
||||
export type SafeCommandKey = (typeof SAFE_COMMANDS)[number]["key"];
|
||||
|
||||
export const DEFAULT_CONFIG = {
|
||||
showSidebarEntry: true,
|
||||
showSidebarPanel: true,
|
||||
showProjectSidebarItem: true,
|
||||
showCommentAnnotation: true,
|
||||
showCommentContextMenuItem: true,
|
||||
enableWorkspaceDemos: true,
|
||||
enableProcessDemos: false,
|
||||
secretRefExample: "",
|
||||
httpDemoUrl: "https://httpbin.org/anything",
|
||||
allowedCommands: SAFE_COMMANDS.map((command) => command.key),
|
||||
workspaceScratchFile: ".paperclip-kitchen-sink-demo.txt",
|
||||
} as const;
|
||||
|
||||
export const RUNTIME_LAUNCHER: PluginLauncherRegistration = {
|
||||
id: "kitchen-sink-runtime-launcher",
|
||||
displayName: "Kitchen Sink Modal",
|
||||
description: "Demonstrates runtime launcher registration from the worker.",
|
||||
placementZone: "toolbarButton",
|
||||
entityTypes: ["project", "issue"],
|
||||
action: {
|
||||
type: "openModal",
|
||||
target: EXPORT_NAMES.launcherModal,
|
||||
},
|
||||
render: {
|
||||
environment: "hostOverlay",
|
||||
bounds: "wide",
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,2 @@
|
||||
export { default as manifest } from "./manifest.js";
|
||||
export { default as worker } from "./worker.js";
|
||||
@@ -0,0 +1,290 @@
|
||||
import type { PaperclipPluginManifestV1 } from "@paperclipai/plugin-sdk";
|
||||
import {
|
||||
DEFAULT_CONFIG,
|
||||
EXPORT_NAMES,
|
||||
JOB_KEYS,
|
||||
PLUGIN_ID,
|
||||
PLUGIN_VERSION,
|
||||
SLOT_IDS,
|
||||
TOOL_NAMES,
|
||||
WEBHOOK_KEYS,
|
||||
} from "./constants.js";
|
||||
|
||||
const manifest: PaperclipPluginManifestV1 = {
|
||||
id: PLUGIN_ID,
|
||||
apiVersion: 1,
|
||||
version: PLUGIN_VERSION,
|
||||
displayName: "Kitchen Sink (Example)",
|
||||
description: "Reference plugin that demonstrates the current Paperclip plugin API surface, UI surfaces, bridge actions, events, jobs, webhooks, tools, local workspace access, and runtime diagnostics in one place.",
|
||||
author: "Paperclip",
|
||||
categories: ["ui", "automation", "workspace", "connector"],
|
||||
capabilities: [
|
||||
"companies.read",
|
||||
"projects.read",
|
||||
"project.workspaces.read",
|
||||
"issues.read",
|
||||
"issues.create",
|
||||
"issues.update",
|
||||
"issue.comments.read",
|
||||
"issue.comments.create",
|
||||
"agents.read",
|
||||
"agents.pause",
|
||||
"agents.resume",
|
||||
"agents.invoke",
|
||||
"agent.sessions.create",
|
||||
"agent.sessions.list",
|
||||
"agent.sessions.send",
|
||||
"agent.sessions.close",
|
||||
"goals.read",
|
||||
"goals.create",
|
||||
"goals.update",
|
||||
"assets.write",
|
||||
"assets.read",
|
||||
"activity.log.write",
|
||||
"metrics.write",
|
||||
"plugin.state.read",
|
||||
"plugin.state.write",
|
||||
"events.subscribe",
|
||||
"events.emit",
|
||||
"jobs.schedule",
|
||||
"webhooks.receive",
|
||||
"http.outbound",
|
||||
"secrets.read-ref",
|
||||
"agent.tools.register",
|
||||
"instance.settings.register",
|
||||
"ui.sidebar.register",
|
||||
"ui.page.register",
|
||||
"ui.detailTab.register",
|
||||
"ui.dashboardWidget.register",
|
||||
"ui.commentAnnotation.register",
|
||||
"ui.action.register",
|
||||
],
|
||||
entrypoints: {
|
||||
worker: "./dist/worker.js",
|
||||
ui: "./dist/ui",
|
||||
},
|
||||
instanceConfigSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
showSidebarEntry: {
|
||||
type: "boolean",
|
||||
title: "Show Sidebar Entry",
|
||||
default: DEFAULT_CONFIG.showSidebarEntry,
|
||||
},
|
||||
showSidebarPanel: {
|
||||
type: "boolean",
|
||||
title: "Show Sidebar Panel",
|
||||
default: DEFAULT_CONFIG.showSidebarPanel,
|
||||
},
|
||||
showProjectSidebarItem: {
|
||||
type: "boolean",
|
||||
title: "Show Project Sidebar Item",
|
||||
default: DEFAULT_CONFIG.showProjectSidebarItem,
|
||||
},
|
||||
showCommentAnnotation: {
|
||||
type: "boolean",
|
||||
title: "Show Comment Annotation",
|
||||
default: DEFAULT_CONFIG.showCommentAnnotation,
|
||||
},
|
||||
showCommentContextMenuItem: {
|
||||
type: "boolean",
|
||||
title: "Show Comment Action",
|
||||
default: DEFAULT_CONFIG.showCommentContextMenuItem,
|
||||
},
|
||||
enableWorkspaceDemos: {
|
||||
type: "boolean",
|
||||
title: "Enable Workspace Demos",
|
||||
default: DEFAULT_CONFIG.enableWorkspaceDemos,
|
||||
},
|
||||
enableProcessDemos: {
|
||||
type: "boolean",
|
||||
title: "Enable Process Demos",
|
||||
default: DEFAULT_CONFIG.enableProcessDemos,
|
||||
description: "Allows curated local child-process demos in project workspaces.",
|
||||
},
|
||||
secretRefExample: {
|
||||
type: "string",
|
||||
title: "Secret Reference Example",
|
||||
default: DEFAULT_CONFIG.secretRefExample,
|
||||
},
|
||||
httpDemoUrl: {
|
||||
type: "string",
|
||||
title: "HTTP Demo URL",
|
||||
default: DEFAULT_CONFIG.httpDemoUrl,
|
||||
},
|
||||
allowedCommands: {
|
||||
type: "array",
|
||||
title: "Allowed Process Commands",
|
||||
items: {
|
||||
type: "string",
|
||||
enum: DEFAULT_CONFIG.allowedCommands,
|
||||
},
|
||||
default: DEFAULT_CONFIG.allowedCommands,
|
||||
},
|
||||
workspaceScratchFile: {
|
||||
type: "string",
|
||||
title: "Workspace Scratch File",
|
||||
default: DEFAULT_CONFIG.workspaceScratchFile,
|
||||
},
|
||||
},
|
||||
},
|
||||
jobs: [
|
||||
{
|
||||
jobKey: JOB_KEYS.heartbeat,
|
||||
displayName: "Demo Heartbeat",
|
||||
description: "Periodic demo job that records plugin runtime activity.",
|
||||
schedule: "*/15 * * * *",
|
||||
},
|
||||
],
|
||||
webhooks: [
|
||||
{
|
||||
endpointKey: WEBHOOK_KEYS.demo,
|
||||
displayName: "Demo Ingest",
|
||||
description: "Accepts arbitrary webhook payloads and records the latest delivery in plugin state.",
|
||||
},
|
||||
],
|
||||
tools: [
|
||||
{
|
||||
name: TOOL_NAMES.echo,
|
||||
displayName: "Kitchen Sink Echo",
|
||||
description: "Returns the provided message and the current run context.",
|
||||
parametersSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
message: { type: "string" },
|
||||
},
|
||||
required: ["message"],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: TOOL_NAMES.companySummary,
|
||||
displayName: "Kitchen Sink Company Summary",
|
||||
description: "Summarizes the current company using the Paperclip domain APIs.",
|
||||
parametersSchema: {
|
||||
type: "object",
|
||||
properties: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: TOOL_NAMES.createIssue,
|
||||
displayName: "Kitchen Sink Create Issue",
|
||||
description: "Creates an issue in the current project from an agent tool call.",
|
||||
parametersSchema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
title: { type: "string" },
|
||||
description: { type: "string" },
|
||||
},
|
||||
required: ["title"],
|
||||
},
|
||||
},
|
||||
],
|
||||
ui: {
|
||||
slots: [
|
||||
{
|
||||
type: "page",
|
||||
id: SLOT_IDS.page,
|
||||
displayName: "Kitchen Sink",
|
||||
exportName: EXPORT_NAMES.page,
|
||||
},
|
||||
{
|
||||
type: "settingsPage",
|
||||
id: SLOT_IDS.settingsPage,
|
||||
displayName: "Kitchen Sink Settings",
|
||||
exportName: EXPORT_NAMES.settingsPage,
|
||||
},
|
||||
{
|
||||
type: "dashboardWidget",
|
||||
id: SLOT_IDS.dashboardWidget,
|
||||
displayName: "Kitchen Sink",
|
||||
exportName: EXPORT_NAMES.dashboardWidget,
|
||||
},
|
||||
{
|
||||
type: "sidebar",
|
||||
id: SLOT_IDS.sidebar,
|
||||
displayName: "Kitchen Sink",
|
||||
exportName: EXPORT_NAMES.sidebar,
|
||||
},
|
||||
{
|
||||
type: "sidebarPanel",
|
||||
id: SLOT_IDS.sidebarPanel,
|
||||
displayName: "Kitchen Sink Panel",
|
||||
exportName: EXPORT_NAMES.sidebarPanel,
|
||||
},
|
||||
{
|
||||
type: "projectSidebarItem",
|
||||
id: SLOT_IDS.projectSidebarItem,
|
||||
displayName: "Kitchen Sink",
|
||||
exportName: EXPORT_NAMES.projectSidebarItem,
|
||||
entityTypes: ["project"],
|
||||
},
|
||||
{
|
||||
type: "detailTab",
|
||||
id: SLOT_IDS.projectTab,
|
||||
displayName: "Kitchen Sink",
|
||||
exportName: EXPORT_NAMES.projectTab,
|
||||
entityTypes: ["project"],
|
||||
},
|
||||
{
|
||||
type: "detailTab",
|
||||
id: SLOT_IDS.issueTab,
|
||||
displayName: "Kitchen Sink",
|
||||
exportName: EXPORT_NAMES.issueTab,
|
||||
entityTypes: ["issue"],
|
||||
},
|
||||
{
|
||||
type: "taskDetailView",
|
||||
id: SLOT_IDS.taskDetailView,
|
||||
displayName: "Kitchen Sink Task View",
|
||||
exportName: EXPORT_NAMES.taskDetailView,
|
||||
entityTypes: ["issue"],
|
||||
},
|
||||
{
|
||||
type: "toolbarButton",
|
||||
id: SLOT_IDS.toolbarButton,
|
||||
displayName: "Kitchen Sink Action",
|
||||
exportName: EXPORT_NAMES.toolbarButton,
|
||||
entityTypes: ["project", "issue"],
|
||||
},
|
||||
{
|
||||
type: "contextMenuItem",
|
||||
id: SLOT_IDS.contextMenuItem,
|
||||
displayName: "Kitchen Sink Context",
|
||||
exportName: EXPORT_NAMES.contextMenuItem,
|
||||
entityTypes: ["project", "issue"],
|
||||
},
|
||||
{
|
||||
type: "commentAnnotation",
|
||||
id: SLOT_IDS.commentAnnotation,
|
||||
displayName: "Kitchen Sink Comment Annotation",
|
||||
exportName: EXPORT_NAMES.commentAnnotation,
|
||||
entityTypes: ["comment"],
|
||||
},
|
||||
{
|
||||
type: "commentContextMenuItem",
|
||||
id: SLOT_IDS.commentContextMenuItem,
|
||||
displayName: "Kitchen Sink Comment Action",
|
||||
exportName: EXPORT_NAMES.commentContextMenuItem,
|
||||
entityTypes: ["comment"],
|
||||
},
|
||||
],
|
||||
launchers: [
|
||||
{
|
||||
id: "kitchen-sink-launcher",
|
||||
displayName: "Kitchen Sink Modal",
|
||||
placementZone: "toolbarButton",
|
||||
entityTypes: ["project", "issue"],
|
||||
action: {
|
||||
type: "openModal",
|
||||
target: EXPORT_NAMES.launcherModal,
|
||||
},
|
||||
render: {
|
||||
environment: "hostOverlay",
|
||||
bounds: "wide",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export default manifest;
|
||||
File diff suppressed because it is too large
Load Diff
1055
packages/plugins/examples/plugin-kitchen-sink-example/src/worker.ts
Normal file
1055
packages/plugins/examples/plugin-kitchen-sink-example/src/worker.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "../../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"rootDir": "src",
|
||||
"lib": ["ES2023", "DOM"],
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
Reference in New Issue
Block a user