Initial commit: Paperclip plugin for Legal AI integration
16 agent tools, event handler for auto-linking, sync job every 15m. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
57
node_modules/@paperclipai/plugin-sdk/dist/bundlers.d.ts
generated
vendored
Normal file
57
node_modules/@paperclipai/plugin-sdk/dist/bundlers.d.ts
generated
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* Bundling presets for Paperclip plugins.
|
||||
*
|
||||
* These helpers return plain config objects so plugin authors can use them
|
||||
* with esbuild or rollup without re-implementing host contract defaults.
|
||||
*/
|
||||
export interface PluginBundlerPresetInput {
|
||||
pluginRoot?: string;
|
||||
manifestEntry?: string;
|
||||
workerEntry?: string;
|
||||
uiEntry?: string;
|
||||
outdir?: string;
|
||||
sourcemap?: boolean;
|
||||
minify?: boolean;
|
||||
}
|
||||
export interface EsbuildLikeOptions {
|
||||
entryPoints: string[];
|
||||
outdir: string;
|
||||
bundle: boolean;
|
||||
format: "esm";
|
||||
platform: "node" | "browser";
|
||||
target: string;
|
||||
sourcemap?: boolean;
|
||||
minify?: boolean;
|
||||
external?: string[];
|
||||
}
|
||||
export interface RollupLikeConfig {
|
||||
input: string;
|
||||
output: {
|
||||
dir: string;
|
||||
format: "es";
|
||||
sourcemap?: boolean;
|
||||
entryFileNames?: string;
|
||||
};
|
||||
external?: string[];
|
||||
plugins?: unknown[];
|
||||
}
|
||||
export interface PluginBundlerPresets {
|
||||
esbuild: {
|
||||
worker: EsbuildLikeOptions;
|
||||
ui?: EsbuildLikeOptions;
|
||||
manifest: EsbuildLikeOptions;
|
||||
};
|
||||
rollup: {
|
||||
worker: RollupLikeConfig;
|
||||
ui?: RollupLikeConfig;
|
||||
manifest: RollupLikeConfig;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Build esbuild/rollup baseline configs for plugin worker, manifest, and UI bundles.
|
||||
*
|
||||
* The presets intentionally externalize host/runtime deps (`react`, SDK packages)
|
||||
* to match the Paperclip plugin loader contract.
|
||||
*/
|
||||
export declare function createPluginBundlerPresets(input?: PluginBundlerPresetInput): PluginBundlerPresets;
|
||||
//# sourceMappingURL=bundlers.d.ts.map
|
||||
1
node_modules/@paperclipai/plugin-sdk/dist/bundlers.d.ts.map
generated
vendored
Normal file
1
node_modules/@paperclipai/plugin-sdk/dist/bundlers.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"bundlers.d.ts","sourceRoot":"","sources":["../src/bundlers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,wBAAwB;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,KAAK,CAAC;IACd,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE;QACN,GAAG,EAAE,MAAM,CAAC;QACZ,MAAM,EAAE,IAAI,CAAC;QACb,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,CAAC;IACF,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE;QACP,MAAM,EAAE,kBAAkB,CAAC;QAC3B,EAAE,CAAC,EAAE,kBAAkB,CAAC;QACxB,QAAQ,EAAE,kBAAkB,CAAC;KAC9B,CAAC;IACF,MAAM,EAAE;QACN,MAAM,EAAE,gBAAgB,CAAC;QACzB,EAAE,CAAC,EAAE,gBAAgB,CAAC;QACtB,QAAQ,EAAE,gBAAgB,CAAC;KAC5B,CAAC;CACH;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,GAAE,wBAA6B,GAAG,oBAAoB,CAmGrG"}
|
||||
105
node_modules/@paperclipai/plugin-sdk/dist/bundlers.js
generated
vendored
Normal file
105
node_modules/@paperclipai/plugin-sdk/dist/bundlers.js
generated
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
* Bundling presets for Paperclip plugins.
|
||||
*
|
||||
* These helpers return plain config objects so plugin authors can use them
|
||||
* with esbuild or rollup without re-implementing host contract defaults.
|
||||
*/
|
||||
/**
|
||||
* Build esbuild/rollup baseline configs for plugin worker, manifest, and UI bundles.
|
||||
*
|
||||
* The presets intentionally externalize host/runtime deps (`react`, SDK packages)
|
||||
* to match the Paperclip plugin loader contract.
|
||||
*/
|
||||
export function createPluginBundlerPresets(input = {}) {
|
||||
const uiExternal = [
|
||||
"@paperclipai/plugin-sdk/ui",
|
||||
"@paperclipai/plugin-sdk/ui/hooks",
|
||||
"react",
|
||||
"react-dom",
|
||||
"react/jsx-runtime",
|
||||
];
|
||||
const outdir = input.outdir ?? "dist";
|
||||
const workerEntry = input.workerEntry ?? "src/worker.ts";
|
||||
const manifestEntry = input.manifestEntry ?? "src/manifest.ts";
|
||||
const uiEntry = input.uiEntry;
|
||||
const sourcemap = input.sourcemap ?? true;
|
||||
const minify = input.minify ?? false;
|
||||
const esbuildWorker = {
|
||||
entryPoints: [workerEntry],
|
||||
outdir,
|
||||
bundle: true,
|
||||
format: "esm",
|
||||
platform: "node",
|
||||
target: "node20",
|
||||
sourcemap,
|
||||
minify,
|
||||
external: ["react", "react-dom"],
|
||||
};
|
||||
const esbuildManifest = {
|
||||
entryPoints: [manifestEntry],
|
||||
outdir,
|
||||
bundle: false,
|
||||
format: "esm",
|
||||
platform: "node",
|
||||
target: "node20",
|
||||
sourcemap,
|
||||
};
|
||||
const esbuildUi = uiEntry
|
||||
? {
|
||||
entryPoints: [uiEntry],
|
||||
outdir: `${outdir}/ui`,
|
||||
bundle: true,
|
||||
format: "esm",
|
||||
platform: "browser",
|
||||
target: "es2022",
|
||||
sourcemap,
|
||||
minify,
|
||||
external: uiExternal,
|
||||
}
|
||||
: undefined;
|
||||
const rollupWorker = {
|
||||
input: workerEntry,
|
||||
output: {
|
||||
dir: outdir,
|
||||
format: "es",
|
||||
sourcemap,
|
||||
entryFileNames: "worker.js",
|
||||
},
|
||||
external: ["react", "react-dom"],
|
||||
};
|
||||
const rollupManifest = {
|
||||
input: manifestEntry,
|
||||
output: {
|
||||
dir: outdir,
|
||||
format: "es",
|
||||
sourcemap,
|
||||
entryFileNames: "manifest.js",
|
||||
},
|
||||
external: ["@paperclipai/plugin-sdk"],
|
||||
};
|
||||
const rollupUi = uiEntry
|
||||
? {
|
||||
input: uiEntry,
|
||||
output: {
|
||||
dir: `${outdir}/ui`,
|
||||
format: "es",
|
||||
sourcemap,
|
||||
entryFileNames: "index.js",
|
||||
},
|
||||
external: uiExternal,
|
||||
}
|
||||
: undefined;
|
||||
return {
|
||||
esbuild: {
|
||||
worker: esbuildWorker,
|
||||
manifest: esbuildManifest,
|
||||
...(esbuildUi ? { ui: esbuildUi } : {}),
|
||||
},
|
||||
rollup: {
|
||||
worker: rollupWorker,
|
||||
manifest: rollupManifest,
|
||||
...(rollupUi ? { ui: rollupUi } : {}),
|
||||
},
|
||||
};
|
||||
}
|
||||
//# sourceMappingURL=bundlers.js.map
|
||||
1
node_modules/@paperclipai/plugin-sdk/dist/bundlers.js.map
generated
vendored
Normal file
1
node_modules/@paperclipai/plugin-sdk/dist/bundlers.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"bundlers.js","sourceRoot":"","sources":["../src/bundlers.ts"],"names":[],"mappings":"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"}
|
||||
218
node_modules/@paperclipai/plugin-sdk/dist/define-plugin.d.ts
generated
vendored
Normal file
218
node_modules/@paperclipai/plugin-sdk/dist/define-plugin.d.ts
generated
vendored
Normal file
@@ -0,0 +1,218 @@
|
||||
/**
|
||||
* `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";
|
||||
/**
|
||||
* 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>;
|
||||
}
|
||||
/**
|
||||
* 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[];
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
/**
|
||||
* 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>;
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
/**
|
||||
* 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 declare function definePlugin(definition: PluginDefinition): PaperclipPlugin;
|
||||
//# sourceMappingURL=define-plugin.d.ts.map
|
||||
1
node_modules/@paperclipai/plugin-sdk/dist/define-plugin.d.ts.map
generated
vendored
Normal file
1
node_modules/@paperclipai/plugin-sdk/dist/define-plugin.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"define-plugin.d.ts","sourceRoot":"","sources":["../src/define-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAMhD;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACtC,kEAAkE;IAClE,MAAM,EAAE,IAAI,GAAG,UAAU,GAAG,OAAO,CAAC;IACpC,8DAA8D;IAC9D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mFAAmF;IACnF,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAMD;;;;GAIG;AACH,MAAM,WAAW,4BAA4B;IAC3C,mCAAmC;IACnC,EAAE,EAAE,OAAO,CAAC;IACZ,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAMD;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,sDAAsD;IACtD,WAAW,EAAE,MAAM,CAAC;IACpB,+BAA+B;IAC/B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IAC3C,0CAA0C;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,sDAAsD;IACtD,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,wDAAwD;IACxD,SAAS,EAAE,MAAM,CAAC;CACnB;AAMD;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;;;;;;OASG;IACH,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzC;;;;;;;;OAQG;IACH,QAAQ,CAAC,IAAI,OAAO,CAAC,uBAAuB,CAAC,CAAC;IAE9C;;;;;;;;OAQG;IACH,eAAe,CAAC,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpE;;;;;;;;OAQG;IACH,UAAU,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAE7B;;;;;;;;;;OAUG;IACH,gBAAgB,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,4BAA4B,CAAC,CAAC;IAE1F;;;;;;;;;;;;OAYG;IACH,SAAS,CAAC,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtD;AAMD;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B,iEAAiE;IACjE,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAC;CACvC;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,gBAAgB,GAAG,eAAe,CAE1E"}
|
||||
85
node_modules/@paperclipai/plugin-sdk/dist/define-plugin.js
generated
vendored
Normal file
85
node_modules/@paperclipai/plugin-sdk/dist/define-plugin.js
generated
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
/**
|
||||
* `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 };
|
||||
* });
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
// ---------------------------------------------------------------------------
|
||||
// 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) {
|
||||
return Object.freeze({ definition });
|
||||
}
|
||||
//# sourceMappingURL=define-plugin.js.map
|
||||
1
node_modules/@paperclipai/plugin-sdk/dist/define-plugin.js.map
generated
vendored
Normal file
1
node_modules/@paperclipai/plugin-sdk/dist/define-plugin.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"define-plugin.js","sourceRoot":"","sources":["../src/define-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AA2KH,8EAA8E;AAC9E,mCAAmC;AACnC,8EAA8E;AAE9E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,UAAU,YAAY,CAAC,UAA4B;IACvD,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC;AACvC,CAAC"}
|
||||
3
node_modules/@paperclipai/plugin-sdk/dist/dev-cli.d.ts
generated
vendored
Normal file
3
node_modules/@paperclipai/plugin-sdk/dist/dev-cli.d.ts
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env node
|
||||
export {};
|
||||
//# sourceMappingURL=dev-cli.d.ts.map
|
||||
1
node_modules/@paperclipai/plugin-sdk/dist/dev-cli.d.ts.map
generated
vendored
Normal file
1
node_modules/@paperclipai/plugin-sdk/dist/dev-cli.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"dev-cli.d.ts","sourceRoot":"","sources":["../src/dev-cli.ts"],"names":[],"mappings":""}
|
||||
49
node_modules/@paperclipai/plugin-sdk/dist/dev-cli.js
generated
vendored
Executable file
49
node_modules/@paperclipai/plugin-sdk/dist/dev-cli.js
generated
vendored
Executable file
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env node
|
||||
import path from "node:path";
|
||||
import { startPluginDevServer } from "./dev-server.js";
|
||||
function parseArg(flag) {
|
||||
const index = process.argv.indexOf(flag);
|
||||
if (index < 0)
|
||||
return undefined;
|
||||
return process.argv[index + 1];
|
||||
}
|
||||
/**
|
||||
* CLI entrypoint for the local plugin UI preview server.
|
||||
*
|
||||
* This is intentionally minimal and delegates all serving behavior to
|
||||
* `startPluginDevServer` so tests and programmatic usage share one path.
|
||||
*/
|
||||
async function main() {
|
||||
const rootDir = parseArg("--root") ?? process.cwd();
|
||||
const uiDir = parseArg("--ui-dir") ?? "dist/ui";
|
||||
const host = parseArg("--host") ?? "127.0.0.1";
|
||||
const rawPort = parseArg("--port") ?? "4177";
|
||||
const port = Number.parseInt(rawPort, 10);
|
||||
if (!Number.isFinite(port) || port <= 0 || port > 65535) {
|
||||
throw new Error(`Invalid --port value: ${rawPort}`);
|
||||
}
|
||||
const server = await startPluginDevServer({
|
||||
rootDir: path.resolve(rootDir),
|
||||
uiDir,
|
||||
host,
|
||||
port,
|
||||
});
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Paperclip plugin dev server listening at ${server.url}`);
|
||||
const shutdown = async () => {
|
||||
await server.close();
|
||||
process.exit(0);
|
||||
};
|
||||
process.on("SIGINT", () => {
|
||||
void shutdown();
|
||||
});
|
||||
process.on("SIGTERM", () => {
|
||||
void shutdown();
|
||||
});
|
||||
}
|
||||
void main().catch((error) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error instanceof Error ? error.message : String(error));
|
||||
process.exit(1);
|
||||
});
|
||||
//# sourceMappingURL=dev-cli.js.map
|
||||
1
node_modules/@paperclipai/plugin-sdk/dist/dev-cli.js.map
generated
vendored
Normal file
1
node_modules/@paperclipai/plugin-sdk/dist/dev-cli.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"dev-cli.js","sourceRoot":"","sources":["../src/dev-cli.ts"],"names":[],"mappings":";AACA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAEvD,SAAS,QAAQ,CAAC,IAAY;IAC5B,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC;IAChC,OAAO,OAAO,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;AACjC,CAAC;AAED;;;;;GAKG;AACH,KAAK,UAAU,IAAI;IACjB,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IACpD,MAAM,KAAK,GAAG,QAAQ,CAAC,UAAU,CAAC,IAAI,SAAS,CAAC;IAChD,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,IAAI,WAAW,CAAC;IAC/C,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC;IAC7C,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC1C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,GAAG,KAAK,EAAE,CAAC;QACxD,MAAM,IAAI,KAAK,CAAC,yBAAyB,OAAO,EAAE,CAAC,CAAC;IACtD,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,oBAAoB,CAAC;QACxC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC;QAC9B,KAAK;QACL,IAAI;QACJ,IAAI;KACL,CAAC,CAAC;IAEH,sCAAsC;IACtC,OAAO,CAAC,GAAG,CAAC,4CAA4C,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC;IAEtE,MAAM,QAAQ,GAAG,KAAK,IAAI,EAAE;QAC1B,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACrB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC;IAEF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE;QACxB,KAAK,QAAQ,EAAE,CAAC;IAClB,CAAC,CAAC,CAAC;IACH,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE;QACzB,KAAK,QAAQ,EAAE,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC;AAED,KAAK,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;IAC1B,sCAAsC;IACtC,OAAO,CAAC,KAAK,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACtE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
||||
34
node_modules/@paperclipai/plugin-sdk/dist/dev-server.d.ts
generated
vendored
Normal file
34
node_modules/@paperclipai/plugin-sdk/dist/dev-server.d.ts
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
export interface PluginDevServerOptions {
|
||||
/** Plugin project root. Defaults to `process.cwd()`. */
|
||||
rootDir?: string;
|
||||
/** Relative path from root to built UI assets. Defaults to `dist/ui`. */
|
||||
uiDir?: string;
|
||||
/** Bind port for local preview server. Defaults to `4177`. */
|
||||
port?: number;
|
||||
/** Bind host. Defaults to `127.0.0.1`. */
|
||||
host?: string;
|
||||
}
|
||||
export interface PluginDevServer {
|
||||
url: string;
|
||||
close(): Promise<void>;
|
||||
}
|
||||
/**
|
||||
* Start a local static server for plugin UI assets with SSE reload events.
|
||||
*
|
||||
* Endpoint summary:
|
||||
* - `GET /__paperclip__/health` for diagnostics
|
||||
* - `GET /__paperclip__/events` for hot-reload stream
|
||||
* - Any other path serves files from the configured UI build directory
|
||||
*/
|
||||
export declare function startPluginDevServer(options?: PluginDevServerOptions): Promise<PluginDevServer>;
|
||||
/**
|
||||
* Return a stable file+mtime snapshot for a built plugin UI directory.
|
||||
*
|
||||
* Used by the polling watcher fallback and useful for tests that need to assert
|
||||
* whether a UI build has changed between runs.
|
||||
*/
|
||||
export declare function getUiBuildSnapshot(rootDir: string, uiDir?: string): Promise<Array<{
|
||||
file: string;
|
||||
mtimeMs: number;
|
||||
}>>;
|
||||
//# sourceMappingURL=dev-server.d.ts.map
|
||||
1
node_modules/@paperclipai/plugin-sdk/dist/dev-server.d.ts.map
generated
vendored
Normal file
1
node_modules/@paperclipai/plugin-sdk/dist/dev-server.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"dev-server.d.ts","sourceRoot":"","sources":["../src/dev-server.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,sBAAsB;IACrC,wDAAwD;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8DAA8D;IAC9D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAkGD;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,GAAE,sBAA2B,GAAG,OAAO,CAAC,eAAe,CAAC,CAiFzG;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,SAAY,GAAG,OAAO,CAAC,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CAY9H"}
|
||||
194
node_modules/@paperclipai/plugin-sdk/dist/dev-server.js
generated
vendored
Normal file
194
node_modules/@paperclipai/plugin-sdk/dist/dev-server.js
generated
vendored
Normal file
@@ -0,0 +1,194 @@
|
||||
import { createReadStream, existsSync, statSync, watch } from "node:fs";
|
||||
import { mkdir, readdir, stat } from "node:fs/promises";
|
||||
import { createServer } from "node:http";
|
||||
import path from "node:path";
|
||||
function contentType(filePath) {
|
||||
if (filePath.endsWith(".js"))
|
||||
return "text/javascript; charset=utf-8";
|
||||
if (filePath.endsWith(".css"))
|
||||
return "text/css; charset=utf-8";
|
||||
if (filePath.endsWith(".json"))
|
||||
return "application/json; charset=utf-8";
|
||||
if (filePath.endsWith(".html"))
|
||||
return "text/html; charset=utf-8";
|
||||
if (filePath.endsWith(".svg"))
|
||||
return "image/svg+xml";
|
||||
return "application/octet-stream";
|
||||
}
|
||||
function normalizeFilePath(baseDir, reqPath) {
|
||||
const pathname = reqPath.split("?")[0] || "/";
|
||||
const resolved = pathname === "/" ? "/index.js" : pathname;
|
||||
const absolute = path.resolve(baseDir, `.${resolved}`);
|
||||
const normalizedBase = `${path.resolve(baseDir)}${path.sep}`;
|
||||
if (!absolute.startsWith(normalizedBase) && absolute !== path.resolve(baseDir)) {
|
||||
throw new Error("path traversal blocked");
|
||||
}
|
||||
return absolute;
|
||||
}
|
||||
function send404(res) {
|
||||
res.statusCode = 404;
|
||||
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
||||
res.end(JSON.stringify({ error: "Not found" }));
|
||||
}
|
||||
function sendJson(res, value) {
|
||||
res.statusCode = 200;
|
||||
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
||||
res.end(JSON.stringify(value));
|
||||
}
|
||||
async function ensureUiDir(uiDir) {
|
||||
if (existsSync(uiDir))
|
||||
return;
|
||||
await mkdir(uiDir, { recursive: true });
|
||||
}
|
||||
async function listFilesRecursive(dir) {
|
||||
const out = [];
|
||||
const entries = await readdir(dir, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
const abs = path.join(dir, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
out.push(...await listFilesRecursive(abs));
|
||||
}
|
||||
else if (entry.isFile()) {
|
||||
out.push(abs);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
function snapshotSignature(rows) {
|
||||
return rows.map((row) => `${row.file}:${Math.trunc(row.mtimeMs)}`).join("|");
|
||||
}
|
||||
async function startUiWatcher(uiDir, onReload) {
|
||||
try {
|
||||
// macOS/Windows support recursive native watching.
|
||||
const watcher = watch(uiDir, { recursive: true }, (_eventType, filename) => {
|
||||
if (!filename)
|
||||
return;
|
||||
onReload(path.join(uiDir, filename));
|
||||
});
|
||||
return watcher;
|
||||
}
|
||||
catch {
|
||||
// Linux may reject recursive watch. Fall back to polling snapshots.
|
||||
let previous = snapshotSignature((await getUiBuildSnapshot(path.dirname(uiDir), path.basename(uiDir))).map((row) => ({
|
||||
file: row.file,
|
||||
mtimeMs: row.mtimeMs,
|
||||
})));
|
||||
const timer = setInterval(async () => {
|
||||
try {
|
||||
const nextRows = await getUiBuildSnapshot(path.dirname(uiDir), path.basename(uiDir));
|
||||
const next = snapshotSignature(nextRows);
|
||||
if (next === previous)
|
||||
return;
|
||||
previous = next;
|
||||
onReload("__snapshot__");
|
||||
}
|
||||
catch {
|
||||
// Ignore transient read errors while bundlers are writing files.
|
||||
}
|
||||
}, 500);
|
||||
return {
|
||||
close() {
|
||||
clearInterval(timer);
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Start a local static server for plugin UI assets with SSE reload events.
|
||||
*
|
||||
* Endpoint summary:
|
||||
* - `GET /__paperclip__/health` for diagnostics
|
||||
* - `GET /__paperclip__/events` for hot-reload stream
|
||||
* - Any other path serves files from the configured UI build directory
|
||||
*/
|
||||
export async function startPluginDevServer(options = {}) {
|
||||
const rootDir = path.resolve(options.rootDir ?? process.cwd());
|
||||
const uiDir = path.resolve(rootDir, options.uiDir ?? "dist/ui");
|
||||
const host = options.host ?? "127.0.0.1";
|
||||
const port = options.port ?? 4177;
|
||||
await ensureUiDir(uiDir);
|
||||
const sseClients = new Set();
|
||||
const handleRequest = async (req, res) => {
|
||||
const url = req.url ?? "/";
|
||||
if (url === "/__paperclip__/health") {
|
||||
sendJson(res, { ok: true, rootDir, uiDir });
|
||||
return;
|
||||
}
|
||||
if (url === "/__paperclip__/events") {
|
||||
res.writeHead(200, {
|
||||
"Content-Type": "text/event-stream",
|
||||
"Cache-Control": "no-cache, no-transform",
|
||||
Connection: "keep-alive",
|
||||
});
|
||||
res.write(`event: connected\ndata: {"ok":true}\n\n`);
|
||||
sseClients.add(res);
|
||||
req.on("close", () => {
|
||||
sseClients.delete(res);
|
||||
});
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const filePath = normalizeFilePath(uiDir, url);
|
||||
if (!existsSync(filePath) || !statSync(filePath).isFile()) {
|
||||
send404(res);
|
||||
return;
|
||||
}
|
||||
res.statusCode = 200;
|
||||
res.setHeader("Content-Type", contentType(filePath));
|
||||
createReadStream(filePath).pipe(res);
|
||||
}
|
||||
catch {
|
||||
send404(res);
|
||||
}
|
||||
};
|
||||
const server = createServer((req, res) => {
|
||||
void handleRequest(req, res);
|
||||
});
|
||||
const notifyReload = (filePath) => {
|
||||
const rel = path.relative(uiDir, filePath);
|
||||
const payload = JSON.stringify({ type: "reload", file: rel, at: new Date().toISOString() });
|
||||
for (const client of sseClients) {
|
||||
client.write(`event: reload\ndata: ${payload}\n\n`);
|
||||
}
|
||||
};
|
||||
const watcher = await startUiWatcher(uiDir, notifyReload);
|
||||
await new Promise((resolve, reject) => {
|
||||
server.once("error", reject);
|
||||
server.listen(port, host, () => resolve());
|
||||
});
|
||||
const address = server.address();
|
||||
const actualPort = address && typeof address === "object" ? address.port : port;
|
||||
return {
|
||||
url: `http://${host}:${actualPort}`,
|
||||
async close() {
|
||||
watcher.close();
|
||||
for (const client of sseClients) {
|
||||
client.end();
|
||||
}
|
||||
await new Promise((resolve, reject) => {
|
||||
server.close((err) => (err ? reject(err) : resolve()));
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Return a stable file+mtime snapshot for a built plugin UI directory.
|
||||
*
|
||||
* Used by the polling watcher fallback and useful for tests that need to assert
|
||||
* whether a UI build has changed between runs.
|
||||
*/
|
||||
export async function getUiBuildSnapshot(rootDir, uiDir = "dist/ui") {
|
||||
const baseDir = path.resolve(rootDir, uiDir);
|
||||
if (!existsSync(baseDir))
|
||||
return [];
|
||||
const files = await listFilesRecursive(baseDir);
|
||||
const rows = await Promise.all(files.map(async (filePath) => {
|
||||
const fileStat = await stat(filePath);
|
||||
return {
|
||||
file: path.relative(baseDir, filePath),
|
||||
mtimeMs: fileStat.mtimeMs,
|
||||
};
|
||||
}));
|
||||
return rows.sort((a, b) => a.file.localeCompare(b.file));
|
||||
}
|
||||
//# sourceMappingURL=dev-server.js.map
|
||||
1
node_modules/@paperclipai/plugin-sdk/dist/dev-server.js.map
generated
vendored
Normal file
1
node_modules/@paperclipai/plugin-sdk/dist/dev-server.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
229
node_modules/@paperclipai/plugin-sdk/dist/host-client-factory.d.ts
generated
vendored
Normal file
229
node_modules/@paperclipai/plugin-sdk/dist/host-client-factory.d.ts
generated
vendored
Normal file
@@ -0,0 +1,229 @@
|
||||
/**
|
||||
* Host-side client factory — creates capability-gated handler maps for
|
||||
* servicing worker→host JSON-RPC calls.
|
||||
*
|
||||
* When a plugin worker calls `ctx.state.get(...)` inside its process, the
|
||||
* SDK serializes the call as a JSON-RPC request over stdio. On the host side,
|
||||
* the `PluginWorkerManager` receives the request and dispatches it to the
|
||||
* handler registered for that method. This module provides a factory that
|
||||
* creates those handlers for all `WorkerToHostMethods`, with automatic
|
||||
* capability enforcement.
|
||||
*
|
||||
* ## Design
|
||||
*
|
||||
* 1. **Capability gating**: Each handler checks the plugin's declared
|
||||
* capabilities before executing. If the plugin lacks a required capability,
|
||||
* the handler throws a `CapabilityDeniedError` (which the worker manager
|
||||
* translates into a JSON-RPC error response with code
|
||||
* `CAPABILITY_DENIED`).
|
||||
*
|
||||
* 2. **Service adapters**: The caller provides a `HostServices` object with
|
||||
* concrete implementations of each platform service. The factory wires
|
||||
* each handler to the appropriate service method.
|
||||
*
|
||||
* 3. **Type safety**: The returned handler map is typed as
|
||||
* `WorkerToHostHandlers` (from `plugin-worker-manager.ts`) so it plugs
|
||||
* directly into `WorkerStartOptions.hostHandlers`.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const handlers = createHostClientHandlers({
|
||||
* pluginId: "acme.linear",
|
||||
* capabilities: manifest.capabilities,
|
||||
* services: {
|
||||
* config: { get: () => registry.getConfig(pluginId) },
|
||||
* state: { get: ..., set: ..., delete: ... },
|
||||
* entities: { upsert: ..., list: ... },
|
||||
* // ... all services
|
||||
* },
|
||||
* });
|
||||
*
|
||||
* await workerManager.startWorker("acme.linear", {
|
||||
* // ...
|
||||
* hostHandlers: handlers,
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §13 — Host-Worker Protocol
|
||||
* @see PLUGIN_SPEC.md §15 — Capability Model
|
||||
*/
|
||||
import type { PluginCapability } from "@paperclipai/shared";
|
||||
import type { WorkerToHostMethods, WorkerToHostMethodName } from "./protocol.js";
|
||||
/**
|
||||
* Thrown when a plugin calls a host method it does not have the capability for.
|
||||
*
|
||||
* The `code` field is set to `PLUGIN_RPC_ERROR_CODES.CAPABILITY_DENIED` so
|
||||
* the worker manager can propagate it as the correct JSON-RPC error code.
|
||||
*/
|
||||
export declare class CapabilityDeniedError extends Error {
|
||||
readonly name = "CapabilityDeniedError";
|
||||
readonly code: -32001;
|
||||
constructor(pluginId: string, method: string, capability: PluginCapability);
|
||||
}
|
||||
/**
|
||||
* Service adapters that the host must provide. Each property maps to a group
|
||||
* of `WorkerToHostMethods`. The factory wires JSON-RPC params to these
|
||||
* function signatures.
|
||||
*
|
||||
* All methods return promises to support async I/O (database, HTTP, etc.).
|
||||
*/
|
||||
export interface HostServices {
|
||||
/** Provides `config.get`. */
|
||||
config: {
|
||||
get(): Promise<Record<string, unknown>>;
|
||||
};
|
||||
/** Provides `state.get`, `state.set`, `state.delete`. */
|
||||
state: {
|
||||
get(params: WorkerToHostMethods["state.get"][0]): Promise<WorkerToHostMethods["state.get"][1]>;
|
||||
set(params: WorkerToHostMethods["state.set"][0]): Promise<void>;
|
||||
delete(params: WorkerToHostMethods["state.delete"][0]): Promise<void>;
|
||||
};
|
||||
/** Provides `entities.upsert`, `entities.list`. */
|
||||
entities: {
|
||||
upsert(params: WorkerToHostMethods["entities.upsert"][0]): Promise<WorkerToHostMethods["entities.upsert"][1]>;
|
||||
list(params: WorkerToHostMethods["entities.list"][0]): Promise<WorkerToHostMethods["entities.list"][1]>;
|
||||
};
|
||||
/** Provides `events.emit` and `events.subscribe`. */
|
||||
events: {
|
||||
emit(params: WorkerToHostMethods["events.emit"][0]): Promise<void>;
|
||||
subscribe(params: WorkerToHostMethods["events.subscribe"][0]): Promise<void>;
|
||||
};
|
||||
/** Provides `http.fetch`. */
|
||||
http: {
|
||||
fetch(params: WorkerToHostMethods["http.fetch"][0]): Promise<WorkerToHostMethods["http.fetch"][1]>;
|
||||
};
|
||||
/** Provides `secrets.resolve`. */
|
||||
secrets: {
|
||||
resolve(params: WorkerToHostMethods["secrets.resolve"][0]): Promise<string>;
|
||||
};
|
||||
/** Provides `activity.log`. */
|
||||
activity: {
|
||||
log(params: {
|
||||
companyId: string;
|
||||
message: string;
|
||||
entityType?: string;
|
||||
entityId?: string;
|
||||
metadata?: Record<string, unknown>;
|
||||
}): Promise<void>;
|
||||
};
|
||||
/** Provides `metrics.write`. */
|
||||
metrics: {
|
||||
write(params: WorkerToHostMethods["metrics.write"][0]): Promise<void>;
|
||||
};
|
||||
/** Provides `log`. */
|
||||
logger: {
|
||||
log(params: WorkerToHostMethods["log"][0]): Promise<void>;
|
||||
};
|
||||
/** Provides `companies.list`, `companies.get`. */
|
||||
companies: {
|
||||
list(params: WorkerToHostMethods["companies.list"][0]): Promise<WorkerToHostMethods["companies.list"][1]>;
|
||||
get(params: WorkerToHostMethods["companies.get"][0]): Promise<WorkerToHostMethods["companies.get"][1]>;
|
||||
};
|
||||
/** Provides `projects.list`, `projects.get`, `projects.listWorkspaces`, `projects.getPrimaryWorkspace`, `projects.getWorkspaceForIssue`. */
|
||||
projects: {
|
||||
list(params: WorkerToHostMethods["projects.list"][0]): Promise<WorkerToHostMethods["projects.list"][1]>;
|
||||
get(params: WorkerToHostMethods["projects.get"][0]): Promise<WorkerToHostMethods["projects.get"][1]>;
|
||||
listWorkspaces(params: WorkerToHostMethods["projects.listWorkspaces"][0]): Promise<WorkerToHostMethods["projects.listWorkspaces"][1]>;
|
||||
getPrimaryWorkspace(params: WorkerToHostMethods["projects.getPrimaryWorkspace"][0]): Promise<WorkerToHostMethods["projects.getPrimaryWorkspace"][1]>;
|
||||
getWorkspaceForIssue(params: WorkerToHostMethods["projects.getWorkspaceForIssue"][0]): Promise<WorkerToHostMethods["projects.getWorkspaceForIssue"][1]>;
|
||||
};
|
||||
/** Provides `issues.list`, `issues.get`, `issues.create`, `issues.update`, `issues.listComments`, `issues.createComment`. */
|
||||
issues: {
|
||||
list(params: WorkerToHostMethods["issues.list"][0]): Promise<WorkerToHostMethods["issues.list"][1]>;
|
||||
get(params: WorkerToHostMethods["issues.get"][0]): Promise<WorkerToHostMethods["issues.get"][1]>;
|
||||
create(params: WorkerToHostMethods["issues.create"][0]): Promise<WorkerToHostMethods["issues.create"][1]>;
|
||||
update(params: WorkerToHostMethods["issues.update"][0]): Promise<WorkerToHostMethods["issues.update"][1]>;
|
||||
listComments(params: WorkerToHostMethods["issues.listComments"][0]): Promise<WorkerToHostMethods["issues.listComments"][1]>;
|
||||
createComment(params: WorkerToHostMethods["issues.createComment"][0]): Promise<WorkerToHostMethods["issues.createComment"][1]>;
|
||||
};
|
||||
/** Provides `issues.documents.list`, `issues.documents.get`, `issues.documents.upsert`, `issues.documents.delete`. */
|
||||
issueDocuments: {
|
||||
list(params: WorkerToHostMethods["issues.documents.list"][0]): Promise<WorkerToHostMethods["issues.documents.list"][1]>;
|
||||
get(params: WorkerToHostMethods["issues.documents.get"][0]): Promise<WorkerToHostMethods["issues.documents.get"][1]>;
|
||||
upsert(params: WorkerToHostMethods["issues.documents.upsert"][0]): Promise<WorkerToHostMethods["issues.documents.upsert"][1]>;
|
||||
delete(params: WorkerToHostMethods["issues.documents.delete"][0]): Promise<WorkerToHostMethods["issues.documents.delete"][1]>;
|
||||
};
|
||||
/** Provides `agents.list`, `agents.get`, `agents.pause`, `agents.resume`, `agents.invoke`. */
|
||||
agents: {
|
||||
list(params: WorkerToHostMethods["agents.list"][0]): Promise<WorkerToHostMethods["agents.list"][1]>;
|
||||
get(params: WorkerToHostMethods["agents.get"][0]): Promise<WorkerToHostMethods["agents.get"][1]>;
|
||||
pause(params: WorkerToHostMethods["agents.pause"][0]): Promise<WorkerToHostMethods["agents.pause"][1]>;
|
||||
resume(params: WorkerToHostMethods["agents.resume"][0]): Promise<WorkerToHostMethods["agents.resume"][1]>;
|
||||
invoke(params: WorkerToHostMethods["agents.invoke"][0]): Promise<WorkerToHostMethods["agents.invoke"][1]>;
|
||||
};
|
||||
/** Provides `agents.sessions.create`, `agents.sessions.list`, `agents.sessions.sendMessage`, `agents.sessions.close`. */
|
||||
agentSessions: {
|
||||
create(params: WorkerToHostMethods["agents.sessions.create"][0]): Promise<WorkerToHostMethods["agents.sessions.create"][1]>;
|
||||
list(params: WorkerToHostMethods["agents.sessions.list"][0]): Promise<WorkerToHostMethods["agents.sessions.list"][1]>;
|
||||
sendMessage(params: WorkerToHostMethods["agents.sessions.sendMessage"][0]): Promise<WorkerToHostMethods["agents.sessions.sendMessage"][1]>;
|
||||
close(params: WorkerToHostMethods["agents.sessions.close"][0]): Promise<void>;
|
||||
};
|
||||
/** Provides `goals.list`, `goals.get`, `goals.create`, `goals.update`. */
|
||||
goals: {
|
||||
list(params: WorkerToHostMethods["goals.list"][0]): Promise<WorkerToHostMethods["goals.list"][1]>;
|
||||
get(params: WorkerToHostMethods["goals.get"][0]): Promise<WorkerToHostMethods["goals.get"][1]>;
|
||||
create(params: WorkerToHostMethods["goals.create"][0]): Promise<WorkerToHostMethods["goals.create"][1]>;
|
||||
update(params: WorkerToHostMethods["goals.update"][0]): Promise<WorkerToHostMethods["goals.update"][1]>;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Options for `createHostClientHandlers`.
|
||||
*/
|
||||
export interface HostClientFactoryOptions {
|
||||
/** The plugin ID. Used for error messages and logging. */
|
||||
pluginId: string;
|
||||
/**
|
||||
* The capabilities declared by the plugin in its manifest. The factory
|
||||
* enforces these at runtime before delegating to the service adapter.
|
||||
*/
|
||||
capabilities: readonly PluginCapability[];
|
||||
/**
|
||||
* Concrete implementations of host platform services. Each handler in the
|
||||
* returned map delegates to the corresponding service method.
|
||||
*/
|
||||
services: HostServices;
|
||||
}
|
||||
/**
|
||||
* A handler function for a specific worker→host method.
|
||||
*/
|
||||
type HostHandler<M extends WorkerToHostMethodName> = (params: WorkerToHostMethods[M][0]) => Promise<WorkerToHostMethods[M][1]>;
|
||||
/**
|
||||
* A complete map of all worker→host method handlers.
|
||||
*
|
||||
* This type matches `WorkerToHostHandlers` from `plugin-worker-manager.ts`
|
||||
* but makes every handler required (the factory always provides all handlers).
|
||||
*/
|
||||
export type HostClientHandlers = {
|
||||
[M in WorkerToHostMethodName]: HostHandler<M>;
|
||||
};
|
||||
/**
|
||||
* Create a complete handler map for all worker→host JSON-RPC methods.
|
||||
*
|
||||
* Each handler:
|
||||
* 1. Checks the plugin's declared capabilities against the required capability
|
||||
* for the method (if any).
|
||||
* 2. Delegates to the corresponding service adapter method.
|
||||
* 3. Returns the service result, which is serialized as the JSON-RPC response
|
||||
* by the worker manager.
|
||||
*
|
||||
* If a capability check fails, the handler throws a `CapabilityDeniedError`
|
||||
* with code `CAPABILITY_DENIED`. The worker manager catches this and sends a
|
||||
* JSON-RPC error response to the worker, which surfaces as a `JsonRpcCallError`
|
||||
* in the plugin's SDK client.
|
||||
*
|
||||
* @param options - Plugin ID, capabilities, and service adapters
|
||||
* @returns A handler map suitable for `WorkerStartOptions.hostHandlers`
|
||||
*/
|
||||
export declare function createHostClientHandlers(options: HostClientFactoryOptions): HostClientHandlers;
|
||||
/**
|
||||
* Get the capability required for a given worker→host method, or `null` if
|
||||
* no capability is required.
|
||||
*
|
||||
* Useful for inspecting capability requirements without calling the factory.
|
||||
*
|
||||
* @param method - The worker→host method name
|
||||
* @returns The required capability, or `null`
|
||||
*/
|
||||
export declare function getRequiredCapability(method: WorkerToHostMethodName): PluginCapability | null;
|
||||
export {};
|
||||
//# sourceMappingURL=host-client-factory.d.ts.map
|
||||
1
node_modules/@paperclipai/plugin-sdk/dist/host-client-factory.d.ts.map
generated
vendored
Normal file
1
node_modules/@paperclipai/plugin-sdk/dist/host-client-factory.d.ts.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
353
node_modules/@paperclipai/plugin-sdk/dist/host-client-factory.js
generated
vendored
Normal file
353
node_modules/@paperclipai/plugin-sdk/dist/host-client-factory.js
generated
vendored
Normal file
@@ -0,0 +1,353 @@
|
||||
/**
|
||||
* Host-side client factory — creates capability-gated handler maps for
|
||||
* servicing worker→host JSON-RPC calls.
|
||||
*
|
||||
* When a plugin worker calls `ctx.state.get(...)` inside its process, the
|
||||
* SDK serializes the call as a JSON-RPC request over stdio. On the host side,
|
||||
* the `PluginWorkerManager` receives the request and dispatches it to the
|
||||
* handler registered for that method. This module provides a factory that
|
||||
* creates those handlers for all `WorkerToHostMethods`, with automatic
|
||||
* capability enforcement.
|
||||
*
|
||||
* ## Design
|
||||
*
|
||||
* 1. **Capability gating**: Each handler checks the plugin's declared
|
||||
* capabilities before executing. If the plugin lacks a required capability,
|
||||
* the handler throws a `CapabilityDeniedError` (which the worker manager
|
||||
* translates into a JSON-RPC error response with code
|
||||
* `CAPABILITY_DENIED`).
|
||||
*
|
||||
* 2. **Service adapters**: The caller provides a `HostServices` object with
|
||||
* concrete implementations of each platform service. The factory wires
|
||||
* each handler to the appropriate service method.
|
||||
*
|
||||
* 3. **Type safety**: The returned handler map is typed as
|
||||
* `WorkerToHostHandlers` (from `plugin-worker-manager.ts`) so it plugs
|
||||
* directly into `WorkerStartOptions.hostHandlers`.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const handlers = createHostClientHandlers({
|
||||
* pluginId: "acme.linear",
|
||||
* capabilities: manifest.capabilities,
|
||||
* services: {
|
||||
* config: { get: () => registry.getConfig(pluginId) },
|
||||
* state: { get: ..., set: ..., delete: ... },
|
||||
* entities: { upsert: ..., list: ... },
|
||||
* // ... all services
|
||||
* },
|
||||
* });
|
||||
*
|
||||
* await workerManager.startWorker("acme.linear", {
|
||||
* // ...
|
||||
* hostHandlers: handlers,
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §13 — Host-Worker Protocol
|
||||
* @see PLUGIN_SPEC.md §15 — Capability Model
|
||||
*/
|
||||
import { PLUGIN_RPC_ERROR_CODES } from "./protocol.js";
|
||||
// ---------------------------------------------------------------------------
|
||||
// Error types
|
||||
// ---------------------------------------------------------------------------
|
||||
/**
|
||||
* Thrown when a plugin calls a host method it does not have the capability for.
|
||||
*
|
||||
* The `code` field is set to `PLUGIN_RPC_ERROR_CODES.CAPABILITY_DENIED` so
|
||||
* the worker manager can propagate it as the correct JSON-RPC error code.
|
||||
*/
|
||||
export class CapabilityDeniedError extends Error {
|
||||
name = "CapabilityDeniedError";
|
||||
code = PLUGIN_RPC_ERROR_CODES.CAPABILITY_DENIED;
|
||||
constructor(pluginId, method, capability) {
|
||||
super(`Plugin "${pluginId}" is missing required capability "${capability}" for method "${method}"`);
|
||||
}
|
||||
}
|
||||
// ---------------------------------------------------------------------------
|
||||
// Capability → method mapping
|
||||
// ---------------------------------------------------------------------------
|
||||
/**
|
||||
* Maps each worker→host RPC method to the capability required to invoke it.
|
||||
* Methods without a capability requirement (e.g. `config.get`, `log`) are
|
||||
* mapped to `null`.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §15 — Capability Model
|
||||
*/
|
||||
const METHOD_CAPABILITY_MAP = {
|
||||
// Config — always allowed
|
||||
"config.get": null,
|
||||
// State
|
||||
"state.get": "plugin.state.read",
|
||||
"state.set": "plugin.state.write",
|
||||
"state.delete": "plugin.state.write",
|
||||
// Entities — no specific capability required (plugin-scoped by design)
|
||||
"entities.upsert": null,
|
||||
"entities.list": null,
|
||||
// Events
|
||||
"events.emit": "events.emit",
|
||||
"events.subscribe": "events.subscribe",
|
||||
// HTTP
|
||||
"http.fetch": "http.outbound",
|
||||
// Secrets
|
||||
"secrets.resolve": "secrets.read-ref",
|
||||
// Activity
|
||||
"activity.log": "activity.log.write",
|
||||
// Metrics
|
||||
"metrics.write": "metrics.write",
|
||||
// Logger — always allowed
|
||||
"log": null,
|
||||
// Companies
|
||||
"companies.list": "companies.read",
|
||||
"companies.get": "companies.read",
|
||||
// Projects
|
||||
"projects.list": "projects.read",
|
||||
"projects.get": "projects.read",
|
||||
"projects.listWorkspaces": "project.workspaces.read",
|
||||
"projects.getPrimaryWorkspace": "project.workspaces.read",
|
||||
"projects.getWorkspaceForIssue": "project.workspaces.read",
|
||||
// Issues
|
||||
"issues.list": "issues.read",
|
||||
"issues.get": "issues.read",
|
||||
"issues.create": "issues.create",
|
||||
"issues.update": "issues.update",
|
||||
"issues.listComments": "issue.comments.read",
|
||||
"issues.createComment": "issue.comments.create",
|
||||
// Issue Documents
|
||||
"issues.documents.list": "issue.documents.read",
|
||||
"issues.documents.get": "issue.documents.read",
|
||||
"issues.documents.upsert": "issue.documents.write",
|
||||
"issues.documents.delete": "issue.documents.write",
|
||||
// Agents
|
||||
"agents.list": "agents.read",
|
||||
"agents.get": "agents.read",
|
||||
"agents.pause": "agents.pause",
|
||||
"agents.resume": "agents.resume",
|
||||
"agents.invoke": "agents.invoke",
|
||||
// Agent Sessions
|
||||
"agents.sessions.create": "agent.sessions.create",
|
||||
"agents.sessions.list": "agent.sessions.list",
|
||||
"agents.sessions.sendMessage": "agent.sessions.send",
|
||||
"agents.sessions.close": "agent.sessions.close",
|
||||
// Goals
|
||||
"goals.list": "goals.read",
|
||||
"goals.get": "goals.read",
|
||||
"goals.create": "goals.create",
|
||||
"goals.update": "goals.update",
|
||||
};
|
||||
// ---------------------------------------------------------------------------
|
||||
// Factory
|
||||
// ---------------------------------------------------------------------------
|
||||
/**
|
||||
* Create a complete handler map for all worker→host JSON-RPC methods.
|
||||
*
|
||||
* Each handler:
|
||||
* 1. Checks the plugin's declared capabilities against the required capability
|
||||
* for the method (if any).
|
||||
* 2. Delegates to the corresponding service adapter method.
|
||||
* 3. Returns the service result, which is serialized as the JSON-RPC response
|
||||
* by the worker manager.
|
||||
*
|
||||
* If a capability check fails, the handler throws a `CapabilityDeniedError`
|
||||
* with code `CAPABILITY_DENIED`. The worker manager catches this and sends a
|
||||
* JSON-RPC error response to the worker, which surfaces as a `JsonRpcCallError`
|
||||
* in the plugin's SDK client.
|
||||
*
|
||||
* @param options - Plugin ID, capabilities, and service adapters
|
||||
* @returns A handler map suitable for `WorkerStartOptions.hostHandlers`
|
||||
*/
|
||||
export function createHostClientHandlers(options) {
|
||||
const { pluginId, services } = options;
|
||||
const capabilitySet = new Set(options.capabilities);
|
||||
/**
|
||||
* Assert that the plugin has the required capability for a method.
|
||||
* Throws `CapabilityDeniedError` if the capability is missing.
|
||||
*/
|
||||
function requireCapability(method) {
|
||||
const required = METHOD_CAPABILITY_MAP[method];
|
||||
if (required === null)
|
||||
return; // No capability required
|
||||
if (capabilitySet.has(required))
|
||||
return;
|
||||
throw new CapabilityDeniedError(pluginId, method, required);
|
||||
}
|
||||
/**
|
||||
* Create a capability-gated proxy handler for a method.
|
||||
*
|
||||
* @param method - The RPC method name (used for capability lookup)
|
||||
* @param handler - The actual handler implementation
|
||||
* @returns A wrapper that checks capabilities before delegating
|
||||
*/
|
||||
function gated(method, handler) {
|
||||
return async (params) => {
|
||||
requireCapability(method);
|
||||
return handler(params);
|
||||
};
|
||||
}
|
||||
// -------------------------------------------------------------------------
|
||||
// Build the complete handler map
|
||||
// -------------------------------------------------------------------------
|
||||
return {
|
||||
// Config
|
||||
"config.get": gated("config.get", async () => {
|
||||
return services.config.get();
|
||||
}),
|
||||
// State
|
||||
"state.get": gated("state.get", async (params) => {
|
||||
return services.state.get(params);
|
||||
}),
|
||||
"state.set": gated("state.set", async (params) => {
|
||||
return services.state.set(params);
|
||||
}),
|
||||
"state.delete": gated("state.delete", async (params) => {
|
||||
return services.state.delete(params);
|
||||
}),
|
||||
// Entities
|
||||
"entities.upsert": gated("entities.upsert", async (params) => {
|
||||
return services.entities.upsert(params);
|
||||
}),
|
||||
"entities.list": gated("entities.list", async (params) => {
|
||||
return services.entities.list(params);
|
||||
}),
|
||||
// Events
|
||||
"events.emit": gated("events.emit", async (params) => {
|
||||
return services.events.emit(params);
|
||||
}),
|
||||
"events.subscribe": gated("events.subscribe", async (params) => {
|
||||
return services.events.subscribe(params);
|
||||
}),
|
||||
// HTTP
|
||||
"http.fetch": gated("http.fetch", async (params) => {
|
||||
return services.http.fetch(params);
|
||||
}),
|
||||
// Secrets
|
||||
"secrets.resolve": gated("secrets.resolve", async (params) => {
|
||||
return services.secrets.resolve(params);
|
||||
}),
|
||||
// Activity
|
||||
"activity.log": gated("activity.log", async (params) => {
|
||||
return services.activity.log(params);
|
||||
}),
|
||||
// Metrics
|
||||
"metrics.write": gated("metrics.write", async (params) => {
|
||||
return services.metrics.write(params);
|
||||
}),
|
||||
// Logger
|
||||
"log": gated("log", async (params) => {
|
||||
return services.logger.log(params);
|
||||
}),
|
||||
// Companies
|
||||
"companies.list": gated("companies.list", async (params) => {
|
||||
return services.companies.list(params);
|
||||
}),
|
||||
"companies.get": gated("companies.get", async (params) => {
|
||||
return services.companies.get(params);
|
||||
}),
|
||||
// Projects
|
||||
"projects.list": gated("projects.list", async (params) => {
|
||||
return services.projects.list(params);
|
||||
}),
|
||||
"projects.get": gated("projects.get", async (params) => {
|
||||
return services.projects.get(params);
|
||||
}),
|
||||
"projects.listWorkspaces": gated("projects.listWorkspaces", async (params) => {
|
||||
return services.projects.listWorkspaces(params);
|
||||
}),
|
||||
"projects.getPrimaryWorkspace": gated("projects.getPrimaryWorkspace", async (params) => {
|
||||
return services.projects.getPrimaryWorkspace(params);
|
||||
}),
|
||||
"projects.getWorkspaceForIssue": gated("projects.getWorkspaceForIssue", async (params) => {
|
||||
return services.projects.getWorkspaceForIssue(params);
|
||||
}),
|
||||
// Issues
|
||||
"issues.list": gated("issues.list", async (params) => {
|
||||
return services.issues.list(params);
|
||||
}),
|
||||
"issues.get": gated("issues.get", async (params) => {
|
||||
return services.issues.get(params);
|
||||
}),
|
||||
"issues.create": gated("issues.create", async (params) => {
|
||||
return services.issues.create(params);
|
||||
}),
|
||||
"issues.update": gated("issues.update", async (params) => {
|
||||
return services.issues.update(params);
|
||||
}),
|
||||
"issues.listComments": gated("issues.listComments", async (params) => {
|
||||
return services.issues.listComments(params);
|
||||
}),
|
||||
"issues.createComment": gated("issues.createComment", async (params) => {
|
||||
return services.issues.createComment(params);
|
||||
}),
|
||||
// Issue Documents
|
||||
"issues.documents.list": gated("issues.documents.list", async (params) => {
|
||||
return services.issueDocuments.list(params);
|
||||
}),
|
||||
"issues.documents.get": gated("issues.documents.get", async (params) => {
|
||||
return services.issueDocuments.get(params);
|
||||
}),
|
||||
"issues.documents.upsert": gated("issues.documents.upsert", async (params) => {
|
||||
return services.issueDocuments.upsert(params);
|
||||
}),
|
||||
"issues.documents.delete": gated("issues.documents.delete", async (params) => {
|
||||
return services.issueDocuments.delete(params);
|
||||
}),
|
||||
// Agents
|
||||
"agents.list": gated("agents.list", async (params) => {
|
||||
return services.agents.list(params);
|
||||
}),
|
||||
"agents.get": gated("agents.get", async (params) => {
|
||||
return services.agents.get(params);
|
||||
}),
|
||||
"agents.pause": gated("agents.pause", async (params) => {
|
||||
return services.agents.pause(params);
|
||||
}),
|
||||
"agents.resume": gated("agents.resume", async (params) => {
|
||||
return services.agents.resume(params);
|
||||
}),
|
||||
"agents.invoke": gated("agents.invoke", async (params) => {
|
||||
return services.agents.invoke(params);
|
||||
}),
|
||||
// Agent Sessions
|
||||
"agents.sessions.create": gated("agents.sessions.create", async (params) => {
|
||||
return services.agentSessions.create(params);
|
||||
}),
|
||||
"agents.sessions.list": gated("agents.sessions.list", async (params) => {
|
||||
return services.agentSessions.list(params);
|
||||
}),
|
||||
"agents.sessions.sendMessage": gated("agents.sessions.sendMessage", async (params) => {
|
||||
return services.agentSessions.sendMessage(params);
|
||||
}),
|
||||
"agents.sessions.close": gated("agents.sessions.close", async (params) => {
|
||||
return services.agentSessions.close(params);
|
||||
}),
|
||||
// Goals
|
||||
"goals.list": gated("goals.list", async (params) => {
|
||||
return services.goals.list(params);
|
||||
}),
|
||||
"goals.get": gated("goals.get", async (params) => {
|
||||
return services.goals.get(params);
|
||||
}),
|
||||
"goals.create": gated("goals.create", async (params) => {
|
||||
return services.goals.create(params);
|
||||
}),
|
||||
"goals.update": gated("goals.update", async (params) => {
|
||||
return services.goals.update(params);
|
||||
}),
|
||||
};
|
||||
}
|
||||
// ---------------------------------------------------------------------------
|
||||
// Utility: getRequiredCapability
|
||||
// ---------------------------------------------------------------------------
|
||||
/**
|
||||
* Get the capability required for a given worker→host method, or `null` if
|
||||
* no capability is required.
|
||||
*
|
||||
* Useful for inspecting capability requirements without calling the factory.
|
||||
*
|
||||
* @param method - The worker→host method name
|
||||
* @returns The required capability, or `null`
|
||||
*/
|
||||
export function getRequiredCapability(method) {
|
||||
return METHOD_CAPABILITY_MAP[method];
|
||||
}
|
||||
//# sourceMappingURL=host-client-factory.js.map
|
||||
1
node_modules/@paperclipai/plugin-sdk/dist/host-client-factory.js.map
generated
vendored
Normal file
1
node_modules/@paperclipai/plugin-sdk/dist/host-client-factory.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
84
node_modules/@paperclipai/plugin-sdk/dist/index.d.ts
generated
vendored
Normal file
84
node_modules/@paperclipai/plugin-sdk/dist/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* `@paperclipai/plugin-sdk` — Paperclip plugin worker-side SDK.
|
||||
*
|
||||
* This is the main entrypoint for plugin worker code. For plugin UI bundles,
|
||||
* import from `@paperclipai/plugin-sdk/ui` instead.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // Plugin worker entrypoint (dist/worker.ts)
|
||||
* import { definePlugin, runWorker, z } from "@paperclipai/plugin-sdk";
|
||||
*
|
||||
* const plugin = definePlugin({
|
||||
* async setup(ctx) {
|
||||
* ctx.logger.info("Plugin starting up");
|
||||
*
|
||||
* ctx.events.on("issue.created", async (event) => {
|
||||
* ctx.logger.info("Issue created", { issueId: event.entityId });
|
||||
* });
|
||||
*
|
||||
* ctx.jobs.register("full-sync", async (job) => {
|
||||
* ctx.logger.info("Starting full sync", { runId: job.runId });
|
||||
* // ... sync implementation
|
||||
* });
|
||||
*
|
||||
* ctx.data.register("sync-health", async ({ companyId }) => {
|
||||
* const state = await ctx.state.get({
|
||||
* scopeKind: "company",
|
||||
* scopeId: String(companyId),
|
||||
* stateKey: "last-sync-at",
|
||||
* });
|
||||
* return { lastSync: state };
|
||||
* });
|
||||
* },
|
||||
*
|
||||
* async onHealth() {
|
||||
* return { status: "ok" };
|
||||
* },
|
||||
* });
|
||||
*
|
||||
* export default plugin;
|
||||
* runWorker(plugin, import.meta.url);
|
||||
* ```
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §14 — SDK Surface
|
||||
* @see PLUGIN_SPEC.md §29.2 — SDK Versioning
|
||||
*/
|
||||
export { definePlugin } from "./define-plugin.js";
|
||||
export { createTestHarness } from "./testing.js";
|
||||
export { createPluginBundlerPresets } from "./bundlers.js";
|
||||
export { startPluginDevServer, getUiBuildSnapshot } from "./dev-server.js";
|
||||
export { startWorkerRpcHost, runWorker } from "./worker-rpc-host.js";
|
||||
export { createHostClientHandlers, getRequiredCapability, CapabilityDeniedError, } from "./host-client-factory.js";
|
||||
export { JSONRPC_VERSION, JSONRPC_ERROR_CODES, PLUGIN_RPC_ERROR_CODES, HOST_TO_WORKER_REQUIRED_METHODS, HOST_TO_WORKER_OPTIONAL_METHODS, MESSAGE_DELIMITER, createRequest, createSuccessResponse, createErrorResponse, createNotification, isJsonRpcRequest, isJsonRpcNotification, isJsonRpcResponse, isJsonRpcSuccessResponse, isJsonRpcErrorResponse, serializeMessage, parseMessage, JsonRpcParseError, JsonRpcCallError, _resetIdCounter, } from "./protocol.js";
|
||||
export type { PluginDefinition, PaperclipPlugin, PluginHealthDiagnostics, PluginConfigValidationResult, PluginWebhookInput, } from "./define-plugin.js";
|
||||
export type { TestHarness, TestHarnessOptions, TestHarnessLogEntry, } from "./testing.js";
|
||||
export type { PluginBundlerPresetInput, PluginBundlerPresets, EsbuildLikeOptions, RollupLikeConfig, } from "./bundlers.js";
|
||||
export type { PluginDevServer, PluginDevServerOptions } from "./dev-server.js";
|
||||
export type { WorkerRpcHostOptions, WorkerRpcHost, RunWorkerOptions, } from "./worker-rpc-host.js";
|
||||
export type { HostServices, HostClientFactoryOptions, HostClientHandlers, } from "./host-client-factory.js";
|
||||
export type { JsonRpcId, JsonRpcRequest, JsonRpcSuccessResponse, JsonRpcError, JsonRpcErrorResponse, JsonRpcResponse, JsonRpcNotification, JsonRpcMessage, JsonRpcErrorCode, PluginRpcErrorCode, InitializeParams, InitializeResult, ConfigChangedParams, ValidateConfigParams, OnEventParams, RunJobParams, GetDataParams, PerformActionParams, ExecuteToolParams, PluginModalBoundsRequest, PluginRenderCloseEvent, PluginLauncherRenderContextSnapshot, HostToWorkerMethods, HostToWorkerMethodName, WorkerToHostMethods, WorkerToHostMethodName, HostToWorkerRequest, HostToWorkerResponse, WorkerToHostRequest, WorkerToHostResponse, WorkerToHostNotifications, WorkerToHostNotificationName, } from "./protocol.js";
|
||||
export type { PluginContext, PluginConfigClient, PluginEventsClient, PluginJobsClient, PluginLaunchersClient, PluginHttpClient, PluginSecretsClient, PluginActivityClient, PluginActivityLogEntry, PluginStateClient, PluginEntitiesClient, PluginProjectsClient, PluginCompaniesClient, PluginIssuesClient, PluginAgentsClient, PluginAgentSessionsClient, AgentSession, AgentSessionEvent, AgentSessionSendResult, PluginGoalsClient, PluginDataClient, PluginActionsClient, PluginStreamsClient, PluginToolsClient, PluginMetricsClient, PluginLogger, } from "./types.js";
|
||||
export type { ScopeKey, EventFilter, PluginEvent, PluginJobContext, PluginLauncherRegistration, ToolRunContext, ToolResult, PluginEntityUpsert, PluginEntityRecord, PluginEntityQuery, PluginWorkspace, Company, Project, Issue, IssueComment, Agent, Goal, } from "./types.js";
|
||||
export type { PaperclipPluginManifestV1, PluginJobDeclaration, PluginWebhookDeclaration, PluginToolDeclaration, PluginUiSlotDeclaration, PluginUiDeclaration, PluginLauncherActionDeclaration, PluginLauncherRenderDeclaration, PluginLauncherDeclaration, PluginMinimumHostVersion, PluginRecord, PluginConfig, JsonSchema, PluginStatus, PluginCategory, PluginCapability, PluginUiSlotType, PluginUiSlotEntityType, PluginLauncherPlacementZone, PluginLauncherAction, PluginLauncherBounds, PluginLauncherRenderEnvironment, PluginStateScopeKind, PluginJobStatus, PluginJobRunStatus, PluginJobRunTrigger, PluginWebhookDeliveryStatus, PluginEventType, PluginBridgeErrorCode, } from "./types.js";
|
||||
/**
|
||||
* Zod is re-exported for plugin authors to use when defining their
|
||||
* `instanceConfigSchema` and tool `parametersSchema`.
|
||||
*
|
||||
* Plugin authors do not need to add a separate `zod` dependency.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §14.1 — Example SDK Shape
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import { z } from "@paperclipai/plugin-sdk";
|
||||
*
|
||||
* const configSchema = z.object({
|
||||
* apiKey: z.string().describe("Your API key"),
|
||||
* workspace: z.string().optional(),
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export { z } from "zod";
|
||||
export { PLUGIN_API_VERSION, PLUGIN_STATUSES, PLUGIN_CATEGORIES, PLUGIN_CAPABILITIES, PLUGIN_UI_SLOT_TYPES, PLUGIN_UI_SLOT_ENTITY_TYPES, PLUGIN_STATE_SCOPE_KINDS, PLUGIN_JOB_STATUSES, PLUGIN_JOB_RUN_STATUSES, PLUGIN_JOB_RUN_TRIGGERS, PLUGIN_WEBHOOK_DELIVERY_STATUSES, PLUGIN_EVENT_TYPES, PLUGIN_BRIDGE_ERROR_CODES, } from "@paperclipai/shared";
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
1
node_modules/@paperclipai/plugin-sdk/dist/index.d.ts.map
generated
vendored
Normal file
1
node_modules/@paperclipai/plugin-sdk/dist/index.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
||||
84
node_modules/@paperclipai/plugin-sdk/dist/index.js
generated
vendored
Normal file
84
node_modules/@paperclipai/plugin-sdk/dist/index.js
generated
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* `@paperclipai/plugin-sdk` — Paperclip plugin worker-side SDK.
|
||||
*
|
||||
* This is the main entrypoint for plugin worker code. For plugin UI bundles,
|
||||
* import from `@paperclipai/plugin-sdk/ui` instead.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // Plugin worker entrypoint (dist/worker.ts)
|
||||
* import { definePlugin, runWorker, z } from "@paperclipai/plugin-sdk";
|
||||
*
|
||||
* const plugin = definePlugin({
|
||||
* async setup(ctx) {
|
||||
* ctx.logger.info("Plugin starting up");
|
||||
*
|
||||
* ctx.events.on("issue.created", async (event) => {
|
||||
* ctx.logger.info("Issue created", { issueId: event.entityId });
|
||||
* });
|
||||
*
|
||||
* ctx.jobs.register("full-sync", async (job) => {
|
||||
* ctx.logger.info("Starting full sync", { runId: job.runId });
|
||||
* // ... sync implementation
|
||||
* });
|
||||
*
|
||||
* ctx.data.register("sync-health", async ({ companyId }) => {
|
||||
* const state = await ctx.state.get({
|
||||
* scopeKind: "company",
|
||||
* scopeId: String(companyId),
|
||||
* stateKey: "last-sync-at",
|
||||
* });
|
||||
* return { lastSync: state };
|
||||
* });
|
||||
* },
|
||||
*
|
||||
* async onHealth() {
|
||||
* return { status: "ok" };
|
||||
* },
|
||||
* });
|
||||
*
|
||||
* export default plugin;
|
||||
* runWorker(plugin, import.meta.url);
|
||||
* ```
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §14 — SDK Surface
|
||||
* @see PLUGIN_SPEC.md §29.2 — SDK Versioning
|
||||
*/
|
||||
// ---------------------------------------------------------------------------
|
||||
// Main factory
|
||||
// ---------------------------------------------------------------------------
|
||||
export { definePlugin } from "./define-plugin.js";
|
||||
export { createTestHarness } from "./testing.js";
|
||||
export { createPluginBundlerPresets } from "./bundlers.js";
|
||||
export { startPluginDevServer, getUiBuildSnapshot } from "./dev-server.js";
|
||||
export { startWorkerRpcHost, runWorker } from "./worker-rpc-host.js";
|
||||
export { createHostClientHandlers, getRequiredCapability, CapabilityDeniedError, } from "./host-client-factory.js";
|
||||
// JSON-RPC protocol helpers and constants
|
||||
export { JSONRPC_VERSION, JSONRPC_ERROR_CODES, PLUGIN_RPC_ERROR_CODES, HOST_TO_WORKER_REQUIRED_METHODS, HOST_TO_WORKER_OPTIONAL_METHODS, MESSAGE_DELIMITER, createRequest, createSuccessResponse, createErrorResponse, createNotification, isJsonRpcRequest, isJsonRpcNotification, isJsonRpcResponse, isJsonRpcSuccessResponse, isJsonRpcErrorResponse, serializeMessage, parseMessage, JsonRpcParseError, JsonRpcCallError, _resetIdCounter, } from "./protocol.js";
|
||||
// ---------------------------------------------------------------------------
|
||||
// Zod re-export
|
||||
// ---------------------------------------------------------------------------
|
||||
/**
|
||||
* Zod is re-exported for plugin authors to use when defining their
|
||||
* `instanceConfigSchema` and tool `parametersSchema`.
|
||||
*
|
||||
* Plugin authors do not need to add a separate `zod` dependency.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §14.1 — Example SDK Shape
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import { z } from "@paperclipai/plugin-sdk";
|
||||
*
|
||||
* const configSchema = z.object({
|
||||
* apiKey: z.string().describe("Your API key"),
|
||||
* workspace: z.string().optional(),
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export { z } from "zod";
|
||||
// ---------------------------------------------------------------------------
|
||||
// Constants re-exports (for plugin code that needs to check values at runtime)
|
||||
// ---------------------------------------------------------------------------
|
||||
export { PLUGIN_API_VERSION, PLUGIN_STATUSES, PLUGIN_CATEGORIES, PLUGIN_CAPABILITIES, PLUGIN_UI_SLOT_TYPES, PLUGIN_UI_SLOT_ENTITY_TYPES, PLUGIN_STATE_SCOPE_KINDS, PLUGIN_JOB_STATUSES, PLUGIN_JOB_RUN_STATUSES, PLUGIN_JOB_RUN_TRIGGERS, PLUGIN_WEBHOOK_DELIVERY_STATUSES, PLUGIN_EVENT_TYPES, PLUGIN_BRIDGE_ERROR_CODES, } from "@paperclipai/shared";
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
node_modules/@paperclipai/plugin-sdk/dist/index.js.map
generated
vendored
Normal file
1
node_modules/@paperclipai/plugin-sdk/dist/index.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAEH,8EAA8E;AAC9E,eAAe;AACf,8EAA8E;AAE9E,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,0BAA0B,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EACL,wBAAwB,EACxB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,0BAA0B,CAAC;AAElC,0CAA0C;AAC1C,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,sBAAsB,EACtB,+BAA+B,EAC/B,+BAA+B,EAC/B,iBAAiB,EACjB,aAAa,EACb,qBAAqB,EACrB,mBAAmB,EACnB,kBAAkB,EAClB,gBAAgB,EAChB,qBAAqB,EACrB,iBAAiB,EACjB,wBAAwB,EACxB,sBAAsB,EACtB,gBAAgB,EAChB,YAAY,EACZ,iBAAiB,EACjB,gBAAgB,EAChB,eAAe,GAChB,MAAM,eAAe,CAAC;AA+JvB,8EAA8E;AAC9E,gBAAgB;AAChB,8EAA8E;AAE9E;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,8EAA8E;AAC9E,+EAA+E;AAC/E,8EAA8E;AAE9E,OAAO,EACL,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,2BAA2B,EAC3B,wBAAwB,EACxB,mBAAmB,EACnB,uBAAuB,EACvB,uBAAuB,EACvB,gCAAgC,EAChC,kBAAkB,EAClB,yBAAyB,GAC1B,MAAM,qBAAqB,CAAC"}
|
||||
881
node_modules/@paperclipai/plugin-sdk/dist/protocol.d.ts
generated
vendored
Normal file
881
node_modules/@paperclipai/plugin-sdk/dist/protocol.d.ts
generated
vendored
Normal file
@@ -0,0 +1,881 @@
|
||||
/**
|
||||
* JSON-RPC 2.0 message types and protocol helpers for the host ↔ worker IPC
|
||||
* channel.
|
||||
*
|
||||
* The Paperclip plugin runtime uses JSON-RPC 2.0 over stdio to communicate
|
||||
* between the host process and each plugin worker process. This module defines:
|
||||
*
|
||||
* - Core JSON-RPC 2.0 envelope types (request, response, notification, error)
|
||||
* - Standard and plugin-specific error codes
|
||||
* - Typed method maps for host→worker and worker→host calls
|
||||
* - Helper functions for creating well-formed messages
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §12.1 — Process Model
|
||||
* @see PLUGIN_SPEC.md §13 — Host-Worker Protocol
|
||||
* @see https://www.jsonrpc.org/specification
|
||||
*/
|
||||
import type { PaperclipPluginManifestV1, PluginLauncherBounds, PluginLauncherRenderContextSnapshot, PluginStateScopeKind, Company, Project, Issue, IssueComment, IssueDocument, IssueDocumentSummary, Agent, Goal } from "@paperclipai/shared";
|
||||
export type { PluginLauncherRenderContextSnapshot } from "@paperclipai/shared";
|
||||
import type { PluginEvent, PluginJobContext, PluginWorkspace, ToolRunContext, ToolResult } from "./types.js";
|
||||
import type { PluginHealthDiagnostics, PluginConfigValidationResult, PluginWebhookInput } from "./define-plugin.js";
|
||||
/** The JSON-RPC protocol version. Always `"2.0"`. */
|
||||
export declare const JSONRPC_VERSION: "2.0";
|
||||
/**
|
||||
* A unique request identifier. JSON-RPC 2.0 allows strings or numbers;
|
||||
* we use strings (UUIDs or monotonic counters) for all Paperclip messages.
|
||||
*/
|
||||
export type JsonRpcId = string | number;
|
||||
/**
|
||||
* A JSON-RPC 2.0 request message.
|
||||
*
|
||||
* The host sends requests to the worker (or vice versa) and expects a
|
||||
* matching response with the same `id`.
|
||||
*/
|
||||
export interface JsonRpcRequest<TMethod extends string = string, TParams = unknown> {
|
||||
readonly jsonrpc: typeof JSONRPC_VERSION;
|
||||
/** Unique request identifier. Must be echoed in the response. */
|
||||
readonly id: JsonRpcId;
|
||||
/** The RPC method name to invoke. */
|
||||
readonly method: TMethod;
|
||||
/** Structured parameters for the method call. */
|
||||
readonly params: TParams;
|
||||
}
|
||||
/**
|
||||
* A JSON-RPC 2.0 success response.
|
||||
*/
|
||||
export interface JsonRpcSuccessResponse<TResult = unknown> {
|
||||
readonly jsonrpc: typeof JSONRPC_VERSION;
|
||||
/** Echoed request identifier. */
|
||||
readonly id: JsonRpcId;
|
||||
/** The method return value. */
|
||||
readonly result: TResult;
|
||||
readonly error?: never;
|
||||
}
|
||||
/**
|
||||
* A JSON-RPC 2.0 error object embedded in an error response.
|
||||
*/
|
||||
export interface JsonRpcError<TData = unknown> {
|
||||
/** Machine-readable error code. */
|
||||
readonly code: number;
|
||||
/** Human-readable error message. */
|
||||
readonly message: string;
|
||||
/** Optional structured error data. */
|
||||
readonly data?: TData;
|
||||
}
|
||||
/**
|
||||
* A JSON-RPC 2.0 error response.
|
||||
*/
|
||||
export interface JsonRpcErrorResponse<TData = unknown> {
|
||||
readonly jsonrpc: typeof JSONRPC_VERSION;
|
||||
/** Echoed request identifier. */
|
||||
readonly id: JsonRpcId | null;
|
||||
readonly result?: never;
|
||||
/** The error object. */
|
||||
readonly error: JsonRpcError<TData>;
|
||||
}
|
||||
/**
|
||||
* A JSON-RPC 2.0 response — either success or error.
|
||||
*/
|
||||
export type JsonRpcResponse<TResult = unknown, TData = unknown> = JsonRpcSuccessResponse<TResult> | JsonRpcErrorResponse<TData>;
|
||||
/**
|
||||
* A JSON-RPC 2.0 notification (a request with no `id`).
|
||||
*
|
||||
* Notifications are fire-and-forget — no response is expected.
|
||||
*/
|
||||
export interface JsonRpcNotification<TMethod extends string = string, TParams = unknown> {
|
||||
readonly jsonrpc: typeof JSONRPC_VERSION;
|
||||
readonly id?: never;
|
||||
/** The notification method name. */
|
||||
readonly method: TMethod;
|
||||
/** Structured parameters for the notification. */
|
||||
readonly params: TParams;
|
||||
}
|
||||
/**
|
||||
* Any well-formed JSON-RPC 2.0 message (request, response, or notification).
|
||||
*/
|
||||
export type JsonRpcMessage = JsonRpcRequest | JsonRpcResponse | JsonRpcNotification;
|
||||
/**
|
||||
* Standard JSON-RPC 2.0 error codes.
|
||||
*
|
||||
* @see https://www.jsonrpc.org/specification#error_object
|
||||
*/
|
||||
export declare const JSONRPC_ERROR_CODES: {
|
||||
/** Invalid JSON was received by the server. */
|
||||
readonly PARSE_ERROR: -32700;
|
||||
/** The JSON sent is not a valid Request object. */
|
||||
readonly INVALID_REQUEST: -32600;
|
||||
/** The method does not exist or is not available. */
|
||||
readonly METHOD_NOT_FOUND: -32601;
|
||||
/** Invalid method parameter(s). */
|
||||
readonly INVALID_PARAMS: -32602;
|
||||
/** Internal JSON-RPC error. */
|
||||
readonly INTERNAL_ERROR: -32603;
|
||||
};
|
||||
export type JsonRpcErrorCode = (typeof JSONRPC_ERROR_CODES)[keyof typeof JSONRPC_ERROR_CODES];
|
||||
/**
|
||||
* Paperclip plugin-specific error codes.
|
||||
*
|
||||
* These live in the JSON-RPC "server error" reserved range (-32000 to -32099)
|
||||
* as specified by JSON-RPC 2.0 for implementation-defined server errors.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.7 — Error Propagation Through The Bridge
|
||||
*/
|
||||
export declare const PLUGIN_RPC_ERROR_CODES: {
|
||||
/** The worker process is not running or not reachable. */
|
||||
readonly WORKER_UNAVAILABLE: -32000;
|
||||
/** The plugin does not have the required capability for this operation. */
|
||||
readonly CAPABILITY_DENIED: -32001;
|
||||
/** The worker reported an unhandled error during method execution. */
|
||||
readonly WORKER_ERROR: -32002;
|
||||
/** The method call timed out waiting for the worker response. */
|
||||
readonly TIMEOUT: -32003;
|
||||
/** The worker does not implement the requested optional method. */
|
||||
readonly METHOD_NOT_IMPLEMENTED: -32004;
|
||||
/** A catch-all for errors that do not fit other categories. */
|
||||
readonly UNKNOWN: -32099;
|
||||
};
|
||||
export type PluginRpcErrorCode = (typeof PLUGIN_RPC_ERROR_CODES)[keyof typeof PLUGIN_RPC_ERROR_CODES];
|
||||
/**
|
||||
* Input for the `initialize` RPC method.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §13.1 — `initialize`
|
||||
*/
|
||||
export interface InitializeParams {
|
||||
/** Full plugin manifest snapshot. */
|
||||
manifest: PaperclipPluginManifestV1;
|
||||
/** Resolved operator configuration (validated against `instanceConfigSchema`). */
|
||||
config: Record<string, unknown>;
|
||||
/** Instance-level metadata. */
|
||||
instanceInfo: {
|
||||
/** UUID of this Paperclip instance. */
|
||||
instanceId: string;
|
||||
/** Semver version of the running Paperclip host. */
|
||||
hostVersion: string;
|
||||
};
|
||||
/** Host API version. */
|
||||
apiVersion: number;
|
||||
}
|
||||
/**
|
||||
* Result returned by the `initialize` RPC method.
|
||||
*/
|
||||
export interface InitializeResult {
|
||||
/** Whether initialization succeeded. */
|
||||
ok: boolean;
|
||||
/** Optional methods the worker has implemented (e.g. "validateConfig", "onEvent"). */
|
||||
supportedMethods?: string[];
|
||||
}
|
||||
/**
|
||||
* Input for the `configChanged` RPC method.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §13.4 — `configChanged`
|
||||
*/
|
||||
export interface ConfigChangedParams {
|
||||
/** The newly resolved configuration. */
|
||||
config: Record<string, unknown>;
|
||||
}
|
||||
/**
|
||||
* Input for the `validateConfig` RPC method.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §13.3 — `validateConfig`
|
||||
*/
|
||||
export interface ValidateConfigParams {
|
||||
/** The configuration to validate. */
|
||||
config: Record<string, unknown>;
|
||||
}
|
||||
/**
|
||||
* Input for the `onEvent` RPC method.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §13.5 — `onEvent`
|
||||
*/
|
||||
export interface OnEventParams {
|
||||
/** The domain event to deliver. */
|
||||
event: PluginEvent;
|
||||
}
|
||||
/**
|
||||
* Input for the `runJob` RPC method.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §13.6 — `runJob`
|
||||
*/
|
||||
export interface RunJobParams {
|
||||
/** Job execution context. */
|
||||
job: PluginJobContext;
|
||||
}
|
||||
/**
|
||||
* Input for the `getData` RPC method.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §13.8 — `getData`
|
||||
*/
|
||||
export interface GetDataParams {
|
||||
/** Plugin-defined data key (e.g. `"sync-health"`). */
|
||||
key: string;
|
||||
/** Context and query parameters from the UI. */
|
||||
params: Record<string, unknown>;
|
||||
/** Optional launcher/container metadata from the host render environment. */
|
||||
renderEnvironment?: PluginLauncherRenderContextSnapshot | null;
|
||||
}
|
||||
/**
|
||||
* Input for the `performAction` RPC method.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §13.9 — `performAction`
|
||||
*/
|
||||
export interface PerformActionParams {
|
||||
/** Plugin-defined action key (e.g. `"resync"`). */
|
||||
key: string;
|
||||
/** Action parameters from the UI. */
|
||||
params: Record<string, unknown>;
|
||||
/** Optional launcher/container metadata from the host render environment. */
|
||||
renderEnvironment?: PluginLauncherRenderContextSnapshot | null;
|
||||
}
|
||||
/**
|
||||
* Input for the `executeTool` RPC method.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §13.10 — `executeTool`
|
||||
*/
|
||||
export interface ExecuteToolParams {
|
||||
/** Tool name (without plugin namespace prefix). */
|
||||
toolName: string;
|
||||
/** Parsed parameters matching the tool's declared schema. */
|
||||
parameters: unknown;
|
||||
/** Agent run context. */
|
||||
runContext: ToolRunContext;
|
||||
}
|
||||
/**
|
||||
* Bounds request issued by a plugin UI running inside a host-managed launcher
|
||||
* container such as a modal, drawer, or popover.
|
||||
*/
|
||||
export interface PluginModalBoundsRequest {
|
||||
/** High-level size preset requested from the host. */
|
||||
bounds: PluginLauncherBounds;
|
||||
/** Optional explicit width override in CSS pixels. */
|
||||
width?: number;
|
||||
/** Optional explicit height override in CSS pixels. */
|
||||
height?: number;
|
||||
/** Optional lower bounds for host resizing decisions. */
|
||||
minWidth?: number;
|
||||
minHeight?: number;
|
||||
/** Optional upper bounds for host resizing decisions. */
|
||||
maxWidth?: number;
|
||||
maxHeight?: number;
|
||||
}
|
||||
/**
|
||||
* Reason metadata supplied by host-managed close lifecycle callbacks.
|
||||
*/
|
||||
export interface PluginRenderCloseEvent {
|
||||
reason: "escapeKey" | "backdrop" | "hostNavigation" | "programmatic" | "submit" | "unknown";
|
||||
nativeEvent?: unknown;
|
||||
}
|
||||
/**
|
||||
* Map of host→worker RPC method names to their `[params, result]` types.
|
||||
*
|
||||
* This type is the single source of truth for all methods the host can call
|
||||
* on a worker. Used by both the host dispatcher and the worker handler to
|
||||
* ensure type safety across the IPC boundary.
|
||||
*/
|
||||
export interface HostToWorkerMethods {
|
||||
/** @see PLUGIN_SPEC.md §13.1 */
|
||||
initialize: [params: InitializeParams, result: InitializeResult];
|
||||
/** @see PLUGIN_SPEC.md §13.2 */
|
||||
health: [params: Record<string, never>, result: PluginHealthDiagnostics];
|
||||
/** @see PLUGIN_SPEC.md §12.5 */
|
||||
shutdown: [params: Record<string, never>, result: void];
|
||||
/** @see PLUGIN_SPEC.md §13.3 */
|
||||
validateConfig: [params: ValidateConfigParams, result: PluginConfigValidationResult];
|
||||
/** @see PLUGIN_SPEC.md §13.4 */
|
||||
configChanged: [params: ConfigChangedParams, result: void];
|
||||
/** @see PLUGIN_SPEC.md §13.5 */
|
||||
onEvent: [params: OnEventParams, result: void];
|
||||
/** @see PLUGIN_SPEC.md §13.6 */
|
||||
runJob: [params: RunJobParams, result: void];
|
||||
/** @see PLUGIN_SPEC.md §13.7 */
|
||||
handleWebhook: [params: PluginWebhookInput, result: void];
|
||||
/** @see PLUGIN_SPEC.md §13.8 */
|
||||
getData: [params: GetDataParams, result: unknown];
|
||||
/** @see PLUGIN_SPEC.md §13.9 */
|
||||
performAction: [params: PerformActionParams, result: unknown];
|
||||
/** @see PLUGIN_SPEC.md §13.10 */
|
||||
executeTool: [params: ExecuteToolParams, result: ToolResult];
|
||||
}
|
||||
/** Union of all host→worker method names. */
|
||||
export type HostToWorkerMethodName = keyof HostToWorkerMethods;
|
||||
/** Required methods the worker MUST implement. */
|
||||
export declare const HOST_TO_WORKER_REQUIRED_METHODS: readonly HostToWorkerMethodName[];
|
||||
/** Optional methods the worker MAY implement. */
|
||||
export declare const HOST_TO_WORKER_OPTIONAL_METHODS: readonly HostToWorkerMethodName[];
|
||||
/**
|
||||
* Map of worker→host RPC method names to their `[params, result]` types.
|
||||
*
|
||||
* These represent the SDK client calls that the worker makes back to the
|
||||
* host to access platform services (state, entities, config, etc.).
|
||||
*/
|
||||
export interface WorkerToHostMethods {
|
||||
"config.get": [params: Record<string, never>, result: Record<string, unknown>];
|
||||
"state.get": [
|
||||
params: {
|
||||
scopeKind: string;
|
||||
scopeId?: string;
|
||||
namespace?: string;
|
||||
stateKey: string;
|
||||
},
|
||||
result: unknown
|
||||
];
|
||||
"state.set": [
|
||||
params: {
|
||||
scopeKind: string;
|
||||
scopeId?: string;
|
||||
namespace?: string;
|
||||
stateKey: string;
|
||||
value: unknown;
|
||||
},
|
||||
result: void
|
||||
];
|
||||
"state.delete": [
|
||||
params: {
|
||||
scopeKind: string;
|
||||
scopeId?: string;
|
||||
namespace?: string;
|
||||
stateKey: string;
|
||||
},
|
||||
result: void
|
||||
];
|
||||
"entities.upsert": [
|
||||
params: {
|
||||
entityType: string;
|
||||
scopeKind: PluginStateScopeKind;
|
||||
scopeId?: string;
|
||||
externalId?: string;
|
||||
title?: string;
|
||||
status?: string;
|
||||
data: Record<string, unknown>;
|
||||
},
|
||||
result: {
|
||||
id: string;
|
||||
entityType: string;
|
||||
scopeKind: PluginStateScopeKind;
|
||||
scopeId: string | null;
|
||||
externalId: string | null;
|
||||
title: string | null;
|
||||
status: string | null;
|
||||
data: Record<string, unknown>;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
];
|
||||
"entities.list": [
|
||||
params: {
|
||||
entityType?: string;
|
||||
scopeKind?: PluginStateScopeKind;
|
||||
scopeId?: string;
|
||||
externalId?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
},
|
||||
result: Array<{
|
||||
id: string;
|
||||
entityType: string;
|
||||
scopeKind: PluginStateScopeKind;
|
||||
scopeId: string | null;
|
||||
externalId: string | null;
|
||||
title: string | null;
|
||||
status: string | null;
|
||||
data: Record<string, unknown>;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}>
|
||||
];
|
||||
"events.emit": [
|
||||
params: {
|
||||
name: string;
|
||||
companyId: string;
|
||||
payload: unknown;
|
||||
},
|
||||
result: void
|
||||
];
|
||||
"events.subscribe": [
|
||||
params: {
|
||||
eventPattern: string;
|
||||
filter?: Record<string, unknown> | null;
|
||||
},
|
||||
result: void
|
||||
];
|
||||
"http.fetch": [
|
||||
params: {
|
||||
url: string;
|
||||
init?: Record<string, unknown>;
|
||||
},
|
||||
result: {
|
||||
status: number;
|
||||
statusText: string;
|
||||
headers: Record<string, string>;
|
||||
body: string;
|
||||
}
|
||||
];
|
||||
"secrets.resolve": [
|
||||
params: {
|
||||
secretRef: string;
|
||||
},
|
||||
result: string
|
||||
];
|
||||
"activity.log": [
|
||||
params: {
|
||||
companyId: string;
|
||||
message: string;
|
||||
entityType?: string;
|
||||
entityId?: string;
|
||||
metadata?: Record<string, unknown>;
|
||||
},
|
||||
result: void
|
||||
];
|
||||
"metrics.write": [
|
||||
params: {
|
||||
name: string;
|
||||
value: number;
|
||||
tags?: Record<string, string>;
|
||||
},
|
||||
result: void
|
||||
];
|
||||
"log": [
|
||||
params: {
|
||||
level: "info" | "warn" | "error" | "debug";
|
||||
message: string;
|
||||
meta?: Record<string, unknown>;
|
||||
},
|
||||
result: void
|
||||
];
|
||||
"companies.list": [
|
||||
params: {
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
},
|
||||
result: Company[]
|
||||
];
|
||||
"companies.get": [
|
||||
params: {
|
||||
companyId: string;
|
||||
},
|
||||
result: Company | null
|
||||
];
|
||||
"projects.list": [
|
||||
params: {
|
||||
companyId: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
},
|
||||
result: Project[]
|
||||
];
|
||||
"projects.get": [
|
||||
params: {
|
||||
projectId: string;
|
||||
companyId: string;
|
||||
},
|
||||
result: Project | null
|
||||
];
|
||||
"projects.listWorkspaces": [
|
||||
params: {
|
||||
projectId: string;
|
||||
companyId: string;
|
||||
},
|
||||
result: PluginWorkspace[]
|
||||
];
|
||||
"projects.getPrimaryWorkspace": [
|
||||
params: {
|
||||
projectId: string;
|
||||
companyId: string;
|
||||
},
|
||||
result: PluginWorkspace | null
|
||||
];
|
||||
"projects.getWorkspaceForIssue": [
|
||||
params: {
|
||||
issueId: string;
|
||||
companyId: string;
|
||||
},
|
||||
result: PluginWorkspace | null
|
||||
];
|
||||
"issues.list": [
|
||||
params: {
|
||||
companyId: string;
|
||||
projectId?: string;
|
||||
assigneeAgentId?: string;
|
||||
status?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
},
|
||||
result: Issue[]
|
||||
];
|
||||
"issues.get": [
|
||||
params: {
|
||||
issueId: string;
|
||||
companyId: string;
|
||||
},
|
||||
result: Issue | null
|
||||
];
|
||||
"issues.create": [
|
||||
params: {
|
||||
companyId: string;
|
||||
projectId?: string;
|
||||
goalId?: string;
|
||||
parentId?: string;
|
||||
title: string;
|
||||
description?: string;
|
||||
priority?: string;
|
||||
assigneeAgentId?: string;
|
||||
},
|
||||
result: Issue
|
||||
];
|
||||
"issues.update": [
|
||||
params: {
|
||||
issueId: string;
|
||||
patch: Record<string, unknown>;
|
||||
companyId: string;
|
||||
},
|
||||
result: Issue
|
||||
];
|
||||
"issues.listComments": [
|
||||
params: {
|
||||
issueId: string;
|
||||
companyId: string;
|
||||
},
|
||||
result: IssueComment[]
|
||||
];
|
||||
"issues.createComment": [
|
||||
params: {
|
||||
issueId: string;
|
||||
body: string;
|
||||
companyId: string;
|
||||
},
|
||||
result: IssueComment
|
||||
];
|
||||
"issues.documents.list": [
|
||||
params: {
|
||||
issueId: string;
|
||||
companyId: string;
|
||||
},
|
||||
result: IssueDocumentSummary[]
|
||||
];
|
||||
"issues.documents.get": [
|
||||
params: {
|
||||
issueId: string;
|
||||
key: string;
|
||||
companyId: string;
|
||||
},
|
||||
result: IssueDocument | null
|
||||
];
|
||||
"issues.documents.upsert": [
|
||||
params: {
|
||||
issueId: string;
|
||||
key: string;
|
||||
body: string;
|
||||
companyId: string;
|
||||
title?: string;
|
||||
format?: string;
|
||||
changeSummary?: string;
|
||||
},
|
||||
result: IssueDocument
|
||||
];
|
||||
"issues.documents.delete": [
|
||||
params: {
|
||||
issueId: string;
|
||||
key: string;
|
||||
companyId: string;
|
||||
},
|
||||
result: void
|
||||
];
|
||||
"agents.list": [
|
||||
params: {
|
||||
companyId: string;
|
||||
status?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
},
|
||||
result: Agent[]
|
||||
];
|
||||
"agents.get": [
|
||||
params: {
|
||||
agentId: string;
|
||||
companyId: string;
|
||||
},
|
||||
result: Agent | null
|
||||
];
|
||||
"agents.pause": [
|
||||
params: {
|
||||
agentId: string;
|
||||
companyId: string;
|
||||
},
|
||||
result: Agent
|
||||
];
|
||||
"agents.resume": [
|
||||
params: {
|
||||
agentId: string;
|
||||
companyId: string;
|
||||
},
|
||||
result: Agent
|
||||
];
|
||||
"agents.invoke": [
|
||||
params: {
|
||||
agentId: string;
|
||||
companyId: string;
|
||||
prompt: string;
|
||||
reason?: string;
|
||||
},
|
||||
result: {
|
||||
runId: string;
|
||||
}
|
||||
];
|
||||
"agents.sessions.create": [
|
||||
params: {
|
||||
agentId: string;
|
||||
companyId: string;
|
||||
taskKey?: string;
|
||||
reason?: string;
|
||||
},
|
||||
result: {
|
||||
sessionId: string;
|
||||
agentId: string;
|
||||
companyId: string;
|
||||
status: "active" | "closed";
|
||||
createdAt: string;
|
||||
}
|
||||
];
|
||||
"agents.sessions.list": [
|
||||
params: {
|
||||
agentId: string;
|
||||
companyId: string;
|
||||
},
|
||||
result: Array<{
|
||||
sessionId: string;
|
||||
agentId: string;
|
||||
companyId: string;
|
||||
status: "active" | "closed";
|
||||
createdAt: string;
|
||||
}>
|
||||
];
|
||||
"agents.sessions.sendMessage": [
|
||||
params: {
|
||||
sessionId: string;
|
||||
companyId: string;
|
||||
prompt: string;
|
||||
reason?: string;
|
||||
},
|
||||
result: {
|
||||
runId: string;
|
||||
}
|
||||
];
|
||||
"agents.sessions.close": [
|
||||
params: {
|
||||
sessionId: string;
|
||||
companyId: string;
|
||||
},
|
||||
result: void
|
||||
];
|
||||
"goals.list": [
|
||||
params: {
|
||||
companyId: string;
|
||||
level?: string;
|
||||
status?: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
},
|
||||
result: Goal[]
|
||||
];
|
||||
"goals.get": [
|
||||
params: {
|
||||
goalId: string;
|
||||
companyId: string;
|
||||
},
|
||||
result: Goal | null
|
||||
];
|
||||
"goals.create": [
|
||||
params: {
|
||||
companyId: string;
|
||||
title: string;
|
||||
description?: string;
|
||||
level?: string;
|
||||
status?: string;
|
||||
parentId?: string;
|
||||
ownerAgentId?: string;
|
||||
},
|
||||
result: Goal
|
||||
];
|
||||
"goals.update": [
|
||||
params: {
|
||||
goalId: string;
|
||||
patch: Record<string, unknown>;
|
||||
companyId: string;
|
||||
},
|
||||
result: Goal
|
||||
];
|
||||
}
|
||||
/** Union of all worker→host method names. */
|
||||
export type WorkerToHostMethodName = keyof WorkerToHostMethods;
|
||||
/**
|
||||
* Typed parameter shapes for worker→host JSON-RPC notifications.
|
||||
*
|
||||
* Notifications are fire-and-forget — the worker does not wait for a response.
|
||||
* These are used for streaming events and logging, not for request-response RPCs.
|
||||
*/
|
||||
export interface WorkerToHostNotifications {
|
||||
/**
|
||||
* Forward a stream event to connected SSE clients.
|
||||
*
|
||||
* Emitted by the worker for each event on a stream channel. The host
|
||||
* publishes to the PluginStreamBus, which fans out to all SSE clients
|
||||
* subscribed to the (pluginId, channel, companyId) tuple.
|
||||
*
|
||||
* The `event` payload is JSON-serializable and sent as SSE `data:`.
|
||||
* The default SSE event type is `"message"`.
|
||||
*/
|
||||
"streams.emit": {
|
||||
channel: string;
|
||||
companyId: string;
|
||||
event: unknown;
|
||||
};
|
||||
/**
|
||||
* Signal that a stream channel has been opened.
|
||||
*
|
||||
* Emitted when the worker calls `ctx.streams.open(channel, companyId)`.
|
||||
* UI clients may use this to display a "connected" indicator or begin
|
||||
* buffering input. The host tracks open channels so it can emit synthetic
|
||||
* close events if the worker crashes.
|
||||
*/
|
||||
"streams.open": {
|
||||
channel: string;
|
||||
companyId: string;
|
||||
};
|
||||
/**
|
||||
* Signal that a stream channel has been closed.
|
||||
*
|
||||
* Emitted when the worker calls `ctx.streams.close(channel)`, or
|
||||
* synthetically by the host when a worker process exits with channels
|
||||
* still open. UI clients should treat this as terminal and disconnect
|
||||
* the SSE connection.
|
||||
*/
|
||||
"streams.close": {
|
||||
channel: string;
|
||||
companyId: string;
|
||||
};
|
||||
}
|
||||
/** Union of all worker→host notification method names. */
|
||||
export type WorkerToHostNotificationName = keyof WorkerToHostNotifications;
|
||||
/**
|
||||
* A typed JSON-RPC request for a specific host→worker method.
|
||||
*/
|
||||
export type HostToWorkerRequest<M extends HostToWorkerMethodName> = JsonRpcRequest<M, HostToWorkerMethods[M][0]>;
|
||||
/**
|
||||
* A typed JSON-RPC success response for a specific host→worker method.
|
||||
*/
|
||||
export type HostToWorkerResponse<M extends HostToWorkerMethodName> = JsonRpcSuccessResponse<HostToWorkerMethods[M][1]>;
|
||||
/**
|
||||
* A typed JSON-RPC request for a specific worker→host method.
|
||||
*/
|
||||
export type WorkerToHostRequest<M extends WorkerToHostMethodName> = JsonRpcRequest<M, WorkerToHostMethods[M][0]>;
|
||||
/**
|
||||
* A typed JSON-RPC success response for a specific worker→host method.
|
||||
*/
|
||||
export type WorkerToHostResponse<M extends WorkerToHostMethodName> = JsonRpcSuccessResponse<WorkerToHostMethods[M][1]>;
|
||||
/**
|
||||
* Create a JSON-RPC 2.0 request message.
|
||||
*
|
||||
* @param method - The RPC method name
|
||||
* @param params - Structured parameters
|
||||
* @param id - Optional explicit request ID (auto-generated if omitted)
|
||||
*/
|
||||
export declare function createRequest<TMethod extends string>(method: TMethod, params: unknown, id?: JsonRpcId): JsonRpcRequest<TMethod>;
|
||||
/**
|
||||
* Create a JSON-RPC 2.0 success response.
|
||||
*
|
||||
* @param id - The request ID being responded to
|
||||
* @param result - The result value
|
||||
*/
|
||||
export declare function createSuccessResponse<TResult>(id: JsonRpcId, result: TResult): JsonRpcSuccessResponse<TResult>;
|
||||
/**
|
||||
* Create a JSON-RPC 2.0 error response.
|
||||
*
|
||||
* @param id - The request ID being responded to (null if the request ID could not be determined)
|
||||
* @param code - Machine-readable error code
|
||||
* @param message - Human-readable error message
|
||||
* @param data - Optional structured error data
|
||||
*/
|
||||
export declare function createErrorResponse<TData = unknown>(id: JsonRpcId | null, code: number, message: string, data?: TData): JsonRpcErrorResponse<TData>;
|
||||
/**
|
||||
* Create a JSON-RPC 2.0 notification (fire-and-forget, no response expected).
|
||||
*
|
||||
* @param method - The notification method name
|
||||
* @param params - Structured parameters
|
||||
*/
|
||||
export declare function createNotification<TMethod extends string>(method: TMethod, params: unknown): JsonRpcNotification<TMethod>;
|
||||
/**
|
||||
* Check whether a value is a well-formed JSON-RPC 2.0 request.
|
||||
*
|
||||
* A request has `jsonrpc: "2.0"`, a string `method`, and an `id`.
|
||||
*/
|
||||
export declare function isJsonRpcRequest(value: unknown): value is JsonRpcRequest;
|
||||
/**
|
||||
* Check whether a value is a well-formed JSON-RPC 2.0 notification.
|
||||
*
|
||||
* A notification has `jsonrpc: "2.0"`, a string `method`, but no `id`.
|
||||
*/
|
||||
export declare function isJsonRpcNotification(value: unknown): value is JsonRpcNotification;
|
||||
/**
|
||||
* Check whether a value is a well-formed JSON-RPC 2.0 response (success or error).
|
||||
*/
|
||||
export declare function isJsonRpcResponse(value: unknown): value is JsonRpcResponse;
|
||||
/**
|
||||
* Check whether a JSON-RPC response is a success response.
|
||||
*/
|
||||
export declare function isJsonRpcSuccessResponse(response: JsonRpcResponse): response is JsonRpcSuccessResponse;
|
||||
/**
|
||||
* Check whether a JSON-RPC response is an error response.
|
||||
*/
|
||||
export declare function isJsonRpcErrorResponse(response: JsonRpcResponse): response is JsonRpcErrorResponse;
|
||||
/**
|
||||
* Line delimiter for JSON-RPC messages over stdio.
|
||||
*
|
||||
* Each message is a single line of JSON terminated by a newline character.
|
||||
* This follows the newline-delimited JSON (NDJSON) convention.
|
||||
*/
|
||||
export declare const MESSAGE_DELIMITER: "\n";
|
||||
/**
|
||||
* Serialize a JSON-RPC message to a newline-delimited string for transmission
|
||||
* over stdio.
|
||||
*
|
||||
* @param message - Any JSON-RPC message (request, response, or notification)
|
||||
* @returns The JSON string terminated with a newline
|
||||
*/
|
||||
export declare function serializeMessage(message: JsonRpcMessage): string;
|
||||
/**
|
||||
* Parse a JSON string into a JSON-RPC message.
|
||||
*
|
||||
* Returns the parsed message or throws a `JsonRpcParseError` if the input
|
||||
* is not valid JSON or does not conform to the JSON-RPC 2.0 structure.
|
||||
*
|
||||
* @param line - A single line of JSON text (with or without trailing newline)
|
||||
* @returns The parsed JSON-RPC message
|
||||
* @throws {JsonRpcParseError} If parsing fails
|
||||
*/
|
||||
export declare function parseMessage(line: string): JsonRpcMessage;
|
||||
/**
|
||||
* Error thrown when a JSON-RPC message cannot be parsed.
|
||||
*/
|
||||
export declare class JsonRpcParseError extends Error {
|
||||
readonly name = "JsonRpcParseError";
|
||||
constructor(message: string);
|
||||
}
|
||||
/**
|
||||
* Error thrown when a JSON-RPC call fails with a structured error response.
|
||||
*
|
||||
* Captures the full `JsonRpcError` so callers can inspect the code and data.
|
||||
*/
|
||||
export declare class JsonRpcCallError extends Error {
|
||||
readonly name = "JsonRpcCallError";
|
||||
/** The JSON-RPC error code. */
|
||||
readonly code: number;
|
||||
/** Optional structured error data from the response. */
|
||||
readonly data: unknown;
|
||||
constructor(error: JsonRpcError);
|
||||
}
|
||||
/**
|
||||
* Reset the internal request ID counter. **For testing only.**
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export declare function _resetIdCounter(): void;
|
||||
//# sourceMappingURL=protocol.d.ts.map
|
||||
1
node_modules/@paperclipai/plugin-sdk/dist/protocol.d.ts.map
generated
vendored
Normal file
1
node_modules/@paperclipai/plugin-sdk/dist/protocol.d.ts.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
297
node_modules/@paperclipai/plugin-sdk/dist/protocol.js
generated
vendored
Normal file
297
node_modules/@paperclipai/plugin-sdk/dist/protocol.js
generated
vendored
Normal file
@@ -0,0 +1,297 @@
|
||||
/**
|
||||
* JSON-RPC 2.0 message types and protocol helpers for the host ↔ worker IPC
|
||||
* channel.
|
||||
*
|
||||
* The Paperclip plugin runtime uses JSON-RPC 2.0 over stdio to communicate
|
||||
* between the host process and each plugin worker process. This module defines:
|
||||
*
|
||||
* - Core JSON-RPC 2.0 envelope types (request, response, notification, error)
|
||||
* - Standard and plugin-specific error codes
|
||||
* - Typed method maps for host→worker and worker→host calls
|
||||
* - Helper functions for creating well-formed messages
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §12.1 — Process Model
|
||||
* @see PLUGIN_SPEC.md §13 — Host-Worker Protocol
|
||||
* @see https://www.jsonrpc.org/specification
|
||||
*/
|
||||
// ---------------------------------------------------------------------------
|
||||
// JSON-RPC 2.0 — Core Protocol Types
|
||||
// ---------------------------------------------------------------------------
|
||||
/** The JSON-RPC protocol version. Always `"2.0"`. */
|
||||
export const JSONRPC_VERSION = "2.0";
|
||||
// ---------------------------------------------------------------------------
|
||||
// Error Codes
|
||||
// ---------------------------------------------------------------------------
|
||||
/**
|
||||
* Standard JSON-RPC 2.0 error codes.
|
||||
*
|
||||
* @see https://www.jsonrpc.org/specification#error_object
|
||||
*/
|
||||
export const JSONRPC_ERROR_CODES = {
|
||||
/** Invalid JSON was received by the server. */
|
||||
PARSE_ERROR: -32700,
|
||||
/** The JSON sent is not a valid Request object. */
|
||||
INVALID_REQUEST: -32600,
|
||||
/** The method does not exist or is not available. */
|
||||
METHOD_NOT_FOUND: -32601,
|
||||
/** Invalid method parameter(s). */
|
||||
INVALID_PARAMS: -32602,
|
||||
/** Internal JSON-RPC error. */
|
||||
INTERNAL_ERROR: -32603,
|
||||
};
|
||||
/**
|
||||
* Paperclip plugin-specific error codes.
|
||||
*
|
||||
* These live in the JSON-RPC "server error" reserved range (-32000 to -32099)
|
||||
* as specified by JSON-RPC 2.0 for implementation-defined server errors.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.7 — Error Propagation Through The Bridge
|
||||
*/
|
||||
export const PLUGIN_RPC_ERROR_CODES = {
|
||||
/** The worker process is not running or not reachable. */
|
||||
WORKER_UNAVAILABLE: -32000,
|
||||
/** The plugin does not have the required capability for this operation. */
|
||||
CAPABILITY_DENIED: -32001,
|
||||
/** The worker reported an unhandled error during method execution. */
|
||||
WORKER_ERROR: -32002,
|
||||
/** The method call timed out waiting for the worker response. */
|
||||
TIMEOUT: -32003,
|
||||
/** The worker does not implement the requested optional method. */
|
||||
METHOD_NOT_IMPLEMENTED: -32004,
|
||||
/** A catch-all for errors that do not fit other categories. */
|
||||
UNKNOWN: -32099,
|
||||
};
|
||||
/** Required methods the worker MUST implement. */
|
||||
export const HOST_TO_WORKER_REQUIRED_METHODS = [
|
||||
"initialize",
|
||||
"health",
|
||||
"shutdown",
|
||||
];
|
||||
/** Optional methods the worker MAY implement. */
|
||||
export const HOST_TO_WORKER_OPTIONAL_METHODS = [
|
||||
"validateConfig",
|
||||
"configChanged",
|
||||
"onEvent",
|
||||
"runJob",
|
||||
"handleWebhook",
|
||||
"getData",
|
||||
"performAction",
|
||||
"executeTool",
|
||||
];
|
||||
// ---------------------------------------------------------------------------
|
||||
// Message Factory Functions
|
||||
// ---------------------------------------------------------------------------
|
||||
/** Counter for generating unique request IDs when no explicit ID is provided. */
|
||||
let _nextId = 1;
|
||||
/** Wrap around before reaching Number.MAX_SAFE_INTEGER to prevent precision loss. */
|
||||
const MAX_SAFE_RPC_ID = Number.MAX_SAFE_INTEGER - 1;
|
||||
/**
|
||||
* Create a JSON-RPC 2.0 request message.
|
||||
*
|
||||
* @param method - The RPC method name
|
||||
* @param params - Structured parameters
|
||||
* @param id - Optional explicit request ID (auto-generated if omitted)
|
||||
*/
|
||||
export function createRequest(method, params, id) {
|
||||
if (_nextId >= MAX_SAFE_RPC_ID) {
|
||||
_nextId = 1;
|
||||
}
|
||||
return {
|
||||
jsonrpc: JSONRPC_VERSION,
|
||||
id: id ?? _nextId++,
|
||||
method,
|
||||
params,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Create a JSON-RPC 2.0 success response.
|
||||
*
|
||||
* @param id - The request ID being responded to
|
||||
* @param result - The result value
|
||||
*/
|
||||
export function createSuccessResponse(id, result) {
|
||||
return {
|
||||
jsonrpc: JSONRPC_VERSION,
|
||||
id,
|
||||
result,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Create a JSON-RPC 2.0 error response.
|
||||
*
|
||||
* @param id - The request ID being responded to (null if the request ID could not be determined)
|
||||
* @param code - Machine-readable error code
|
||||
* @param message - Human-readable error message
|
||||
* @param data - Optional structured error data
|
||||
*/
|
||||
export function createErrorResponse(id, code, message, data) {
|
||||
const response = {
|
||||
jsonrpc: JSONRPC_VERSION,
|
||||
id,
|
||||
error: data !== undefined
|
||||
? { code, message, data }
|
||||
: { code, message },
|
||||
};
|
||||
return response;
|
||||
}
|
||||
/**
|
||||
* Create a JSON-RPC 2.0 notification (fire-and-forget, no response expected).
|
||||
*
|
||||
* @param method - The notification method name
|
||||
* @param params - Structured parameters
|
||||
*/
|
||||
export function createNotification(method, params) {
|
||||
return {
|
||||
jsonrpc: JSONRPC_VERSION,
|
||||
method,
|
||||
params,
|
||||
};
|
||||
}
|
||||
// ---------------------------------------------------------------------------
|
||||
// Type Guards
|
||||
// ---------------------------------------------------------------------------
|
||||
/**
|
||||
* Check whether a value is a well-formed JSON-RPC 2.0 request.
|
||||
*
|
||||
* A request has `jsonrpc: "2.0"`, a string `method`, and an `id`.
|
||||
*/
|
||||
export function isJsonRpcRequest(value) {
|
||||
if (typeof value !== "object" || value === null)
|
||||
return false;
|
||||
const obj = value;
|
||||
return (obj.jsonrpc === JSONRPC_VERSION &&
|
||||
typeof obj.method === "string" &&
|
||||
"id" in obj &&
|
||||
obj.id !== undefined &&
|
||||
obj.id !== null);
|
||||
}
|
||||
/**
|
||||
* Check whether a value is a well-formed JSON-RPC 2.0 notification.
|
||||
*
|
||||
* A notification has `jsonrpc: "2.0"`, a string `method`, but no `id`.
|
||||
*/
|
||||
export function isJsonRpcNotification(value) {
|
||||
if (typeof value !== "object" || value === null)
|
||||
return false;
|
||||
const obj = value;
|
||||
return (obj.jsonrpc === JSONRPC_VERSION &&
|
||||
typeof obj.method === "string" &&
|
||||
!("id" in obj));
|
||||
}
|
||||
/**
|
||||
* Check whether a value is a well-formed JSON-RPC 2.0 response (success or error).
|
||||
*/
|
||||
export function isJsonRpcResponse(value) {
|
||||
if (typeof value !== "object" || value === null)
|
||||
return false;
|
||||
const obj = value;
|
||||
return (obj.jsonrpc === JSONRPC_VERSION &&
|
||||
"id" in obj &&
|
||||
("result" in obj || "error" in obj));
|
||||
}
|
||||
/**
|
||||
* Check whether a JSON-RPC response is a success response.
|
||||
*/
|
||||
export function isJsonRpcSuccessResponse(response) {
|
||||
return "result" in response && !("error" in response && response.error !== undefined);
|
||||
}
|
||||
/**
|
||||
* Check whether a JSON-RPC response is an error response.
|
||||
*/
|
||||
export function isJsonRpcErrorResponse(response) {
|
||||
return "error" in response && response.error !== undefined;
|
||||
}
|
||||
// ---------------------------------------------------------------------------
|
||||
// Serialization Helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
/**
|
||||
* Line delimiter for JSON-RPC messages over stdio.
|
||||
*
|
||||
* Each message is a single line of JSON terminated by a newline character.
|
||||
* This follows the newline-delimited JSON (NDJSON) convention.
|
||||
*/
|
||||
export const MESSAGE_DELIMITER = "\n";
|
||||
/**
|
||||
* Serialize a JSON-RPC message to a newline-delimited string for transmission
|
||||
* over stdio.
|
||||
*
|
||||
* @param message - Any JSON-RPC message (request, response, or notification)
|
||||
* @returns The JSON string terminated with a newline
|
||||
*/
|
||||
export function serializeMessage(message) {
|
||||
return JSON.stringify(message) + MESSAGE_DELIMITER;
|
||||
}
|
||||
/**
|
||||
* Parse a JSON string into a JSON-RPC message.
|
||||
*
|
||||
* Returns the parsed message or throws a `JsonRpcParseError` if the input
|
||||
* is not valid JSON or does not conform to the JSON-RPC 2.0 structure.
|
||||
*
|
||||
* @param line - A single line of JSON text (with or without trailing newline)
|
||||
* @returns The parsed JSON-RPC message
|
||||
* @throws {JsonRpcParseError} If parsing fails
|
||||
*/
|
||||
export function parseMessage(line) {
|
||||
const trimmed = line.trim();
|
||||
if (trimmed.length === 0) {
|
||||
throw new JsonRpcParseError("Empty message");
|
||||
}
|
||||
let parsed;
|
||||
try {
|
||||
parsed = JSON.parse(trimmed);
|
||||
}
|
||||
catch {
|
||||
throw new JsonRpcParseError(`Invalid JSON: ${trimmed.slice(0, 200)}`);
|
||||
}
|
||||
if (typeof parsed !== "object" || parsed === null) {
|
||||
throw new JsonRpcParseError("Message must be a JSON object");
|
||||
}
|
||||
const obj = parsed;
|
||||
if (obj.jsonrpc !== JSONRPC_VERSION) {
|
||||
throw new JsonRpcParseError(`Invalid or missing jsonrpc version (expected "${JSONRPC_VERSION}", got ${JSON.stringify(obj.jsonrpc)})`);
|
||||
}
|
||||
// It's a valid JSON-RPC 2.0 envelope — return as-is and let the caller
|
||||
// use the type guards for more specific classification.
|
||||
return parsed;
|
||||
}
|
||||
// ---------------------------------------------------------------------------
|
||||
// Error Classes
|
||||
// ---------------------------------------------------------------------------
|
||||
/**
|
||||
* Error thrown when a JSON-RPC message cannot be parsed.
|
||||
*/
|
||||
export class JsonRpcParseError extends Error {
|
||||
name = "JsonRpcParseError";
|
||||
constructor(message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Error thrown when a JSON-RPC call fails with a structured error response.
|
||||
*
|
||||
* Captures the full `JsonRpcError` so callers can inspect the code and data.
|
||||
*/
|
||||
export class JsonRpcCallError extends Error {
|
||||
name = "JsonRpcCallError";
|
||||
/** The JSON-RPC error code. */
|
||||
code;
|
||||
/** Optional structured error data from the response. */
|
||||
data;
|
||||
constructor(error) {
|
||||
super(error.message);
|
||||
this.code = error.code;
|
||||
this.data = error.data;
|
||||
}
|
||||
}
|
||||
// ---------------------------------------------------------------------------
|
||||
// Reset helper (testing only)
|
||||
// ---------------------------------------------------------------------------
|
||||
/**
|
||||
* Reset the internal request ID counter. **For testing only.**
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export function _resetIdCounter() {
|
||||
_nextId = 1;
|
||||
}
|
||||
//# sourceMappingURL=protocol.js.map
|
||||
1
node_modules/@paperclipai/plugin-sdk/dist/protocol.js.map
generated
vendored
Normal file
1
node_modules/@paperclipai/plugin-sdk/dist/protocol.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"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"}
|
||||
63
node_modules/@paperclipai/plugin-sdk/dist/testing.d.ts
generated
vendored
Normal file
63
node_modules/@paperclipai/plugin-sdk/dist/testing.d.ts
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
import type { PaperclipPluginManifestV1, PluginCapability, PluginEventType, Company, Project, Issue, IssueComment, Agent, Goal } from "@paperclipai/shared";
|
||||
import type { PluginContext, PluginJobContext, PluginEvent, ScopeKey, ToolResult, ToolRunContext, AgentSessionEvent } from "./types.js";
|
||||
export interface TestHarnessOptions {
|
||||
/** Plugin manifest used to seed capability checks and metadata. */
|
||||
manifest: PaperclipPluginManifestV1;
|
||||
/** Optional capability override. Defaults to `manifest.capabilities`. */
|
||||
capabilities?: PluginCapability[];
|
||||
/** Initial config returned by `ctx.config.get()`. */
|
||||
config?: Record<string, unknown>;
|
||||
}
|
||||
export interface TestHarnessLogEntry {
|
||||
level: "info" | "warn" | "error" | "debug";
|
||||
message: string;
|
||||
meta?: Record<string, unknown>;
|
||||
}
|
||||
export interface TestHarness {
|
||||
/** Fully-typed in-memory plugin context passed to `plugin.setup(ctx)`. */
|
||||
ctx: PluginContext;
|
||||
/** Seed host entities for `ctx.companies/projects/issues/agents/goals` reads. */
|
||||
seed(input: {
|
||||
companies?: Company[];
|
||||
projects?: Project[];
|
||||
issues?: Issue[];
|
||||
issueComments?: IssueComment[];
|
||||
agents?: Agent[];
|
||||
goals?: Goal[];
|
||||
}): void;
|
||||
setConfig(config: Record<string, unknown>): void;
|
||||
/** Dispatch a host or plugin event to registered handlers. */
|
||||
emit(eventType: PluginEventType | `plugin.${string}`, payload: unknown, base?: Partial<PluginEvent>): Promise<void>;
|
||||
/** Execute a previously-registered scheduled job handler. */
|
||||
runJob(jobKey: string, partial?: Partial<PluginJobContext>): Promise<void>;
|
||||
/** Invoke a `ctx.data.register(...)` handler by key. */
|
||||
getData<T = unknown>(key: string, params?: Record<string, unknown>): Promise<T>;
|
||||
/** Invoke a `ctx.actions.register(...)` handler by key. */
|
||||
performAction<T = unknown>(key: string, params?: Record<string, unknown>): Promise<T>;
|
||||
/** Execute a registered tool handler via `ctx.tools.execute(...)`. */
|
||||
executeTool<T = ToolResult>(name: string, params: unknown, runCtx?: Partial<ToolRunContext>): Promise<T>;
|
||||
/** Read raw in-memory state for assertions. */
|
||||
getState(input: ScopeKey): unknown;
|
||||
/** Simulate a streaming event arriving for an active session. */
|
||||
simulateSessionEvent(sessionId: string, event: Omit<AgentSessionEvent, "sessionId">): void;
|
||||
logs: TestHarnessLogEntry[];
|
||||
activity: Array<{
|
||||
message: string;
|
||||
entityType?: string;
|
||||
entityId?: string;
|
||||
metadata?: Record<string, unknown>;
|
||||
}>;
|
||||
metrics: Array<{
|
||||
name: string;
|
||||
value: number;
|
||||
tags?: Record<string, string>;
|
||||
}>;
|
||||
}
|
||||
/**
|
||||
* Create an in-memory host harness for plugin worker tests.
|
||||
*
|
||||
* The harness enforces declared capabilities and simulates host APIs, so tests
|
||||
* can validate plugin behavior without spinning up the Paperclip server runtime.
|
||||
*/
|
||||
export declare function createTestHarness(options: TestHarnessOptions): TestHarness;
|
||||
//# sourceMappingURL=testing.d.ts.map
|
||||
1
node_modules/@paperclipai/plugin-sdk/dist/testing.d.ts.map
generated
vendored
Normal file
1
node_modules/@paperclipai/plugin-sdk/dist/testing.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"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"}
|
||||
700
node_modules/@paperclipai/plugin-sdk/dist/testing.js
generated
vendored
Normal file
700
node_modules/@paperclipai/plugin-sdk/dist/testing.js
generated
vendored
Normal file
@@ -0,0 +1,700 @@
|
||||
import { randomUUID } from "node:crypto";
|
||||
function normalizeScope(input) {
|
||||
return {
|
||||
scopeKind: input.scopeKind,
|
||||
scopeId: input.scopeId,
|
||||
namespace: input.namespace ?? "default",
|
||||
stateKey: input.stateKey,
|
||||
};
|
||||
}
|
||||
function stateMapKey(input) {
|
||||
const normalized = normalizeScope(input);
|
||||
return `${normalized.scopeKind}|${normalized.scopeId ?? ""}|${normalized.namespace}|${normalized.stateKey}`;
|
||||
}
|
||||
function allowsEvent(filter, event) {
|
||||
if (!filter)
|
||||
return true;
|
||||
if (filter.companyId && filter.companyId !== String(event.payload?.companyId ?? ""))
|
||||
return false;
|
||||
if (filter.projectId && filter.projectId !== String(event.payload?.projectId ?? ""))
|
||||
return false;
|
||||
if (filter.agentId && filter.agentId !== String(event.payload?.agentId ?? ""))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
function requireCapability(manifest, allowed, capability) {
|
||||
if (allowed.has(capability))
|
||||
return;
|
||||
throw new Error(`Plugin '${manifest.id}' is missing required capability '${capability}' in test harness`);
|
||||
}
|
||||
function requireCompanyId(companyId) {
|
||||
if (!companyId)
|
||||
throw new Error("companyId is required for this operation");
|
||||
return companyId;
|
||||
}
|
||||
function isInCompany(record, companyId) {
|
||||
return Boolean(record && record.companyId === companyId);
|
||||
}
|
||||
/**
|
||||
* Create an in-memory host harness for plugin worker tests.
|
||||
*
|
||||
* The harness enforces declared capabilities and simulates host APIs, so tests
|
||||
* can validate plugin behavior without spinning up the Paperclip server runtime.
|
||||
*/
|
||||
export function createTestHarness(options) {
|
||||
const manifest = options.manifest;
|
||||
const capabilitySet = new Set(options.capabilities ?? manifest.capabilities);
|
||||
let currentConfig = { ...(options.config ?? {}) };
|
||||
const logs = [];
|
||||
const activity = [];
|
||||
const metrics = [];
|
||||
const state = new Map();
|
||||
const entities = new Map();
|
||||
const entityExternalIndex = new Map();
|
||||
const companies = new Map();
|
||||
const projects = new Map();
|
||||
const issues = new Map();
|
||||
const issueComments = new Map();
|
||||
const agents = new Map();
|
||||
const goals = new Map();
|
||||
const projectWorkspaces = new Map();
|
||||
const sessions = new Map();
|
||||
const sessionEventCallbacks = new Map();
|
||||
const events = [];
|
||||
const jobs = new Map();
|
||||
const launchers = new Map();
|
||||
const dataHandlers = new Map();
|
||||
const actionHandlers = new Map();
|
||||
const toolHandlers = new Map();
|
||||
const ctx = {
|
||||
manifest,
|
||||
config: {
|
||||
async get() {
|
||||
return { ...currentConfig };
|
||||
},
|
||||
},
|
||||
events: {
|
||||
on(name, filterOrFn, maybeFn) {
|
||||
requireCapability(manifest, capabilitySet, "events.subscribe");
|
||||
let registration;
|
||||
if (typeof filterOrFn === "function") {
|
||||
registration = { name, fn: filterOrFn };
|
||||
}
|
||||
else {
|
||||
if (!maybeFn)
|
||||
throw new Error("event handler is required");
|
||||
registration = { name, filter: filterOrFn, fn: maybeFn };
|
||||
}
|
||||
events.push(registration);
|
||||
return () => {
|
||||
const idx = events.indexOf(registration);
|
||||
if (idx !== -1)
|
||||
events.splice(idx, 1);
|
||||
};
|
||||
},
|
||||
async emit(name, companyId, payload) {
|
||||
requireCapability(manifest, capabilitySet, "events.emit");
|
||||
await harness.emit(`plugin.${manifest.id}.${name}`, payload, { companyId });
|
||||
},
|
||||
},
|
||||
jobs: {
|
||||
register(key, fn) {
|
||||
requireCapability(manifest, capabilitySet, "jobs.schedule");
|
||||
jobs.set(key, fn);
|
||||
},
|
||||
},
|
||||
launchers: {
|
||||
register(launcher) {
|
||||
launchers.set(launcher.id, launcher);
|
||||
},
|
||||
},
|
||||
http: {
|
||||
async fetch(url, init) {
|
||||
requireCapability(manifest, capabilitySet, "http.outbound");
|
||||
return fetch(url, init);
|
||||
},
|
||||
},
|
||||
secrets: {
|
||||
async resolve(secretRef) {
|
||||
requireCapability(manifest, capabilitySet, "secrets.read-ref");
|
||||
return `resolved:${secretRef}`;
|
||||
},
|
||||
},
|
||||
activity: {
|
||||
async log(entry) {
|
||||
requireCapability(manifest, capabilitySet, "activity.log.write");
|
||||
activity.push(entry);
|
||||
},
|
||||
},
|
||||
state: {
|
||||
async get(input) {
|
||||
requireCapability(manifest, capabilitySet, "plugin.state.read");
|
||||
return state.has(stateMapKey(input)) ? state.get(stateMapKey(input)) : null;
|
||||
},
|
||||
async set(input, value) {
|
||||
requireCapability(manifest, capabilitySet, "plugin.state.write");
|
||||
state.set(stateMapKey(input), value);
|
||||
},
|
||||
async delete(input) {
|
||||
requireCapability(manifest, capabilitySet, "plugin.state.write");
|
||||
state.delete(stateMapKey(input));
|
||||
},
|
||||
},
|
||||
entities: {
|
||||
async upsert(input) {
|
||||
const externalKey = input.externalId
|
||||
? `${input.entityType}|${input.scopeKind}|${input.scopeId ?? ""}|${input.externalId}`
|
||||
: null;
|
||||
const existingId = externalKey ? entityExternalIndex.get(externalKey) : undefined;
|
||||
const existing = existingId ? entities.get(existingId) : undefined;
|
||||
const now = new Date().toISOString();
|
||||
const previousExternalKey = existing?.externalId
|
||||
? `${existing.entityType}|${existing.scopeKind}|${existing.scopeId ?? ""}|${existing.externalId}`
|
||||
: null;
|
||||
const record = existing
|
||||
? {
|
||||
...existing,
|
||||
entityType: input.entityType,
|
||||
scopeKind: input.scopeKind,
|
||||
scopeId: input.scopeId ?? null,
|
||||
externalId: input.externalId ?? null,
|
||||
title: input.title ?? null,
|
||||
status: input.status ?? null,
|
||||
data: input.data,
|
||||
updatedAt: now,
|
||||
}
|
||||
: {
|
||||
id: randomUUID(),
|
||||
entityType: input.entityType,
|
||||
scopeKind: input.scopeKind,
|
||||
scopeId: input.scopeId ?? null,
|
||||
externalId: input.externalId ?? null,
|
||||
title: input.title ?? null,
|
||||
status: input.status ?? null,
|
||||
data: input.data,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
};
|
||||
entities.set(record.id, record);
|
||||
if (previousExternalKey && previousExternalKey !== externalKey) {
|
||||
entityExternalIndex.delete(previousExternalKey);
|
||||
}
|
||||
if (externalKey)
|
||||
entityExternalIndex.set(externalKey, record.id);
|
||||
return record;
|
||||
},
|
||||
async list(query) {
|
||||
let out = [...entities.values()];
|
||||
if (query.entityType)
|
||||
out = out.filter((r) => r.entityType === query.entityType);
|
||||
if (query.scopeKind)
|
||||
out = out.filter((r) => r.scopeKind === query.scopeKind);
|
||||
if (query.scopeId)
|
||||
out = out.filter((r) => r.scopeId === query.scopeId);
|
||||
if (query.externalId)
|
||||
out = out.filter((r) => r.externalId === query.externalId);
|
||||
if (query.offset)
|
||||
out = out.slice(query.offset);
|
||||
if (query.limit)
|
||||
out = out.slice(0, query.limit);
|
||||
return out;
|
||||
},
|
||||
},
|
||||
projects: {
|
||||
async list(input) {
|
||||
requireCapability(manifest, capabilitySet, "projects.read");
|
||||
const companyId = requireCompanyId(input?.companyId);
|
||||
let out = [...projects.values()];
|
||||
out = out.filter((project) => project.companyId === companyId);
|
||||
if (input?.offset)
|
||||
out = out.slice(input.offset);
|
||||
if (input?.limit)
|
||||
out = out.slice(0, input.limit);
|
||||
return out;
|
||||
},
|
||||
async get(projectId, companyId) {
|
||||
requireCapability(manifest, capabilitySet, "projects.read");
|
||||
const project = projects.get(projectId);
|
||||
return isInCompany(project, companyId) ? project : null;
|
||||
},
|
||||
async listWorkspaces(projectId, companyId) {
|
||||
requireCapability(manifest, capabilitySet, "project.workspaces.read");
|
||||
if (!isInCompany(projects.get(projectId), companyId))
|
||||
return [];
|
||||
return projectWorkspaces.get(projectId) ?? [];
|
||||
},
|
||||
async getPrimaryWorkspace(projectId, companyId) {
|
||||
requireCapability(manifest, capabilitySet, "project.workspaces.read");
|
||||
if (!isInCompany(projects.get(projectId), companyId))
|
||||
return null;
|
||||
const workspaces = projectWorkspaces.get(projectId) ?? [];
|
||||
return workspaces.find((workspace) => workspace.isPrimary) ?? null;
|
||||
},
|
||||
async getWorkspaceForIssue(issueId, companyId) {
|
||||
requireCapability(manifest, capabilitySet, "project.workspaces.read");
|
||||
const issue = issues.get(issueId);
|
||||
if (!isInCompany(issue, companyId))
|
||||
return null;
|
||||
const projectId = issue?.projectId;
|
||||
if (!projectId)
|
||||
return null;
|
||||
if (!isInCompany(projects.get(projectId), companyId))
|
||||
return null;
|
||||
const workspaces = projectWorkspaces.get(projectId) ?? [];
|
||||
return workspaces.find((workspace) => workspace.isPrimary) ?? null;
|
||||
},
|
||||
},
|
||||
companies: {
|
||||
async list(input) {
|
||||
requireCapability(manifest, capabilitySet, "companies.read");
|
||||
let out = [...companies.values()];
|
||||
if (input?.offset)
|
||||
out = out.slice(input.offset);
|
||||
if (input?.limit)
|
||||
out = out.slice(0, input.limit);
|
||||
return out;
|
||||
},
|
||||
async get(companyId) {
|
||||
requireCapability(manifest, capabilitySet, "companies.read");
|
||||
return companies.get(companyId) ?? null;
|
||||
},
|
||||
},
|
||||
issues: {
|
||||
async list(input) {
|
||||
requireCapability(manifest, capabilitySet, "issues.read");
|
||||
const companyId = requireCompanyId(input?.companyId);
|
||||
let out = [...issues.values()];
|
||||
out = out.filter((issue) => issue.companyId === companyId);
|
||||
if (input?.projectId)
|
||||
out = out.filter((issue) => issue.projectId === input.projectId);
|
||||
if (input?.assigneeAgentId)
|
||||
out = out.filter((issue) => issue.assigneeAgentId === input.assigneeAgentId);
|
||||
if (input?.status)
|
||||
out = out.filter((issue) => issue.status === input.status);
|
||||
if (input?.offset)
|
||||
out = out.slice(input.offset);
|
||||
if (input?.limit)
|
||||
out = out.slice(0, input.limit);
|
||||
return out;
|
||||
},
|
||||
async get(issueId, companyId) {
|
||||
requireCapability(manifest, capabilitySet, "issues.read");
|
||||
const issue = issues.get(issueId);
|
||||
return isInCompany(issue, companyId) ? issue : null;
|
||||
},
|
||||
async create(input) {
|
||||
requireCapability(manifest, capabilitySet, "issues.create");
|
||||
const now = new Date();
|
||||
const record = {
|
||||
id: randomUUID(),
|
||||
companyId: input.companyId,
|
||||
projectId: input.projectId ?? null,
|
||||
projectWorkspaceId: null,
|
||||
goalId: input.goalId ?? null,
|
||||
parentId: input.parentId ?? null,
|
||||
title: input.title,
|
||||
description: input.description ?? null,
|
||||
status: "todo",
|
||||
priority: input.priority ?? "medium",
|
||||
assigneeAgentId: input.assigneeAgentId ?? null,
|
||||
assigneeUserId: null,
|
||||
checkoutRunId: null,
|
||||
executionRunId: null,
|
||||
executionAgentNameKey: null,
|
||||
executionLockedAt: null,
|
||||
createdByAgentId: null,
|
||||
createdByUserId: null,
|
||||
issueNumber: null,
|
||||
identifier: null,
|
||||
requestDepth: 0,
|
||||
billingCode: null,
|
||||
assigneeAdapterOverrides: null,
|
||||
executionWorkspaceId: null,
|
||||
executionWorkspacePreference: null,
|
||||
executionWorkspaceSettings: null,
|
||||
startedAt: null,
|
||||
completedAt: null,
|
||||
cancelledAt: null,
|
||||
hiddenAt: null,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
};
|
||||
issues.set(record.id, record);
|
||||
return record;
|
||||
},
|
||||
async update(issueId, patch, companyId) {
|
||||
requireCapability(manifest, capabilitySet, "issues.update");
|
||||
const record = issues.get(issueId);
|
||||
if (!isInCompany(record, companyId))
|
||||
throw new Error(`Issue not found: ${issueId}`);
|
||||
const updated = {
|
||||
...record,
|
||||
...patch,
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
issues.set(issueId, updated);
|
||||
return updated;
|
||||
},
|
||||
async listComments(issueId, companyId) {
|
||||
requireCapability(manifest, capabilitySet, "issue.comments.read");
|
||||
if (!isInCompany(issues.get(issueId), companyId))
|
||||
return [];
|
||||
return issueComments.get(issueId) ?? [];
|
||||
},
|
||||
async createComment(issueId, body, companyId) {
|
||||
requireCapability(manifest, capabilitySet, "issue.comments.create");
|
||||
const parentIssue = issues.get(issueId);
|
||||
if (!isInCompany(parentIssue, companyId)) {
|
||||
throw new Error(`Issue not found: ${issueId}`);
|
||||
}
|
||||
const now = new Date();
|
||||
const comment = {
|
||||
id: randomUUID(),
|
||||
companyId: parentIssue.companyId,
|
||||
issueId,
|
||||
authorAgentId: null,
|
||||
authorUserId: null,
|
||||
body,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
};
|
||||
const current = issueComments.get(issueId) ?? [];
|
||||
current.push(comment);
|
||||
issueComments.set(issueId, current);
|
||||
return comment;
|
||||
},
|
||||
documents: {
|
||||
async list(issueId, companyId) {
|
||||
requireCapability(manifest, capabilitySet, "issue.documents.read");
|
||||
if (!isInCompany(issues.get(issueId), companyId))
|
||||
return [];
|
||||
return [];
|
||||
},
|
||||
async get(issueId, _key, companyId) {
|
||||
requireCapability(manifest, capabilitySet, "issue.documents.read");
|
||||
if (!isInCompany(issues.get(issueId), companyId))
|
||||
return null;
|
||||
return null;
|
||||
},
|
||||
async upsert(input) {
|
||||
requireCapability(manifest, capabilitySet, "issue.documents.write");
|
||||
const parentIssue = issues.get(input.issueId);
|
||||
if (!isInCompany(parentIssue, input.companyId)) {
|
||||
throw new Error(`Issue not found: ${input.issueId}`);
|
||||
}
|
||||
throw new Error("documents.upsert is not implemented in test context");
|
||||
},
|
||||
async delete(issueId, _key, companyId) {
|
||||
requireCapability(manifest, capabilitySet, "issue.documents.write");
|
||||
const parentIssue = issues.get(issueId);
|
||||
if (!isInCompany(parentIssue, companyId)) {
|
||||
throw new Error(`Issue not found: ${issueId}`);
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
agents: {
|
||||
async list(input) {
|
||||
requireCapability(manifest, capabilitySet, "agents.read");
|
||||
const companyId = requireCompanyId(input?.companyId);
|
||||
let out = [...agents.values()];
|
||||
out = out.filter((agent) => agent.companyId === companyId);
|
||||
if (input?.status)
|
||||
out = out.filter((agent) => agent.status === input.status);
|
||||
if (input?.offset)
|
||||
out = out.slice(input.offset);
|
||||
if (input?.limit)
|
||||
out = out.slice(0, input.limit);
|
||||
return out;
|
||||
},
|
||||
async get(agentId, companyId) {
|
||||
requireCapability(manifest, capabilitySet, "agents.read");
|
||||
const agent = agents.get(agentId);
|
||||
return isInCompany(agent, companyId) ? agent : null;
|
||||
},
|
||||
async pause(agentId, companyId) {
|
||||
requireCapability(manifest, capabilitySet, "agents.pause");
|
||||
const cid = requireCompanyId(companyId);
|
||||
const agent = agents.get(agentId);
|
||||
if (!isInCompany(agent, cid))
|
||||
throw new Error(`Agent not found: ${agentId}`);
|
||||
if (agent.status === "terminated")
|
||||
throw new Error("Cannot pause terminated agent");
|
||||
const updated = { ...agent, status: "paused", updatedAt: new Date() };
|
||||
agents.set(agentId, updated);
|
||||
return updated;
|
||||
},
|
||||
async resume(agentId, companyId) {
|
||||
requireCapability(manifest, capabilitySet, "agents.resume");
|
||||
const cid = requireCompanyId(companyId);
|
||||
const agent = agents.get(agentId);
|
||||
if (!isInCompany(agent, cid))
|
||||
throw new Error(`Agent not found: ${agentId}`);
|
||||
if (agent.status === "terminated")
|
||||
throw new Error("Cannot resume terminated agent");
|
||||
if (agent.status === "pending_approval")
|
||||
throw new Error("Pending approval agents cannot be resumed");
|
||||
const updated = { ...agent, status: "idle", updatedAt: new Date() };
|
||||
agents.set(agentId, updated);
|
||||
return updated;
|
||||
},
|
||||
async invoke(agentId, companyId, opts) {
|
||||
requireCapability(manifest, capabilitySet, "agents.invoke");
|
||||
const cid = requireCompanyId(companyId);
|
||||
const agent = agents.get(agentId);
|
||||
if (!isInCompany(agent, cid))
|
||||
throw new Error(`Agent not found: ${agentId}`);
|
||||
if (agent.status === "paused" ||
|
||||
agent.status === "terminated" ||
|
||||
agent.status === "pending_approval") {
|
||||
throw new Error(`Agent is not invokable in its current state: ${agent.status}`);
|
||||
}
|
||||
return { runId: randomUUID() };
|
||||
},
|
||||
sessions: {
|
||||
async create(agentId, companyId, opts) {
|
||||
requireCapability(manifest, capabilitySet, "agent.sessions.create");
|
||||
const cid = requireCompanyId(companyId);
|
||||
const agent = agents.get(agentId);
|
||||
if (!isInCompany(agent, cid))
|
||||
throw new Error(`Agent not found: ${agentId}`);
|
||||
const session = {
|
||||
sessionId: randomUUID(),
|
||||
agentId,
|
||||
companyId: cid,
|
||||
status: "active",
|
||||
createdAt: new Date().toISOString(),
|
||||
};
|
||||
sessions.set(session.sessionId, session);
|
||||
return session;
|
||||
},
|
||||
async list(agentId, companyId) {
|
||||
requireCapability(manifest, capabilitySet, "agent.sessions.list");
|
||||
const cid = requireCompanyId(companyId);
|
||||
return [...sessions.values()].filter((s) => s.agentId === agentId && s.companyId === cid && s.status === "active");
|
||||
},
|
||||
async sendMessage(sessionId, companyId, opts) {
|
||||
requireCapability(manifest, capabilitySet, "agent.sessions.send");
|
||||
const session = sessions.get(sessionId);
|
||||
if (!session || session.status !== "active")
|
||||
throw new Error(`Session not found or closed: ${sessionId}`);
|
||||
if (session.companyId !== companyId)
|
||||
throw new Error(`Session not found: ${sessionId}`);
|
||||
if (opts.onEvent) {
|
||||
sessionEventCallbacks.set(sessionId, opts.onEvent);
|
||||
}
|
||||
return { runId: randomUUID() };
|
||||
},
|
||||
async close(sessionId, companyId) {
|
||||
requireCapability(manifest, capabilitySet, "agent.sessions.close");
|
||||
const session = sessions.get(sessionId);
|
||||
if (!session)
|
||||
throw new Error(`Session not found: ${sessionId}`);
|
||||
if (session.companyId !== companyId)
|
||||
throw new Error(`Session not found: ${sessionId}`);
|
||||
session.status = "closed";
|
||||
sessionEventCallbacks.delete(sessionId);
|
||||
},
|
||||
},
|
||||
},
|
||||
goals: {
|
||||
async list(input) {
|
||||
requireCapability(manifest, capabilitySet, "goals.read");
|
||||
const companyId = requireCompanyId(input?.companyId);
|
||||
let out = [...goals.values()];
|
||||
out = out.filter((goal) => goal.companyId === companyId);
|
||||
if (input?.level)
|
||||
out = out.filter((goal) => goal.level === input.level);
|
||||
if (input?.status)
|
||||
out = out.filter((goal) => goal.status === input.status);
|
||||
if (input?.offset)
|
||||
out = out.slice(input.offset);
|
||||
if (input?.limit)
|
||||
out = out.slice(0, input.limit);
|
||||
return out;
|
||||
},
|
||||
async get(goalId, companyId) {
|
||||
requireCapability(manifest, capabilitySet, "goals.read");
|
||||
const goal = goals.get(goalId);
|
||||
return isInCompany(goal, companyId) ? goal : null;
|
||||
},
|
||||
async create(input) {
|
||||
requireCapability(manifest, capabilitySet, "goals.create");
|
||||
const now = new Date();
|
||||
const record = {
|
||||
id: randomUUID(),
|
||||
companyId: input.companyId,
|
||||
title: input.title,
|
||||
description: input.description ?? null,
|
||||
level: input.level ?? "task",
|
||||
status: input.status ?? "planned",
|
||||
parentId: input.parentId ?? null,
|
||||
ownerAgentId: input.ownerAgentId ?? null,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
};
|
||||
goals.set(record.id, record);
|
||||
return record;
|
||||
},
|
||||
async update(goalId, patch, companyId) {
|
||||
requireCapability(manifest, capabilitySet, "goals.update");
|
||||
const record = goals.get(goalId);
|
||||
if (!isInCompany(record, companyId))
|
||||
throw new Error(`Goal not found: ${goalId}`);
|
||||
const updated = {
|
||||
...record,
|
||||
...patch,
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
goals.set(goalId, updated);
|
||||
return updated;
|
||||
},
|
||||
},
|
||||
data: {
|
||||
register(key, handler) {
|
||||
dataHandlers.set(key, handler);
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
register(key, handler) {
|
||||
actionHandlers.set(key, handler);
|
||||
},
|
||||
},
|
||||
streams: (() => {
|
||||
const channelCompanyMap = new Map();
|
||||
return {
|
||||
open(channel, companyId) {
|
||||
channelCompanyMap.set(channel, companyId);
|
||||
},
|
||||
emit(_channel, _event) {
|
||||
// No-op in test harness — events are not forwarded
|
||||
},
|
||||
close(channel) {
|
||||
channelCompanyMap.delete(channel);
|
||||
},
|
||||
};
|
||||
})(),
|
||||
tools: {
|
||||
register(name, _decl, fn) {
|
||||
requireCapability(manifest, capabilitySet, "agent.tools.register");
|
||||
toolHandlers.set(name, fn);
|
||||
},
|
||||
},
|
||||
metrics: {
|
||||
async write(name, value, tags) {
|
||||
requireCapability(manifest, capabilitySet, "metrics.write");
|
||||
metrics.push({ name, value, tags });
|
||||
},
|
||||
},
|
||||
logger: {
|
||||
info(message, meta) {
|
||||
logs.push({ level: "info", message, meta });
|
||||
},
|
||||
warn(message, meta) {
|
||||
logs.push({ level: "warn", message, meta });
|
||||
},
|
||||
error(message, meta) {
|
||||
logs.push({ level: "error", message, meta });
|
||||
},
|
||||
debug(message, meta) {
|
||||
logs.push({ level: "debug", message, meta });
|
||||
},
|
||||
},
|
||||
};
|
||||
const harness = {
|
||||
ctx,
|
||||
seed(input) {
|
||||
for (const row of input.companies ?? [])
|
||||
companies.set(row.id, row);
|
||||
for (const row of input.projects ?? [])
|
||||
projects.set(row.id, row);
|
||||
for (const row of input.issues ?? [])
|
||||
issues.set(row.id, row);
|
||||
for (const row of input.issueComments ?? []) {
|
||||
const list = issueComments.get(row.issueId) ?? [];
|
||||
list.push(row);
|
||||
issueComments.set(row.issueId, list);
|
||||
}
|
||||
for (const row of input.agents ?? [])
|
||||
agents.set(row.id, row);
|
||||
for (const row of input.goals ?? [])
|
||||
goals.set(row.id, row);
|
||||
},
|
||||
setConfig(config) {
|
||||
currentConfig = { ...config };
|
||||
},
|
||||
async emit(eventType, payload, base) {
|
||||
const event = {
|
||||
eventId: base?.eventId ?? randomUUID(),
|
||||
eventType,
|
||||
companyId: base?.companyId ?? "test-company",
|
||||
occurredAt: base?.occurredAt ?? new Date().toISOString(),
|
||||
actorId: base?.actorId,
|
||||
actorType: base?.actorType,
|
||||
entityId: base?.entityId,
|
||||
entityType: base?.entityType,
|
||||
payload,
|
||||
};
|
||||
for (const handler of events) {
|
||||
const exactMatch = handler.name === event.eventType;
|
||||
const wildcardPluginAll = handler.name === "plugin.*" && String(event.eventType).startsWith("plugin.");
|
||||
const wildcardPluginOne = String(handler.name).endsWith(".*")
|
||||
&& String(event.eventType).startsWith(String(handler.name).slice(0, -1));
|
||||
if (!exactMatch && !wildcardPluginAll && !wildcardPluginOne)
|
||||
continue;
|
||||
if (!allowsEvent(handler.filter, event))
|
||||
continue;
|
||||
await handler.fn(event);
|
||||
}
|
||||
},
|
||||
async runJob(jobKey, partial = {}) {
|
||||
const handler = jobs.get(jobKey);
|
||||
if (!handler)
|
||||
throw new Error(`No job handler registered for '${jobKey}'`);
|
||||
await handler({
|
||||
jobKey,
|
||||
runId: partial.runId ?? randomUUID(),
|
||||
trigger: partial.trigger ?? "manual",
|
||||
scheduledAt: partial.scheduledAt ?? new Date().toISOString(),
|
||||
});
|
||||
},
|
||||
async getData(key, params = {}) {
|
||||
const handler = dataHandlers.get(key);
|
||||
if (!handler)
|
||||
throw new Error(`No data handler registered for '${key}'`);
|
||||
return await handler(params);
|
||||
},
|
||||
async performAction(key, params = {}) {
|
||||
const handler = actionHandlers.get(key);
|
||||
if (!handler)
|
||||
throw new Error(`No action handler registered for '${key}'`);
|
||||
return await handler(params);
|
||||
},
|
||||
async executeTool(name, params, runCtx = {}) {
|
||||
const handler = toolHandlers.get(name);
|
||||
if (!handler)
|
||||
throw new Error(`No tool handler registered for '${name}'`);
|
||||
const ctxToPass = {
|
||||
agentId: runCtx.agentId ?? "agent-test",
|
||||
runId: runCtx.runId ?? randomUUID(),
|
||||
companyId: runCtx.companyId ?? "company-test",
|
||||
projectId: runCtx.projectId ?? "project-test",
|
||||
};
|
||||
return await handler(params, ctxToPass);
|
||||
},
|
||||
getState(input) {
|
||||
return state.get(stateMapKey(input));
|
||||
},
|
||||
simulateSessionEvent(sessionId, event) {
|
||||
const cb = sessionEventCallbacks.get(sessionId);
|
||||
if (!cb)
|
||||
throw new Error(`No active session event callback for session: ${sessionId}`);
|
||||
cb({ ...event, sessionId });
|
||||
},
|
||||
logs,
|
||||
activity,
|
||||
metrics,
|
||||
};
|
||||
return harness;
|
||||
}
|
||||
//# sourceMappingURL=testing.js.map
|
||||
1
node_modules/@paperclipai/plugin-sdk/dist/testing.js.map
generated
vendored
Normal file
1
node_modules/@paperclipai/plugin-sdk/dist/testing.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
982
node_modules/@paperclipai/plugin-sdk/dist/types.d.ts
generated
vendored
Normal file
982
node_modules/@paperclipai/plugin-sdk/dist/types.d.ts
generated
vendored
Normal file
@@ -0,0 +1,982 @@
|
||||
/**
|
||||
* Core types for the Paperclip plugin worker-side SDK.
|
||||
*
|
||||
* These types define the stable public API surface that plugin workers import
|
||||
* from `@paperclipai/plugin-sdk`. The host provides a concrete implementation
|
||||
* of `PluginContext` to the plugin at initialisation time.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §14 — SDK Surface
|
||||
* @see PLUGIN_SPEC.md §29.2 — SDK Versioning
|
||||
*/
|
||||
import type { PaperclipPluginManifestV1, PluginStateScopeKind, PluginEventType, PluginToolDeclaration, PluginLauncherDeclaration, Company, Project, Issue, IssueComment, IssueDocument, IssueDocumentSummary, Agent, Goal } from "@paperclipai/shared";
|
||||
export type { PaperclipPluginManifestV1, PluginJobDeclaration, PluginWebhookDeclaration, PluginToolDeclaration, PluginUiSlotDeclaration, PluginUiDeclaration, PluginLauncherActionDeclaration, PluginLauncherRenderDeclaration, PluginLauncherDeclaration, PluginMinimumHostVersion, PluginRecord, PluginConfig, JsonSchema, PluginStatus, PluginCategory, PluginCapability, PluginUiSlotType, PluginUiSlotEntityType, PluginLauncherPlacementZone, PluginLauncherAction, PluginLauncherBounds, PluginLauncherRenderEnvironment, PluginStateScopeKind, PluginJobStatus, PluginJobRunStatus, PluginJobRunTrigger, PluginWebhookDeliveryStatus, PluginEventType, PluginBridgeErrorCode, Company, Project, Issue, IssueComment, IssueDocument, IssueDocumentSummary, Agent, Goal, } from "@paperclipai/shared";
|
||||
/**
|
||||
* A scope key identifies the exact location where plugin state is stored.
|
||||
* Scope is partitioned by `scopeKind` and optional `scopeId`.
|
||||
*
|
||||
* Examples:
|
||||
* - `{ scopeKind: "instance" }` — single global value for the whole instance
|
||||
* - `{ scopeKind: "project", scopeId: "proj-uuid" }` — per-project state
|
||||
* - `{ scopeKind: "issue", scopeId: "iss-uuid" }` — per-issue state
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §21.3 `plugin_state`
|
||||
*/
|
||||
export interface ScopeKey {
|
||||
/** What kind of Paperclip object this state is scoped to. */
|
||||
scopeKind: PluginStateScopeKind;
|
||||
/** UUID or text identifier for the scoped object. Omit for `instance` scope. */
|
||||
scopeId?: string;
|
||||
/** Optional sub-namespace within the scope to avoid key collisions. Defaults to `"default"`. */
|
||||
namespace?: string;
|
||||
/** The state key within the namespace. */
|
||||
stateKey: string;
|
||||
}
|
||||
/**
|
||||
* Optional filter applied when subscribing to an event. The host evaluates
|
||||
* the filter server-side so filtered-out events never cross the process boundary.
|
||||
*
|
||||
* All filter fields are optional. If omitted the plugin receives every event
|
||||
* of the subscribed type.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §16.1 — Event Filtering
|
||||
*/
|
||||
export interface EventFilter {
|
||||
/** Only receive events for this project. */
|
||||
projectId?: string;
|
||||
/** Only receive events for this company. */
|
||||
companyId?: string;
|
||||
/** Only receive events for this agent. */
|
||||
agentId?: string;
|
||||
/** Additional arbitrary filter fields. */
|
||||
[key: string]: unknown;
|
||||
}
|
||||
/**
|
||||
* Envelope wrapping every domain event delivered to a plugin worker.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §16 — Event System
|
||||
*/
|
||||
export interface PluginEvent<TPayload = unknown> {
|
||||
/** Unique event identifier (UUID). */
|
||||
eventId: string;
|
||||
/** The event type (e.g. `"issue.created"`). */
|
||||
eventType: PluginEventType | `plugin.${string}`;
|
||||
/** ISO 8601 timestamp when the event occurred. */
|
||||
occurredAt: string;
|
||||
/** ID of the actor that caused the event, if applicable. */
|
||||
actorId?: string;
|
||||
/** Type of actor: `"user"`, `"agent"`, `"system"`, or `"plugin"`. */
|
||||
actorType?: "user" | "agent" | "system" | "plugin";
|
||||
/** Primary entity involved in the event. */
|
||||
entityId?: string;
|
||||
/** Type of the primary entity. */
|
||||
entityType?: string;
|
||||
/** UUID of the company this event belongs to. */
|
||||
companyId: string;
|
||||
/** Typed event payload. */
|
||||
payload: TPayload;
|
||||
}
|
||||
/**
|
||||
* Context passed to a plugin job handler when the host triggers a scheduled run.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §13.6 — `runJob`
|
||||
*/
|
||||
export interface PluginJobContext {
|
||||
/** Stable job key matching the declaration in the manifest. */
|
||||
jobKey: string;
|
||||
/** UUID for this specific job run instance. */
|
||||
runId: string;
|
||||
/** What triggered this run. */
|
||||
trigger: "schedule" | "manual" | "retry";
|
||||
/** ISO 8601 timestamp when the run was scheduled to start. */
|
||||
scheduledAt: string;
|
||||
}
|
||||
/**
|
||||
* Run context passed to a plugin tool handler when an agent invokes the tool.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §13.10 — `executeTool`
|
||||
*/
|
||||
export interface ToolRunContext {
|
||||
/** UUID of the agent invoking the tool. */
|
||||
agentId: string;
|
||||
/** UUID of the current agent run. */
|
||||
runId: string;
|
||||
/** UUID of the company the run belongs to. */
|
||||
companyId: string;
|
||||
/** UUID of the project the run belongs to. */
|
||||
projectId: string;
|
||||
}
|
||||
/**
|
||||
* Result returned from a plugin tool handler.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §13.10 — `executeTool`
|
||||
*/
|
||||
export interface ToolResult {
|
||||
/** String content returned to the agent. Required for success responses. */
|
||||
content?: string;
|
||||
/** Structured data returned alongside or instead of string content. */
|
||||
data?: unknown;
|
||||
/** If present, indicates the tool call failed. */
|
||||
error?: string;
|
||||
}
|
||||
/**
|
||||
* Input for creating or updating a plugin-owned entity.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §21.3 `plugin_entities`
|
||||
*/
|
||||
export interface PluginEntityUpsert {
|
||||
/** Plugin-defined entity type (e.g. `"linear-issue"`, `"github-pr"`). */
|
||||
entityType: string;
|
||||
/** Scope where this entity lives. */
|
||||
scopeKind: PluginStateScopeKind;
|
||||
/** Optional scope ID. */
|
||||
scopeId?: string;
|
||||
/** External identifier in the remote system (e.g. Linear issue ID). */
|
||||
externalId?: string;
|
||||
/** Human-readable title for display in the Paperclip UI. */
|
||||
title?: string;
|
||||
/** Optional status string. */
|
||||
status?: string;
|
||||
/** Full entity data blob. Must be JSON-serializable. */
|
||||
data: Record<string, unknown>;
|
||||
}
|
||||
/**
|
||||
* A plugin-owned entity record as returned by `ctx.entities.list()`.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §21.3 `plugin_entities`
|
||||
*/
|
||||
export interface PluginEntityRecord {
|
||||
/** UUID primary key. */
|
||||
id: string;
|
||||
/** Plugin-defined entity type. */
|
||||
entityType: string;
|
||||
/** Scope kind. */
|
||||
scopeKind: PluginStateScopeKind;
|
||||
/** Scope ID, if any. */
|
||||
scopeId: string | null;
|
||||
/** External identifier, if any. */
|
||||
externalId: string | null;
|
||||
/** Human-readable title. */
|
||||
title: string | null;
|
||||
/** Status string. */
|
||||
status: string | null;
|
||||
/** Full entity data. */
|
||||
data: Record<string, unknown>;
|
||||
/** ISO 8601 creation timestamp. */
|
||||
createdAt: string;
|
||||
/** ISO 8601 last-updated timestamp. */
|
||||
updatedAt: string;
|
||||
}
|
||||
/**
|
||||
* Query parameters for `ctx.entities.list()`.
|
||||
*/
|
||||
export interface PluginEntityQuery {
|
||||
/** Filter by entity type. */
|
||||
entityType?: string;
|
||||
/** Filter by scope kind. */
|
||||
scopeKind?: PluginStateScopeKind;
|
||||
/** Filter by scope ID. */
|
||||
scopeId?: string;
|
||||
/** Filter by external ID. */
|
||||
externalId?: string;
|
||||
/** Maximum number of results to return. */
|
||||
limit?: number;
|
||||
/** Number of results to skip (for pagination). */
|
||||
offset?: number;
|
||||
}
|
||||
/**
|
||||
* Workspace metadata provided by the host. Plugins use this to resolve local
|
||||
* filesystem paths for file browsing, git, terminal, and process operations.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §7 — Project Workspaces
|
||||
* @see PLUGIN_SPEC.md §20 — Local Tooling
|
||||
*/
|
||||
export interface PluginWorkspace {
|
||||
/** UUID primary key. */
|
||||
id: string;
|
||||
/** UUID of the parent project. */
|
||||
projectId: string;
|
||||
/** Display name for this workspace. */
|
||||
name: string;
|
||||
/** Absolute filesystem path to the workspace directory. */
|
||||
path: string;
|
||||
/** Whether this is the project's primary workspace. */
|
||||
isPrimary: boolean;
|
||||
/** ISO 8601 creation timestamp. */
|
||||
createdAt: string;
|
||||
/** ISO 8601 last-updated timestamp. */
|
||||
updatedAt: string;
|
||||
}
|
||||
/**
|
||||
* `ctx.config` — read resolved operator configuration for this plugin.
|
||||
*
|
||||
* Plugin workers receive the resolved config at initialisation. Use `get()`
|
||||
* to access the current configuration at any time. The host calls
|
||||
* `configChanged` on the worker when the operator updates config at runtime.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §13.3 — `validateConfig`
|
||||
* @see PLUGIN_SPEC.md §13.4 — `configChanged`
|
||||
*/
|
||||
export interface PluginConfigClient {
|
||||
/**
|
||||
* Returns the resolved operator configuration for this plugin instance.
|
||||
* Values are validated against the plugin's `instanceConfigSchema` by the
|
||||
* host before being passed to the worker.
|
||||
*/
|
||||
get(): Promise<Record<string, unknown>>;
|
||||
}
|
||||
/**
|
||||
* `ctx.events` — subscribe to and emit Paperclip domain events.
|
||||
*
|
||||
* Requires `events.subscribe` capability for `on()`.
|
||||
* Requires `events.emit` capability for `emit()`.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §16 — Event System
|
||||
*/
|
||||
export interface PluginEventsClient {
|
||||
/**
|
||||
* Subscribe to a core Paperclip domain event or a plugin-namespaced event.
|
||||
*
|
||||
* @param name - Event type, e.g. `"issue.created"` or `"plugin.@acme/linear.sync-done"`
|
||||
* @param fn - Async event handler
|
||||
*/
|
||||
on(name: PluginEventType | `plugin.${string}`, fn: (event: PluginEvent) => Promise<void>): () => void;
|
||||
/**
|
||||
* Subscribe to an event with an optional server-side filter.
|
||||
*
|
||||
* @param name - Event type
|
||||
* @param filter - Server-side filter evaluated before dispatching to the worker
|
||||
* @param fn - Async event handler
|
||||
* @returns An unsubscribe function that removes the handler
|
||||
*/
|
||||
on(name: PluginEventType | `plugin.${string}`, filter: EventFilter, fn: (event: PluginEvent) => Promise<void>): () => void;
|
||||
/**
|
||||
* Emit a plugin-namespaced event. Other plugins with `events.subscribe` can
|
||||
* subscribe to it using `"plugin.<pluginId>.<eventName>"`.
|
||||
*
|
||||
* Requires the `events.emit` capability.
|
||||
*
|
||||
* Plugin-emitted events are automatically namespaced: if the plugin ID is
|
||||
* `"acme.linear"` and the event name is `"sync-done"`, the full event type
|
||||
* becomes `"plugin.acme.linear.sync-done"`.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §16.2 — Plugin-to-Plugin Events
|
||||
*
|
||||
* @param name - Bare event name (e.g. `"sync-done"`)
|
||||
* @param companyId - UUID of the company this event belongs to
|
||||
* @param payload - JSON-serializable event payload
|
||||
*/
|
||||
emit(name: string, companyId: string, payload: unknown): Promise<void>;
|
||||
}
|
||||
/**
|
||||
* `ctx.jobs` — register handlers for scheduled jobs declared in the manifest.
|
||||
*
|
||||
* Requires `jobs.schedule` capability.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §17 — Scheduled Jobs
|
||||
*/
|
||||
export interface PluginJobsClient {
|
||||
/**
|
||||
* Register a handler for a scheduled job.
|
||||
*
|
||||
* The `key` must match a `jobKey` declared in the plugin manifest.
|
||||
* The host calls this handler according to the job's declared `schedule`.
|
||||
*
|
||||
* @param key - Job key matching the manifest declaration
|
||||
* @param fn - Async job handler
|
||||
*/
|
||||
register(key: string, fn: (job: PluginJobContext) => Promise<void>): void;
|
||||
}
|
||||
/**
|
||||
* A runtime launcher registration uses the same declaration shape as a
|
||||
* manifest launcher entry.
|
||||
*/
|
||||
export type PluginLauncherRegistration = PluginLauncherDeclaration;
|
||||
/**
|
||||
* `ctx.launchers` — register launcher declarations at runtime.
|
||||
*/
|
||||
export interface PluginLaunchersClient {
|
||||
/**
|
||||
* Register launcher metadata for host discovery.
|
||||
*
|
||||
* If a launcher with the same id is registered more than once, the latest
|
||||
* declaration replaces the previous one.
|
||||
*/
|
||||
register(launcher: PluginLauncherRegistration): void;
|
||||
}
|
||||
/**
|
||||
* `ctx.http` — make outbound HTTP requests.
|
||||
*
|
||||
* Requires `http.outbound` capability.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §15.1 — Capabilities: Runtime/Integration
|
||||
*/
|
||||
export interface PluginHttpClient {
|
||||
/**
|
||||
* Perform an outbound HTTP request.
|
||||
*
|
||||
* The host enforces `http.outbound` capability before allowing the call.
|
||||
* Plugins may also use standard Node `fetch` or other libraries directly —
|
||||
* this client exists for host-managed tracing and audit logging.
|
||||
*
|
||||
* @param url - Target URL
|
||||
* @param init - Standard `RequestInit` options
|
||||
* @returns The response
|
||||
*/
|
||||
fetch(url: string, init?: RequestInit): Promise<Response>;
|
||||
}
|
||||
/**
|
||||
* `ctx.secrets` — resolve secret references.
|
||||
*
|
||||
* Requires `secrets.read-ref` capability.
|
||||
*
|
||||
* Plugins store secret *references* in their config (e.g. a secret name).
|
||||
* This client resolves the reference through the Paperclip secret provider
|
||||
* system and returns the resolved value at execution time.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §22 — Secrets
|
||||
*/
|
||||
export interface PluginSecretsClient {
|
||||
/**
|
||||
* Resolve a secret reference to its current value.
|
||||
*
|
||||
* The reference is a string identifier pointing to a secret configured
|
||||
* in the Paperclip secret provider (e.g. `"MY_API_KEY"`).
|
||||
*
|
||||
* Secret values are resolved at call time and must never be cached or
|
||||
* written to logs, config, or other persistent storage.
|
||||
*
|
||||
* @param secretRef - The secret reference string from plugin config
|
||||
* @returns The resolved secret value
|
||||
*/
|
||||
resolve(secretRef: string): Promise<string>;
|
||||
}
|
||||
/**
|
||||
* Input for writing a plugin activity log entry.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §21.4 — Activity Log Changes
|
||||
*/
|
||||
export interface PluginActivityLogEntry {
|
||||
/** UUID of the company this activity belongs to. Required for auditing. */
|
||||
companyId: string;
|
||||
/** Human-readable description of the activity. */
|
||||
message: string;
|
||||
/** Optional entity type this activity relates to. */
|
||||
entityType?: string;
|
||||
/** Optional entity ID this activity relates to. */
|
||||
entityId?: string;
|
||||
/** Optional additional metadata. */
|
||||
metadata?: Record<string, unknown>;
|
||||
}
|
||||
/**
|
||||
* `ctx.activity` — write plugin-originated activity log entries.
|
||||
*
|
||||
* Requires `activity.log.write` capability.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §21.4 — Activity Log Changes
|
||||
*/
|
||||
export interface PluginActivityClient {
|
||||
/**
|
||||
* Write an activity log entry attributed to this plugin.
|
||||
*
|
||||
* The host writes the entry with `actor_type = plugin` and
|
||||
* `actor_id = <pluginId>`.
|
||||
*
|
||||
* @param entry - The activity log entry to write
|
||||
*/
|
||||
log(entry: PluginActivityLogEntry): Promise<void>;
|
||||
}
|
||||
/**
|
||||
* `ctx.state` — read and write plugin-scoped key-value state.
|
||||
*
|
||||
* Each plugin gets an isolated namespace: state written by plugin A can never
|
||||
* be read or overwritten by plugin B. Within a plugin, state is partitioned by
|
||||
* a five-part composite key: `(pluginId, scopeKind, scopeId, namespace, stateKey)`.
|
||||
*
|
||||
* **Scope kinds**
|
||||
*
|
||||
* | `scopeKind` | `scopeId` | Typical use |
|
||||
* |-------------|-----------|-------------|
|
||||
* | `"instance"` | omit | Global flags, last full-sync timestamps |
|
||||
* | `"company"` | company UUID | Per-company sync cursors |
|
||||
* | `"project"` | project UUID | Per-project settings, branch tracking |
|
||||
* | `"project_workspace"` | workspace UUID | Per-workspace state |
|
||||
* | `"agent"` | agent UUID | Per-agent memory |
|
||||
* | `"issue"` | issue UUID | Idempotency keys, linked external IDs |
|
||||
* | `"goal"` | goal UUID | Per-goal progress |
|
||||
* | `"run"` | run UUID | Per-run checkpoints |
|
||||
*
|
||||
* **Namespaces**
|
||||
*
|
||||
* The optional `namespace` field (default: `"default"`) lets you group related
|
||||
* keys within a scope without risking collisions between different logical
|
||||
* subsystems inside the same plugin.
|
||||
*
|
||||
* **Security**
|
||||
*
|
||||
* Never store resolved secret values. Store only secret references and resolve
|
||||
* them at call time via `ctx.secrets.resolve()`.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // Instance-global flag
|
||||
* await ctx.state.set({ scopeKind: "instance", stateKey: "schema-version" }, 2);
|
||||
*
|
||||
* // Idempotency key per issue
|
||||
* const synced = await ctx.state.get({ scopeKind: "issue", scopeId: issueId, stateKey: "synced-to-linear" });
|
||||
* if (!synced) {
|
||||
* await syncToLinear(issueId);
|
||||
* await ctx.state.set({ scopeKind: "issue", scopeId: issueId, stateKey: "synced-to-linear" }, true);
|
||||
* }
|
||||
*
|
||||
* // Per-project, namespaced for two integrations
|
||||
* await ctx.state.set({ scopeKind: "project", scopeId: projectId, namespace: "linear", stateKey: "cursor" }, cursor);
|
||||
* await ctx.state.set({ scopeKind: "project", scopeId: projectId, namespace: "github", stateKey: "last-event" }, eventId);
|
||||
* ```
|
||||
*
|
||||
* `plugin.state.read` capability required for `get()`.
|
||||
* `plugin.state.write` capability required for `set()` and `delete()`.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §21.3 `plugin_state`
|
||||
*/
|
||||
export interface PluginStateClient {
|
||||
/**
|
||||
* Read a state value.
|
||||
*
|
||||
* Returns the stored JSON value as-is, or `null` if no entry has been set
|
||||
* for this scope+key combination. Falsy values (`false`, `0`, `""`) are
|
||||
* returned correctly and are not confused with "not set".
|
||||
*
|
||||
* @param input - Scope key identifying the entry to read
|
||||
* @returns The stored JSON value, or `null` if no value has been set
|
||||
*/
|
||||
get(input: ScopeKey): Promise<unknown>;
|
||||
/**
|
||||
* Write a state value. Creates the row if it does not exist; replaces it
|
||||
* atomically (upsert) if it does. Safe to call concurrently.
|
||||
*
|
||||
* Any JSON-serializable value is accepted: objects, arrays, strings,
|
||||
* numbers, booleans, and `null`.
|
||||
*
|
||||
* @param input - Scope key identifying the entry to write
|
||||
* @param value - JSON-serializable value to store
|
||||
*/
|
||||
set(input: ScopeKey, value: unknown): Promise<void>;
|
||||
/**
|
||||
* Delete a state value. No-ops silently if the entry does not exist
|
||||
* (idempotent by design — safe to call without prior `get()`).
|
||||
*
|
||||
* @param input - Scope key identifying the entry to delete
|
||||
*/
|
||||
delete(input: ScopeKey): Promise<void>;
|
||||
}
|
||||
/**
|
||||
* `ctx.entities` — create and query plugin-owned entity records.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §21.3 `plugin_entities`
|
||||
*/
|
||||
export interface PluginEntitiesClient {
|
||||
/**
|
||||
* Create or update a plugin entity record (upsert by `externalId` within
|
||||
* the given scope, or by `id` if provided).
|
||||
*
|
||||
* @param input - Entity data to upsert
|
||||
*/
|
||||
upsert(input: PluginEntityUpsert): Promise<PluginEntityRecord>;
|
||||
/**
|
||||
* Query plugin entity records.
|
||||
*
|
||||
* @param query - Filter criteria
|
||||
* @returns Matching entity records
|
||||
*/
|
||||
list(query: PluginEntityQuery): Promise<PluginEntityRecord[]>;
|
||||
}
|
||||
/**
|
||||
* `ctx.projects` — read project and workspace metadata.
|
||||
*
|
||||
* Requires `projects.read` capability.
|
||||
* Requires `project.workspaces.read` capability for workspace operations.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §7 — Project Workspaces
|
||||
*/
|
||||
export interface PluginProjectsClient {
|
||||
/**
|
||||
* List projects visible to the plugin.
|
||||
*
|
||||
* Requires the `projects.read` capability.
|
||||
*/
|
||||
list(input: {
|
||||
companyId: string;
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
}): Promise<Project[]>;
|
||||
/**
|
||||
* Get a single project by ID.
|
||||
*
|
||||
* Requires the `projects.read` capability.
|
||||
*/
|
||||
get(projectId: string, companyId: string): Promise<Project | null>;
|
||||
/**
|
||||
* List all workspaces attached to a project.
|
||||
*
|
||||
* @param projectId - UUID of the project
|
||||
* @param companyId - UUID of the company that owns the project
|
||||
* @returns All workspaces for the project, ordered with primary first
|
||||
*/
|
||||
listWorkspaces(projectId: string, companyId: string): Promise<PluginWorkspace[]>;
|
||||
/**
|
||||
* Get the primary workspace for a project.
|
||||
*
|
||||
* @param projectId - UUID of the project
|
||||
* @param companyId - UUID of the company that owns the project
|
||||
* @returns The primary workspace, or `null` if no workspace is configured
|
||||
*/
|
||||
getPrimaryWorkspace(projectId: string, companyId: string): Promise<PluginWorkspace | null>;
|
||||
/**
|
||||
* Resolve the primary workspace for an issue by looking up the issue's
|
||||
* project and returning its primary workspace.
|
||||
*
|
||||
* This is a convenience method that combines `issues.get()` and
|
||||
* `getPrimaryWorkspace()` in a single RPC call.
|
||||
*
|
||||
* @param issueId - UUID of the issue
|
||||
* @param companyId - UUID of the company that owns the issue
|
||||
* @returns The primary workspace for the issue's project, or `null` if
|
||||
* the issue has no project or the project has no workspace
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §20 — Local Tooling
|
||||
*/
|
||||
getWorkspaceForIssue(issueId: string, companyId: string): Promise<PluginWorkspace | null>;
|
||||
}
|
||||
/**
|
||||
* `ctx.data` — register `getData` handlers that back `usePluginData()` in the
|
||||
* plugin's frontend components.
|
||||
*
|
||||
* The plugin's UI calls `usePluginData(key, params)` which routes through the
|
||||
* host bridge to the worker's registered handler.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §13.8 — `getData`
|
||||
*/
|
||||
export interface PluginDataClient {
|
||||
/**
|
||||
* Register a handler for a plugin-defined data key.
|
||||
*
|
||||
* @param key - Stable string identifier for this data type (e.g. `"sync-health"`)
|
||||
* @param handler - Async function that receives request params and returns JSON-serializable data
|
||||
*/
|
||||
register(key: string, handler: (params: Record<string, unknown>) => Promise<unknown>): void;
|
||||
}
|
||||
/**
|
||||
* `ctx.actions` — register `performAction` handlers that back
|
||||
* `usePluginAction()` in the plugin's frontend components.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §13.9 — `performAction`
|
||||
*/
|
||||
export interface PluginActionsClient {
|
||||
/**
|
||||
* Register a handler for a plugin-defined action key.
|
||||
*
|
||||
* @param key - Stable string identifier for this action (e.g. `"resync"`)
|
||||
* @param handler - Async function that receives action params and returns a result
|
||||
*/
|
||||
register(key: string, handler: (params: Record<string, unknown>) => Promise<unknown>): void;
|
||||
}
|
||||
/**
|
||||
* `ctx.tools` — register handlers for agent tools declared in the manifest.
|
||||
*
|
||||
* Requires `agent.tools.register` capability.
|
||||
*
|
||||
* Tool names are automatically namespaced by plugin ID at runtime.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §11 — Agent Tools
|
||||
*/
|
||||
export interface PluginToolsClient {
|
||||
/**
|
||||
* Register a handler for a plugin-contributed agent tool.
|
||||
*
|
||||
* @param name - Tool name matching the manifest declaration (without namespace prefix)
|
||||
* @param declaration - Tool metadata (displayName, description, parametersSchema)
|
||||
* @param fn - Async handler that executes the tool
|
||||
*/
|
||||
register(name: string, declaration: Pick<PluginToolDeclaration, "displayName" | "description" | "parametersSchema">, fn: (params: unknown, runCtx: ToolRunContext) => Promise<ToolResult>): void;
|
||||
}
|
||||
/**
|
||||
* `ctx.logger` — structured logging from the plugin worker.
|
||||
*
|
||||
* Log output is captured by the host, stored, and surfaced in the plugin
|
||||
* health dashboard.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §26.1 — Logging
|
||||
*/
|
||||
export interface PluginLogger {
|
||||
/** Log an informational message. */
|
||||
info(message: string, meta?: Record<string, unknown>): void;
|
||||
/** Log a warning. */
|
||||
warn(message: string, meta?: Record<string, unknown>): void;
|
||||
/** Log an error. */
|
||||
error(message: string, meta?: Record<string, unknown>): void;
|
||||
/** Log a debug message (may be suppressed in production). */
|
||||
debug(message: string, meta?: Record<string, unknown>): void;
|
||||
}
|
||||
/**
|
||||
* `ctx.metrics` — write plugin-contributed metrics.
|
||||
*
|
||||
* Requires `metrics.write` capability.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §15.1 — Capabilities: Data Write
|
||||
*/
|
||||
export interface PluginMetricsClient {
|
||||
/**
|
||||
* Write a numeric metric data point.
|
||||
*
|
||||
* @param name - Metric name (plugin-namespaced by the host)
|
||||
* @param value - Numeric value
|
||||
* @param tags - Optional key-value tags for filtering
|
||||
*/
|
||||
write(name: string, value: number, tags?: Record<string, string>): Promise<void>;
|
||||
}
|
||||
/**
|
||||
* `ctx.companies` — read company metadata.
|
||||
*
|
||||
* Requires `companies.read` capability.
|
||||
*/
|
||||
export interface PluginCompaniesClient {
|
||||
/**
|
||||
* List companies visible to this plugin.
|
||||
*/
|
||||
list(input?: {
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
}): Promise<Company[]>;
|
||||
/**
|
||||
* Get one company by ID.
|
||||
*/
|
||||
get(companyId: string): Promise<Company | null>;
|
||||
}
|
||||
/**
|
||||
* `ctx.issues.documents` — read and write issue documents.
|
||||
*
|
||||
* Requires:
|
||||
* - `issue.documents.read` for `list` and `get`
|
||||
* - `issue.documents.write` for `upsert` and `delete`
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §14 — SDK Surface
|
||||
*/
|
||||
export interface PluginIssueDocumentsClient {
|
||||
/**
|
||||
* List all documents attached to an issue.
|
||||
*
|
||||
* Returns summary metadata (id, key, title, format, timestamps) without
|
||||
* the full document body. Use `get()` to fetch a specific document's body.
|
||||
*
|
||||
* Requires the `issue.documents.read` capability.
|
||||
*/
|
||||
list(issueId: string, companyId: string): Promise<IssueDocumentSummary[]>;
|
||||
/**
|
||||
* Get a single document by key, including its full body content.
|
||||
*
|
||||
* Returns `null` if no document exists with the given key.
|
||||
*
|
||||
* Requires the `issue.documents.read` capability.
|
||||
*
|
||||
* @param issueId - UUID of the issue
|
||||
* @param key - Document key (e.g. `"plan"`, `"design-spec"`)
|
||||
* @param companyId - UUID of the company
|
||||
*/
|
||||
get(issueId: string, key: string, companyId: string): Promise<IssueDocument | null>;
|
||||
/**
|
||||
* Create or update a document on an issue.
|
||||
*
|
||||
* If a document with the given key already exists, it is updated and a new
|
||||
* revision is created. If it does not exist, it is created.
|
||||
*
|
||||
* Requires the `issue.documents.write` capability.
|
||||
*
|
||||
* @param input - Document data including issueId, key, body, and optional title/format/changeSummary
|
||||
*/
|
||||
upsert(input: {
|
||||
issueId: string;
|
||||
key: string;
|
||||
body: string;
|
||||
companyId: string;
|
||||
title?: string;
|
||||
format?: string;
|
||||
changeSummary?: string;
|
||||
}): Promise<IssueDocument>;
|
||||
/**
|
||||
* Delete a document and all its revisions.
|
||||
*
|
||||
* No-ops silently if the document does not exist (idempotent).
|
||||
*
|
||||
* Requires the `issue.documents.write` capability.
|
||||
*
|
||||
* @param issueId - UUID of the issue
|
||||
* @param key - Document key to delete
|
||||
* @param companyId - UUID of the company
|
||||
*/
|
||||
delete(issueId: string, key: string, companyId: string): Promise<void>;
|
||||
}
|
||||
/**
|
||||
* `ctx.issues` — read and mutate issues plus comments.
|
||||
*
|
||||
* Requires:
|
||||
* - `issues.read` for read operations
|
||||
* - `issues.create` for create
|
||||
* - `issues.update` for update
|
||||
* - `issue.comments.read` for `listComments`
|
||||
* - `issue.comments.create` for `createComment`
|
||||
* - `issue.documents.read` for `documents.list` and `documents.get`
|
||||
* - `issue.documents.write` for `documents.upsert` and `documents.delete`
|
||||
*/
|
||||
export interface PluginIssuesClient {
|
||||
list(input: {
|
||||
companyId: string;
|
||||
projectId?: string;
|
||||
assigneeAgentId?: string;
|
||||
status?: Issue["status"];
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
}): Promise<Issue[]>;
|
||||
get(issueId: string, companyId: string): Promise<Issue | null>;
|
||||
create(input: {
|
||||
companyId: string;
|
||||
projectId?: string;
|
||||
goalId?: string;
|
||||
parentId?: string;
|
||||
title: string;
|
||||
description?: string;
|
||||
priority?: Issue["priority"];
|
||||
assigneeAgentId?: string;
|
||||
}): Promise<Issue>;
|
||||
update(issueId: string, patch: Partial<Pick<Issue, "title" | "description" | "status" | "priority" | "assigneeAgentId">>, companyId: string): Promise<Issue>;
|
||||
listComments(issueId: string, companyId: string): Promise<IssueComment[]>;
|
||||
createComment(issueId: string, body: string, companyId: string): Promise<IssueComment>;
|
||||
/** Read and write issue documents. Requires `issue.documents.read` / `issue.documents.write`. */
|
||||
documents: PluginIssueDocumentsClient;
|
||||
}
|
||||
/**
|
||||
* `ctx.agents` — read and manage agents.
|
||||
*
|
||||
* Requires `agents.read` for reads; `agents.pause` / `agents.resume` /
|
||||
* `agents.invoke` for write operations.
|
||||
*/
|
||||
export interface PluginAgentsClient {
|
||||
list(input: {
|
||||
companyId: string;
|
||||
status?: Agent["status"];
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
}): Promise<Agent[]>;
|
||||
get(agentId: string, companyId: string): Promise<Agent | null>;
|
||||
/** Pause an agent. Throws if agent is terminated or not found. Requires `agents.pause`. */
|
||||
pause(agentId: string, companyId: string): Promise<Agent>;
|
||||
/** Resume a paused agent (sets status to idle). Throws if terminated, pending_approval, or not found. Requires `agents.resume`. */
|
||||
resume(agentId: string, companyId: string): Promise<Agent>;
|
||||
/** Invoke (wake up) an agent with a prompt payload. Throws if paused, terminated, pending_approval, or not found. Requires `agents.invoke`. */
|
||||
invoke(agentId: string, companyId: string, opts: {
|
||||
prompt: string;
|
||||
reason?: string;
|
||||
}): Promise<{
|
||||
runId: string;
|
||||
}>;
|
||||
/** Create, message, and close agent chat sessions. Requires `agent.sessions.*` capabilities. */
|
||||
sessions: PluginAgentSessionsClient;
|
||||
}
|
||||
/**
|
||||
* Represents an active conversational session with an agent.
|
||||
* Maps to an `AgentTaskSession` row on the host.
|
||||
*/
|
||||
export interface AgentSession {
|
||||
sessionId: string;
|
||||
agentId: string;
|
||||
companyId: string;
|
||||
status: "active" | "closed";
|
||||
createdAt: string;
|
||||
}
|
||||
/**
|
||||
* A streaming event received during a session's `sendMessage` call.
|
||||
* Delivered via JSON-RPC notifications from host to worker.
|
||||
*/
|
||||
export interface AgentSessionEvent {
|
||||
sessionId: string;
|
||||
runId: string;
|
||||
seq: number;
|
||||
/** The kind of event: "chunk" for output data, "status" for run state changes, "done" for end-of-stream, "error" for failures. */
|
||||
eventType: "chunk" | "status" | "done" | "error";
|
||||
stream: "stdout" | "stderr" | "system" | null;
|
||||
message: string | null;
|
||||
payload: Record<string, unknown> | null;
|
||||
}
|
||||
/**
|
||||
* Result of sending a message to a session.
|
||||
*/
|
||||
export interface AgentSessionSendResult {
|
||||
runId: string;
|
||||
}
|
||||
/**
|
||||
* `ctx.agents.sessions` — create, message, and close agent chat sessions.
|
||||
*
|
||||
* Requires `agent.sessions.create` for create, `agent.sessions.list` for list,
|
||||
* `agent.sessions.send` for sendMessage, `agent.sessions.close` for close.
|
||||
*/
|
||||
export interface PluginAgentSessionsClient {
|
||||
/** Create a new conversational session with an agent. Requires `agent.sessions.create`. */
|
||||
create(agentId: string, companyId: string, opts?: {
|
||||
taskKey?: string;
|
||||
reason?: string;
|
||||
}): Promise<AgentSession>;
|
||||
/** List active sessions for an agent owned by this plugin. Requires `agent.sessions.list`. */
|
||||
list(agentId: string, companyId: string): Promise<AgentSession[]>;
|
||||
/**
|
||||
* Send a message to a session and receive streaming events via the `onEvent` callback.
|
||||
* Returns immediately with `{ runId }`. Events are delivered asynchronously.
|
||||
* Requires `agent.sessions.send`.
|
||||
*/
|
||||
sendMessage(sessionId: string, companyId: string, opts: {
|
||||
prompt: string;
|
||||
reason?: string;
|
||||
onEvent?: (event: AgentSessionEvent) => void;
|
||||
}): Promise<AgentSessionSendResult>;
|
||||
/** Close a session, releasing resources. Requires `agent.sessions.close`. */
|
||||
close(sessionId: string, companyId: string): Promise<void>;
|
||||
}
|
||||
/**
|
||||
* `ctx.goals` — read and mutate goals.
|
||||
*
|
||||
* Requires:
|
||||
* - `goals.read` for read operations
|
||||
* - `goals.create` for create
|
||||
* - `goals.update` for update
|
||||
*/
|
||||
export interface PluginGoalsClient {
|
||||
list(input: {
|
||||
companyId: string;
|
||||
level?: Goal["level"];
|
||||
status?: Goal["status"];
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
}): Promise<Goal[]>;
|
||||
get(goalId: string, companyId: string): Promise<Goal | null>;
|
||||
create(input: {
|
||||
companyId: string;
|
||||
title: string;
|
||||
description?: string;
|
||||
level?: Goal["level"];
|
||||
status?: Goal["status"];
|
||||
parentId?: string;
|
||||
ownerAgentId?: string;
|
||||
}): Promise<Goal>;
|
||||
update(goalId: string, patch: Partial<Pick<Goal, "title" | "description" | "level" | "status" | "parentId" | "ownerAgentId">>, companyId: string): Promise<Goal>;
|
||||
}
|
||||
/**
|
||||
* `ctx.streams` — push real-time events from the worker to the plugin UI.
|
||||
*
|
||||
* The worker opens a named channel, emits events on it, and closes it when
|
||||
* done. On the UI side, `usePluginStream(channel)` receives these events in
|
||||
* real time via SSE.
|
||||
*
|
||||
* Streams are scoped to `(pluginId, channel, companyId)`. Multiple UI clients
|
||||
* can subscribe to the same channel concurrently.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // Worker: stream chat tokens to the UI
|
||||
* ctx.streams.open("chat", companyId);
|
||||
* for await (const token of tokenStream) {
|
||||
* ctx.streams.emit("chat", { type: "token", text: token });
|
||||
* }
|
||||
* ctx.streams.close("chat");
|
||||
* ```
|
||||
*
|
||||
* @see usePluginStream in `@paperclipai/plugin-sdk/ui`
|
||||
*/
|
||||
export interface PluginStreamsClient {
|
||||
/**
|
||||
* Open a named stream channel. Optional — `emit()` implicitly opens if needed.
|
||||
* Sends a `stream:open` event to connected UI clients.
|
||||
*/
|
||||
open(channel: string, companyId: string): void;
|
||||
/**
|
||||
* Push an event to all UI clients subscribed to this channel.
|
||||
*
|
||||
* @param channel - Stream channel name (e.g. `"chat"`, `"logs"`)
|
||||
* @param event - JSON-serializable event payload
|
||||
*/
|
||||
emit(channel: string, event: unknown): void;
|
||||
/**
|
||||
* Close a stream channel. Sends a `stream:close` event to connected UI
|
||||
* clients so they know no more events will arrive.
|
||||
*/
|
||||
close(channel: string): void;
|
||||
}
|
||||
/**
|
||||
* The full plugin context object passed to the plugin worker at initialisation.
|
||||
*
|
||||
* This is the central interface plugin authors use to interact with the host.
|
||||
* Every client is capability-gated: calling a client method without the
|
||||
* required capability declared in the manifest results in a runtime error.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import { definePlugin } from "@paperclipai/plugin-sdk";
|
||||
*
|
||||
* export default definePlugin({
|
||||
* async setup(ctx) {
|
||||
* ctx.events.on("issue.created", async (event) => {
|
||||
* ctx.logger.info("Issue created", { issueId: event.entityId });
|
||||
* });
|
||||
*
|
||||
* ctx.data.register("sync-health", async ({ companyId }) => {
|
||||
* const state = await ctx.state.get({ scopeKind: "company", scopeId: String(companyId), stateKey: "last-sync" });
|
||||
* return { lastSync: state };
|
||||
* });
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §14 — SDK Surface
|
||||
*/
|
||||
export interface PluginContext {
|
||||
/** The plugin's manifest as validated at install time. */
|
||||
manifest: PaperclipPluginManifestV1;
|
||||
/** Read resolved operator configuration. */
|
||||
config: PluginConfigClient;
|
||||
/** Subscribe to and emit domain events. Requires `events.subscribe` / `events.emit`. */
|
||||
events: PluginEventsClient;
|
||||
/** Register handlers for scheduled jobs. Requires `jobs.schedule`. */
|
||||
jobs: PluginJobsClient;
|
||||
/** Register launcher metadata that the host can surface in plugin UI entry points. */
|
||||
launchers: PluginLaunchersClient;
|
||||
/** Make outbound HTTP requests. Requires `http.outbound`. */
|
||||
http: PluginHttpClient;
|
||||
/** Resolve secret references. Requires `secrets.read-ref`. */
|
||||
secrets: PluginSecretsClient;
|
||||
/** Write activity log entries. Requires `activity.log.write`. */
|
||||
activity: PluginActivityClient;
|
||||
/** Read and write scoped plugin state. Requires `plugin.state.read` / `plugin.state.write`. */
|
||||
state: PluginStateClient;
|
||||
/** Create and query plugin-owned entity records. */
|
||||
entities: PluginEntitiesClient;
|
||||
/** Read project and workspace metadata. Requires `projects.read` / `project.workspaces.read`. */
|
||||
projects: PluginProjectsClient;
|
||||
/** Read company metadata. Requires `companies.read`. */
|
||||
companies: PluginCompaniesClient;
|
||||
/** Read and write issues, comments, and documents. Requires issue capabilities. */
|
||||
issues: PluginIssuesClient;
|
||||
/** Read and manage agents. Requires `agents.read` for reads; `agents.pause` / `agents.resume` / `agents.invoke` for write ops. */
|
||||
agents: PluginAgentsClient;
|
||||
/** Read and mutate goals. Requires `goals.read` for reads; `goals.create` / `goals.update` for write ops. */
|
||||
goals: PluginGoalsClient;
|
||||
/** Register getData handlers for the plugin's UI components. */
|
||||
data: PluginDataClient;
|
||||
/** Register performAction handlers for the plugin's UI components. */
|
||||
actions: PluginActionsClient;
|
||||
/** Push real-time events from the worker to the plugin UI via SSE. */
|
||||
streams: PluginStreamsClient;
|
||||
/** Register agent tool handlers. Requires `agent.tools.register`. */
|
||||
tools: PluginToolsClient;
|
||||
/** Write plugin metrics. Requires `metrics.write`. */
|
||||
metrics: PluginMetricsClient;
|
||||
/** Structured logger. Output is captured and surfaced in the plugin health dashboard. */
|
||||
logger: PluginLogger;
|
||||
}
|
||||
//# sourceMappingURL=types.d.ts.map
|
||||
1
node_modules/@paperclipai/plugin-sdk/dist/types.d.ts.map
generated
vendored
Normal file
1
node_modules/@paperclipai/plugin-sdk/dist/types.d.ts.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
12
node_modules/@paperclipai/plugin-sdk/dist/types.js
generated
vendored
Normal file
12
node_modules/@paperclipai/plugin-sdk/dist/types.js
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Core types for the Paperclip plugin worker-side SDK.
|
||||
*
|
||||
* These types define the stable public API surface that plugin workers import
|
||||
* from `@paperclipai/plugin-sdk`. The host provides a concrete implementation
|
||||
* of `PluginContext` to the plugin at initialisation time.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §14 — SDK Surface
|
||||
* @see PLUGIN_SPEC.md §29.2 — SDK Versioning
|
||||
*/
|
||||
export {};
|
||||
//# sourceMappingURL=types.js.map
|
||||
1
node_modules/@paperclipai/plugin-sdk/dist/types.js.map
generated
vendored
Normal file
1
node_modules/@paperclipai/plugin-sdk/dist/types.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
|
||||
257
node_modules/@paperclipai/plugin-sdk/dist/ui/components.d.ts
generated
vendored
Normal file
257
node_modules/@paperclipai/plugin-sdk/dist/ui/components.d.ts
generated
vendored
Normal file
@@ -0,0 +1,257 @@
|
||||
/**
|
||||
* Shared UI component declarations for plugin frontends.
|
||||
*
|
||||
* These components are exported from `@paperclipai/plugin-sdk/ui` and are
|
||||
* provided by the host at runtime. They match the host's design tokens and
|
||||
* visual language, reducing the boilerplate needed to build consistent plugin UIs.
|
||||
*
|
||||
* **Plugins are not required to use these components.** They exist to reduce
|
||||
* boilerplate and keep visual consistency. A plugin may render entirely custom
|
||||
* UI using any React component library.
|
||||
*
|
||||
* Component implementations are provided by the host — plugin bundles contain
|
||||
* only the type declarations; the runtime implementations are injected via the
|
||||
* host module registry.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.6 — Shared Components In `@paperclipai/plugin-sdk/ui`
|
||||
*/
|
||||
import type React from "react";
|
||||
/**
|
||||
* A trend value that can accompany a metric.
|
||||
* Positive values indicate upward trends; negative values indicate downward trends.
|
||||
*/
|
||||
export interface MetricTrend {
|
||||
/** Direction of the trend. */
|
||||
direction: "up" | "down" | "flat";
|
||||
/** Percentage change value (e.g. `12.5` for 12.5%). */
|
||||
percentage?: number;
|
||||
}
|
||||
/** Props for `MetricCard`. */
|
||||
export interface MetricCardProps {
|
||||
/** Short label describing the metric (e.g. `"Synced Issues"`). */
|
||||
label: string;
|
||||
/** The metric value to display. */
|
||||
value: number | string;
|
||||
/** Optional trend indicator. */
|
||||
trend?: MetricTrend;
|
||||
/** Optional sparkline data (array of numbers, latest last). */
|
||||
sparkline?: number[];
|
||||
/** Optional unit suffix (e.g. `"%"`, `"ms"`). */
|
||||
unit?: string;
|
||||
}
|
||||
/** Status variants for `StatusBadge`. */
|
||||
export type StatusBadgeVariant = "ok" | "warning" | "error" | "info" | "pending";
|
||||
/** Props for `StatusBadge`. */
|
||||
export interface StatusBadgeProps {
|
||||
/** Human-readable label. */
|
||||
label: string;
|
||||
/** Visual variant determining colour. */
|
||||
status: StatusBadgeVariant;
|
||||
}
|
||||
/** A single column definition for `DataTable`. */
|
||||
export interface DataTableColumn<T = Record<string, unknown>> {
|
||||
/** Column key, matching a field on the row object. */
|
||||
key: keyof T & string;
|
||||
/** Column header label. */
|
||||
header: string;
|
||||
/** Optional custom cell renderer. */
|
||||
render?: (value: unknown, row: T) => React.ReactNode;
|
||||
/** Whether this column is sortable. */
|
||||
sortable?: boolean;
|
||||
/** CSS width (e.g. `"120px"`, `"20%"`). */
|
||||
width?: string;
|
||||
}
|
||||
/** Props for `DataTable`. */
|
||||
export interface DataTableProps<T = Record<string, unknown>> {
|
||||
/** Column definitions. */
|
||||
columns: DataTableColumn<T>[];
|
||||
/** Row data. Each row should have a stable `id` field. */
|
||||
rows: T[];
|
||||
/** Whether the table is currently loading. */
|
||||
loading?: boolean;
|
||||
/** Message shown when `rows` is empty. */
|
||||
emptyMessage?: string;
|
||||
/** Total row count for pagination (if different from `rows.length`). */
|
||||
totalCount?: number;
|
||||
/** Current page (0-based, for pagination). */
|
||||
page?: number;
|
||||
/** Rows per page (for pagination). */
|
||||
pageSize?: number;
|
||||
/** Callback when page changes. */
|
||||
onPageChange?: (page: number) => void;
|
||||
/** Callback when a column header is clicked to sort. */
|
||||
onSort?: (key: string, direction: "asc" | "desc") => void;
|
||||
}
|
||||
/** A single data point for `TimeseriesChart`. */
|
||||
export interface TimeseriesDataPoint {
|
||||
/** ISO 8601 timestamp. */
|
||||
timestamp: string;
|
||||
/** Numeric value. */
|
||||
value: number;
|
||||
/** Optional label for the point. */
|
||||
label?: string;
|
||||
}
|
||||
/** Props for `TimeseriesChart`. */
|
||||
export interface TimeseriesChartProps {
|
||||
/** Series data. */
|
||||
data: TimeseriesDataPoint[];
|
||||
/** Chart title. */
|
||||
title?: string;
|
||||
/** Y-axis label. */
|
||||
yLabel?: string;
|
||||
/** Chart type. Defaults to `"line"`. */
|
||||
type?: "line" | "bar";
|
||||
/** Height of the chart in pixels. Defaults to `200`. */
|
||||
height?: number;
|
||||
/** Whether the chart is currently loading. */
|
||||
loading?: boolean;
|
||||
}
|
||||
/** Props for `MarkdownBlock`. */
|
||||
export interface MarkdownBlockProps {
|
||||
/** Markdown content to render. */
|
||||
content: string;
|
||||
}
|
||||
/** A single key-value pair for `KeyValueList`. */
|
||||
export interface KeyValuePair {
|
||||
/** Label for the key. */
|
||||
label: string;
|
||||
/** Value to display. May be a string, number, or a React node. */
|
||||
value: React.ReactNode;
|
||||
}
|
||||
/** Props for `KeyValueList`. */
|
||||
export interface KeyValueListProps {
|
||||
/** Pairs to render in the list. */
|
||||
pairs: KeyValuePair[];
|
||||
}
|
||||
/** A single action button for `ActionBar`. */
|
||||
export interface ActionBarItem {
|
||||
/** Button label. */
|
||||
label: string;
|
||||
/** Action key to call via the plugin bridge. */
|
||||
actionKey: string;
|
||||
/** Optional parameters to pass to the action handler. */
|
||||
params?: Record<string, unknown>;
|
||||
/** Button variant. Defaults to `"default"`. */
|
||||
variant?: "default" | "primary" | "destructive";
|
||||
/** Whether to show a confirmation dialog before executing. */
|
||||
confirm?: boolean;
|
||||
/** Text for the confirmation dialog (used when `confirm` is true). */
|
||||
confirmMessage?: string;
|
||||
}
|
||||
/** Props for `ActionBar`. */
|
||||
export interface ActionBarProps {
|
||||
/** Action definitions. */
|
||||
actions: ActionBarItem[];
|
||||
/** Called after an action succeeds. Use to trigger data refresh. */
|
||||
onSuccess?: (actionKey: string, result: unknown) => void;
|
||||
/** Called when an action fails. */
|
||||
onError?: (actionKey: string, error: unknown) => void;
|
||||
}
|
||||
/** A single log line for `LogView`. */
|
||||
export interface LogViewEntry {
|
||||
/** ISO 8601 timestamp. */
|
||||
timestamp: string;
|
||||
/** Log level. */
|
||||
level: "info" | "warn" | "error" | "debug";
|
||||
/** Log message. */
|
||||
message: string;
|
||||
/** Optional structured metadata. */
|
||||
meta?: Record<string, unknown>;
|
||||
}
|
||||
/** Props for `LogView`. */
|
||||
export interface LogViewProps {
|
||||
/** Log entries to display. */
|
||||
entries: LogViewEntry[];
|
||||
/** Maximum height of the scrollable container (CSS value). Defaults to `"400px"`. */
|
||||
maxHeight?: string;
|
||||
/** Whether to auto-scroll to the latest entry. */
|
||||
autoScroll?: boolean;
|
||||
/** Whether the log is currently loading. */
|
||||
loading?: boolean;
|
||||
}
|
||||
/** Props for `JsonTree`. */
|
||||
export interface JsonTreeProps {
|
||||
/** The data to render as a collapsible JSON tree. */
|
||||
data: unknown;
|
||||
/** Initial depth to expand. Defaults to `2`. */
|
||||
defaultExpandDepth?: number;
|
||||
}
|
||||
/** Props for `Spinner`. */
|
||||
export interface SpinnerProps {
|
||||
/** Size of the spinner. Defaults to `"md"`. */
|
||||
size?: "sm" | "md" | "lg";
|
||||
/** Accessible label for the spinner (used as `aria-label`). */
|
||||
label?: string;
|
||||
}
|
||||
/** Props for `ErrorBoundary`. */
|
||||
export interface ErrorBoundaryProps {
|
||||
/** Content to render inside the error boundary. */
|
||||
children: React.ReactNode;
|
||||
/** Optional custom fallback to render when an error is caught. */
|
||||
fallback?: React.ReactNode;
|
||||
/** Called when an error is caught, for logging or reporting. */
|
||||
onError?: (error: Error, info: React.ErrorInfo) => void;
|
||||
}
|
||||
export declare const MetricCard: React.ComponentType<MetricCardProps>;
|
||||
/**
|
||||
* Displays an inline status badge (ok / warning / error / info / pending).
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.6 — Shared Components
|
||||
*/
|
||||
export declare const StatusBadge: React.ComponentType<StatusBadgeProps>;
|
||||
/**
|
||||
* Sortable, paginated data table.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.6 — Shared Components
|
||||
*/
|
||||
export declare const DataTable: React.ComponentType<DataTableProps<Record<string, unknown>>>;
|
||||
/**
|
||||
* Line or bar chart for time-series data.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.6 — Shared Components
|
||||
*/
|
||||
export declare const TimeseriesChart: React.ComponentType<TimeseriesChartProps>;
|
||||
/**
|
||||
* Renders Markdown text as HTML.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.6 — Shared Components
|
||||
*/
|
||||
export declare const MarkdownBlock: React.ComponentType<MarkdownBlockProps>;
|
||||
/**
|
||||
* Renders a definition-list of label/value pairs.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.6 — Shared Components
|
||||
*/
|
||||
export declare const KeyValueList: React.ComponentType<KeyValueListProps>;
|
||||
/**
|
||||
* Row of action buttons wired to the plugin bridge's `performAction` handlers.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.6 — Shared Components
|
||||
*/
|
||||
export declare const ActionBar: React.ComponentType<ActionBarProps>;
|
||||
/**
|
||||
* Scrollable, timestamped log output viewer.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.6 — Shared Components
|
||||
*/
|
||||
export declare const LogView: React.ComponentType<LogViewProps>;
|
||||
/**
|
||||
* Collapsible JSON tree for debugging or raw data inspection.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.6 — Shared Components
|
||||
*/
|
||||
export declare const JsonTree: React.ComponentType<JsonTreeProps>;
|
||||
/**
|
||||
* Loading indicator.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.6 — Shared Components
|
||||
*/
|
||||
export declare const Spinner: React.ComponentType<SpinnerProps>;
|
||||
/**
|
||||
* React error boundary that prevents plugin rendering errors from crashing
|
||||
* the host page.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.7 — Error Propagation Through The Bridge
|
||||
*/
|
||||
export declare const ErrorBoundary: React.ComponentType<ErrorBoundaryProps>;
|
||||
//# sourceMappingURL=components.d.ts.map
|
||||
1
node_modules/@paperclipai/plugin-sdk/dist/ui/components.d.ts.map
generated
vendored
Normal file
1
node_modules/@paperclipai/plugin-sdk/dist/ui/components.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"components.d.ts","sourceRoot":"","sources":["../../src/ui/components.ts"],"names":[],"mappings":"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"}
|
||||
97
node_modules/@paperclipai/plugin-sdk/dist/ui/components.js
generated
vendored
Normal file
97
node_modules/@paperclipai/plugin-sdk/dist/ui/components.js
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
/**
|
||||
* Shared UI component declarations for plugin frontends.
|
||||
*
|
||||
* These components are exported from `@paperclipai/plugin-sdk/ui` and are
|
||||
* provided by the host at runtime. They match the host's design tokens and
|
||||
* visual language, reducing the boilerplate needed to build consistent plugin UIs.
|
||||
*
|
||||
* **Plugins are not required to use these components.** They exist to reduce
|
||||
* boilerplate and keep visual consistency. A plugin may render entirely custom
|
||||
* UI using any React component library.
|
||||
*
|
||||
* Component implementations are provided by the host — plugin bundles contain
|
||||
* only the type declarations; the runtime implementations are injected via the
|
||||
* host module registry.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.6 — Shared Components In `@paperclipai/plugin-sdk/ui`
|
||||
*/
|
||||
import { renderSdkUiComponent } from "./runtime.js";
|
||||
// ---------------------------------------------------------------------------
|
||||
// Component declarations (provided by host at runtime)
|
||||
// ---------------------------------------------------------------------------
|
||||
// These are declared as ambient values so plugin TypeScript code can import
|
||||
// and use them with full type-checking. The host's module registry provides
|
||||
// the concrete React component implementations at bundle load time.
|
||||
/**
|
||||
* Displays a single metric with an optional trend indicator and sparkline.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.6 — Shared Components
|
||||
*/
|
||||
function createSdkUiComponent(name) {
|
||||
return function PaperclipSdkUiComponent(props) {
|
||||
return renderSdkUiComponent(name, props);
|
||||
};
|
||||
}
|
||||
export const MetricCard = createSdkUiComponent("MetricCard");
|
||||
/**
|
||||
* Displays an inline status badge (ok / warning / error / info / pending).
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.6 — Shared Components
|
||||
*/
|
||||
export const StatusBadge = createSdkUiComponent("StatusBadge");
|
||||
/**
|
||||
* Sortable, paginated data table.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.6 — Shared Components
|
||||
*/
|
||||
export const DataTable = createSdkUiComponent("DataTable");
|
||||
/**
|
||||
* Line or bar chart for time-series data.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.6 — Shared Components
|
||||
*/
|
||||
export const TimeseriesChart = createSdkUiComponent("TimeseriesChart");
|
||||
/**
|
||||
* Renders Markdown text as HTML.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.6 — Shared Components
|
||||
*/
|
||||
export const MarkdownBlock = createSdkUiComponent("MarkdownBlock");
|
||||
/**
|
||||
* Renders a definition-list of label/value pairs.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.6 — Shared Components
|
||||
*/
|
||||
export const KeyValueList = createSdkUiComponent("KeyValueList");
|
||||
/**
|
||||
* Row of action buttons wired to the plugin bridge's `performAction` handlers.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.6 — Shared Components
|
||||
*/
|
||||
export const ActionBar = createSdkUiComponent("ActionBar");
|
||||
/**
|
||||
* Scrollable, timestamped log output viewer.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.6 — Shared Components
|
||||
*/
|
||||
export const LogView = createSdkUiComponent("LogView");
|
||||
/**
|
||||
* Collapsible JSON tree for debugging or raw data inspection.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.6 — Shared Components
|
||||
*/
|
||||
export const JsonTree = createSdkUiComponent("JsonTree");
|
||||
/**
|
||||
* Loading indicator.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.6 — Shared Components
|
||||
*/
|
||||
export const Spinner = createSdkUiComponent("Spinner");
|
||||
/**
|
||||
* React error boundary that prevents plugin rendering errors from crashing
|
||||
* the host page.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.7 — Error Propagation Through The Bridge
|
||||
*/
|
||||
export const ErrorBoundary = createSdkUiComponent("ErrorBoundary");
|
||||
//# sourceMappingURL=components.js.map
|
||||
1
node_modules/@paperclipai/plugin-sdk/dist/ui/components.js.map
generated
vendored
Normal file
1
node_modules/@paperclipai/plugin-sdk/dist/ui/components.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"components.js","sourceRoot":"","sources":["../../src/ui/components.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAwMpD,8EAA8E;AAC9E,uDAAuD;AACvD,8EAA8E;AAE9E,4EAA4E;AAC5E,4EAA4E;AAC5E,oEAAoE;AAEpE;;;;GAIG;AACH,SAAS,oBAAoB,CAAS,IAAY;IAChD,OAAO,SAAS,uBAAuB,CAAC,KAAa;QACnD,OAAO,oBAAoB,CAAC,IAAI,EAAE,KAAK,CAAoB,CAAC;IAC9D,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,UAAU,GAAG,oBAAoB,CAAkB,YAAY,CAAC,CAAC;AAE9E;;;;GAIG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,oBAAoB,CAAmB,aAAa,CAAC,CAAC;AAEjF;;;;GAIG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,oBAAoB,CAAiB,WAAW,CAAC,CAAC;AAE3E;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,oBAAoB,CAAuB,iBAAiB,CAAC,CAAC;AAE7F;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,oBAAoB,CAAqB,eAAe,CAAC,CAAC;AAEvF;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,oBAAoB,CAAoB,cAAc,CAAC,CAAC;AAEpF;;;;GAIG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,oBAAoB,CAAiB,WAAW,CAAC,CAAC;AAE3E;;;;GAIG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,oBAAoB,CAAe,SAAS,CAAC,CAAC;AAErE;;;;GAIG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,oBAAoB,CAAgB,UAAU,CAAC,CAAC;AAExE;;;;GAIG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,oBAAoB,CAAe,SAAS,CAAC,CAAC;AAErE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,oBAAoB,CAAqB,eAAe,CAAC,CAAC"}
|
||||
120
node_modules/@paperclipai/plugin-sdk/dist/ui/hooks.d.ts
generated
vendored
Normal file
120
node_modules/@paperclipai/plugin-sdk/dist/ui/hooks.d.ts
generated
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
import type { PluginDataResult, PluginActionFn, PluginHostContext, PluginStreamResult, PluginToastFn } from "./types.js";
|
||||
/**
|
||||
* Fetch data from the plugin worker's registered `getData` handler.
|
||||
*
|
||||
* Calls `ctx.data.register(key, handler)` in the worker and returns the
|
||||
* result as reactive state. Re-fetches when `params` changes.
|
||||
*
|
||||
* @template T The expected shape of the returned data
|
||||
* @param key - The data key matching the handler registered with `ctx.data.register()`
|
||||
* @param params - Optional parameters forwarded to the handler
|
||||
* @returns `PluginDataResult<T>` with `data`, `loading`, `error`, and `refresh`
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* function SyncWidget({ context }: PluginWidgetProps) {
|
||||
* const { data, loading, error } = usePluginData<SyncHealth>("sync-health", {
|
||||
* companyId: context.companyId,
|
||||
* });
|
||||
*
|
||||
* if (loading) return <div>Loading…</div>;
|
||||
* if (error) return <div>Error: {error.message}</div>;
|
||||
* return <div>Synced Issues: {data!.syncedCount}</div>;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §13.8 — `getData`
|
||||
* @see PLUGIN_SPEC.md §19.7 — Error Propagation Through The Bridge
|
||||
*/
|
||||
export declare function usePluginData<T = unknown>(key: string, params?: Record<string, unknown>): PluginDataResult<T>;
|
||||
/**
|
||||
* Get a callable function that invokes the plugin worker's registered
|
||||
* `performAction` handler.
|
||||
*
|
||||
* The returned function is async and throws a `PluginBridgeError` on failure.
|
||||
*
|
||||
* @param key - The action key matching the handler registered with `ctx.actions.register()`
|
||||
* @returns An async function that sends the action to the worker and resolves with the result
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* function ResyncButton({ context }: PluginWidgetProps) {
|
||||
* const resync = usePluginAction("resync");
|
||||
* const [error, setError] = useState<string | null>(null);
|
||||
*
|
||||
* async function handleClick() {
|
||||
* try {
|
||||
* await resync({ companyId: context.companyId });
|
||||
* } catch (err) {
|
||||
* setError((err as PluginBridgeError).message);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* return <button onClick={handleClick}>Resync Now</button>;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §13.9 — `performAction`
|
||||
* @see PLUGIN_SPEC.md §19.7 — Error Propagation Through The Bridge
|
||||
*/
|
||||
export declare function usePluginAction(key: string): PluginActionFn;
|
||||
/**
|
||||
* Read the current host context (active company, project, entity, user).
|
||||
*
|
||||
* Use this to know which context the plugin component is being rendered in
|
||||
* so you can scope data requests and actions accordingly.
|
||||
*
|
||||
* @returns The current `PluginHostContext`
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* function IssueTab() {
|
||||
* const { companyId, entityId } = useHostContext();
|
||||
* const { data } = usePluginData("linear-link", { issueId: entityId });
|
||||
* return <div>{data?.linearIssueUrl}</div>;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19 — UI Extension Model
|
||||
*/
|
||||
export declare function useHostContext(): PluginHostContext;
|
||||
/**
|
||||
* Subscribe to a real-time event stream pushed from the plugin worker.
|
||||
*
|
||||
* Opens an SSE connection to `GET /api/plugins/:pluginId/bridge/stream/:channel`
|
||||
* and accumulates events as they arrive. The worker pushes events using
|
||||
* `ctx.streams.emit(channel, event)`.
|
||||
*
|
||||
* @template T The expected shape of each streamed event
|
||||
* @param channel - The stream channel name (must match what the worker uses in `ctx.streams.emit`)
|
||||
* @param options - Optional configuration for the stream
|
||||
* @returns `PluginStreamResult<T>` with `events`, `lastEvent`, connection status, and `close()`
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* function ChatMessages() {
|
||||
* const { events, connected, close } = usePluginStream<ChatToken>("chat-stream");
|
||||
*
|
||||
* return (
|
||||
* <div>
|
||||
* {events.map((e, i) => <span key={i}>{e.text}</span>)}
|
||||
* {connected && <span className="pulse" />}
|
||||
* <button onClick={close}>Stop</button>
|
||||
* </div>
|
||||
* );
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.8 — Real-Time Streaming
|
||||
*/
|
||||
export declare function usePluginStream<T = unknown>(channel: string, options?: {
|
||||
companyId?: string;
|
||||
}): PluginStreamResult<T>;
|
||||
/**
|
||||
* Trigger a host toast notification from plugin UI.
|
||||
*
|
||||
* This lets plugin pages and widgets surface user-facing feedback through the
|
||||
* same toast system as the host app without reaching into host internals.
|
||||
*/
|
||||
export declare function usePluginToast(): PluginToastFn;
|
||||
//# sourceMappingURL=hooks.d.ts.map
|
||||
1
node_modules/@paperclipai/plugin-sdk/dist/ui/hooks.d.ts.map
generated
vendored
Normal file
1
node_modules/@paperclipai/plugin-sdk/dist/ui/hooks.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/ui/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EAClB,aAAa,EACd,MAAM,YAAY,CAAC;AAOpB;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,aAAa,CAAC,CAAC,GAAG,OAAO,EACvC,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,gBAAgB,CAAC,CAAC,CAAC,CAKrB;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAG3D;AAMD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,cAAc,IAAI,iBAAiB,CAGlD;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,eAAe,CAAC,CAAC,GAAG,OAAO,EACzC,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/B,kBAAkB,CAAC,CAAC,CAAC,CAKvB;AAMD;;;;;GAKG;AACH,wBAAgB,cAAc,IAAI,aAAa,CAG9C"}
|
||||
148
node_modules/@paperclipai/plugin-sdk/dist/ui/hooks.js
generated
vendored
Normal file
148
node_modules/@paperclipai/plugin-sdk/dist/ui/hooks.js
generated
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
import { getSdkUiRuntimeValue } from "./runtime.js";
|
||||
// ---------------------------------------------------------------------------
|
||||
// usePluginData
|
||||
// ---------------------------------------------------------------------------
|
||||
/**
|
||||
* Fetch data from the plugin worker's registered `getData` handler.
|
||||
*
|
||||
* Calls `ctx.data.register(key, handler)` in the worker and returns the
|
||||
* result as reactive state. Re-fetches when `params` changes.
|
||||
*
|
||||
* @template T The expected shape of the returned data
|
||||
* @param key - The data key matching the handler registered with `ctx.data.register()`
|
||||
* @param params - Optional parameters forwarded to the handler
|
||||
* @returns `PluginDataResult<T>` with `data`, `loading`, `error`, and `refresh`
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* function SyncWidget({ context }: PluginWidgetProps) {
|
||||
* const { data, loading, error } = usePluginData<SyncHealth>("sync-health", {
|
||||
* companyId: context.companyId,
|
||||
* });
|
||||
*
|
||||
* if (loading) return <div>Loading…</div>;
|
||||
* if (error) return <div>Error: {error.message}</div>;
|
||||
* return <div>Synced Issues: {data!.syncedCount}</div>;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §13.8 — `getData`
|
||||
* @see PLUGIN_SPEC.md §19.7 — Error Propagation Through The Bridge
|
||||
*/
|
||||
export function usePluginData(key, params) {
|
||||
const impl = getSdkUiRuntimeValue("usePluginData");
|
||||
return impl(key, params);
|
||||
}
|
||||
// ---------------------------------------------------------------------------
|
||||
// usePluginAction
|
||||
// ---------------------------------------------------------------------------
|
||||
/**
|
||||
* Get a callable function that invokes the plugin worker's registered
|
||||
* `performAction` handler.
|
||||
*
|
||||
* The returned function is async and throws a `PluginBridgeError` on failure.
|
||||
*
|
||||
* @param key - The action key matching the handler registered with `ctx.actions.register()`
|
||||
* @returns An async function that sends the action to the worker and resolves with the result
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* function ResyncButton({ context }: PluginWidgetProps) {
|
||||
* const resync = usePluginAction("resync");
|
||||
* const [error, setError] = useState<string | null>(null);
|
||||
*
|
||||
* async function handleClick() {
|
||||
* try {
|
||||
* await resync({ companyId: context.companyId });
|
||||
* } catch (err) {
|
||||
* setError((err as PluginBridgeError).message);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* return <button onClick={handleClick}>Resync Now</button>;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §13.9 — `performAction`
|
||||
* @see PLUGIN_SPEC.md §19.7 — Error Propagation Through The Bridge
|
||||
*/
|
||||
export function usePluginAction(key) {
|
||||
const impl = getSdkUiRuntimeValue("usePluginAction");
|
||||
return impl(key);
|
||||
}
|
||||
// ---------------------------------------------------------------------------
|
||||
// useHostContext
|
||||
// ---------------------------------------------------------------------------
|
||||
/**
|
||||
* Read the current host context (active company, project, entity, user).
|
||||
*
|
||||
* Use this to know which context the plugin component is being rendered in
|
||||
* so you can scope data requests and actions accordingly.
|
||||
*
|
||||
* @returns The current `PluginHostContext`
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* function IssueTab() {
|
||||
* const { companyId, entityId } = useHostContext();
|
||||
* const { data } = usePluginData("linear-link", { issueId: entityId });
|
||||
* return <div>{data?.linearIssueUrl}</div>;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19 — UI Extension Model
|
||||
*/
|
||||
export function useHostContext() {
|
||||
const impl = getSdkUiRuntimeValue("useHostContext");
|
||||
return impl();
|
||||
}
|
||||
// ---------------------------------------------------------------------------
|
||||
// usePluginStream
|
||||
// ---------------------------------------------------------------------------
|
||||
/**
|
||||
* Subscribe to a real-time event stream pushed from the plugin worker.
|
||||
*
|
||||
* Opens an SSE connection to `GET /api/plugins/:pluginId/bridge/stream/:channel`
|
||||
* and accumulates events as they arrive. The worker pushes events using
|
||||
* `ctx.streams.emit(channel, event)`.
|
||||
*
|
||||
* @template T The expected shape of each streamed event
|
||||
* @param channel - The stream channel name (must match what the worker uses in `ctx.streams.emit`)
|
||||
* @param options - Optional configuration for the stream
|
||||
* @returns `PluginStreamResult<T>` with `events`, `lastEvent`, connection status, and `close()`
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* function ChatMessages() {
|
||||
* const { events, connected, close } = usePluginStream<ChatToken>("chat-stream");
|
||||
*
|
||||
* return (
|
||||
* <div>
|
||||
* {events.map((e, i) => <span key={i}>{e.text}</span>)}
|
||||
* {connected && <span className="pulse" />}
|
||||
* <button onClick={close}>Stop</button>
|
||||
* </div>
|
||||
* );
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.8 — Real-Time Streaming
|
||||
*/
|
||||
export function usePluginStream(channel, options) {
|
||||
const impl = getSdkUiRuntimeValue("usePluginStream");
|
||||
return impl(channel, options);
|
||||
}
|
||||
// ---------------------------------------------------------------------------
|
||||
// usePluginToast
|
||||
// ---------------------------------------------------------------------------
|
||||
/**
|
||||
* Trigger a host toast notification from plugin UI.
|
||||
*
|
||||
* This lets plugin pages and widgets surface user-facing feedback through the
|
||||
* same toast system as the host app without reaching into host internals.
|
||||
*/
|
||||
export function usePluginToast() {
|
||||
const impl = getSdkUiRuntimeValue("usePluginToast");
|
||||
return impl();
|
||||
}
|
||||
//# sourceMappingURL=hooks.js.map
|
||||
1
node_modules/@paperclipai/plugin-sdk/dist/ui/hooks.js.map
generated
vendored
Normal file
1
node_modules/@paperclipai/plugin-sdk/dist/ui/hooks.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/ui/hooks.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEpD,8EAA8E;AAC9E,gBAAgB;AAChB,8EAA8E;AAE9E;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,UAAU,aAAa,CAC3B,GAAW,EACX,MAAgC;IAEhC,MAAM,IAAI,GAAG,oBAAoB,CAE/B,eAAe,CAAC,CAAC;IACnB,OAAO,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;AAC3B,CAAC;AAED,8EAA8E;AAC9E,kBAAkB;AAClB,8EAA8E;AAE9E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,MAAM,IAAI,GAAG,oBAAoB,CAAsC,iBAAiB,CAAC,CAAC;IAC1F,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;AACnB,CAAC;AAED,8EAA8E;AAC9E,iBAAiB;AACjB,8EAA8E;AAE9E;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,IAAI,GAAG,oBAAoB,CAA0B,gBAAgB,CAAC,CAAC;IAC7E,OAAO,IAAI,EAAE,CAAC;AAChB,CAAC;AAED,8EAA8E;AAC9E,kBAAkB;AAClB,8EAA8E;AAE9E;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,UAAU,eAAe,CAC7B,OAAe,EACf,OAAgC;IAEhC,MAAM,IAAI,GAAG,oBAAoB,CAE/B,iBAAiB,CAAC,CAAC;IACrB,OAAO,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAChC,CAAC;AAED,8EAA8E;AAC9E,iBAAiB;AACjB,8EAA8E;AAE9E;;;;;GAKG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,IAAI,GAAG,oBAAoB,CAAsB,gBAAgB,CAAC,CAAC;IACzE,OAAO,IAAI,EAAE,CAAC;AAChB,CAAC"}
|
||||
50
node_modules/@paperclipai/plugin-sdk/dist/ui/index.d.ts
generated
vendored
Normal file
50
node_modules/@paperclipai/plugin-sdk/dist/ui/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* `@paperclipai/plugin-sdk/ui` — Paperclip plugin UI SDK.
|
||||
*
|
||||
* Import this subpath from plugin UI bundles (React components that run in
|
||||
* the host frontend). Do **not** import this from plugin worker code.
|
||||
*
|
||||
* The worker-side SDK is available from `@paperclipai/plugin-sdk` (root).
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.0.1 — Plugin UI SDK
|
||||
* @see PLUGIN_SPEC.md §29.2 — SDK Versioning
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* // Plugin UI bundle entry (dist/ui/index.tsx)
|
||||
* import { usePluginData, usePluginAction } from "@paperclipai/plugin-sdk/ui";
|
||||
* import type { PluginWidgetProps } from "@paperclipai/plugin-sdk/ui";
|
||||
*
|
||||
* export function DashboardWidget({ context }: PluginWidgetProps) {
|
||||
* const { data, loading, error } = usePluginData("sync-health", {
|
||||
* companyId: context.companyId,
|
||||
* });
|
||||
* const resync = usePluginAction("resync");
|
||||
*
|
||||
* if (loading) return <div>Loading…</div>;
|
||||
* if (error) return <div>Error: {error.message}</div>;
|
||||
*
|
||||
* return (
|
||||
* <div style={{ display: "grid", gap: 8 }}>
|
||||
* <strong>Synced Issues</strong>
|
||||
* <div>{data!.syncedCount}</div>
|
||||
* <button onClick={() => resync({ companyId: context.companyId })}>
|
||||
* Resync Now
|
||||
* </button>
|
||||
* </div>
|
||||
* );
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
/**
|
||||
* Bridge hooks for plugin UI components to communicate with the plugin worker.
|
||||
*
|
||||
* - `usePluginData(key, params)` — fetch data from the worker's `getData` handler
|
||||
* - `usePluginAction(key)` — get a callable that invokes the worker's `performAction` handler
|
||||
* - `useHostContext()` — read the current active company, project, entity, and user IDs
|
||||
* - `usePluginStream(channel)` — subscribe to real-time SSE events from the worker
|
||||
*/
|
||||
export { usePluginData, usePluginAction, useHostContext, usePluginStream, usePluginToast, } from "./hooks.js";
|
||||
export type { PluginBridgeError, PluginBridgeErrorCode, PluginHostContext, PluginModalBoundsRequest, PluginRenderCloseEvent, PluginRenderCloseHandler, PluginRenderCloseLifecycle, PluginRenderEnvironmentContext, PluginLauncherBounds, PluginLauncherRenderEnvironment, PluginDataResult, PluginActionFn, PluginStreamResult, PluginToastTone, PluginToastAction, PluginToastInput, PluginToastFn, } from "./types.js";
|
||||
export type { PluginPageProps, PluginWidgetProps, PluginDetailTabProps, PluginSidebarProps, PluginProjectSidebarItemProps, PluginCommentAnnotationProps, PluginCommentContextMenuItemProps, PluginSettingsPageProps, } from "./types.js";
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
1
node_modules/@paperclipai/plugin-sdk/dist/ui/index.d.ts.map
generated
vendored
Normal file
1
node_modules/@paperclipai/plugin-sdk/dist/ui/index.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH;;;;;;;GAOG;AACH,OAAO,EACL,aAAa,EACb,eAAe,EACf,cAAc,EACd,eAAe,EACf,cAAc,GACf,MAAM,YAAY,CAAC;AAGpB,YAAY,EACV,iBAAiB,EACjB,qBAAqB,EACrB,iBAAiB,EACjB,wBAAwB,EACxB,sBAAsB,EACtB,wBAAwB,EACxB,0BAA0B,EAC1B,8BAA8B,EAC9B,oBAAoB,EACpB,+BAA+B,EAC/B,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,GACd,MAAM,YAAY,CAAC;AAGpB,YAAY,EACV,eAAe,EACf,iBAAiB,EACjB,oBAAoB,EACpB,kBAAkB,EAClB,6BAA6B,EAC7B,4BAA4B,EAC5B,iCAAiC,EACjC,uBAAuB,GACxB,MAAM,YAAY,CAAC"}
|
||||
48
node_modules/@paperclipai/plugin-sdk/dist/ui/index.js
generated
vendored
Normal file
48
node_modules/@paperclipai/plugin-sdk/dist/ui/index.js
generated
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
/**
|
||||
* `@paperclipai/plugin-sdk/ui` — Paperclip plugin UI SDK.
|
||||
*
|
||||
* Import this subpath from plugin UI bundles (React components that run in
|
||||
* the host frontend). Do **not** import this from plugin worker code.
|
||||
*
|
||||
* The worker-side SDK is available from `@paperclipai/plugin-sdk` (root).
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.0.1 — Plugin UI SDK
|
||||
* @see PLUGIN_SPEC.md §29.2 — SDK Versioning
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* // Plugin UI bundle entry (dist/ui/index.tsx)
|
||||
* import { usePluginData, usePluginAction } from "@paperclipai/plugin-sdk/ui";
|
||||
* import type { PluginWidgetProps } from "@paperclipai/plugin-sdk/ui";
|
||||
*
|
||||
* export function DashboardWidget({ context }: PluginWidgetProps) {
|
||||
* const { data, loading, error } = usePluginData("sync-health", {
|
||||
* companyId: context.companyId,
|
||||
* });
|
||||
* const resync = usePluginAction("resync");
|
||||
*
|
||||
* if (loading) return <div>Loading…</div>;
|
||||
* if (error) return <div>Error: {error.message}</div>;
|
||||
*
|
||||
* return (
|
||||
* <div style={{ display: "grid", gap: 8 }}>
|
||||
* <strong>Synced Issues</strong>
|
||||
* <div>{data!.syncedCount}</div>
|
||||
* <button onClick={() => resync({ companyId: context.companyId })}>
|
||||
* Resync Now
|
||||
* </button>
|
||||
* </div>
|
||||
* );
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
/**
|
||||
* Bridge hooks for plugin UI components to communicate with the plugin worker.
|
||||
*
|
||||
* - `usePluginData(key, params)` — fetch data from the worker's `getData` handler
|
||||
* - `usePluginAction(key)` — get a callable that invokes the worker's `performAction` handler
|
||||
* - `useHostContext()` — read the current active company, project, entity, and user IDs
|
||||
* - `usePluginStream(channel)` — subscribe to real-time SSE events from the worker
|
||||
*/
|
||||
export { usePluginData, usePluginAction, useHostContext, usePluginStream, usePluginToast, } from "./hooks.js";
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
node_modules/@paperclipai/plugin-sdk/dist/ui/index.js.map
generated
vendored
Normal file
1
node_modules/@paperclipai/plugin-sdk/dist/ui/index.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/ui/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH;;;;;;;GAOG;AACH,OAAO,EACL,aAAa,EACb,eAAe,EACf,cAAc,EACd,eAAe,EACf,cAAc,GACf,MAAM,YAAY,CAAC"}
|
||||
3
node_modules/@paperclipai/plugin-sdk/dist/ui/runtime.d.ts
generated
vendored
Normal file
3
node_modules/@paperclipai/plugin-sdk/dist/ui/runtime.d.ts
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export declare function getSdkUiRuntimeValue<T>(name: string): T;
|
||||
export declare function renderSdkUiComponent<TProps>(name: string, props: TProps): unknown;
|
||||
//# sourceMappingURL=runtime.d.ts.map
|
||||
1
node_modules/@paperclipai/plugin-sdk/dist/ui/runtime.d.ts.map
generated
vendored
Normal file
1
node_modules/@paperclipai/plugin-sdk/dist/ui/runtime.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/ui/runtime.ts"],"names":[],"mappings":"AAsBA,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,CAMvD;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EACzC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,GACZ,OAAO,CAiBT"}
|
||||
30
node_modules/@paperclipai/plugin-sdk/dist/ui/runtime.js
generated
vendored
Normal file
30
node_modules/@paperclipai/plugin-sdk/dist/ui/runtime.js
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
function getBridgeRegistry() {
|
||||
return globalThis.__paperclipPluginBridge__;
|
||||
}
|
||||
function missingBridgeValueError(name) {
|
||||
return new Error(`Paperclip plugin UI runtime is not initialized for "${name}". ` +
|
||||
'Ensure the host loaded the plugin bridge before rendering this UI module.');
|
||||
}
|
||||
export function getSdkUiRuntimeValue(name) {
|
||||
const value = getBridgeRegistry()?.sdkUi?.[name];
|
||||
if (value === undefined) {
|
||||
throw missingBridgeValueError(name);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
export function renderSdkUiComponent(name, props) {
|
||||
const registry = getBridgeRegistry();
|
||||
const component = registry?.sdkUi?.[name];
|
||||
if (component === undefined) {
|
||||
throw missingBridgeValueError(name);
|
||||
}
|
||||
const createElement = registry?.react?.createElement;
|
||||
if (typeof createElement === "function") {
|
||||
return createElement(component, props);
|
||||
}
|
||||
if (typeof component === "function") {
|
||||
return component(props);
|
||||
}
|
||||
throw new Error(`Paperclip plugin UI component "${name}" is not callable`);
|
||||
}
|
||||
//# sourceMappingURL=runtime.js.map
|
||||
1
node_modules/@paperclipai/plugin-sdk/dist/ui/runtime.js.map
generated
vendored
Normal file
1
node_modules/@paperclipai/plugin-sdk/dist/ui/runtime.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/ui/runtime.ts"],"names":[],"mappings":"AAWA,SAAS,iBAAiB;IACxB,OAAQ,UAA2B,CAAC,yBAAyB,CAAC;AAChE,CAAC;AAED,SAAS,uBAAuB,CAAC,IAAY;IAC3C,OAAO,IAAI,KAAK,CACd,uDAAuD,IAAI,KAAK;QAC9D,2EAA2E,CAC9E,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAI,IAAY;IAClD,MAAM,KAAK,GAAG,iBAAiB,EAAE,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC;IACjD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,MAAM,uBAAuB,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,KAAU,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,oBAAoB,CAClC,IAAY,EACZ,KAAa;IAEb,MAAM,QAAQ,GAAG,iBAAiB,EAAE,CAAC;IACrC,MAAM,SAAS,GAAG,QAAQ,EAAE,KAAK,EAAE,CAAC,IAAI,CAAC,CAAC;IAC1C,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,MAAM,uBAAuB,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC;IAED,MAAM,aAAa,GAAG,QAAQ,EAAE,KAAK,EAAE,aAAa,CAAC;IACrD,IAAI,OAAO,aAAa,KAAK,UAAU,EAAE,CAAC;QACxC,OAAO,aAAa,CAAC,SAAS,EAAE,KAAgC,CAAC,CAAC;IACpE,CAAC;IAED,IAAI,OAAO,SAAS,KAAK,UAAU,EAAE,CAAC;QACpC,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,kCAAkC,IAAI,mBAAmB,CAAC,CAAC;AAC7E,CAAC"}
|
||||
308
node_modules/@paperclipai/plugin-sdk/dist/ui/types.d.ts
generated
vendored
Normal file
308
node_modules/@paperclipai/plugin-sdk/dist/ui/types.d.ts
generated
vendored
Normal file
@@ -0,0 +1,308 @@
|
||||
/**
|
||||
* Paperclip plugin UI SDK — types for plugin frontend components.
|
||||
*
|
||||
* Plugin UI bundles import from `@paperclipai/plugin-sdk/ui`. This subpath
|
||||
* provides the bridge hooks, component prop interfaces, and error types that
|
||||
* plugin React components use to communicate with the host.
|
||||
*
|
||||
* Plugin UI bundles are loaded as ES modules into designated extension slots.
|
||||
* All communication with the plugin worker goes through the host bridge — plugin
|
||||
* components must NOT access host internals or call host APIs directly.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19 — UI Extension Model
|
||||
* @see PLUGIN_SPEC.md §19.0.1 — Plugin UI SDK
|
||||
* @see PLUGIN_SPEC.md §29.2 — SDK Versioning
|
||||
*/
|
||||
import type { PluginBridgeErrorCode } from "@paperclipai/shared";
|
||||
import type { PluginLauncherRenderContextSnapshot, PluginModalBoundsRequest, PluginRenderCloseEvent } from "../protocol.js";
|
||||
export type { PluginBridgeErrorCode, PluginLauncherBounds, PluginLauncherRenderEnvironment, } from "@paperclipai/shared";
|
||||
export type { PluginLauncherRenderContextSnapshot, PluginModalBoundsRequest, PluginRenderCloseEvent, } from "../protocol.js";
|
||||
/**
|
||||
* Structured error returned by the bridge when a UI → worker call fails.
|
||||
*
|
||||
* Plugin components receive this in `usePluginData()` as the `error` field
|
||||
* and may encounter it as a thrown value from `usePluginAction()`.
|
||||
*
|
||||
* Error codes:
|
||||
* - `WORKER_UNAVAILABLE` — plugin worker is not running
|
||||
* - `CAPABILITY_DENIED` — plugin lacks the required capability
|
||||
* - `WORKER_ERROR` — worker returned an error from its handler
|
||||
* - `TIMEOUT` — worker did not respond within the configured timeout
|
||||
* - `UNKNOWN` — unexpected bridge-level failure
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.7 — Error Propagation Through The Bridge
|
||||
*/
|
||||
export interface PluginBridgeError {
|
||||
/** Machine-readable error code. */
|
||||
code: PluginBridgeErrorCode;
|
||||
/** Human-readable error message. */
|
||||
message: string;
|
||||
/**
|
||||
* Original error details from the worker, if available.
|
||||
* Only present when `code === "WORKER_ERROR"`.
|
||||
*/
|
||||
details?: unknown;
|
||||
}
|
||||
/**
|
||||
* Read-only host context passed to every plugin component via `useHostContext()`.
|
||||
*
|
||||
* Plugin components use this to know which company, project, or entity is
|
||||
* currently active so they can scope their data requests accordingly.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19 — UI Extension Model
|
||||
*/
|
||||
export interface PluginHostContext {
|
||||
/** UUID of the currently active company, if any. */
|
||||
companyId: string | null;
|
||||
/** URL prefix for the current company (e.g. `"my-company"`). */
|
||||
companyPrefix: string | null;
|
||||
/** UUID of the currently active project, if any. */
|
||||
projectId: string | null;
|
||||
/** UUID of the current entity (for detail tab contexts), if any. */
|
||||
entityId: string | null;
|
||||
/** Type of the current entity (e.g. `"issue"`, `"agent"`). */
|
||||
entityType: string | null;
|
||||
/**
|
||||
* UUID of the parent entity when rendering nested slots.
|
||||
* For `commentAnnotation` slots this is the issue ID containing the comment.
|
||||
*/
|
||||
parentEntityId?: string | null;
|
||||
/** UUID of the current authenticated user. */
|
||||
userId: string | null;
|
||||
/** Runtime metadata for the host container currently rendering this plugin UI. */
|
||||
renderEnvironment?: PluginRenderEnvironmentContext | null;
|
||||
}
|
||||
/**
|
||||
* Async-capable callback invoked during a host-managed close lifecycle.
|
||||
*/
|
||||
export type PluginRenderCloseHandler = (event: PluginRenderCloseEvent) => void | Promise<void>;
|
||||
/**
|
||||
* Close lifecycle hooks available when the plugin UI is rendered inside a
|
||||
* host-managed launcher environment.
|
||||
*/
|
||||
export interface PluginRenderCloseLifecycle {
|
||||
/** Register a callback before the host closes the current environment. */
|
||||
onBeforeClose?(handler: PluginRenderCloseHandler): () => void;
|
||||
/** Register a callback after the host closes the current environment. */
|
||||
onClose?(handler: PluginRenderCloseHandler): () => void;
|
||||
}
|
||||
/**
|
||||
* Runtime information about the host container currently rendering a plugin UI.
|
||||
*/
|
||||
export interface PluginRenderEnvironmentContext extends PluginLauncherRenderContextSnapshot {
|
||||
/** Optional host callback for requesting new bounds while a modal is open. */
|
||||
requestModalBounds?(request: PluginModalBoundsRequest): Promise<void>;
|
||||
/** Optional close lifecycle callbacks for host-managed overlays. */
|
||||
closeLifecycle?: PluginRenderCloseLifecycle | null;
|
||||
}
|
||||
/**
|
||||
* Props passed to a plugin page component.
|
||||
*
|
||||
* A page is a full-page extension at `/plugins/:pluginId` or `/:company/plugins/:pluginId`.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.1 — Global Operator Routes
|
||||
* @see PLUGIN_SPEC.md §19.2 — Company-Context Routes
|
||||
*/
|
||||
export interface PluginPageProps {
|
||||
/** The current host context. */
|
||||
context: PluginHostContext;
|
||||
}
|
||||
/**
|
||||
* Props passed to a plugin dashboard widget component.
|
||||
*
|
||||
* A dashboard widget is rendered as a card or section on the main dashboard.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.4 — Dashboard Widgets
|
||||
*/
|
||||
export interface PluginWidgetProps {
|
||||
/** The current host context. */
|
||||
context: PluginHostContext;
|
||||
}
|
||||
/**
|
||||
* Props passed to a plugin detail tab component.
|
||||
*
|
||||
* A detail tab is rendered as an additional tab on a project, issue, agent,
|
||||
* goal, or run detail page.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.3 — Detail Tabs
|
||||
*/
|
||||
export interface PluginDetailTabProps {
|
||||
/** The current host context, always including `entityId` and `entityType`. */
|
||||
context: PluginHostContext & {
|
||||
entityId: string;
|
||||
entityType: string;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Props passed to a plugin sidebar component.
|
||||
*
|
||||
* A sidebar entry adds a link or section to the application sidebar.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.5 — Sidebar Entries
|
||||
*/
|
||||
export interface PluginSidebarProps {
|
||||
/** The current host context. */
|
||||
context: PluginHostContext;
|
||||
}
|
||||
/**
|
||||
* Props passed to a plugin project sidebar item component.
|
||||
*
|
||||
* A project sidebar item is rendered **once per project** under that project's
|
||||
* row in the sidebar Projects list. The host passes the current project's id
|
||||
* in `context.entityId` and `context.entityType` is `"project"`.
|
||||
*
|
||||
* Use this slot to add a link (e.g. "Files", "Linear Sync") that navigates to
|
||||
* the project detail with a plugin tab selected: `/projects/:projectRef?tab=plugin:key:slotId`.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.5.1 — Project sidebar items
|
||||
*/
|
||||
export interface PluginProjectSidebarItemProps {
|
||||
/** Host context plus entityId (project id) and entityType "project". */
|
||||
context: PluginHostContext & {
|
||||
entityId: string;
|
||||
entityType: "project";
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Props passed to a plugin comment annotation component.
|
||||
*
|
||||
* A comment annotation is rendered below each individual comment in the
|
||||
* issue detail timeline. The host passes the comment ID as `entityId`
|
||||
* and `"comment"` as `entityType`, plus the parent issue ID as
|
||||
* `parentEntityId` so the plugin can scope data fetches to both.
|
||||
*
|
||||
* Use this slot to augment comments with parsed file links, sentiment
|
||||
* badges, inline actions, or any per-comment metadata.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.6 — Comment Annotations
|
||||
*/
|
||||
export interface PluginCommentAnnotationProps {
|
||||
/** Host context with comment and parent issue identifiers. */
|
||||
context: PluginHostContext & {
|
||||
/** UUID of the comment being annotated. */
|
||||
entityId: string;
|
||||
/** Always `"comment"` for comment annotation slots. */
|
||||
entityType: "comment";
|
||||
/** UUID of the parent issue containing this comment. */
|
||||
parentEntityId: string;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Props passed to a plugin comment context menu item component.
|
||||
*
|
||||
* A comment context menu item is rendered in a "more" dropdown menu on
|
||||
* each comment in the issue detail timeline. The host passes the comment
|
||||
* ID as `entityId` and `"comment"` as `entityType`, plus the parent
|
||||
* issue ID as `parentEntityId`.
|
||||
*
|
||||
* Use this slot to add per-comment actions such as "Create sub-issue from
|
||||
* comment", "Translate", "Flag for review", or any custom plugin action.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.7 — Comment Context Menu Items
|
||||
*/
|
||||
export interface PluginCommentContextMenuItemProps {
|
||||
/** Host context with comment and parent issue identifiers. */
|
||||
context: PluginHostContext & {
|
||||
/** UUID of the comment this menu item acts on. */
|
||||
entityId: string;
|
||||
/** Always `"comment"` for comment context menu item slots. */
|
||||
entityType: "comment";
|
||||
/** UUID of the parent issue containing this comment. */
|
||||
parentEntityId: string;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Props passed to a plugin settings page component.
|
||||
*
|
||||
* Overrides the auto-generated JSON Schema form when the plugin declares
|
||||
* a `settingsPage` UI slot. The component is responsible for reading and
|
||||
* writing config through the bridge.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.8 — Plugin Settings UI
|
||||
*/
|
||||
export interface PluginSettingsPageProps {
|
||||
/** The current host context. */
|
||||
context: PluginHostContext;
|
||||
}
|
||||
/**
|
||||
* Return value of `usePluginData(key, params)`.
|
||||
*
|
||||
* Mirrors a standard async data-fetching hook pattern:
|
||||
* exactly one of `data` or `error` is non-null at any time (unless `loading`).
|
||||
*
|
||||
* @template T The type of the data returned by the worker handler
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.7 — Error Propagation Through The Bridge
|
||||
*/
|
||||
export interface PluginDataResult<T = unknown> {
|
||||
/** The data returned by the worker's `getData` handler. `null` while loading or on error. */
|
||||
data: T | null;
|
||||
/** `true` while the initial request or a refresh is in flight. */
|
||||
loading: boolean;
|
||||
/** Bridge error if the request failed. `null` on success or while loading. */
|
||||
error: PluginBridgeError | null;
|
||||
/**
|
||||
* Manually trigger a data refresh.
|
||||
* Useful for poll-based updates or post-action refreshes.
|
||||
*/
|
||||
refresh(): void;
|
||||
}
|
||||
export type PluginToastTone = "info" | "success" | "warn" | "error";
|
||||
export interface PluginToastAction {
|
||||
label: string;
|
||||
href: string;
|
||||
}
|
||||
export interface PluginToastInput {
|
||||
id?: string;
|
||||
dedupeKey?: string;
|
||||
title: string;
|
||||
body?: string;
|
||||
tone?: PluginToastTone;
|
||||
ttlMs?: number;
|
||||
action?: PluginToastAction;
|
||||
}
|
||||
export type PluginToastFn = (input: PluginToastInput) => string | null;
|
||||
/**
|
||||
* Return value of `usePluginStream<T>(channel)`.
|
||||
*
|
||||
* Provides a growing array of events pushed from the plugin worker via SSE,
|
||||
* plus connection status metadata.
|
||||
*
|
||||
* @template T The type of each event emitted by the worker
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.8 — Real-Time Streaming
|
||||
*/
|
||||
export interface PluginStreamResult<T = unknown> {
|
||||
/** All events received so far, in arrival order. */
|
||||
events: T[];
|
||||
/** The most recently received event, or `null` if none yet. */
|
||||
lastEvent: T | null;
|
||||
/** `true` while the SSE connection is being established. */
|
||||
connecting: boolean;
|
||||
/** `true` once the SSE connection is open and receiving events. */
|
||||
connected: boolean;
|
||||
/** Error if the SSE connection failed or was interrupted. `null` otherwise. */
|
||||
error: Error | null;
|
||||
/** Close the SSE connection and stop receiving events. */
|
||||
close(): void;
|
||||
}
|
||||
/**
|
||||
* Return value of `usePluginAction(key)`.
|
||||
*
|
||||
* Returns an async function that, when called, sends an action request
|
||||
* to the worker's `performAction` handler and returns the result.
|
||||
*
|
||||
* On failure, the async function throws a `PluginBridgeError`.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19.7 — Error Propagation Through The Bridge
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* const resync = usePluginAction("resync");
|
||||
* <button onClick={() => resync({ companyId }).catch(err => console.error(err))}>
|
||||
* Resync Now
|
||||
* </button>
|
||||
* ```
|
||||
*/
|
||||
export type PluginActionFn = (params?: Record<string, unknown>) => Promise<unknown>;
|
||||
//# sourceMappingURL=types.d.ts.map
|
||||
1
node_modules/@paperclipai/plugin-sdk/dist/ui/types.d.ts.map
generated
vendored
Normal file
1
node_modules/@paperclipai/plugin-sdk/dist/ui/types.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/ui/types.ts"],"names":[],"mappings":"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"}
|
||||
17
node_modules/@paperclipai/plugin-sdk/dist/ui/types.js
generated
vendored
Normal file
17
node_modules/@paperclipai/plugin-sdk/dist/ui/types.js
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Paperclip plugin UI SDK — types for plugin frontend components.
|
||||
*
|
||||
* Plugin UI bundles import from `@paperclipai/plugin-sdk/ui`. This subpath
|
||||
* provides the bridge hooks, component prop interfaces, and error types that
|
||||
* plugin React components use to communicate with the host.
|
||||
*
|
||||
* Plugin UI bundles are loaded as ES modules into designated extension slots.
|
||||
* All communication with the plugin worker goes through the host bridge — plugin
|
||||
* components must NOT access host internals or call host APIs directly.
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §19 — UI Extension Model
|
||||
* @see PLUGIN_SPEC.md §19.0.1 — Plugin UI SDK
|
||||
* @see PLUGIN_SPEC.md §29.2 — SDK Versioning
|
||||
*/
|
||||
export {};
|
||||
//# sourceMappingURL=types.js.map
|
||||
1
node_modules/@paperclipai/plugin-sdk/dist/ui/types.js.map
generated
vendored
Normal file
1
node_modules/@paperclipai/plugin-sdk/dist/ui/types.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/ui/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG"}
|
||||
127
node_modules/@paperclipai/plugin-sdk/dist/worker-rpc-host.d.ts
generated
vendored
Normal file
127
node_modules/@paperclipai/plugin-sdk/dist/worker-rpc-host.d.ts
generated
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
/**
|
||||
* Worker-side RPC host — runs inside the child process spawned by the host.
|
||||
*
|
||||
* This module is the worker-side counterpart to the server's
|
||||
* `PluginWorkerManager`. It:
|
||||
*
|
||||
* 1. Reads newline-delimited JSON-RPC 2.0 requests from **stdin**
|
||||
* 2. Dispatches them to the appropriate plugin handler (events, jobs, tools, …)
|
||||
* 3. Writes JSON-RPC 2.0 responses back on **stdout**
|
||||
* 4. Provides a concrete `PluginContext` whose SDK client methods (e.g.
|
||||
* `ctx.state.get()`, `ctx.events.emit()`) send JSON-RPC requests to the
|
||||
* host on stdout and await responses on stdin.
|
||||
*
|
||||
* ## Message flow
|
||||
*
|
||||
* ```
|
||||
* Host (parent) Worker (this module)
|
||||
* | |
|
||||
* |--- request(initialize) -------------> | → calls plugin.setup(ctx)
|
||||
* |<-- response(ok:true) ---------------- |
|
||||
* | |
|
||||
* |--- notification(onEvent) -----------> | → dispatches to registered handler
|
||||
* | |
|
||||
* |<-- request(state.get) --------------- | ← SDK client call from plugin code
|
||||
* |--- response(result) ----------------> |
|
||||
* | |
|
||||
* |--- request(shutdown) ---------------> | → calls plugin.onShutdown()
|
||||
* |<-- response(void) ------------------ |
|
||||
* | (process exits)
|
||||
* ```
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §12 — Process Model
|
||||
* @see PLUGIN_SPEC.md §13 — Host-Worker Protocol
|
||||
* @see PLUGIN_SPEC.md §14 — SDK Surface
|
||||
*/
|
||||
import type { PaperclipPlugin } from "./define-plugin.js";
|
||||
/**
|
||||
* Options for starting the worker-side RPC host.
|
||||
*/
|
||||
export interface WorkerRpcHostOptions {
|
||||
/**
|
||||
* The plugin definition returned by `definePlugin()`.
|
||||
*
|
||||
* The worker entrypoint should import its plugin and pass it here.
|
||||
*/
|
||||
plugin: PaperclipPlugin;
|
||||
/**
|
||||
* Input stream to read JSON-RPC messages from.
|
||||
* Defaults to `process.stdin`.
|
||||
*/
|
||||
stdin?: NodeJS.ReadableStream;
|
||||
/**
|
||||
* Output stream to write JSON-RPC messages to.
|
||||
* Defaults to `process.stdout`.
|
||||
*/
|
||||
stdout?: NodeJS.WritableStream;
|
||||
/**
|
||||
* Default timeout (ms) for worker→host RPC calls.
|
||||
* Defaults to 30 000 ms.
|
||||
*/
|
||||
rpcTimeoutMs?: number;
|
||||
}
|
||||
/**
|
||||
* A running worker RPC host instance.
|
||||
*
|
||||
* Returned by `startWorkerRpcHost()`. Callers (usually just the worker
|
||||
* bootstrap) hold a reference so they can inspect status or force-stop.
|
||||
*/
|
||||
export interface WorkerRpcHost {
|
||||
/** Whether the host is currently running and listening for messages. */
|
||||
readonly running: boolean;
|
||||
/**
|
||||
* Stop the RPC host immediately. Closes readline, rejects pending
|
||||
* outbound calls, and does NOT call the plugin's shutdown hook (that
|
||||
* should have already been called via the `shutdown` RPC method).
|
||||
*/
|
||||
stop(): void;
|
||||
}
|
||||
/**
|
||||
* Options for runWorker when testing (optional stdio to avoid using process streams).
|
||||
* When both stdin and stdout are provided, the "is main module" check is skipped
|
||||
* and the host is started with these streams. Used by tests.
|
||||
*/
|
||||
export interface RunWorkerOptions {
|
||||
stdin?: NodeJS.ReadableStream;
|
||||
stdout?: NodeJS.WritableStream;
|
||||
}
|
||||
/**
|
||||
* Start the worker when this module is the process entrypoint.
|
||||
*
|
||||
* Call this at the bottom of your worker file so that when the host runs
|
||||
* `node dist/worker.js`, the RPC host starts and the process stays alive.
|
||||
* When the module is imported (e.g. for re-exports or tests), nothing runs.
|
||||
*
|
||||
* When `options.stdin` and `options.stdout` are provided (e.g. in tests),
|
||||
* the main-module check is skipped and the host is started with those streams.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const plugin = definePlugin({ ... });
|
||||
* export default plugin;
|
||||
* runWorker(plugin, import.meta.url);
|
||||
* ```
|
||||
*/
|
||||
export declare function runWorker(plugin: PaperclipPlugin, moduleUrl: string, options?: RunWorkerOptions): WorkerRpcHost | void;
|
||||
/**
|
||||
* Start the worker-side RPC host.
|
||||
*
|
||||
* This function is typically called from a thin bootstrap script that is the
|
||||
* actual entrypoint of the child process:
|
||||
*
|
||||
* ```ts
|
||||
* // worker-bootstrap.ts
|
||||
* import plugin from "./worker.js";
|
||||
* import { startWorkerRpcHost } from "@paperclipai/plugin-sdk";
|
||||
*
|
||||
* startWorkerRpcHost({ plugin });
|
||||
* ```
|
||||
*
|
||||
* The host begins listening on stdin immediately. It does NOT call
|
||||
* `plugin.definition.setup()` yet — that happens when the host sends the
|
||||
* `initialize` RPC.
|
||||
*
|
||||
* @returns A handle for inspecting or stopping the RPC host
|
||||
*/
|
||||
export declare function startWorkerRpcHost(options: WorkerRpcHostOptions): WorkerRpcHost;
|
||||
//# sourceMappingURL=worker-rpc-host.d.ts.map
|
||||
1
node_modules/@paperclipai/plugin-sdk/dist/worker-rpc-host.d.ts.map
generated
vendored
Normal file
1
node_modules/@paperclipai/plugin-sdk/dist/worker-rpc-host.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"worker-rpc-host.d.ts","sourceRoot":"","sources":["../src/worker-rpc-host.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAQH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAwD1D;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;OAIG;IACH,MAAM,EAAE,eAAe,CAAC;IAExB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;IAE9B;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;IAE/B;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,wEAAwE;IACxE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAE1B;;;;OAIG;IACH,IAAI,IAAI,IAAI,CAAC;CACd;AAuBD;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;CAChC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,SAAS,CACvB,MAAM,EAAE,eAAe,EACvB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,gBAAgB,GACzB,aAAa,GAAG,IAAI,CAkBtB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,oBAAoB,GAAG,aAAa,CA0+B/E"}
|
||||
941
node_modules/@paperclipai/plugin-sdk/dist/worker-rpc-host.js
generated
vendored
Normal file
941
node_modules/@paperclipai/plugin-sdk/dist/worker-rpc-host.js
generated
vendored
Normal file
@@ -0,0 +1,941 @@
|
||||
/**
|
||||
* Worker-side RPC host — runs inside the child process spawned by the host.
|
||||
*
|
||||
* This module is the worker-side counterpart to the server's
|
||||
* `PluginWorkerManager`. It:
|
||||
*
|
||||
* 1. Reads newline-delimited JSON-RPC 2.0 requests from **stdin**
|
||||
* 2. Dispatches them to the appropriate plugin handler (events, jobs, tools, …)
|
||||
* 3. Writes JSON-RPC 2.0 responses back on **stdout**
|
||||
* 4. Provides a concrete `PluginContext` whose SDK client methods (e.g.
|
||||
* `ctx.state.get()`, `ctx.events.emit()`) send JSON-RPC requests to the
|
||||
* host on stdout and await responses on stdin.
|
||||
*
|
||||
* ## Message flow
|
||||
*
|
||||
* ```
|
||||
* Host (parent) Worker (this module)
|
||||
* | |
|
||||
* |--- request(initialize) -------------> | → calls plugin.setup(ctx)
|
||||
* |<-- response(ok:true) ---------------- |
|
||||
* | |
|
||||
* |--- notification(onEvent) -----------> | → dispatches to registered handler
|
||||
* | |
|
||||
* |<-- request(state.get) --------------- | ← SDK client call from plugin code
|
||||
* |--- response(result) ----------------> |
|
||||
* | |
|
||||
* |--- request(shutdown) ---------------> | → calls plugin.onShutdown()
|
||||
* |<-- response(void) ------------------ |
|
||||
* | (process exits)
|
||||
* ```
|
||||
*
|
||||
* @see PLUGIN_SPEC.md §12 — Process Model
|
||||
* @see PLUGIN_SPEC.md §13 — Host-Worker Protocol
|
||||
* @see PLUGIN_SPEC.md §14 — SDK Surface
|
||||
*/
|
||||
import path from "node:path";
|
||||
import { createInterface } from "node:readline";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { JSONRPC_ERROR_CODES, PLUGIN_RPC_ERROR_CODES, createRequest, createSuccessResponse, createErrorResponse, createNotification, parseMessage, serializeMessage, isJsonRpcRequest, isJsonRpcResponse, isJsonRpcNotification, isJsonRpcSuccessResponse, isJsonRpcErrorResponse, JsonRpcParseError, JsonRpcCallError, } from "./protocol.js";
|
||||
// ---------------------------------------------------------------------------
|
||||
// Constants
|
||||
// ---------------------------------------------------------------------------
|
||||
/** Default timeout for worker→host RPC calls. */
|
||||
const DEFAULT_RPC_TIMEOUT_MS = 30_000;
|
||||
/**
|
||||
* Start the worker when this module is the process entrypoint.
|
||||
*
|
||||
* Call this at the bottom of your worker file so that when the host runs
|
||||
* `node dist/worker.js`, the RPC host starts and the process stays alive.
|
||||
* When the module is imported (e.g. for re-exports or tests), nothing runs.
|
||||
*
|
||||
* When `options.stdin` and `options.stdout` are provided (e.g. in tests),
|
||||
* the main-module check is skipped and the host is started with those streams.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const plugin = definePlugin({ ... });
|
||||
* export default plugin;
|
||||
* runWorker(plugin, import.meta.url);
|
||||
* ```
|
||||
*/
|
||||
export function runWorker(plugin, moduleUrl, options) {
|
||||
if (options?.stdin != null &&
|
||||
options?.stdout != null) {
|
||||
return startWorkerRpcHost({
|
||||
plugin,
|
||||
stdin: options.stdin,
|
||||
stdout: options.stdout,
|
||||
});
|
||||
}
|
||||
const entry = process.argv[1];
|
||||
if (typeof entry !== "string")
|
||||
return;
|
||||
const thisFile = path.resolve(fileURLToPath(moduleUrl));
|
||||
const entryPath = path.resolve(entry);
|
||||
if (thisFile === entryPath) {
|
||||
startWorkerRpcHost({ plugin });
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Start the worker-side RPC host.
|
||||
*
|
||||
* This function is typically called from a thin bootstrap script that is the
|
||||
* actual entrypoint of the child process:
|
||||
*
|
||||
* ```ts
|
||||
* // worker-bootstrap.ts
|
||||
* import plugin from "./worker.js";
|
||||
* import { startWorkerRpcHost } from "@paperclipai/plugin-sdk";
|
||||
*
|
||||
* startWorkerRpcHost({ plugin });
|
||||
* ```
|
||||
*
|
||||
* The host begins listening on stdin immediately. It does NOT call
|
||||
* `plugin.definition.setup()` yet — that happens when the host sends the
|
||||
* `initialize` RPC.
|
||||
*
|
||||
* @returns A handle for inspecting or stopping the RPC host
|
||||
*/
|
||||
export function startWorkerRpcHost(options) {
|
||||
const { plugin } = options;
|
||||
const stdinStream = options.stdin ?? process.stdin;
|
||||
const stdoutStream = options.stdout ?? process.stdout;
|
||||
const rpcTimeoutMs = options.rpcTimeoutMs ?? DEFAULT_RPC_TIMEOUT_MS;
|
||||
// -----------------------------------------------------------------------
|
||||
// State
|
||||
// -----------------------------------------------------------------------
|
||||
let running = true;
|
||||
let initialized = false;
|
||||
let manifest = null;
|
||||
let currentConfig = {};
|
||||
// Plugin handler registrations (populated during setup())
|
||||
const eventHandlers = [];
|
||||
const jobHandlers = new Map();
|
||||
const launcherRegistrations = new Map();
|
||||
const dataHandlers = new Map();
|
||||
const actionHandlers = new Map();
|
||||
const toolHandlers = new Map();
|
||||
// Agent session event callbacks (populated by sendMessage, cleared by close)
|
||||
const sessionEventCallbacks = new Map();
|
||||
// Pending outbound (worker→host) requests
|
||||
const pendingRequests = new Map();
|
||||
let nextOutboundId = 1;
|
||||
const MAX_OUTBOUND_ID = Number.MAX_SAFE_INTEGER - 1;
|
||||
// -----------------------------------------------------------------------
|
||||
// Outbound messaging (worker → host)
|
||||
// -----------------------------------------------------------------------
|
||||
function sendMessage(message) {
|
||||
if (!running)
|
||||
return;
|
||||
const serialized = serializeMessage(message);
|
||||
stdoutStream.write(serialized);
|
||||
}
|
||||
/**
|
||||
* Send a typed JSON-RPC request to the host and await the response.
|
||||
*/
|
||||
function callHost(method, params, timeoutMs) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!running) {
|
||||
reject(new Error(`Cannot call "${method}" — worker RPC host is not running`));
|
||||
return;
|
||||
}
|
||||
if (nextOutboundId >= MAX_OUTBOUND_ID) {
|
||||
nextOutboundId = 1;
|
||||
}
|
||||
const id = nextOutboundId++;
|
||||
const timeout = timeoutMs ?? rpcTimeoutMs;
|
||||
let settled = false;
|
||||
const settle = (fn, value) => {
|
||||
if (settled)
|
||||
return;
|
||||
settled = true;
|
||||
clearTimeout(timer);
|
||||
pendingRequests.delete(id);
|
||||
fn(value);
|
||||
};
|
||||
const timer = setTimeout(() => {
|
||||
settle(reject, new JsonRpcCallError({
|
||||
code: PLUGIN_RPC_ERROR_CODES.TIMEOUT,
|
||||
message: `Worker→host call "${method}" timed out after ${timeout}ms`,
|
||||
}));
|
||||
}, timeout);
|
||||
pendingRequests.set(id, {
|
||||
resolve: (response) => {
|
||||
if (isJsonRpcSuccessResponse(response)) {
|
||||
settle(resolve, response.result);
|
||||
}
|
||||
else if (isJsonRpcErrorResponse(response)) {
|
||||
settle(reject, new JsonRpcCallError(response.error));
|
||||
}
|
||||
else {
|
||||
settle(reject, new Error(`Unexpected response format for "${method}"`));
|
||||
}
|
||||
},
|
||||
timer,
|
||||
});
|
||||
try {
|
||||
const request = createRequest(method, params, id);
|
||||
sendMessage(request);
|
||||
}
|
||||
catch (err) {
|
||||
settle(reject, err instanceof Error ? err : new Error(String(err)));
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Send a JSON-RPC notification to the host (fire-and-forget).
|
||||
*/
|
||||
function notifyHost(method, params) {
|
||||
try {
|
||||
sendMessage(createNotification(method, params));
|
||||
}
|
||||
catch {
|
||||
// Swallow — the host may have closed stdin
|
||||
}
|
||||
}
|
||||
// -----------------------------------------------------------------------
|
||||
// Build the PluginContext (SDK surface for plugin code)
|
||||
// -----------------------------------------------------------------------
|
||||
function buildContext() {
|
||||
return {
|
||||
get manifest() {
|
||||
if (!manifest)
|
||||
throw new Error("Plugin context accessed before initialization");
|
||||
return manifest;
|
||||
},
|
||||
config: {
|
||||
async get() {
|
||||
return callHost("config.get", {});
|
||||
},
|
||||
},
|
||||
events: {
|
||||
on(name, filterOrFn, maybeFn) {
|
||||
let registration;
|
||||
if (typeof filterOrFn === "function") {
|
||||
registration = { name, fn: filterOrFn };
|
||||
}
|
||||
else {
|
||||
if (!maybeFn)
|
||||
throw new Error("Event handler function is required");
|
||||
registration = { name, filter: filterOrFn, fn: maybeFn };
|
||||
}
|
||||
eventHandlers.push(registration);
|
||||
// Register subscription on the host so events are forwarded to this worker
|
||||
void callHost("events.subscribe", { eventPattern: name, filter: registration.filter ?? null }).catch((err) => {
|
||||
notifyHost("log", {
|
||||
level: "warn",
|
||||
message: `Failed to subscribe to event "${name}" on host: ${err instanceof Error ? err.message : String(err)}`,
|
||||
});
|
||||
});
|
||||
return () => {
|
||||
const idx = eventHandlers.indexOf(registration);
|
||||
if (idx !== -1)
|
||||
eventHandlers.splice(idx, 1);
|
||||
};
|
||||
},
|
||||
async emit(name, companyId, payload) {
|
||||
await callHost("events.emit", { name, companyId, payload });
|
||||
},
|
||||
},
|
||||
jobs: {
|
||||
register(key, fn) {
|
||||
jobHandlers.set(key, fn);
|
||||
},
|
||||
},
|
||||
launchers: {
|
||||
register(launcher) {
|
||||
launcherRegistrations.set(launcher.id, launcher);
|
||||
},
|
||||
},
|
||||
http: {
|
||||
async fetch(url, init) {
|
||||
const serializedInit = {};
|
||||
if (init) {
|
||||
if (init.method)
|
||||
serializedInit.method = init.method;
|
||||
if (init.headers) {
|
||||
// Normalize headers to a plain object
|
||||
if (init.headers instanceof Headers) {
|
||||
const obj = {};
|
||||
init.headers.forEach((v, k) => { obj[k] = v; });
|
||||
serializedInit.headers = obj;
|
||||
}
|
||||
else if (Array.isArray(init.headers)) {
|
||||
const obj = {};
|
||||
for (const [k, v] of init.headers)
|
||||
obj[k] = v;
|
||||
serializedInit.headers = obj;
|
||||
}
|
||||
else {
|
||||
serializedInit.headers = init.headers;
|
||||
}
|
||||
}
|
||||
if (init.body !== undefined && init.body !== null) {
|
||||
serializedInit.body = typeof init.body === "string"
|
||||
? init.body
|
||||
: String(init.body);
|
||||
}
|
||||
}
|
||||
const result = await callHost("http.fetch", {
|
||||
url,
|
||||
init: Object.keys(serializedInit).length > 0 ? serializedInit : undefined,
|
||||
});
|
||||
// Reconstruct a Response-like object from the serialized result
|
||||
return new Response(result.body, {
|
||||
status: result.status,
|
||||
statusText: result.statusText,
|
||||
headers: result.headers,
|
||||
});
|
||||
},
|
||||
},
|
||||
secrets: {
|
||||
async resolve(secretRef) {
|
||||
return callHost("secrets.resolve", { secretRef });
|
||||
},
|
||||
},
|
||||
activity: {
|
||||
async log(entry) {
|
||||
await callHost("activity.log", {
|
||||
companyId: entry.companyId,
|
||||
message: entry.message,
|
||||
entityType: entry.entityType,
|
||||
entityId: entry.entityId,
|
||||
metadata: entry.metadata,
|
||||
});
|
||||
},
|
||||
},
|
||||
state: {
|
||||
async get(input) {
|
||||
return callHost("state.get", {
|
||||
scopeKind: input.scopeKind,
|
||||
scopeId: input.scopeId,
|
||||
namespace: input.namespace,
|
||||
stateKey: input.stateKey,
|
||||
});
|
||||
},
|
||||
async set(input, value) {
|
||||
await callHost("state.set", {
|
||||
scopeKind: input.scopeKind,
|
||||
scopeId: input.scopeId,
|
||||
namespace: input.namespace,
|
||||
stateKey: input.stateKey,
|
||||
value,
|
||||
});
|
||||
},
|
||||
async delete(input) {
|
||||
await callHost("state.delete", {
|
||||
scopeKind: input.scopeKind,
|
||||
scopeId: input.scopeId,
|
||||
namespace: input.namespace,
|
||||
stateKey: input.stateKey,
|
||||
});
|
||||
},
|
||||
},
|
||||
entities: {
|
||||
async upsert(input) {
|
||||
return callHost("entities.upsert", {
|
||||
entityType: input.entityType,
|
||||
scopeKind: input.scopeKind,
|
||||
scopeId: input.scopeId,
|
||||
externalId: input.externalId,
|
||||
title: input.title,
|
||||
status: input.status,
|
||||
data: input.data,
|
||||
});
|
||||
},
|
||||
async list(query) {
|
||||
return callHost("entities.list", {
|
||||
entityType: query.entityType,
|
||||
scopeKind: query.scopeKind,
|
||||
scopeId: query.scopeId,
|
||||
externalId: query.externalId,
|
||||
limit: query.limit,
|
||||
offset: query.offset,
|
||||
});
|
||||
},
|
||||
},
|
||||
projects: {
|
||||
async list(input) {
|
||||
return callHost("projects.list", {
|
||||
companyId: input.companyId,
|
||||
limit: input.limit,
|
||||
offset: input.offset,
|
||||
});
|
||||
},
|
||||
async get(projectId, companyId) {
|
||||
return callHost("projects.get", { projectId, companyId });
|
||||
},
|
||||
async listWorkspaces(projectId, companyId) {
|
||||
return callHost("projects.listWorkspaces", { projectId, companyId });
|
||||
},
|
||||
async getPrimaryWorkspace(projectId, companyId) {
|
||||
return callHost("projects.getPrimaryWorkspace", { projectId, companyId });
|
||||
},
|
||||
async getWorkspaceForIssue(issueId, companyId) {
|
||||
return callHost("projects.getWorkspaceForIssue", { issueId, companyId });
|
||||
},
|
||||
},
|
||||
companies: {
|
||||
async list(input) {
|
||||
return callHost("companies.list", {
|
||||
limit: input?.limit,
|
||||
offset: input?.offset,
|
||||
});
|
||||
},
|
||||
async get(companyId) {
|
||||
return callHost("companies.get", { companyId });
|
||||
},
|
||||
},
|
||||
issues: {
|
||||
async list(input) {
|
||||
return callHost("issues.list", {
|
||||
companyId: input.companyId,
|
||||
projectId: input.projectId,
|
||||
assigneeAgentId: input.assigneeAgentId,
|
||||
status: input.status,
|
||||
limit: input.limit,
|
||||
offset: input.offset,
|
||||
});
|
||||
},
|
||||
async get(issueId, companyId) {
|
||||
return callHost("issues.get", { issueId, companyId });
|
||||
},
|
||||
async create(input) {
|
||||
return callHost("issues.create", {
|
||||
companyId: input.companyId,
|
||||
projectId: input.projectId,
|
||||
goalId: input.goalId,
|
||||
parentId: input.parentId,
|
||||
title: input.title,
|
||||
description: input.description,
|
||||
priority: input.priority,
|
||||
assigneeAgentId: input.assigneeAgentId,
|
||||
});
|
||||
},
|
||||
async update(issueId, patch, companyId) {
|
||||
return callHost("issues.update", {
|
||||
issueId,
|
||||
patch: patch,
|
||||
companyId,
|
||||
});
|
||||
},
|
||||
async listComments(issueId, companyId) {
|
||||
return callHost("issues.listComments", { issueId, companyId });
|
||||
},
|
||||
async createComment(issueId, body, companyId) {
|
||||
return callHost("issues.createComment", { issueId, body, companyId });
|
||||
},
|
||||
documents: {
|
||||
async list(issueId, companyId) {
|
||||
return callHost("issues.documents.list", { issueId, companyId });
|
||||
},
|
||||
async get(issueId, key, companyId) {
|
||||
return callHost("issues.documents.get", { issueId, key, companyId });
|
||||
},
|
||||
async upsert(input) {
|
||||
return callHost("issues.documents.upsert", {
|
||||
issueId: input.issueId,
|
||||
key: input.key,
|
||||
body: input.body,
|
||||
companyId: input.companyId,
|
||||
title: input.title,
|
||||
format: input.format,
|
||||
changeSummary: input.changeSummary,
|
||||
});
|
||||
},
|
||||
async delete(issueId, key, companyId) {
|
||||
return callHost("issues.documents.delete", { issueId, key, companyId });
|
||||
},
|
||||
},
|
||||
},
|
||||
agents: {
|
||||
async list(input) {
|
||||
return callHost("agents.list", {
|
||||
companyId: input.companyId,
|
||||
status: input.status,
|
||||
limit: input.limit,
|
||||
offset: input.offset,
|
||||
});
|
||||
},
|
||||
async get(agentId, companyId) {
|
||||
return callHost("agents.get", { agentId, companyId });
|
||||
},
|
||||
async pause(agentId, companyId) {
|
||||
return callHost("agents.pause", { agentId, companyId });
|
||||
},
|
||||
async resume(agentId, companyId) {
|
||||
return callHost("agents.resume", { agentId, companyId });
|
||||
},
|
||||
async invoke(agentId, companyId, opts) {
|
||||
return callHost("agents.invoke", { agentId, companyId, prompt: opts.prompt, reason: opts.reason });
|
||||
},
|
||||
sessions: {
|
||||
async create(agentId, companyId, opts) {
|
||||
return callHost("agents.sessions.create", {
|
||||
agentId,
|
||||
companyId,
|
||||
taskKey: opts?.taskKey,
|
||||
reason: opts?.reason,
|
||||
});
|
||||
},
|
||||
async list(agentId, companyId) {
|
||||
return callHost("agents.sessions.list", { agentId, companyId });
|
||||
},
|
||||
async sendMessage(sessionId, companyId, opts) {
|
||||
if (opts.onEvent) {
|
||||
sessionEventCallbacks.set(sessionId, opts.onEvent);
|
||||
}
|
||||
try {
|
||||
return await callHost("agents.sessions.sendMessage", {
|
||||
sessionId,
|
||||
companyId,
|
||||
prompt: opts.prompt,
|
||||
reason: opts.reason,
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
sessionEventCallbacks.delete(sessionId);
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
async close(sessionId, companyId) {
|
||||
sessionEventCallbacks.delete(sessionId);
|
||||
await callHost("agents.sessions.close", { sessionId, companyId });
|
||||
},
|
||||
},
|
||||
},
|
||||
goals: {
|
||||
async list(input) {
|
||||
return callHost("goals.list", {
|
||||
companyId: input.companyId,
|
||||
level: input.level,
|
||||
status: input.status,
|
||||
limit: input.limit,
|
||||
offset: input.offset,
|
||||
});
|
||||
},
|
||||
async get(goalId, companyId) {
|
||||
return callHost("goals.get", { goalId, companyId });
|
||||
},
|
||||
async create(input) {
|
||||
return callHost("goals.create", {
|
||||
companyId: input.companyId,
|
||||
title: input.title,
|
||||
description: input.description,
|
||||
level: input.level,
|
||||
status: input.status,
|
||||
parentId: input.parentId,
|
||||
ownerAgentId: input.ownerAgentId,
|
||||
});
|
||||
},
|
||||
async update(goalId, patch, companyId) {
|
||||
return callHost("goals.update", {
|
||||
goalId,
|
||||
patch: patch,
|
||||
companyId,
|
||||
});
|
||||
},
|
||||
},
|
||||
data: {
|
||||
register(key, handler) {
|
||||
dataHandlers.set(key, handler);
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
register(key, handler) {
|
||||
actionHandlers.set(key, handler);
|
||||
},
|
||||
},
|
||||
streams: (() => {
|
||||
// Track channel → companyId so emit/close don't require companyId
|
||||
const channelCompanyMap = new Map();
|
||||
return {
|
||||
open(channel, companyId) {
|
||||
channelCompanyMap.set(channel, companyId);
|
||||
notifyHost("streams.open", { channel, companyId });
|
||||
},
|
||||
emit(channel, event) {
|
||||
const companyId = channelCompanyMap.get(channel) ?? "";
|
||||
notifyHost("streams.emit", { channel, companyId, event });
|
||||
},
|
||||
close(channel) {
|
||||
const companyId = channelCompanyMap.get(channel) ?? "";
|
||||
channelCompanyMap.delete(channel);
|
||||
notifyHost("streams.close", { channel, companyId });
|
||||
},
|
||||
};
|
||||
})(),
|
||||
tools: {
|
||||
register(name, declaration, fn) {
|
||||
toolHandlers.set(name, { declaration, fn });
|
||||
},
|
||||
},
|
||||
metrics: {
|
||||
async write(name, value, tags) {
|
||||
await callHost("metrics.write", { name, value, tags });
|
||||
},
|
||||
},
|
||||
logger: {
|
||||
info(message, meta) {
|
||||
notifyHost("log", { level: "info", message, meta });
|
||||
},
|
||||
warn(message, meta) {
|
||||
notifyHost("log", { level: "warn", message, meta });
|
||||
},
|
||||
error(message, meta) {
|
||||
notifyHost("log", { level: "error", message, meta });
|
||||
},
|
||||
debug(message, meta) {
|
||||
notifyHost("log", { level: "debug", message, meta });
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
const ctx = buildContext();
|
||||
// -----------------------------------------------------------------------
|
||||
// Inbound message handling (host → worker)
|
||||
// -----------------------------------------------------------------------
|
||||
/**
|
||||
* Handle an incoming JSON-RPC request from the host.
|
||||
*
|
||||
* Dispatches to the correct handler based on the method name.
|
||||
*/
|
||||
async function handleHostRequest(request) {
|
||||
const { id, method, params } = request;
|
||||
try {
|
||||
const result = await dispatchMethod(method, params);
|
||||
sendMessage(createSuccessResponse(id, result ?? null));
|
||||
}
|
||||
catch (err) {
|
||||
const errorMessage = err instanceof Error ? err.message : String(err);
|
||||
// Propagate specific error codes from handler errors (e.g.
|
||||
// METHOD_NOT_FOUND, METHOD_NOT_IMPLEMENTED) — fall back to
|
||||
// WORKER_ERROR for untyped exceptions.
|
||||
const errorCode = typeof err?.code === "number"
|
||||
? err.code
|
||||
: PLUGIN_RPC_ERROR_CODES.WORKER_ERROR;
|
||||
sendMessage(createErrorResponse(id, errorCode, errorMessage));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Dispatch a host→worker method call to the appropriate handler.
|
||||
*/
|
||||
async function dispatchMethod(method, params) {
|
||||
switch (method) {
|
||||
case "initialize":
|
||||
return handleInitialize(params);
|
||||
case "health":
|
||||
return handleHealth();
|
||||
case "shutdown":
|
||||
return handleShutdown();
|
||||
case "validateConfig":
|
||||
return handleValidateConfig(params);
|
||||
case "configChanged":
|
||||
return handleConfigChanged(params);
|
||||
case "onEvent":
|
||||
return handleOnEvent(params);
|
||||
case "runJob":
|
||||
return handleRunJob(params);
|
||||
case "handleWebhook":
|
||||
return handleWebhook(params);
|
||||
case "getData":
|
||||
return handleGetData(params);
|
||||
case "performAction":
|
||||
return handlePerformAction(params);
|
||||
case "executeTool":
|
||||
return handleExecuteTool(params);
|
||||
default:
|
||||
throw Object.assign(new Error(`Unknown method: ${method}`), { code: JSONRPC_ERROR_CODES.METHOD_NOT_FOUND });
|
||||
}
|
||||
}
|
||||
// -----------------------------------------------------------------------
|
||||
// Host→Worker method handlers
|
||||
// -----------------------------------------------------------------------
|
||||
async function handleInitialize(params) {
|
||||
if (initialized) {
|
||||
throw new Error("Worker already initialized");
|
||||
}
|
||||
manifest = params.manifest;
|
||||
currentConfig = params.config;
|
||||
// Call the plugin's setup function
|
||||
await plugin.definition.setup(ctx);
|
||||
initialized = true;
|
||||
// Report which optional methods this plugin implements
|
||||
const supportedMethods = [];
|
||||
if (plugin.definition.onValidateConfig)
|
||||
supportedMethods.push("validateConfig");
|
||||
if (plugin.definition.onConfigChanged)
|
||||
supportedMethods.push("configChanged");
|
||||
if (plugin.definition.onHealth)
|
||||
supportedMethods.push("health");
|
||||
if (plugin.definition.onShutdown)
|
||||
supportedMethods.push("shutdown");
|
||||
return { ok: true, supportedMethods };
|
||||
}
|
||||
async function handleHealth() {
|
||||
if (plugin.definition.onHealth) {
|
||||
return plugin.definition.onHealth();
|
||||
}
|
||||
// Default: report OK if the worker is alive
|
||||
return { status: "ok" };
|
||||
}
|
||||
async function handleShutdown() {
|
||||
if (plugin.definition.onShutdown) {
|
||||
await plugin.definition.onShutdown();
|
||||
}
|
||||
// Schedule cleanup after we send the response.
|
||||
// Use setImmediate to let the response flush before exiting.
|
||||
// Only call process.exit() when running with real process streams.
|
||||
// When custom streams are provided (tests), just clean up.
|
||||
setImmediate(() => {
|
||||
cleanup();
|
||||
if (!options.stdin && !options.stdout) {
|
||||
process.exit(0);
|
||||
}
|
||||
});
|
||||
}
|
||||
async function handleValidateConfig(params) {
|
||||
if (!plugin.definition.onValidateConfig) {
|
||||
throw Object.assign(new Error("validateConfig is not implemented by this plugin"), { code: PLUGIN_RPC_ERROR_CODES.METHOD_NOT_IMPLEMENTED });
|
||||
}
|
||||
return plugin.definition.onValidateConfig(params.config);
|
||||
}
|
||||
async function handleConfigChanged(params) {
|
||||
currentConfig = params.config;
|
||||
if (plugin.definition.onConfigChanged) {
|
||||
await plugin.definition.onConfigChanged(params.config);
|
||||
}
|
||||
}
|
||||
async function handleOnEvent(params) {
|
||||
const event = params.event;
|
||||
for (const registration of eventHandlers) {
|
||||
// Check event type match
|
||||
const exactMatch = registration.name === event.eventType;
|
||||
const wildcardPluginAll = registration.name === "plugin.*" &&
|
||||
event.eventType.startsWith("plugin.");
|
||||
const wildcardPluginOne = registration.name.endsWith(".*") &&
|
||||
event.eventType.startsWith(registration.name.slice(0, -1));
|
||||
if (!exactMatch && !wildcardPluginAll && !wildcardPluginOne)
|
||||
continue;
|
||||
// Check filter
|
||||
if (registration.filter && !allowsEvent(registration.filter, event))
|
||||
continue;
|
||||
try {
|
||||
await registration.fn(event);
|
||||
}
|
||||
catch (err) {
|
||||
// Log error but continue processing other handlers so one failing
|
||||
// handler doesn't prevent the rest from running.
|
||||
notifyHost("log", {
|
||||
level: "error",
|
||||
message: `Event handler for "${registration.name}" failed: ${err instanceof Error ? err.message : String(err)}`,
|
||||
meta: { eventType: event.eventType, stack: err instanceof Error ? err.stack : undefined },
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
async function handleRunJob(params) {
|
||||
const handler = jobHandlers.get(params.job.jobKey);
|
||||
if (!handler) {
|
||||
throw new Error(`No handler registered for job "${params.job.jobKey}"`);
|
||||
}
|
||||
await handler(params.job);
|
||||
}
|
||||
async function handleWebhook(params) {
|
||||
if (!plugin.definition.onWebhook) {
|
||||
throw Object.assign(new Error("handleWebhook is not implemented by this plugin"), { code: PLUGIN_RPC_ERROR_CODES.METHOD_NOT_IMPLEMENTED });
|
||||
}
|
||||
await plugin.definition.onWebhook(params);
|
||||
}
|
||||
async function handleGetData(params) {
|
||||
const handler = dataHandlers.get(params.key);
|
||||
if (!handler) {
|
||||
throw new Error(`No data handler registered for key "${params.key}"`);
|
||||
}
|
||||
return handler(params.renderEnvironment === undefined
|
||||
? params.params
|
||||
: { ...params.params, renderEnvironment: params.renderEnvironment });
|
||||
}
|
||||
async function handlePerformAction(params) {
|
||||
const handler = actionHandlers.get(params.key);
|
||||
if (!handler) {
|
||||
throw new Error(`No action handler registered for key "${params.key}"`);
|
||||
}
|
||||
return handler(params.renderEnvironment === undefined
|
||||
? params.params
|
||||
: { ...params.params, renderEnvironment: params.renderEnvironment });
|
||||
}
|
||||
async function handleExecuteTool(params) {
|
||||
const entry = toolHandlers.get(params.toolName);
|
||||
if (!entry) {
|
||||
throw new Error(`No tool handler registered for "${params.toolName}"`);
|
||||
}
|
||||
return entry.fn(params.parameters, params.runContext);
|
||||
}
|
||||
// -----------------------------------------------------------------------
|
||||
// Event filter helper
|
||||
// -----------------------------------------------------------------------
|
||||
function allowsEvent(filter, event) {
|
||||
const payload = event.payload;
|
||||
if (filter.companyId !== undefined) {
|
||||
const companyId = event.companyId ?? String(payload?.companyId ?? "");
|
||||
if (companyId !== filter.companyId)
|
||||
return false;
|
||||
}
|
||||
if (filter.projectId !== undefined) {
|
||||
const projectId = event.entityType === "project"
|
||||
? event.entityId
|
||||
: String(payload?.projectId ?? "");
|
||||
if (projectId !== filter.projectId)
|
||||
return false;
|
||||
}
|
||||
if (filter.agentId !== undefined) {
|
||||
const agentId = event.entityType === "agent"
|
||||
? event.entityId
|
||||
: String(payload?.agentId ?? "");
|
||||
if (agentId !== filter.agentId)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// -----------------------------------------------------------------------
|
||||
// Inbound response handling (host → worker, response to our outbound call)
|
||||
// -----------------------------------------------------------------------
|
||||
function handleHostResponse(response) {
|
||||
const id = response.id;
|
||||
if (id === null || id === undefined)
|
||||
return;
|
||||
const pending = pendingRequests.get(id);
|
||||
if (!pending)
|
||||
return;
|
||||
clearTimeout(pending.timer);
|
||||
pendingRequests.delete(id);
|
||||
pending.resolve(response);
|
||||
}
|
||||
// -----------------------------------------------------------------------
|
||||
// Incoming line handler
|
||||
// -----------------------------------------------------------------------
|
||||
function handleLine(line) {
|
||||
if (!line.trim())
|
||||
return;
|
||||
let message;
|
||||
try {
|
||||
message = parseMessage(line);
|
||||
}
|
||||
catch (err) {
|
||||
if (err instanceof JsonRpcParseError) {
|
||||
// Send parse error response
|
||||
sendMessage(createErrorResponse(null, JSONRPC_ERROR_CODES.PARSE_ERROR, `Parse error: ${err.message}`));
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (isJsonRpcResponse(message)) {
|
||||
// This is a response to one of our outbound worker→host calls
|
||||
handleHostResponse(message);
|
||||
}
|
||||
else if (isJsonRpcRequest(message)) {
|
||||
// This is a host→worker RPC call — dispatch it
|
||||
handleHostRequest(message).catch((err) => {
|
||||
// Unhandled error in the async handler — send error response
|
||||
const errorMessage = err instanceof Error ? err.message : String(err);
|
||||
const errorCode = err?.code ?? PLUGIN_RPC_ERROR_CODES.WORKER_ERROR;
|
||||
try {
|
||||
sendMessage(createErrorResponse(message.id, typeof errorCode === "number" ? errorCode : PLUGIN_RPC_ERROR_CODES.WORKER_ERROR, errorMessage));
|
||||
}
|
||||
catch {
|
||||
// Cannot send response, stdout may be closed
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (isJsonRpcNotification(message)) {
|
||||
// Dispatch host→worker push notifications
|
||||
const notif = message;
|
||||
if (notif.method === "agents.sessions.event" && notif.params) {
|
||||
const event = notif.params;
|
||||
const cb = sessionEventCallbacks.get(event.sessionId);
|
||||
if (cb)
|
||||
cb(event);
|
||||
}
|
||||
else if (notif.method === "onEvent" && notif.params) {
|
||||
// Plugin event bus notifications — dispatch to registered event handlers
|
||||
handleOnEvent(notif.params).catch((err) => {
|
||||
notifyHost("log", {
|
||||
level: "error",
|
||||
message: `Failed to handle event notification: ${err instanceof Error ? err.message : String(err)}`,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// -----------------------------------------------------------------------
|
||||
// Cleanup
|
||||
// -----------------------------------------------------------------------
|
||||
function cleanup() {
|
||||
running = false;
|
||||
// Close readline
|
||||
if (readline) {
|
||||
readline.close();
|
||||
readline = null;
|
||||
}
|
||||
// Reject all pending outbound calls
|
||||
for (const [id, pending] of pendingRequests) {
|
||||
clearTimeout(pending.timer);
|
||||
pending.resolve(createErrorResponse(id, PLUGIN_RPC_ERROR_CODES.WORKER_UNAVAILABLE, "Worker RPC host is shutting down"));
|
||||
}
|
||||
pendingRequests.clear();
|
||||
sessionEventCallbacks.clear();
|
||||
}
|
||||
// -----------------------------------------------------------------------
|
||||
// Bootstrap: wire up stdin readline
|
||||
// -----------------------------------------------------------------------
|
||||
let readline = createInterface({
|
||||
input: stdinStream,
|
||||
crlfDelay: Infinity,
|
||||
});
|
||||
readline.on("line", handleLine);
|
||||
// If stdin closes, we should exit gracefully
|
||||
readline.on("close", () => {
|
||||
if (running) {
|
||||
cleanup();
|
||||
if (!options.stdin && !options.stdout) {
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
// Handle uncaught errors in the worker process.
|
||||
// Only install these when using the real process streams (not in tests
|
||||
// where the caller provides custom streams).
|
||||
if (!options.stdin && !options.stdout) {
|
||||
process.on("uncaughtException", (err) => {
|
||||
notifyHost("log", {
|
||||
level: "error",
|
||||
message: `Uncaught exception: ${err.message}`,
|
||||
meta: { stack: err.stack },
|
||||
});
|
||||
// Give the notification a moment to flush, then exit
|
||||
setTimeout(() => process.exit(1), 100);
|
||||
});
|
||||
process.on("unhandledRejection", (reason) => {
|
||||
const message = reason instanceof Error ? reason.message : String(reason);
|
||||
const stack = reason instanceof Error ? reason.stack : undefined;
|
||||
notifyHost("log", {
|
||||
level: "error",
|
||||
message: `Unhandled rejection: ${message}`,
|
||||
meta: { stack },
|
||||
});
|
||||
});
|
||||
}
|
||||
// -----------------------------------------------------------------------
|
||||
// Return the handle
|
||||
// -----------------------------------------------------------------------
|
||||
return {
|
||||
get running() {
|
||||
return running;
|
||||
},
|
||||
stop() {
|
||||
cleanup();
|
||||
},
|
||||
};
|
||||
}
|
||||
//# sourceMappingURL=worker-rpc-host.js.map
|
||||
1
node_modules/@paperclipai/plugin-sdk/dist/worker-rpc-host.js.map
generated
vendored
Normal file
1
node_modules/@paperclipai/plugin-sdk/dist/worker-rpc-host.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user