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:
103
dist/legal-api.d.ts
vendored
Normal file
103
dist/legal-api.d.ts
vendored
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
/**
|
||||||
|
* HTTP client for Ezer Mishpati legal-ai REST API.
|
||||||
|
*/
|
||||||
|
export declare class LegalApi {
|
||||||
|
private baseUrl;
|
||||||
|
constructor(baseUrl: string);
|
||||||
|
private request;
|
||||||
|
listCases(): Promise<CaseSummary[]>;
|
||||||
|
getCase(caseNumber: string): Promise<CaseDetails>;
|
||||||
|
createCase(data: CaseCreateInput): Promise<CaseDetails>;
|
||||||
|
updateCase(caseNumber: string, data: CaseUpdateInput): Promise<CaseDetails>;
|
||||||
|
getCaseStatus(caseNumber: string): Promise<WorkflowStatus>;
|
||||||
|
search(query: string, limit?: number, sectionType?: string): Promise<SearchResult[]>;
|
||||||
|
searchCase(caseNumber: string, query: string, limit?: number): Promise<SearchResult[]>;
|
||||||
|
getTemplate(caseNumber: string): Promise<{
|
||||||
|
template: string;
|
||||||
|
}>;
|
||||||
|
getProcessingStatus(): Promise<ProcessingStatus>;
|
||||||
|
health(): Promise<{
|
||||||
|
status: string;
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
export interface CaseSummary {
|
||||||
|
case_number: string;
|
||||||
|
title: string;
|
||||||
|
status: string;
|
||||||
|
}
|
||||||
|
export interface CaseDetails {
|
||||||
|
id: string;
|
||||||
|
case_number: string;
|
||||||
|
title: string;
|
||||||
|
status: string;
|
||||||
|
appellants: string[];
|
||||||
|
respondents: string[];
|
||||||
|
subject: string;
|
||||||
|
property_address: string;
|
||||||
|
expected_outcome: string;
|
||||||
|
documents?: DocumentInfo[];
|
||||||
|
[key: string]: unknown;
|
||||||
|
}
|
||||||
|
export interface CaseCreateInput {
|
||||||
|
case_number: string;
|
||||||
|
title: string;
|
||||||
|
appellants?: string[];
|
||||||
|
respondents?: string[];
|
||||||
|
subject?: string;
|
||||||
|
property_address?: string;
|
||||||
|
permit_number?: string;
|
||||||
|
committee_type?: string;
|
||||||
|
hearing_date?: string;
|
||||||
|
notes?: string;
|
||||||
|
expected_outcome?: string;
|
||||||
|
}
|
||||||
|
export interface CaseUpdateInput {
|
||||||
|
status?: string;
|
||||||
|
title?: string;
|
||||||
|
subject?: string;
|
||||||
|
notes?: string;
|
||||||
|
hearing_date?: string;
|
||||||
|
decision_date?: string;
|
||||||
|
tags?: string[];
|
||||||
|
expected_outcome?: string;
|
||||||
|
}
|
||||||
|
export interface DocumentInfo {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
doc_type: string;
|
||||||
|
extraction_status: string;
|
||||||
|
page_count?: number;
|
||||||
|
}
|
||||||
|
export interface WorkflowStatus {
|
||||||
|
case_number: string;
|
||||||
|
title: string;
|
||||||
|
status: string;
|
||||||
|
documents: Array<{
|
||||||
|
title: string;
|
||||||
|
type: string;
|
||||||
|
extraction: string;
|
||||||
|
chunks: number;
|
||||||
|
pages?: number;
|
||||||
|
}>;
|
||||||
|
total_documents: number;
|
||||||
|
total_chunks: number;
|
||||||
|
has_draft: boolean;
|
||||||
|
draft_size_bytes: number;
|
||||||
|
next_steps: string[];
|
||||||
|
}
|
||||||
|
export interface SearchResult {
|
||||||
|
score: number;
|
||||||
|
case_number?: string;
|
||||||
|
document: string;
|
||||||
|
section: string;
|
||||||
|
page: number;
|
||||||
|
content: string;
|
||||||
|
}
|
||||||
|
export interface ProcessingStatus {
|
||||||
|
cases: number;
|
||||||
|
documents: number;
|
||||||
|
pending_processing: number;
|
||||||
|
chunks: number;
|
||||||
|
style_corpus_entries: number;
|
||||||
|
style_patterns: number;
|
||||||
|
}
|
||||||
63
dist/legal-api.js
vendored
Normal file
63
dist/legal-api.js
vendored
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
/**
|
||||||
|
* HTTP client for Ezer Mishpati legal-ai REST API.
|
||||||
|
*/
|
||||||
|
export class LegalApi {
|
||||||
|
baseUrl;
|
||||||
|
constructor(baseUrl) {
|
||||||
|
this.baseUrl = baseUrl;
|
||||||
|
}
|
||||||
|
async request(path, init) {
|
||||||
|
const res = await fetch(`${this.baseUrl}${path}`, {
|
||||||
|
...init,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
...init?.headers,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!res.ok) {
|
||||||
|
const text = await res.text();
|
||||||
|
throw new Error(`Legal API ${res.status}: ${text}`);
|
||||||
|
}
|
||||||
|
return res.json();
|
||||||
|
}
|
||||||
|
async listCases() {
|
||||||
|
return this.request("/api/cases");
|
||||||
|
}
|
||||||
|
async getCase(caseNumber) {
|
||||||
|
return this.request(`/api/cases/${encodeURIComponent(caseNumber)}/details`);
|
||||||
|
}
|
||||||
|
async createCase(data) {
|
||||||
|
return this.request("/api/cases/create", {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async updateCase(caseNumber, data) {
|
||||||
|
return this.request(`/api/cases/${encodeURIComponent(caseNumber)}`, {
|
||||||
|
method: "PUT",
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async getCaseStatus(caseNumber) {
|
||||||
|
return this.request(`/api/cases/${encodeURIComponent(caseNumber)}/status`);
|
||||||
|
}
|
||||||
|
async search(query, limit = 10, sectionType = "") {
|
||||||
|
const params = new URLSearchParams({ query, limit: String(limit) });
|
||||||
|
if (sectionType)
|
||||||
|
params.set("section_type", sectionType);
|
||||||
|
return this.request(`/api/search?${params}`);
|
||||||
|
}
|
||||||
|
async searchCase(caseNumber, query, limit = 10) {
|
||||||
|
const params = new URLSearchParams({ query, limit: String(limit) });
|
||||||
|
return this.request(`/api/cases/${encodeURIComponent(caseNumber)}/search?${params}`);
|
||||||
|
}
|
||||||
|
async getTemplate(caseNumber) {
|
||||||
|
return this.request(`/api/cases/${encodeURIComponent(caseNumber)}/template`);
|
||||||
|
}
|
||||||
|
async getProcessingStatus() {
|
||||||
|
return this.request("/api/processing-status");
|
||||||
|
}
|
||||||
|
async health() {
|
||||||
|
return this.request("/health");
|
||||||
|
}
|
||||||
|
}
|
||||||
206
dist/manifest.d.ts
vendored
Normal file
206
dist/manifest.d.ts
vendored
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
declare const _default: {
|
||||||
|
id: string;
|
||||||
|
apiVersion: number;
|
||||||
|
version: string;
|
||||||
|
displayName: string;
|
||||||
|
description: string;
|
||||||
|
author: string;
|
||||||
|
categories: readonly ["connector"];
|
||||||
|
capabilities: readonly ["events.subscribe", "issues.read", "issues.create", "issues.update", "issue.comments.create", "agent.tools.register", "http.outbound", "plugin.state.read", "plugin.state.write", "jobs.schedule", "activity.log.write", "companies.read", "projects.read"];
|
||||||
|
entrypoints: {
|
||||||
|
worker: string;
|
||||||
|
};
|
||||||
|
instanceConfigSchema: {
|
||||||
|
type: "object";
|
||||||
|
properties: {
|
||||||
|
legalApiBaseUrl: {
|
||||||
|
type: "string";
|
||||||
|
default: string;
|
||||||
|
description: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
tools: ({
|
||||||
|
toolKey: string;
|
||||||
|
name: string;
|
||||||
|
displayName: string;
|
||||||
|
description: string;
|
||||||
|
parametersSchema: {
|
||||||
|
type: "object";
|
||||||
|
properties: {
|
||||||
|
case_number?: undefined;
|
||||||
|
title?: undefined;
|
||||||
|
appellants?: undefined;
|
||||||
|
respondents?: undefined;
|
||||||
|
subject?: undefined;
|
||||||
|
property_address?: undefined;
|
||||||
|
expected_outcome?: undefined;
|
||||||
|
status?: undefined;
|
||||||
|
query?: undefined;
|
||||||
|
limit?: undefined;
|
||||||
|
section_type?: undefined;
|
||||||
|
};
|
||||||
|
required?: undefined;
|
||||||
|
};
|
||||||
|
} | {
|
||||||
|
toolKey: string;
|
||||||
|
name: string;
|
||||||
|
displayName: string;
|
||||||
|
description: string;
|
||||||
|
parametersSchema: {
|
||||||
|
type: "object";
|
||||||
|
properties: {
|
||||||
|
case_number: {
|
||||||
|
type: "string";
|
||||||
|
description: string;
|
||||||
|
};
|
||||||
|
title?: undefined;
|
||||||
|
appellants?: undefined;
|
||||||
|
respondents?: undefined;
|
||||||
|
subject?: undefined;
|
||||||
|
property_address?: undefined;
|
||||||
|
expected_outcome?: undefined;
|
||||||
|
status?: undefined;
|
||||||
|
query?: undefined;
|
||||||
|
limit?: undefined;
|
||||||
|
section_type?: undefined;
|
||||||
|
};
|
||||||
|
required: string[];
|
||||||
|
};
|
||||||
|
} | {
|
||||||
|
toolKey: string;
|
||||||
|
name: string;
|
||||||
|
displayName: string;
|
||||||
|
description: string;
|
||||||
|
parametersSchema: {
|
||||||
|
type: "object";
|
||||||
|
properties: {
|
||||||
|
case_number: {
|
||||||
|
type: "string";
|
||||||
|
description?: undefined;
|
||||||
|
};
|
||||||
|
title: {
|
||||||
|
type: "string";
|
||||||
|
};
|
||||||
|
appellants: {
|
||||||
|
type: "array";
|
||||||
|
items: {
|
||||||
|
type: "string";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
respondents: {
|
||||||
|
type: "array";
|
||||||
|
items: {
|
||||||
|
type: "string";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
subject: {
|
||||||
|
type: "string";
|
||||||
|
};
|
||||||
|
property_address: {
|
||||||
|
type: "string";
|
||||||
|
};
|
||||||
|
expected_outcome: {
|
||||||
|
type: "string";
|
||||||
|
};
|
||||||
|
status?: undefined;
|
||||||
|
query?: undefined;
|
||||||
|
limit?: undefined;
|
||||||
|
section_type?: undefined;
|
||||||
|
};
|
||||||
|
required: string[];
|
||||||
|
};
|
||||||
|
} | {
|
||||||
|
toolKey: string;
|
||||||
|
name: string;
|
||||||
|
displayName: string;
|
||||||
|
description: string;
|
||||||
|
parametersSchema: {
|
||||||
|
type: "object";
|
||||||
|
properties: {
|
||||||
|
case_number: {
|
||||||
|
type: "string";
|
||||||
|
description?: undefined;
|
||||||
|
};
|
||||||
|
status: {
|
||||||
|
type: "string";
|
||||||
|
};
|
||||||
|
title: {
|
||||||
|
type: "string";
|
||||||
|
};
|
||||||
|
expected_outcome: {
|
||||||
|
type: "string";
|
||||||
|
};
|
||||||
|
appellants?: undefined;
|
||||||
|
respondents?: undefined;
|
||||||
|
subject?: undefined;
|
||||||
|
property_address?: undefined;
|
||||||
|
query?: undefined;
|
||||||
|
limit?: undefined;
|
||||||
|
section_type?: undefined;
|
||||||
|
};
|
||||||
|
required: string[];
|
||||||
|
};
|
||||||
|
} | {
|
||||||
|
toolKey: string;
|
||||||
|
name: string;
|
||||||
|
displayName: string;
|
||||||
|
description: string;
|
||||||
|
parametersSchema: {
|
||||||
|
type: "object";
|
||||||
|
properties: {
|
||||||
|
case_number: {
|
||||||
|
type: "string";
|
||||||
|
description?: undefined;
|
||||||
|
};
|
||||||
|
title?: undefined;
|
||||||
|
appellants?: undefined;
|
||||||
|
respondents?: undefined;
|
||||||
|
subject?: undefined;
|
||||||
|
property_address?: undefined;
|
||||||
|
expected_outcome?: undefined;
|
||||||
|
status?: undefined;
|
||||||
|
query?: undefined;
|
||||||
|
limit?: undefined;
|
||||||
|
section_type?: undefined;
|
||||||
|
};
|
||||||
|
required: string[];
|
||||||
|
};
|
||||||
|
} | {
|
||||||
|
toolKey: string;
|
||||||
|
name: string;
|
||||||
|
displayName: string;
|
||||||
|
description: string;
|
||||||
|
parametersSchema: {
|
||||||
|
type: "object";
|
||||||
|
properties: {
|
||||||
|
query: {
|
||||||
|
type: "string";
|
||||||
|
description: string;
|
||||||
|
};
|
||||||
|
limit: {
|
||||||
|
type: "number";
|
||||||
|
};
|
||||||
|
section_type: {
|
||||||
|
type: "string";
|
||||||
|
};
|
||||||
|
case_number?: undefined;
|
||||||
|
title?: undefined;
|
||||||
|
appellants?: undefined;
|
||||||
|
respondents?: undefined;
|
||||||
|
subject?: undefined;
|
||||||
|
property_address?: undefined;
|
||||||
|
expected_outcome?: undefined;
|
||||||
|
status?: undefined;
|
||||||
|
};
|
||||||
|
required: string[];
|
||||||
|
};
|
||||||
|
})[];
|
||||||
|
jobs: {
|
||||||
|
jobKey: string;
|
||||||
|
displayName: string;
|
||||||
|
description: string;
|
||||||
|
schedule: string;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
export default _default;
|
||||||
150
dist/manifest.js
vendored
Normal file
150
dist/manifest.js
vendored
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
export default {
|
||||||
|
id: "marcusgroup.legal-ai",
|
||||||
|
apiVersion: 1,
|
||||||
|
version: "0.1.0",
|
||||||
|
displayName: "Ezer Mishpati - Legal AI",
|
||||||
|
description: "Integration with legal decision drafting system — case management, semantic search, and workflow tracking",
|
||||||
|
author: "Marcus Group",
|
||||||
|
categories: ["connector"],
|
||||||
|
capabilities: [
|
||||||
|
"events.subscribe",
|
||||||
|
"issues.read",
|
||||||
|
"issues.create",
|
||||||
|
"issues.update",
|
||||||
|
"issue.comments.create",
|
||||||
|
"agent.tools.register",
|
||||||
|
"http.outbound",
|
||||||
|
"plugin.state.read",
|
||||||
|
"plugin.state.write",
|
||||||
|
"jobs.schedule",
|
||||||
|
"activity.log.write",
|
||||||
|
"companies.read",
|
||||||
|
"projects.read",
|
||||||
|
],
|
||||||
|
entrypoints: {
|
||||||
|
worker: "dist/worker.js",
|
||||||
|
},
|
||||||
|
instanceConfigSchema: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
legalApiBaseUrl: {
|
||||||
|
type: "string",
|
||||||
|
default: "http://localhost:8085",
|
||||||
|
description: "Base URL for the Ezer Mishpati API",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tools: [
|
||||||
|
{
|
||||||
|
toolKey: "legal_case_list",
|
||||||
|
name: "legal_case_list",
|
||||||
|
displayName: "List Legal Cases",
|
||||||
|
description: "List all appeal cases in the legal system",
|
||||||
|
parametersSchema: { type: "object", properties: {} },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
toolKey: "legal_case_get",
|
||||||
|
name: "legal_case_get",
|
||||||
|
displayName: "Get Legal Case Details",
|
||||||
|
description: "Get full details of a legal case including documents",
|
||||||
|
parametersSchema: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
case_number: { type: "string", description: "Case number" },
|
||||||
|
},
|
||||||
|
required: ["case_number"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
toolKey: "legal_case_create",
|
||||||
|
name: "legal_case_create",
|
||||||
|
displayName: "Create Legal Case",
|
||||||
|
description: "Create a new appeal case with linked Paperclip issue",
|
||||||
|
parametersSchema: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
case_number: { type: "string" },
|
||||||
|
title: { type: "string" },
|
||||||
|
appellants: { type: "array", items: { type: "string" } },
|
||||||
|
respondents: { type: "array", items: { type: "string" } },
|
||||||
|
subject: { type: "string" },
|
||||||
|
property_address: { type: "string" },
|
||||||
|
expected_outcome: { type: "string" },
|
||||||
|
},
|
||||||
|
required: ["case_number", "title"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
toolKey: "legal_case_update",
|
||||||
|
name: "legal_case_update",
|
||||||
|
displayName: "Update Legal Case",
|
||||||
|
description: "Update a legal case status, title, or outcome",
|
||||||
|
parametersSchema: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
case_number: { type: "string" },
|
||||||
|
status: { type: "string" },
|
||||||
|
title: { type: "string" },
|
||||||
|
expected_outcome: { type: "string" },
|
||||||
|
},
|
||||||
|
required: ["case_number"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
toolKey: "legal_case_status",
|
||||||
|
name: "legal_case_status",
|
||||||
|
displayName: "Get Case Workflow Status",
|
||||||
|
description: "Get full workflow status: documents, drafts, next steps",
|
||||||
|
parametersSchema: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
case_number: { type: "string" },
|
||||||
|
},
|
||||||
|
required: ["case_number"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
toolKey: "legal_search",
|
||||||
|
name: "legal_search",
|
||||||
|
displayName: "Search Legal Precedents",
|
||||||
|
description: "Semantic RAG search across previous decisions",
|
||||||
|
parametersSchema: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
query: { type: "string", description: "Search query in Hebrew" },
|
||||||
|
limit: { type: "number" },
|
||||||
|
section_type: { type: "string" },
|
||||||
|
},
|
||||||
|
required: ["query"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
toolKey: "legal_case_template",
|
||||||
|
name: "legal_case_template",
|
||||||
|
displayName: "Get Decision Template",
|
||||||
|
description: "Get outcome-aware decision template for 12-block structure",
|
||||||
|
parametersSchema: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
case_number: { type: "string" },
|
||||||
|
},
|
||||||
|
required: ["case_number"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
toolKey: "legal_processing_status",
|
||||||
|
name: "legal_processing_status",
|
||||||
|
displayName: "Get Processing Status",
|
||||||
|
description: "Get overall system processing status",
|
||||||
|
parametersSchema: { type: "object", properties: {} },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
jobs: [
|
||||||
|
{
|
||||||
|
jobKey: "sync-case-status",
|
||||||
|
displayName: "Sync Legal Case Status",
|
||||||
|
description: "Polls legal-ai for case status changes and updates Paperclip issues",
|
||||||
|
schedule: "*/15 * * * *",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
2
dist/worker.d.ts
vendored
Normal file
2
dist/worker.d.ts
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
declare const plugin: import("@paperclipai/plugin-sdk").PaperclipPlugin;
|
||||||
|
export default plugin;
|
||||||
311
dist/worker.js
vendored
Normal file
311
dist/worker.js
vendored
Normal file
@@ -0,0 +1,311 @@
|
|||||||
|
import { definePlugin, runWorker } from "@paperclipai/plugin-sdk";
|
||||||
|
import { LegalApi } from "./legal-api.js";
|
||||||
|
const plugin = definePlugin({
|
||||||
|
async setup(ctx) {
|
||||||
|
const config = (await ctx.config.get());
|
||||||
|
const baseUrl = config?.legalApiBaseUrl || "http://localhost:8085";
|
||||||
|
const api = new LegalApi(baseUrl);
|
||||||
|
ctx.logger.info("Legal AI plugin starting", { url: baseUrl });
|
||||||
|
// ── Tools ──────────────────────────────────────────────────────
|
||||||
|
ctx.tools.register("legal_case_list", {
|
||||||
|
displayName: "רשימת תיקי ערר",
|
||||||
|
description: "List all appeal cases in the legal system. Returns case number, title, and status (new/in_progress/drafted/reviewed/final).",
|
||||||
|
parametersSchema: {
|
||||||
|
type: "object",
|
||||||
|
properties: {},
|
||||||
|
},
|
||||||
|
}, async () => {
|
||||||
|
const cases = await api.listCases();
|
||||||
|
return {
|
||||||
|
content: JSON.stringify(cases, null, 2),
|
||||||
|
data: cases,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
ctx.tools.register("legal_case_get", {
|
||||||
|
displayName: "פרטי תיק ערר",
|
||||||
|
description: "Get full details of a legal case including documents list. Provide the case number (e.g. 123/24).",
|
||||||
|
parametersSchema: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
case_number: {
|
||||||
|
type: "string",
|
||||||
|
description: "Case number (e.g. 123/24)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ["case_number"],
|
||||||
|
},
|
||||||
|
}, async (params) => {
|
||||||
|
const { case_number } = params;
|
||||||
|
const result = await api.getCase(case_number);
|
||||||
|
return {
|
||||||
|
content: JSON.stringify(result, null, 2),
|
||||||
|
data: result,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
ctx.tools.register("legal_case_create", {
|
||||||
|
displayName: "יצירת תיק ערר",
|
||||||
|
description: "Create a new appeal case. Case numbers: 1xxx=licensing, 8xxx=betterment levy, 9xxx=compensation. Also creates a linked Paperclip issue.",
|
||||||
|
parametersSchema: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
case_number: {
|
||||||
|
type: "string",
|
||||||
|
description: "Case number (e.g. 1234/24)",
|
||||||
|
},
|
||||||
|
title: { type: "string", description: "Case title" },
|
||||||
|
appellants: {
|
||||||
|
type: "array",
|
||||||
|
items: { type: "string" },
|
||||||
|
description: "Appellant names",
|
||||||
|
},
|
||||||
|
respondents: {
|
||||||
|
type: "array",
|
||||||
|
items: { type: "string" },
|
||||||
|
description: "Respondent names",
|
||||||
|
},
|
||||||
|
subject: { type: "string", description: "Case subject" },
|
||||||
|
property_address: {
|
||||||
|
type: "string",
|
||||||
|
description: "Property address",
|
||||||
|
},
|
||||||
|
expected_outcome: {
|
||||||
|
type: "string",
|
||||||
|
enum: [
|
||||||
|
"rejection",
|
||||||
|
"partial_acceptance",
|
||||||
|
"full_acceptance",
|
||||||
|
"betterment_levy",
|
||||||
|
],
|
||||||
|
description: "Expected outcome type",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ["case_number", "title"],
|
||||||
|
},
|
||||||
|
}, async (params, runCtx) => {
|
||||||
|
const input = params;
|
||||||
|
// Create case in legal-ai
|
||||||
|
const legalCase = await api.createCase(input);
|
||||||
|
// Create linked Paperclip issue
|
||||||
|
const issue = await ctx.issues.create({
|
||||||
|
companyId: runCtx.companyId,
|
||||||
|
title: `[ערר ${input.case_number}] ${input.title}`,
|
||||||
|
description: `תיק ערר חדש\nנושא: ${input.subject || ""}\nתוצאה צפויה: ${input.expected_outcome || "לא הוגדרה"}`,
|
||||||
|
});
|
||||||
|
// Store mapping in plugin state
|
||||||
|
await ctx.state.set({
|
||||||
|
scopeKind: "issue",
|
||||||
|
scopeId: issue.id,
|
||||||
|
stateKey: "legal-case-number",
|
||||||
|
}, input.case_number);
|
||||||
|
await ctx.activity.log({
|
||||||
|
companyId: runCtx.companyId,
|
||||||
|
message: `נוצר תיק ערר ${input.case_number} וקושר ל-issue ${issue.id}`,
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
content: `Case ${input.case_number} created and linked to Paperclip issue.\n\n${JSON.stringify(legalCase, null, 2)}`,
|
||||||
|
data: { legalCase, issueId: issue.id },
|
||||||
|
};
|
||||||
|
});
|
||||||
|
ctx.tools.register("legal_case_update", {
|
||||||
|
displayName: "עדכון תיק ערר",
|
||||||
|
description: "Update a legal case's status, title, subject, or expected outcome.",
|
||||||
|
parametersSchema: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
case_number: {
|
||||||
|
type: "string",
|
||||||
|
description: "Case number (e.g. 123/24)",
|
||||||
|
},
|
||||||
|
status: {
|
||||||
|
type: "string",
|
||||||
|
enum: [
|
||||||
|
"new",
|
||||||
|
"in_progress",
|
||||||
|
"drafted",
|
||||||
|
"reviewed",
|
||||||
|
"final",
|
||||||
|
],
|
||||||
|
description: "New case status",
|
||||||
|
},
|
||||||
|
title: { type: "string" },
|
||||||
|
subject: { type: "string" },
|
||||||
|
notes: { type: "string" },
|
||||||
|
expected_outcome: {
|
||||||
|
type: "string",
|
||||||
|
enum: [
|
||||||
|
"rejection",
|
||||||
|
"partial_acceptance",
|
||||||
|
"full_acceptance",
|
||||||
|
"betterment_levy",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ["case_number"],
|
||||||
|
},
|
||||||
|
}, async (params) => {
|
||||||
|
const { case_number, ...updates } = params;
|
||||||
|
const result = await api.updateCase(case_number, updates);
|
||||||
|
return {
|
||||||
|
content: JSON.stringify(result, null, 2),
|
||||||
|
data: result,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
ctx.tools.register("legal_case_status", {
|
||||||
|
displayName: "סטטוס תהליך עבודה",
|
||||||
|
description: "Get full workflow status for a case: documents processed, chunks created, draft progress, and suggested next steps.",
|
||||||
|
parametersSchema: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
case_number: {
|
||||||
|
type: "string",
|
||||||
|
description: "Case number (e.g. 123/24)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ["case_number"],
|
||||||
|
},
|
||||||
|
}, async (params) => {
|
||||||
|
const { case_number } = params;
|
||||||
|
const result = await api.getCaseStatus(case_number);
|
||||||
|
return {
|
||||||
|
content: JSON.stringify(result, null, 2),
|
||||||
|
data: result,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
ctx.tools.register("legal_search", {
|
||||||
|
displayName: "חיפוש תקדימים משפטיים",
|
||||||
|
description: "Semantic search (RAG) across previous decisions and documents. Query in Hebrew for best results.",
|
||||||
|
parametersSchema: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
query: {
|
||||||
|
type: "string",
|
||||||
|
description: "Search query in Hebrew",
|
||||||
|
},
|
||||||
|
limit: { type: "number", description: "Max results (default 10)" },
|
||||||
|
section_type: {
|
||||||
|
type: "string",
|
||||||
|
description: "Filter by section type: facts, legal_analysis, conclusion, ruling",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ["query"],
|
||||||
|
},
|
||||||
|
}, async (params) => {
|
||||||
|
const { query, limit, section_type } = params;
|
||||||
|
const results = await api.search(query, limit || 10, section_type || "");
|
||||||
|
return {
|
||||||
|
content: JSON.stringify(results, null, 2),
|
||||||
|
data: results,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
ctx.tools.register("legal_case_template", {
|
||||||
|
displayName: "תבנית החלטה",
|
||||||
|
description: "Get an outcome-aware decision template for a case, with guidance for the 12-block structure.",
|
||||||
|
parametersSchema: {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
case_number: {
|
||||||
|
type: "string",
|
||||||
|
description: "Case number (e.g. 123/24)",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: ["case_number"],
|
||||||
|
},
|
||||||
|
}, async (params) => {
|
||||||
|
const { case_number } = params;
|
||||||
|
const result = await api.getTemplate(case_number);
|
||||||
|
return {
|
||||||
|
content: result.template,
|
||||||
|
data: result,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
ctx.tools.register("legal_processing_status", {
|
||||||
|
displayName: "סטטוס עיבוד כללי",
|
||||||
|
description: "Get overall processing status: total cases, documents, pending processing, chunks, and style corpus entries.",
|
||||||
|
parametersSchema: {
|
||||||
|
type: "object",
|
||||||
|
properties: {},
|
||||||
|
},
|
||||||
|
}, async () => {
|
||||||
|
const result = await api.getProcessingStatus();
|
||||||
|
return {
|
||||||
|
content: JSON.stringify(result, null, 2),
|
||||||
|
data: result,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
// ── Events ─────────────────────────────────────────────────────
|
||||||
|
ctx.events.on("issue.created", async (event) => {
|
||||||
|
// Auto-link issues with case number in title
|
||||||
|
if (!event.companyId || !event.entityId)
|
||||||
|
return;
|
||||||
|
const issue = await ctx.issues.get(event.entityId, event.companyId);
|
||||||
|
if (!issue)
|
||||||
|
return;
|
||||||
|
const match = issue.title.match(/ערר\s+(\d+\/\d+)/);
|
||||||
|
if (match) {
|
||||||
|
const caseNumber = match[1];
|
||||||
|
await ctx.state.set({
|
||||||
|
scopeKind: "issue",
|
||||||
|
scopeId: issue.id,
|
||||||
|
stateKey: "legal-case-number",
|
||||||
|
}, caseNumber);
|
||||||
|
ctx.logger.info("Auto-linked issue to legal case", {
|
||||||
|
issueId: issue.id,
|
||||||
|
caseNumber,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// ── Jobs ───────────────────────────────────────────────────────
|
||||||
|
ctx.jobs.register("sync-case-status", async (job) => {
|
||||||
|
ctx.logger.info("Starting case status sync", { runId: job.runId });
|
||||||
|
try {
|
||||||
|
const cases = await api.listCases();
|
||||||
|
const companies = await ctx.companies.list();
|
||||||
|
if (!companies.length)
|
||||||
|
return;
|
||||||
|
const companyId = companies[0].id;
|
||||||
|
const issues = await ctx.issues.list({ companyId });
|
||||||
|
for (const legalCase of cases) {
|
||||||
|
for (const issue of issues) {
|
||||||
|
const linkedCase = await ctx.state.get({
|
||||||
|
scopeKind: "issue",
|
||||||
|
scopeId: issue.id,
|
||||||
|
stateKey: "legal-case-number",
|
||||||
|
});
|
||||||
|
if (linkedCase === legalCase.case_number) {
|
||||||
|
// Map legal-ai status to Paperclip issue status
|
||||||
|
const statusMap = {
|
||||||
|
new: "todo",
|
||||||
|
in_progress: "in_progress",
|
||||||
|
drafted: "in_progress",
|
||||||
|
reviewed: "in_progress",
|
||||||
|
final: "done",
|
||||||
|
};
|
||||||
|
const targetStatus = statusMap[legalCase.status];
|
||||||
|
if (targetStatus && issue.status !== targetStatus) {
|
||||||
|
await ctx.issues.update(issue.id, { status: targetStatus }, companyId);
|
||||||
|
await ctx.issues.createComment(issue.id, `סטטוס תיק עודכן ל: ${legalCase.status}`, companyId);
|
||||||
|
ctx.logger.info("Synced issue status", {
|
||||||
|
issueId: issue.id,
|
||||||
|
caseNumber: legalCase.case_number,
|
||||||
|
newStatus: targetStatus,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.logger.info("Case status sync completed", {
|
||||||
|
casesChecked: cases.length,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
ctx.logger.error("Case status sync failed", { error: String(err) });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
ctx.logger.info("Legal AI plugin ready");
|
||||||
|
},
|
||||||
|
async onHealth() {
|
||||||
|
return { status: "ok" };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
export default plugin;
|
||||||
|
runWorker(plugin, import.meta.url);
|
||||||
1
node_modules/.bin/paperclip-plugin-dev-server
generated
vendored
Symbolic link
1
node_modules/.bin/paperclip-plugin-dev-server
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../@paperclipai/plugin-sdk/dist/dev-cli.js
|
||||||
1
node_modules/.bin/tsc
generated
vendored
Symbolic link
1
node_modules/.bin/tsc
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../typescript/bin/tsc
|
||||||
1
node_modules/.bin/tsserver
generated
vendored
Symbolic link
1
node_modules/.bin/tsserver
generated
vendored
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../typescript/bin/tsserver
|
||||||
78
node_modules/.package-lock.json
generated
vendored
Normal file
78
node_modules/.package-lock.json
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
{
|
||||||
|
"name": "@marcusgroup/plugin-legal-ai",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"node_modules/@paperclipai/plugin-sdk": {
|
||||||
|
"version": "2026.325.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@paperclipai/plugin-sdk/-/plugin-sdk-2026.325.0.tgz",
|
||||||
|
"integrity": "sha512-axAIK90QVrRihlCXcGoePDc0qaf0weqOwUagWsp88v6T2NXAWXALCzFtY4IipPnaVli6xZe5ZsuC+gTlPXhMKQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@paperclipai/shared": "2026.325.0",
|
||||||
|
"zod": "^3.24.2"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"paperclip-plugin-dev-server": "dist/dev-cli.js"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=18"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@paperclipai/shared": {
|
||||||
|
"version": "2026.325.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@paperclipai/shared/-/shared-2026.325.0.tgz",
|
||||||
|
"integrity": "sha512-l0ZeaQhswxjAyJ6G/TdgyrfbEgFKqdkly4zn4QgOPCu+x8j8Gma51Xf45M2Br3ILJlIx93HhT3hAzWKChdxgMg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"zod": "^3.24.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/node": {
|
||||||
|
"version": "25.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz",
|
||||||
|
"integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~7.18.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/typescript": {
|
||||||
|
"version": "6.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz",
|
||||||
|
"integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"bin": {
|
||||||
|
"tsc": "bin/tsc",
|
||||||
|
"tsserver": "bin/tsserver"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.17"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "7.18.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
|
||||||
|
"integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/zod": {
|
||||||
|
"version": "3.25.76",
|
||||||
|
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
||||||
|
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/colinhacks"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
node_modules/@paperclipai/plugin-sdk/LICENSE
generated
vendored
Normal file
21
node_modules/@paperclipai/plugin-sdk/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 Paperclip AI
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
888
node_modules/@paperclipai/plugin-sdk/README.md
generated
vendored
Normal file
888
node_modules/@paperclipai/plugin-sdk/README.md
generated
vendored
Normal file
@@ -0,0 +1,888 @@
|
|||||||
|
# `@paperclipai/plugin-sdk`
|
||||||
|
|
||||||
|
Official TypeScript SDK for Paperclip plugin authors.
|
||||||
|
|
||||||
|
- **Worker SDK:** `@paperclipai/plugin-sdk` — `definePlugin`, context, lifecycle
|
||||||
|
- **UI SDK:** `@paperclipai/plugin-sdk/ui` — React hooks and slot props
|
||||||
|
- **Testing:** `@paperclipai/plugin-sdk/testing` — in-memory host harness
|
||||||
|
- **Bundlers:** `@paperclipai/plugin-sdk/bundlers` — esbuild/rollup presets
|
||||||
|
- **Dev server:** `@paperclipai/plugin-sdk/dev-server` — static UI server + SSE reload
|
||||||
|
|
||||||
|
Reference: `doc/plugins/PLUGIN_SPEC.md`
|
||||||
|
|
||||||
|
## Package surface
|
||||||
|
|
||||||
|
| Import | Purpose |
|
||||||
|
|--------|--------|
|
||||||
|
| `@paperclipai/plugin-sdk` | Worker entry: `definePlugin`, `runWorker`, context types, protocol helpers |
|
||||||
|
| `@paperclipai/plugin-sdk/ui` | UI entry: `usePluginData`, `usePluginAction`, `usePluginStream`, `useHostContext`, slot prop types |
|
||||||
|
| `@paperclipai/plugin-sdk/ui/hooks` | Hooks only |
|
||||||
|
| `@paperclipai/plugin-sdk/ui/types` | UI types and slot prop interfaces |
|
||||||
|
| `@paperclipai/plugin-sdk/testing` | `createTestHarness` for unit/integration tests |
|
||||||
|
| `@paperclipai/plugin-sdk/bundlers` | `createPluginBundlerPresets` for worker/manifest/ui builds |
|
||||||
|
| `@paperclipai/plugin-sdk/dev-server` | `startPluginDevServer`, `getUiBuildSnapshot` |
|
||||||
|
| `@paperclipai/plugin-sdk/protocol` | JSON-RPC protocol types and helpers (advanced) |
|
||||||
|
| `@paperclipai/plugin-sdk/types` | Worker context and API types (advanced) |
|
||||||
|
|
||||||
|
## Manifest entrypoints
|
||||||
|
|
||||||
|
In your plugin manifest you declare:
|
||||||
|
|
||||||
|
- **`entrypoints.worker`** (required) — Path to the worker bundle (e.g. `dist/worker.js`). The host loads this and calls `setup(ctx)`.
|
||||||
|
- **`entrypoints.ui`** (required if you use UI) — Path to the UI bundle directory. The host loads components from here for slots and launchers.
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pnpm add @paperclipai/plugin-sdk
|
||||||
|
```
|
||||||
|
|
||||||
|
## Current deployment caveats
|
||||||
|
|
||||||
|
The SDK is stable enough for local development and first-party examples, but the runtime deployment model is still early.
|
||||||
|
|
||||||
|
- Plugin workers and plugin UI should both be treated as trusted code today.
|
||||||
|
- Plugin UI bundles run as same-origin JavaScript inside the main Paperclip app. They can call ordinary Paperclip HTTP APIs with the board session, so manifest capabilities are not a frontend sandbox.
|
||||||
|
- Local-path installs and the repo example plugins are development workflows. They assume the plugin source checkout exists on disk.
|
||||||
|
- For deployed plugins, publish an npm package and install that package into the Paperclip instance at runtime.
|
||||||
|
- The current host runtime expects a writable filesystem, `npm` available at runtime, and network access to the package registry used for plugin installation.
|
||||||
|
- Dynamic plugin install is currently best suited to single-node persistent deployments. Multi-instance cloud deployments still need a shared artifact/distribution model before runtime installs are reliable across nodes.
|
||||||
|
- The host does not currently ship a real shared React component kit for plugins. Build your plugin UI with ordinary React components and CSS.
|
||||||
|
- `ctx.assets` is not part of the supported runtime in this build. Do not depend on asset upload/read APIs yet.
|
||||||
|
|
||||||
|
If you are authoring a plugin for others to deploy, treat npm-packaged installation as the supported path and treat repo-local example installs as a development convenience.
|
||||||
|
|
||||||
|
## Worker quick start
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { definePlugin, runWorker } from "@paperclipai/plugin-sdk";
|
||||||
|
|
||||||
|
const plugin = definePlugin({
|
||||||
|
async setup(ctx) {
|
||||||
|
ctx.events.on("issue.created", async (event) => {
|
||||||
|
ctx.logger.info("Issue created", { issueId: event.entityId });
|
||||||
|
});
|
||||||
|
|
||||||
|
ctx.data.register("health", async () => ({ status: "ok" }));
|
||||||
|
ctx.actions.register("ping", async () => ({ pong: true }));
|
||||||
|
|
||||||
|
ctx.tools.register("calculator", {
|
||||||
|
displayName: "Calculator",
|
||||||
|
description: "Basic math",
|
||||||
|
parametersSchema: {
|
||||||
|
type: "object",
|
||||||
|
properties: { a: { type: "number" }, b: { type: "number" } },
|
||||||
|
required: ["a", "b"]
|
||||||
|
}
|
||||||
|
}, async (params) => {
|
||||||
|
const { a, b } = params as { a: number; b: number };
|
||||||
|
return { content: `Result: ${a + b}`, data: { result: a + b } };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default plugin;
|
||||||
|
runWorker(plugin, import.meta.url);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note:** `runWorker(plugin, import.meta.url)` must be called so that when the host runs your worker (e.g. `node dist/worker.js`), the RPC host starts and the process stays alive. When the file is imported (e.g. for tests), the main-module check prevents the host from starting.
|
||||||
|
|
||||||
|
### Worker lifecycle and context
|
||||||
|
|
||||||
|
**Lifecycle (definePlugin):**
|
||||||
|
|
||||||
|
| Hook | Purpose |
|
||||||
|
|------|--------|
|
||||||
|
| `setup(ctx)` | **Required.** Called once at startup. Register event handlers, jobs, data/actions/tools, etc. |
|
||||||
|
| `onHealth?()` | Optional. Return `{ status, message?, details? }` for health dashboard. |
|
||||||
|
| `onConfigChanged?(newConfig)` | Optional. Apply new config without restart; if omitted, host restarts worker. |
|
||||||
|
| `onShutdown?()` | Optional. Clean up before process exit (limited time window). |
|
||||||
|
| `onValidateConfig?(config)` | Optional. Return `{ ok, warnings?, errors? }` for settings UI / Test Connection. |
|
||||||
|
| `onWebhook?(input)` | Optional. Handle `POST /api/plugins/:pluginId/webhooks/:endpointKey`; required if webhooks declared. |
|
||||||
|
|
||||||
|
**Context (`ctx`) in setup:** `config`, `events`, `jobs`, `launchers`, `http`, `secrets`, `activity`, `state`, `entities`, `projects`, `companies`, `issues`, `agents`, `goals`, `data`, `actions`, `streams`, `tools`, `metrics`, `logger`, `manifest`. Worker-side host APIs are capability-gated; declare capabilities in the manifest.
|
||||||
|
|
||||||
|
**Agents:** `ctx.agents.invoke(agentId, companyId, opts)` for one-shot invocation. `ctx.agents.sessions` for two-way chat: `create`, `list`, `sendMessage` (with streaming `onEvent` callback), `close`. See the [Plugin Authoring Guide](../../doc/plugins/PLUGIN_AUTHORING_GUIDE.md#agent-sessions-two-way-chat) for details.
|
||||||
|
|
||||||
|
**Jobs:** Declare in `manifest.jobs` with `jobKey`, `displayName`, `schedule` (cron). Register handler with `ctx.jobs.register(jobKey, fn)`. **Webhooks:** Declare in `manifest.webhooks` with `endpointKey`; handle in `onWebhook(input)`. **State:** `ctx.state.get/set/delete(scopeKey)`; scope kinds: `instance`, `company`, `project`, `project_workspace`, `agent`, `issue`, `goal`, `run`.
|
||||||
|
|
||||||
|
## Events
|
||||||
|
|
||||||
|
Subscribe in `setup` with `ctx.events.on(name, handler)` or `ctx.events.on(name, filter, handler)`. Emit plugin-scoped events with `ctx.events.emit(name, companyId, payload)` (requires `events.emit`).
|
||||||
|
|
||||||
|
**Core domain events (subscribe with `events.subscribe`):**
|
||||||
|
|
||||||
|
| Event | Typical entity |
|
||||||
|
|-------|-----------------|
|
||||||
|
| `company.created`, `company.updated` | company |
|
||||||
|
| `project.created`, `project.updated` | project |
|
||||||
|
| `project.workspace_created`, `project.workspace_updated`, `project.workspace_deleted` | project_workspace |
|
||||||
|
| `issue.created`, `issue.updated`, `issue.comment.created` | issue |
|
||||||
|
| `agent.created`, `agent.updated`, `agent.status_changed` | agent |
|
||||||
|
| `agent.run.started`, `agent.run.finished`, `agent.run.failed`, `agent.run.cancelled` | run |
|
||||||
|
| `goal.created`, `goal.updated` | goal |
|
||||||
|
| `approval.created`, `approval.decided` | approval |
|
||||||
|
| `cost_event.created` | cost |
|
||||||
|
| `activity.logged` | activity |
|
||||||
|
|
||||||
|
**Plugin-to-plugin:** Subscribe to `plugin.<pluginId>.<eventName>` (e.g. `plugin.acme.linear.sync-done`). Emit with `ctx.events.emit("sync-done", companyId, payload)`; the host namespaces it automatically.
|
||||||
|
|
||||||
|
**Filter (optional):** Pass a second argument to `on()`: `{ projectId?, companyId?, agentId? }` so the host only delivers matching events.
|
||||||
|
|
||||||
|
**Company context:** Events still carry `companyId` for company-scoped data, but plugin installation and activation are instance-wide in the current runtime.
|
||||||
|
|
||||||
|
## Scheduled (recurring) jobs
|
||||||
|
|
||||||
|
Plugins can declare **scheduled jobs** that the host runs on a cron schedule. Use this for recurring tasks like syncs, digest reports, or cleanup.
|
||||||
|
|
||||||
|
1. **Capability:** Add `jobs.schedule` to `manifest.capabilities`.
|
||||||
|
2. **Declare jobs** in `manifest.jobs`: each entry has `jobKey`, `displayName`, optional `description`, and `schedule` (a 5-field cron expression).
|
||||||
|
3. **Register a handler** in `setup()` with `ctx.jobs.register(jobKey, async (job) => { ... })`.
|
||||||
|
|
||||||
|
**Cron format** (5 fields: minute, hour, day-of-month, month, day-of-week):
|
||||||
|
|
||||||
|
| Field | Values | Example |
|
||||||
|
|-------------|----------|---------|
|
||||||
|
| minute | 0–59 | `0`, `*/15` |
|
||||||
|
| hour | 0–23 | `2`, `*` |
|
||||||
|
| day of month | 1–31 | `1`, `*` |
|
||||||
|
| month | 1–12 | `*` |
|
||||||
|
| day of week | 0–6 (Sun=0) | `*`, `1-5` |
|
||||||
|
|
||||||
|
Examples: `"0 * * * *"` = every hour at minute 0; `"*/5 * * * *"` = every 5 minutes; `"0 2 * * *"` = daily at 2:00.
|
||||||
|
|
||||||
|
**Job handler context** (`PluginJobContext`):
|
||||||
|
|
||||||
|
| Field | Type | Description |
|
||||||
|
|-------------|----------|-------------|
|
||||||
|
| `jobKey` | string | Matches the manifest declaration. |
|
||||||
|
| `runId` | string | UUID for this run. |
|
||||||
|
| `trigger` | `"schedule" \| "manual" \| "retry"` | What caused this run. |
|
||||||
|
| `scheduledAt` | string | ISO 8601 time when the run was scheduled. |
|
||||||
|
|
||||||
|
Runs can be triggered by the **schedule**, **manually** from the UI/API, or as a **retry** (when an operator re-runs a job after a failure). Re-throw from the handler to mark the run as failed; the host records the failure. The host does not automatically retry—operators can trigger another run manually from the UI or API.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
**Manifest** — include `jobs.schedule` and declare the job:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// In your manifest (e.g. manifest.ts):
|
||||||
|
const manifest = {
|
||||||
|
// ...
|
||||||
|
capabilities: ["jobs.schedule", "plugin.state.write"],
|
||||||
|
jobs: [
|
||||||
|
{
|
||||||
|
jobKey: "heartbeat",
|
||||||
|
displayName: "Heartbeat",
|
||||||
|
description: "Runs every 5 minutes",
|
||||||
|
schedule: "*/5 * * * *",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
**Worker** — register the handler in `setup()`:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
ctx.jobs.register("heartbeat", async (job) => {
|
||||||
|
ctx.logger.info("Heartbeat run", { runId: job.runId, trigger: job.trigger });
|
||||||
|
await ctx.state.set({ scopeKind: "instance", stateKey: "last-heartbeat" }, new Date().toISOString());
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## UI slots and launchers
|
||||||
|
|
||||||
|
Slots are mount points for plugin React components. Launchers are host-rendered entry points (buttons, menu items) that open plugin UI. Declare slots in `manifest.ui.slots` with `type`, `id`, `displayName`, `exportName`; for context-sensitive slots add `entityTypes`. Declare launchers in `manifest.ui.launchers` (or legacy `manifest.launchers`).
|
||||||
|
|
||||||
|
### Slot types / launcher placement zones
|
||||||
|
|
||||||
|
The same set of values is used as **slot types** (where a component mounts) and **launcher placement zones** (where a launcher can appear). Hierarchy:
|
||||||
|
|
||||||
|
| Slot type / placement zone | Scope | Entity types (when context-sensitive) |
|
||||||
|
|----------------------------|-------|---------------------------------------|
|
||||||
|
| `page` | Global | — |
|
||||||
|
| `sidebar` | Global | — |
|
||||||
|
| `sidebarPanel` | Global | — |
|
||||||
|
| `settingsPage` | Global | — |
|
||||||
|
| `dashboardWidget` | Global | — |
|
||||||
|
| `globalToolbarButton` | Global | — |
|
||||||
|
| `detailTab` | Entity | `project`, `issue`, `agent`, `goal`, `run` |
|
||||||
|
| `taskDetailView` | Entity | (task/issue context) |
|
||||||
|
| `commentAnnotation` | Entity | `comment` |
|
||||||
|
| `commentContextMenuItem` | Entity | `comment` |
|
||||||
|
| `projectSidebarItem` | Entity | `project` |
|
||||||
|
| `toolbarButton` | Entity | varies by host surface |
|
||||||
|
| `contextMenuItem` | Entity | varies by host surface |
|
||||||
|
|
||||||
|
**Scope** describes whether the slot requires an entity to render. **Global** slots render without a specific entity but still receive the active `companyId` through `PluginHostContext` — use it to scope data fetches to the current company. **Entity** slots additionally require `entityId` and `entityType` (e.g. a detail tab on a specific issue).
|
||||||
|
|
||||||
|
**Entity types** (for `entityTypes` on slots): `project` \| `issue` \| `agent` \| `goal` \| `run` \| `comment`. Full list: import `PLUGIN_UI_SLOT_TYPES` and `PLUGIN_UI_SLOT_ENTITY_TYPES` from `@paperclipai/plugin-sdk`.
|
||||||
|
|
||||||
|
### Slot component descriptions
|
||||||
|
|
||||||
|
#### `page`
|
||||||
|
|
||||||
|
A full-page extension mounted at `/plugins/:pluginId` (global) or `/:company/plugins/:pluginId` (company-context route). Use this for rich, standalone plugin experiences such as dashboards, configuration wizards, or multi-step workflows. Receives `PluginPageProps` with `context.companyId` set to the active company. Requires the `ui.page.register` capability.
|
||||||
|
|
||||||
|
#### `sidebar`
|
||||||
|
|
||||||
|
Adds a navigation-style entry to the main company sidebar navigation area, rendered alongside the core nav items (Dashboard, Issues, Goals, etc.). Use this for lightweight, always-visible links or status indicators that feel native to the sidebar. Receives `PluginSidebarProps` with `context.companyId` set to the active company. Requires the `ui.sidebar.register` capability.
|
||||||
|
|
||||||
|
#### `sidebarPanel`
|
||||||
|
|
||||||
|
Renders richer inline content in a dedicated panel area below the company sidebar navigation sections. Use this for mini-widgets, summary cards, quick-action panels, or at-a-glance status views that need more vertical space than a nav link. Receives `context.companyId` set to the active company via `useHostContext()`. Requires the `ui.sidebar.register` capability.
|
||||||
|
|
||||||
|
#### `settingsPage`
|
||||||
|
|
||||||
|
Replaces the auto-generated JSON Schema settings form with a custom React component. Use this when the default form is insufficient — for example, when your plugin needs multi-step configuration, OAuth flows, "Test Connection" buttons, or rich input controls. Receives `PluginSettingsPageProps` with `context.companyId` set to the active company. The component is responsible for reading and writing config through the bridge (via `usePluginData` and `usePluginAction`).
|
||||||
|
|
||||||
|
#### `dashboardWidget`
|
||||||
|
|
||||||
|
A card or section rendered on the main dashboard. Use this for at-a-glance metrics, status indicators, or summary views that surface plugin data alongside core Paperclip information. Receives `PluginWidgetProps` with `context.companyId` set to the active company. Requires the `ui.dashboardWidget.register` capability.
|
||||||
|
|
||||||
|
#### `detailTab`
|
||||||
|
|
||||||
|
An additional tab on a project, issue, agent, goal, or run detail page. Rendered when the user navigates to that entity's detail view. Receives `PluginDetailTabProps` with `context.companyId` set to the active company and `context.entityId` / `context.entityType` guaranteed to be non-null, so you can immediately scope data fetches to the relevant entity. Specify which entity types the tab applies to via the `entityTypes` array in the manifest slot declaration. Requires the `ui.detailTab.register` capability.
|
||||||
|
|
||||||
|
#### `taskDetailView`
|
||||||
|
|
||||||
|
A specialized slot rendered in the context of a task or issue detail view. Similar to `detailTab` but designed for inline content within the task detail layout rather than a separate tab. Receives `context.companyId`, `context.entityId`, and `context.entityType` like `detailTab`. Requires the `ui.detailTab.register` capability.
|
||||||
|
|
||||||
|
#### `projectSidebarItem`
|
||||||
|
|
||||||
|
A link or small component rendered **once per project** under that project's row in the sidebar Projects list. Use this to add project-scoped navigation entries (e.g. "Files", "Linear Sync") that deep-link into a plugin detail tab: `/:company/projects/:projectRef?tab=plugin:<key>:<slotId>`. Receives `PluginProjectSidebarItemProps` with `context.companyId` set to the active company, `context.entityId` set to the project id, and `context.entityType` set to `"project"`. Use the optional `order` field in the manifest slot to control sort position. Requires the `ui.sidebar.register` capability.
|
||||||
|
|
||||||
|
#### `globalToolbarButton`
|
||||||
|
|
||||||
|
A button rendered in the global top bar (breadcrumb bar) that appears on every page. Use this for company-wide actions that are not scoped to a specific entity — for example, a universal search trigger, a global sync status indicator, or a floating action that applies across the whole workspace. Receives only `context.companyId` and `context.companyPrefix`; no entity context is available. Requires the `ui.action.register` capability.
|
||||||
|
|
||||||
|
#### `toolbarButton`
|
||||||
|
|
||||||
|
A button rendered in the toolbar of an entity page (e.g. project detail, issue detail). Use this for short-lived, contextual actions scoped to the current entity — like triggering a project sync, opening a picker, or running a quick command on that entity. The component can open a plugin-owned modal internally for confirmations or compact forms. Receives `context.companyId`, `context.entityId`, and `context.entityType`; declare `entityTypes` in the manifest to control which entity pages the button appears on. Requires the `ui.action.register` capability.
|
||||||
|
|
||||||
|
#### `contextMenuItem`
|
||||||
|
|
||||||
|
An entry added to a right-click or overflow context menu on a host surface. Use this for secondary actions that apply to the entity under the cursor (e.g. "Copy to Linear", "Re-run analysis"). Receives `context.companyId` set to the active company; entity context varies by host surface. Requires the `ui.action.register` capability.
|
||||||
|
|
||||||
|
#### `commentAnnotation`
|
||||||
|
|
||||||
|
A per-comment annotation region rendered below each individual comment in the issue detail timeline. Use this to augment comments with parsed file links, sentiment badges, inline actions, or any per-comment metadata. Receives `PluginCommentAnnotationProps` with `context.entityId` set to the comment UUID, `context.entityType` set to `"comment"`, `context.parentEntityId` set to the parent issue UUID, `context.projectId` set to the issue's project (if any), and `context.companyPrefix` set to the active company slug. Requires the `ui.commentAnnotation.register` capability.
|
||||||
|
|
||||||
|
#### `commentContextMenuItem`
|
||||||
|
|
||||||
|
A per-comment context menu item rendered in the "more" dropdown menu (⋮) on each comment in the issue detail timeline. Use this to add per-comment actions such as "Create sub-issue from comment", "Translate", "Flag for review", or custom plugin actions. Receives `PluginCommentContextMenuItemProps` with `context.entityId` set to the comment UUID, `context.entityType` set to `"comment"`, `context.parentEntityId` set to the parent issue UUID, `context.projectId` set to the issue's project (if any), and `context.companyPrefix` set to the active company slug. Plugins can open drawers, modals, or popovers scoped to that comment. The ⋮ menu button only appears on comments where at least one plugin renders visible content. Requires the `ui.action.register` capability.
|
||||||
|
|
||||||
|
### Launcher actions and render options
|
||||||
|
|
||||||
|
| Launcher action | Description |
|
||||||
|
|-----------------|-------------|
|
||||||
|
| `navigate` | Navigate to a route (plugin or host). |
|
||||||
|
| `openModal` | Open a modal. |
|
||||||
|
| `openDrawer` | Open a drawer. |
|
||||||
|
| `openPopover` | Open a popover. |
|
||||||
|
| `performAction` | Run an action (e.g. call plugin). |
|
||||||
|
| `deepLink` | Deep link to plugin or external URL. |
|
||||||
|
|
||||||
|
| Render option | Values | Description |
|
||||||
|
|---------------|--------|-------------|
|
||||||
|
| `environment` | `hostInline`, `hostOverlay`, `hostRoute`, `external`, `iframe` | Container the launcher expects after activation. |
|
||||||
|
| `bounds` | `inline`, `compact`, `default`, `wide`, `full` | Size hint for overlays/drawers. |
|
||||||
|
|
||||||
|
### Capabilities
|
||||||
|
|
||||||
|
Declare in `manifest.capabilities`. Grouped by scope:
|
||||||
|
|
||||||
|
| Scope | Capability |
|
||||||
|
|-------|------------|
|
||||||
|
| **Company** | `companies.read` |
|
||||||
|
| | `projects.read` |
|
||||||
|
| | `project.workspaces.read` |
|
||||||
|
| | `issues.read` |
|
||||||
|
| | `issue.comments.read` |
|
||||||
|
| | `agents.read` |
|
||||||
|
| | `goals.read` |
|
||||||
|
| | `goals.create` |
|
||||||
|
| | `goals.update` |
|
||||||
|
| | `activity.read` |
|
||||||
|
| | `costs.read` |
|
||||||
|
| | `issues.create` |
|
||||||
|
| | `issues.update` |
|
||||||
|
| | `issue.comments.create` |
|
||||||
|
| | `activity.log.write` |
|
||||||
|
| | `metrics.write` |
|
||||||
|
| **Instance** | `instance.settings.register` |
|
||||||
|
| | `plugin.state.read` |
|
||||||
|
| | `plugin.state.write` |
|
||||||
|
| **Runtime** | `events.subscribe` |
|
||||||
|
| | `events.emit` |
|
||||||
|
| | `jobs.schedule` |
|
||||||
|
| | `webhooks.receive` |
|
||||||
|
| | `http.outbound` |
|
||||||
|
| | `secrets.read-ref` |
|
||||||
|
| **Agent** | `agent.tools.register` |
|
||||||
|
| | `agents.invoke` |
|
||||||
|
| | `agent.sessions.create` |
|
||||||
|
| | `agent.sessions.list` |
|
||||||
|
| | `agent.sessions.send` |
|
||||||
|
| | `agent.sessions.close` |
|
||||||
|
| **UI** | `ui.sidebar.register` |
|
||||||
|
| | `ui.page.register` |
|
||||||
|
| | `ui.detailTab.register` |
|
||||||
|
| | `ui.dashboardWidget.register` |
|
||||||
|
| | `ui.commentAnnotation.register` |
|
||||||
|
| | `ui.action.register` |
|
||||||
|
|
||||||
|
Full list in code: import `PLUGIN_CAPABILITIES` from `@paperclipai/plugin-sdk`.
|
||||||
|
|
||||||
|
## UI quick start
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { usePluginData, usePluginAction } from "@paperclipai/plugin-sdk/ui";
|
||||||
|
|
||||||
|
export function DashboardWidget() {
|
||||||
|
const { data } = usePluginData<{ status: string }>("health");
|
||||||
|
const ping = usePluginAction("ping");
|
||||||
|
return (
|
||||||
|
<div style={{ display: "grid", gap: 8 }}>
|
||||||
|
<strong>Health</strong>
|
||||||
|
<div>{data?.status ?? "unknown"}</div>
|
||||||
|
<button onClick={() => void ping()}>Ping</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Hooks reference
|
||||||
|
|
||||||
|
#### `usePluginData<T>(key, params?)`
|
||||||
|
|
||||||
|
Fetches data from the worker's registered `getData` handler. Re-fetches when `params` changes. Returns `{ data, loading, error, refresh }`.
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { usePluginData } from "@paperclipai/plugin-sdk/ui";
|
||||||
|
|
||||||
|
interface SyncStatus {
|
||||||
|
lastSyncAt: string;
|
||||||
|
syncedCount: number;
|
||||||
|
healthy: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SyncStatusWidget({ context }: PluginWidgetProps) {
|
||||||
|
const { data, loading, error, refresh } = usePluginData<SyncStatus>("sync-status", {
|
||||||
|
companyId: context.companyId,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (loading) return <div>Loading…</div>;
|
||||||
|
if (error) return <div>Error: {error.message}</div>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<p>Status: {data!.healthy ? "Healthy" : "Unhealthy"}</p>
|
||||||
|
<p>Synced {data!.syncedCount} items</p>
|
||||||
|
<p>Last sync: {data!.lastSyncAt}</p>
|
||||||
|
<button onClick={refresh}>Refresh</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `usePluginAction(key)`
|
||||||
|
|
||||||
|
Returns an async function that calls the worker's `performAction` handler. Throws `PluginBridgeError` on failure.
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { useState } from "react";
|
||||||
|
import { usePluginAction, type PluginBridgeError } from "@paperclipai/plugin-sdk/ui";
|
||||||
|
|
||||||
|
export function ResyncButton({ context }: PluginWidgetProps) {
|
||||||
|
const resync = usePluginAction("resync");
|
||||||
|
const [busy, setBusy] = useState(false);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
async function handleClick() {
|
||||||
|
setBusy(true);
|
||||||
|
setError(null);
|
||||||
|
try {
|
||||||
|
await resync({ companyId: context.companyId });
|
||||||
|
} catch (err) {
|
||||||
|
setError((err as PluginBridgeError).message);
|
||||||
|
} finally {
|
||||||
|
setBusy(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<button onClick={handleClick} disabled={busy}>
|
||||||
|
{busy ? "Syncing..." : "Resync Now"}
|
||||||
|
</button>
|
||||||
|
{error && <p style={{ color: "red" }}>{error}</p>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `useHostContext()`
|
||||||
|
|
||||||
|
Reads the active company, project, entity, and user context. Use this to scope data fetches and actions.
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { useHostContext, usePluginData } from "@paperclipai/plugin-sdk/ui";
|
||||||
|
import type { PluginDetailTabProps } from "@paperclipai/plugin-sdk/ui";
|
||||||
|
|
||||||
|
export function IssueLinearLink({ context }: PluginDetailTabProps) {
|
||||||
|
const { companyId, entityId, entityType } = context;
|
||||||
|
const { data } = usePluginData<{ url: string }>("linear-link", {
|
||||||
|
companyId,
|
||||||
|
issueId: entityId,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!data?.url) return <p>No linked Linear issue.</p>;
|
||||||
|
return <a href={data.url} target="_blank" rel="noopener">View in Linear</a>;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `usePluginStream<T>(channel, options?)`
|
||||||
|
|
||||||
|
Subscribes to a real-time event stream pushed from the plugin worker via SSE. The worker pushes events using `ctx.streams.emit(channel, event)` and the hook receives them as they arrive. Returns `{ events, lastEvent, connecting, connected, error, close }`.
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { usePluginStream } from "@paperclipai/plugin-sdk/ui";
|
||||||
|
|
||||||
|
interface ChatToken {
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ChatMessages({ context }: PluginWidgetProps) {
|
||||||
|
const { events, connected, close } = usePluginStream<ChatToken>("chat-stream", {
|
||||||
|
companyId: context.companyId ?? undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{events.map((e, i) => <span key={i}>{e.text}</span>)}
|
||||||
|
{connected && <span className="pulse" />}
|
||||||
|
<button onClick={close}>Stop</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The SSE connection targets `GET /api/plugins/:pluginId/bridge/stream/:channel?companyId=...`. The host bridge manages the EventSource lifecycle; `close()` terminates the connection.
|
||||||
|
|
||||||
|
### UI authoring note
|
||||||
|
|
||||||
|
The current host does **not** provide a real shared component library to plugins yet. Use normal React components, your own CSS, or your own small design primitives inside the plugin package.
|
||||||
|
|
||||||
|
### Slot component props
|
||||||
|
|
||||||
|
Each slot type receives a typed props object with `context: PluginHostContext`. Import from `@paperclipai/plugin-sdk/ui`.
|
||||||
|
|
||||||
|
| Slot type | Props interface | `context` extras |
|
||||||
|
|-----------|----------------|------------------|
|
||||||
|
| `page` | `PluginPageProps` | — |
|
||||||
|
| `sidebar` | `PluginSidebarProps` | — |
|
||||||
|
| `settingsPage` | `PluginSettingsPageProps` | — |
|
||||||
|
| `dashboardWidget` | `PluginWidgetProps` | — |
|
||||||
|
| `globalToolbarButton` | `PluginGlobalToolbarButtonProps` | — |
|
||||||
|
| `detailTab` | `PluginDetailTabProps` | `entityId: string`, `entityType: string` |
|
||||||
|
| `toolbarButton` | `PluginToolbarButtonProps` | `entityId: string`, `entityType: string` |
|
||||||
|
| `commentAnnotation` | `PluginCommentAnnotationProps` | `entityId: string`, `entityType: "comment"`, `parentEntityId: string`, `projectId`, `companyPrefix` |
|
||||||
|
| `commentContextMenuItem` | `PluginCommentContextMenuItemProps` | `entityId: string`, `entityType: "comment"`, `parentEntityId: string`, `projectId`, `companyPrefix` |
|
||||||
|
| `projectSidebarItem` | `PluginProjectSidebarItemProps` | `entityId: string`, `entityType: "project"` |
|
||||||
|
|
||||||
|
Example detail tab with entity context:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import type { PluginDetailTabProps } from "@paperclipai/plugin-sdk/ui";
|
||||||
|
import { usePluginData } from "@paperclipai/plugin-sdk/ui";
|
||||||
|
|
||||||
|
export function AgentMetricsTab({ context }: PluginDetailTabProps) {
|
||||||
|
const { data, loading } = usePluginData<Record<string, string>>("agent-metrics", {
|
||||||
|
agentId: context.entityId,
|
||||||
|
companyId: context.companyId,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (loading) return <div>Loading…</div>;
|
||||||
|
if (!data) return <p>No metrics available.</p>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<dl>
|
||||||
|
{Object.entries(data).map(([label, value]) => (
|
||||||
|
<div key={label}>
|
||||||
|
<dt>{label}</dt>
|
||||||
|
<dd>{value}</dd>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</dl>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Launcher surfaces and modals
|
||||||
|
|
||||||
|
V1 does not provide a dedicated `modal` slot. Plugins can either:
|
||||||
|
|
||||||
|
- declare concrete UI mount points in `ui.slots`
|
||||||
|
- declare host-rendered entry points in `ui.launchers`
|
||||||
|
|
||||||
|
Supported launcher placement zones currently mirror the major host surfaces such as `projectSidebarItem`, `globalToolbarButton`, `toolbarButton`, `detailTab`, `settingsPage`, and `contextMenuItem`. Plugins may still open their own local modal from those entry points when needed.
|
||||||
|
|
||||||
|
Declarative launcher example:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"ui": {
|
||||||
|
"launchers": [
|
||||||
|
{
|
||||||
|
"id": "sync-project",
|
||||||
|
"displayName": "Sync",
|
||||||
|
"placementZone": "toolbarButton",
|
||||||
|
"entityTypes": ["project"],
|
||||||
|
"action": {
|
||||||
|
"type": "openDrawer",
|
||||||
|
"target": "sync-project"
|
||||||
|
},
|
||||||
|
"render": {
|
||||||
|
"environment": "hostOverlay",
|
||||||
|
"bounds": "wide"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The host returns launcher metadata from `GET /api/plugins/ui-contributions` alongside slot declarations.
|
||||||
|
|
||||||
|
When a launcher opens a host-owned overlay or page, `useHostContext()`,
|
||||||
|
`usePluginData()`, and `usePluginAction()` receive the current
|
||||||
|
`renderEnvironment` through the bridge. Use that to tailor compact modal UI vs.
|
||||||
|
full-page layouts without adding custom route parsing in the plugin.
|
||||||
|
|
||||||
|
## Project sidebar item
|
||||||
|
|
||||||
|
Plugins can add a link under each project in the sidebar via the `projectSidebarItem` slot. This is the recommended slot-based launcher pattern for project-scoped workflows because it can deep-link into a richer plugin tab. The component is rendered once per project with that project’s id in `context.entityId`. Declare the slot and capability in your manifest:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"ui": {
|
||||||
|
"slots": [
|
||||||
|
{
|
||||||
|
"type": "projectSidebarItem",
|
||||||
|
"id": "files",
|
||||||
|
"displayName": "Files",
|
||||||
|
"exportName": "FilesLink",
|
||||||
|
"entityTypes": ["project"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"capabilities": ["ui.sidebar.register", "ui.detailTab.register"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Minimal React component that links to the project’s plugin tab (see project detail tabs in the spec):
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import type { PluginProjectSidebarItemProps } from "@paperclipai/plugin-sdk/ui";
|
||||||
|
|
||||||
|
export function FilesLink({ context }: PluginProjectSidebarItemProps) {
|
||||||
|
const projectId = context.entityId;
|
||||||
|
const prefix = context.companyPrefix ? `/${context.companyPrefix}` : "";
|
||||||
|
const projectRef = projectId; // or resolve from host; entityId is project id
|
||||||
|
return (
|
||||||
|
<a href={`${prefix}/projects/${projectRef}?tab=plugin:your-plugin:files`}>
|
||||||
|
Files
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Use optional `order` in the slot to sort among other project sidebar items. See §19.5.1 in the plugin spec and project detail plugin tabs (§19.3) for the full flow.
|
||||||
|
|
||||||
|
## Toolbar launcher with a local modal
|
||||||
|
|
||||||
|
Two toolbar slot types are available depending on where the button should appear:
|
||||||
|
|
||||||
|
- **`globalToolbarButton`** — renders in the top bar on every page, scoped to the company. No entity context. Use for workspace-wide actions.
|
||||||
|
- **`toolbarButton`** — renders on entity detail pages (project, issue, etc.). Receives `entityId` and `entityType`. Declare `entityTypes` to control which pages the button appears on.
|
||||||
|
|
||||||
|
For short-lived actions, mount the appropriate slot type and open a plugin-owned modal inside the component. Use `useHostContext()` to scope the action to the current company or entity.
|
||||||
|
|
||||||
|
Project-scoped example (appears only on project detail pages):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"ui": {
|
||||||
|
"slots": [
|
||||||
|
{
|
||||||
|
"type": "toolbarButton",
|
||||||
|
"id": "sync-toolbar-button",
|
||||||
|
"displayName": "Sync",
|
||||||
|
"exportName": "SyncToolbarButton",
|
||||||
|
"entityTypes": ["project"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"capabilities": ["ui.action.register"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { useState } from "react";
|
||||||
|
import {
|
||||||
|
useHostContext,
|
||||||
|
usePluginAction,
|
||||||
|
} from "@paperclipai/plugin-sdk/ui";
|
||||||
|
|
||||||
|
export function SyncToolbarButton() {
|
||||||
|
const context = useHostContext();
|
||||||
|
const syncProject = usePluginAction("sync-project");
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const [submitting, setSubmitting] = useState(false);
|
||||||
|
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
||||||
|
|
||||||
|
async function confirm() {
|
||||||
|
if (!context.projectId) return;
|
||||||
|
setSubmitting(true);
|
||||||
|
setErrorMessage(null);
|
||||||
|
try {
|
||||||
|
await syncProject({ projectId: context.projectId });
|
||||||
|
setOpen(false);
|
||||||
|
} catch (err) {
|
||||||
|
setErrorMessage(err instanceof Error ? err.message : "Sync failed");
|
||||||
|
} finally {
|
||||||
|
setSubmitting(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<button type="button" onClick={() => setOpen(true)}>
|
||||||
|
Sync
|
||||||
|
</button>
|
||||||
|
{open ? (
|
||||||
|
<div
|
||||||
|
role="dialog"
|
||||||
|
aria-modal="true"
|
||||||
|
className="fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4"
|
||||||
|
onClick={() => !submitting && setOpen(false)}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="w-full max-w-md rounded-lg bg-background p-4 shadow-xl"
|
||||||
|
onClick={(event) => event.stopPropagation()}
|
||||||
|
>
|
||||||
|
<h2 className="text-base font-semibold">Sync this project?</h2>
|
||||||
|
<p className="mt-2 text-sm text-muted-foreground">
|
||||||
|
Queue a sync for <code>{context.projectId}</code>.
|
||||||
|
</p>
|
||||||
|
{errorMessage ? (
|
||||||
|
<p className="mt-2 text-sm text-destructive">{errorMessage}</p>
|
||||||
|
) : null}
|
||||||
|
<div className="mt-4 flex justify-end gap-2">
|
||||||
|
<button type="button" onClick={() => setOpen(false)}>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button type="button" onClick={() => void confirm()} disabled={submitting}>
|
||||||
|
{submitting ? "Running…" : "Run sync"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Prefer deep-linkable tabs and pages for primary workflows. Reserve plugin-owned modals for confirmations, pickers, and compact editors.
|
||||||
|
|
||||||
|
## Real-time streaming (`ctx.streams`)
|
||||||
|
|
||||||
|
Plugins can push real-time events from the worker to the UI using server-sent events (SSE). This is useful for streaming LLM tokens, live sync progress, or any push-based data.
|
||||||
|
|
||||||
|
### Worker side
|
||||||
|
|
||||||
|
In `setup()`, use `ctx.streams` to open a channel, emit events, and close when done:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const plugin = definePlugin({
|
||||||
|
async setup(ctx) {
|
||||||
|
ctx.actions.register("chat", async (params) => {
|
||||||
|
const companyId = params.companyId as string;
|
||||||
|
ctx.streams.open("chat-stream", companyId);
|
||||||
|
|
||||||
|
for await (const token of streamFromLLM(params.prompt as string)) {
|
||||||
|
ctx.streams.emit("chat-stream", { text: token });
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.streams.close("chat-stream");
|
||||||
|
return { ok: true };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**API:**
|
||||||
|
|
||||||
|
| Method | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| `ctx.streams.open(channel, companyId)` | Open a named stream channel and associate it with a company. Sends a `streams.open` notification to the host. |
|
||||||
|
| `ctx.streams.emit(channel, event)` | Push an event to the channel. The `companyId` is automatically resolved from the prior `open()` call. |
|
||||||
|
| `ctx.streams.close(channel)` | Close the channel and clear the company mapping. Sends a `streams.close` notification. |
|
||||||
|
|
||||||
|
Stream notifications are fire-and-forget JSON-RPC messages (no `id` field). They are sent via `notifyHost()` synchronously during handler execution.
|
||||||
|
|
||||||
|
### UI side
|
||||||
|
|
||||||
|
Use the `usePluginStream` hook (see [Hooks reference](#usepluginstreamtchannel-options) above) to subscribe to events from the UI.
|
||||||
|
|
||||||
|
### Host-side architecture
|
||||||
|
|
||||||
|
The host maintains an in-memory `PluginStreamBus` that fans out worker notifications to connected SSE clients:
|
||||||
|
|
||||||
|
1. Worker emits `streams.emit` notification via stdout
|
||||||
|
2. Host (`plugin-worker-manager`) receives the notification and publishes to `PluginStreamBus`
|
||||||
|
3. SSE endpoint (`GET /api/plugins/:pluginId/bridge/stream/:channel?companyId=...`) subscribes to the bus and writes events to the response
|
||||||
|
|
||||||
|
The bus is keyed by `pluginId:channel:companyId`, so multiple UI clients can subscribe to the same stream independently.
|
||||||
|
|
||||||
|
### Streaming agent responses to the UI
|
||||||
|
|
||||||
|
`ctx.streams` and `ctx.agents.sessions` are complementary. The worker sits between them, relaying agent events to the browser in real time:
|
||||||
|
|
||||||
|
```
|
||||||
|
UI ──usePluginAction──▶ Worker ──sessions.sendMessage──▶ Agent
|
||||||
|
UI ◀──usePluginStream── Worker ◀──onEvent callback────── Agent
|
||||||
|
```
|
||||||
|
|
||||||
|
The agent doesn't know about streams — the worker decides what to relay. Encode the agent ID in the channel name to scope streams per agent.
|
||||||
|
|
||||||
|
**Worker:**
|
||||||
|
|
||||||
|
```ts
|
||||||
|
ctx.actions.register("ask-agent", async (params) => {
|
||||||
|
const { agentId, companyId, prompt } = params as {
|
||||||
|
agentId: string; companyId: string; prompt: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const channel = `agent:${agentId}`;
|
||||||
|
ctx.streams.open(channel, companyId);
|
||||||
|
|
||||||
|
const session = await ctx.agents.sessions.create(agentId, companyId);
|
||||||
|
|
||||||
|
await ctx.agents.sessions.sendMessage(session.sessionId, companyId, {
|
||||||
|
prompt,
|
||||||
|
onEvent: (event) => {
|
||||||
|
ctx.streams.emit(channel, {
|
||||||
|
type: event.eventType, // "chunk" | "done" | "error"
|
||||||
|
text: event.message ?? "",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
ctx.streams.close(channel);
|
||||||
|
return { sessionId: session.sessionId };
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**UI:**
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
import { useState } from "react";
|
||||||
|
import { usePluginAction, usePluginStream } from "@paperclipai/plugin-sdk/ui";
|
||||||
|
|
||||||
|
interface AgentEvent {
|
||||||
|
type: "chunk" | "done" | "error";
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AgentChat({ agentId, companyId }: { agentId: string; companyId: string }) {
|
||||||
|
const askAgent = usePluginAction("ask-agent");
|
||||||
|
const { events, connected, close } = usePluginStream<AgentEvent>(`agent:${agentId}`, { companyId });
|
||||||
|
const [prompt, setPrompt] = useState("");
|
||||||
|
|
||||||
|
async function send() {
|
||||||
|
setPrompt("");
|
||||||
|
await askAgent({ agentId, companyId, prompt });
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div>{events.filter(e => e.type === "chunk").map((e, i) => <span key={i}>{e.text}</span>)}</div>
|
||||||
|
<input value={prompt} onChange={(e) => setPrompt(e.target.value)} />
|
||||||
|
<button onClick={send}>Send</button>
|
||||||
|
{connected && <button onClick={close}>Stop</button>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Agent sessions (two-way chat)
|
||||||
|
|
||||||
|
Plugins can hold multi-turn conversational sessions with agents:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// Create a session
|
||||||
|
const session = await ctx.agents.sessions.create(agentId, companyId);
|
||||||
|
|
||||||
|
// Send a message and stream the response
|
||||||
|
await ctx.agents.sessions.sendMessage(session.sessionId, companyId, {
|
||||||
|
prompt: "Help me triage this issue",
|
||||||
|
onEvent: (event) => {
|
||||||
|
if (event.eventType === "chunk") console.log(event.message);
|
||||||
|
if (event.eventType === "done") console.log("Stream complete");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// List active sessions
|
||||||
|
const sessions = await ctx.agents.sessions.list(agentId, companyId);
|
||||||
|
|
||||||
|
// Close when done
|
||||||
|
await ctx.agents.sessions.close(session.sessionId, companyId);
|
||||||
|
```
|
||||||
|
|
||||||
|
Requires capabilities: `agent.sessions.create`, `agent.sessions.list`, `agent.sessions.send`, `agent.sessions.close`.
|
||||||
|
|
||||||
|
Exported types: `AgentSession`, `AgentSessionEvent`, `AgentSessionSendResult`, `PluginAgentSessionsClient`.
|
||||||
|
|
||||||
|
## Testing utilities
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { createTestHarness } from "@paperclipai/plugin-sdk/testing";
|
||||||
|
import plugin from "../src/worker.js";
|
||||||
|
import manifest from "../src/manifest.js";
|
||||||
|
|
||||||
|
const harness = createTestHarness({ manifest });
|
||||||
|
await plugin.definition.setup(harness.ctx);
|
||||||
|
await harness.emit("issue.created", { issueId: "iss_1" }, { entityId: "iss_1", entityType: "issue" });
|
||||||
|
```
|
||||||
|
|
||||||
|
## Bundler presets
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { createPluginBundlerPresets } from "@paperclipai/plugin-sdk/bundlers";
|
||||||
|
|
||||||
|
const presets = createPluginBundlerPresets({ uiEntry: "src/ui/index.tsx" });
|
||||||
|
// presets.esbuild.worker / presets.esbuild.manifest / presets.esbuild.ui
|
||||||
|
// presets.rollup.worker / presets.rollup.manifest / presets.rollup.ui
|
||||||
|
```
|
||||||
|
|
||||||
|
## Local dev server (hot-reload events)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
paperclip-plugin-dev-server --root . --ui-dir dist/ui --port 4177
|
||||||
|
```
|
||||||
|
|
||||||
|
Or programmatically:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { startPluginDevServer } from "@paperclipai/plugin-sdk/dev-server";
|
||||||
|
const server = await startPluginDevServer({ rootDir: process.cwd() });
|
||||||
|
```
|
||||||
|
|
||||||
|
Dev server endpoints:
|
||||||
|
- `GET /__paperclip__/health` returns `{ ok, rootDir, uiDir }`
|
||||||
|
- `GET /__paperclip__/events` streams `reload` SSE events on UI build changes
|
||||||
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":"AACA,OAAO,KAAK,EACV,yBAAyB,EACzB,gBAAgB,EAChB,eAAe,EACf,OAAO,EACP,OAAO,EACP,KAAK,EACL,YAAY,EACZ,KAAK,EACL,IAAI,EACL,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAEV,aAAa,EAGb,gBAAgB,EAEhB,WAAW,EACX,QAAQ,EACR,UAAU,EACV,cAAc,EAGd,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,kBAAkB;IACjC,mEAAmE;IACnE,QAAQ,EAAE,yBAAyB,CAAC;IACpC,yEAAyE;IACzE,YAAY,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAClC,qDAAqD;IACrD,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,WAAW;IAC1B,0EAA0E;IAC1E,GAAG,EAAE,aAAa,CAAC;IACnB,iFAAiF;IACjF,IAAI,CAAC,KAAK,EAAE;QACV,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC;QACtB,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;QACrB,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;QACjB,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;QAC/B,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC;QACjB,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;KAChB,GAAG,IAAI,CAAC;IACT,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACjD,8DAA8D;IAC9D,IAAI,CAAC,SAAS,EAAE,eAAe,GAAG,UAAU,MAAM,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpH,6DAA6D;IAC7D,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3E,wDAAwD;IACxD,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAChF,2DAA2D;IAC3D,aAAa,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACtF,sEAAsE;IACtE,WAAW,CAAC,CAAC,GAAG,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACzG,+CAA+C;IAC/C,QAAQ,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC;IACnC,iEAAiE;IACjE,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,iBAAiB,EAAE,WAAW,CAAC,GAAG,IAAI,CAAC;IAC3F,IAAI,EAAE,mBAAmB,EAAE,CAAC;IAC5B,QAAQ,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,CAAC;IACjH,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC,CAAC;CAChF;AA+CD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,WAAW,CAgmB1E"}
|
||||||
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
88
node_modules/@paperclipai/plugin-sdk/package.json
generated
vendored
Normal file
88
node_modules/@paperclipai/plugin-sdk/package.json
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
{
|
||||||
|
"name": "@paperclipai/plugin-sdk",
|
||||||
|
"version": "2026.325.0",
|
||||||
|
"description": "Stable public API for Paperclip plugins — worker-side context and UI bridge hooks",
|
||||||
|
"license": "MIT",
|
||||||
|
"homepage": "https://github.com/paperclipai/paperclip",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/paperclipai/paperclip/issues"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/paperclipai/paperclip",
|
||||||
|
"directory": "packages/plugins/sdk"
|
||||||
|
},
|
||||||
|
"type": "module",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"import": "./dist/index.js"
|
||||||
|
},
|
||||||
|
"./protocol": {
|
||||||
|
"types": "./dist/protocol.d.ts",
|
||||||
|
"import": "./dist/protocol.js"
|
||||||
|
},
|
||||||
|
"./types": {
|
||||||
|
"types": "./dist/types.d.ts",
|
||||||
|
"import": "./dist/types.js"
|
||||||
|
},
|
||||||
|
"./ui": {
|
||||||
|
"types": "./dist/ui/index.d.ts",
|
||||||
|
"import": "./dist/ui/index.js"
|
||||||
|
},
|
||||||
|
"./ui/hooks": {
|
||||||
|
"types": "./dist/ui/hooks.d.ts",
|
||||||
|
"import": "./dist/ui/hooks.js"
|
||||||
|
},
|
||||||
|
"./ui/types": {
|
||||||
|
"types": "./dist/ui/types.d.ts",
|
||||||
|
"import": "./dist/ui/types.js"
|
||||||
|
},
|
||||||
|
"./testing": {
|
||||||
|
"types": "./dist/testing.d.ts",
|
||||||
|
"import": "./dist/testing.js"
|
||||||
|
},
|
||||||
|
"./bundlers": {
|
||||||
|
"types": "./dist/bundlers.d.ts",
|
||||||
|
"import": "./dist/bundlers.js"
|
||||||
|
},
|
||||||
|
"./dev-server": {
|
||||||
|
"types": "./dist/dev-server.d.ts",
|
||||||
|
"import": "./dist/dev-server.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"paperclip-plugin-dev-server": "./dist/dev-cli.js"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"@paperclipai/shared": "2026.325.0",
|
||||||
|
"zod": "^3.24.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^24.6.0",
|
||||||
|
"@types/react": "^19.0.8",
|
||||||
|
"typescript": "^5.7.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=18"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "pnpm --filter @paperclipai/shared build && tsc",
|
||||||
|
"clean": "rm -rf dist",
|
||||||
|
"typecheck": "pnpm --filter @paperclipai/shared build && tsc --noEmit",
|
||||||
|
"dev:server": "tsx src/dev-cli.ts"
|
||||||
|
},
|
||||||
|
"main": "./dist/index.js",
|
||||||
|
"types": "./dist/index.d.ts"
|
||||||
|
}
|
||||||
21
node_modules/@paperclipai/shared/LICENSE
generated
vendored
Normal file
21
node_modules/@paperclipai/shared/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 Paperclip AI
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
4
node_modules/@paperclipai/shared/dist/agent-url-key.d.ts
generated
vendored
Normal file
4
node_modules/@paperclipai/shared/dist/agent-url-key.d.ts
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export declare function isUuidLike(value: string | null | undefined): boolean;
|
||||||
|
export declare function normalizeAgentUrlKey(value: string | null | undefined): string | null;
|
||||||
|
export declare function deriveAgentUrlKey(name: string | null | undefined, fallback?: string | null): string;
|
||||||
|
//# sourceMappingURL=agent-url-key.d.ts.map
|
||||||
1
node_modules/@paperclipai/shared/dist/agent-url-key.d.ts.map
generated
vendored
Normal file
1
node_modules/@paperclipai/shared/dist/agent-url-key.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"agent-url-key.d.ts","sourceRoot":"","sources":["../src/agent-url-key.ts"],"names":[],"mappings":"AAIA,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAGpE;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAQpF;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAEnG"}
|
||||||
22
node_modules/@paperclipai/shared/dist/agent-url-key.js
generated
vendored
Normal file
22
node_modules/@paperclipai/shared/dist/agent-url-key.js
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
const AGENT_URL_KEY_DELIM_RE = /[^a-z0-9]+/g;
|
||||||
|
const AGENT_URL_KEY_TRIM_RE = /^-+|-+$/g;
|
||||||
|
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
||||||
|
export function isUuidLike(value) {
|
||||||
|
if (typeof value !== "string")
|
||||||
|
return false;
|
||||||
|
return UUID_RE.test(value.trim());
|
||||||
|
}
|
||||||
|
export function normalizeAgentUrlKey(value) {
|
||||||
|
if (typeof value !== "string")
|
||||||
|
return null;
|
||||||
|
const normalized = value
|
||||||
|
.trim()
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(AGENT_URL_KEY_DELIM_RE, "-")
|
||||||
|
.replace(AGENT_URL_KEY_TRIM_RE, "");
|
||||||
|
return normalized.length > 0 ? normalized : null;
|
||||||
|
}
|
||||||
|
export function deriveAgentUrlKey(name, fallback) {
|
||||||
|
return normalizeAgentUrlKey(name) ?? normalizeAgentUrlKey(fallback) ?? "agent";
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=agent-url-key.js.map
|
||||||
1
node_modules/@paperclipai/shared/dist/agent-url-key.js.map
generated
vendored
Normal file
1
node_modules/@paperclipai/shared/dist/agent-url-key.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"agent-url-key.js","sourceRoot":"","sources":["../src/agent-url-key.ts"],"names":[],"mappings":"AAAA,MAAM,sBAAsB,GAAG,aAAa,CAAC;AAC7C,MAAM,qBAAqB,GAAG,UAAU,CAAC;AACzC,MAAM,OAAO,GAAG,4EAA4E,CAAC;AAE7F,MAAM,UAAU,UAAU,CAAC,KAAgC;IACzD,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5C,OAAO,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,KAAgC;IACnE,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3C,MAAM,UAAU,GAAG,KAAK;SACrB,IAAI,EAAE;SACN,WAAW,EAAE;SACb,OAAO,CAAC,sBAAsB,EAAE,GAAG,CAAC;SACpC,OAAO,CAAC,qBAAqB,EAAE,EAAE,CAAC,CAAC;IACtC,OAAO,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,IAA+B,EAAE,QAAwB;IACzF,OAAO,oBAAoB,CAAC,IAAI,CAAC,IAAI,oBAAoB,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC;AACjF,CAAC"}
|
||||||
20
node_modules/@paperclipai/shared/dist/api.d.ts
generated
vendored
Normal file
20
node_modules/@paperclipai/shared/dist/api.d.ts
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
export declare const API_PREFIX = "/api";
|
||||||
|
export declare const API: {
|
||||||
|
readonly health: "/api/health";
|
||||||
|
readonly companies: "/api/companies";
|
||||||
|
readonly agents: "/api/agents";
|
||||||
|
readonly projects: "/api/projects";
|
||||||
|
readonly issues: "/api/issues";
|
||||||
|
readonly goals: "/api/goals";
|
||||||
|
readonly approvals: "/api/approvals";
|
||||||
|
readonly secrets: "/api/secrets";
|
||||||
|
readonly costs: "/api/costs";
|
||||||
|
readonly activity: "/api/activity";
|
||||||
|
readonly dashboard: "/api/dashboard";
|
||||||
|
readonly sidebarBadges: "/api/sidebar-badges";
|
||||||
|
readonly invites: "/api/invites";
|
||||||
|
readonly joinRequests: "/api/join-requests";
|
||||||
|
readonly members: "/api/members";
|
||||||
|
readonly admin: "/api/admin";
|
||||||
|
};
|
||||||
|
//# sourceMappingURL=api.d.ts.map
|
||||||
1
node_modules/@paperclipai/shared/dist/api.d.ts.map
generated
vendored
Normal file
1
node_modules/@paperclipai/shared/dist/api.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU,SAAS,CAAC;AAEjC,eAAO,MAAM,GAAG;;;;;;;;;;;;;;;;;CAiBN,CAAC"}
|
||||||
20
node_modules/@paperclipai/shared/dist/api.js
generated
vendored
Normal file
20
node_modules/@paperclipai/shared/dist/api.js
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
export const API_PREFIX = "/api";
|
||||||
|
export const API = {
|
||||||
|
health: `${API_PREFIX}/health`,
|
||||||
|
companies: `${API_PREFIX}/companies`,
|
||||||
|
agents: `${API_PREFIX}/agents`,
|
||||||
|
projects: `${API_PREFIX}/projects`,
|
||||||
|
issues: `${API_PREFIX}/issues`,
|
||||||
|
goals: `${API_PREFIX}/goals`,
|
||||||
|
approvals: `${API_PREFIX}/approvals`,
|
||||||
|
secrets: `${API_PREFIX}/secrets`,
|
||||||
|
costs: `${API_PREFIX}/costs`,
|
||||||
|
activity: `${API_PREFIX}/activity`,
|
||||||
|
dashboard: `${API_PREFIX}/dashboard`,
|
||||||
|
sidebarBadges: `${API_PREFIX}/sidebar-badges`,
|
||||||
|
invites: `${API_PREFIX}/invites`,
|
||||||
|
joinRequests: `${API_PREFIX}/join-requests`,
|
||||||
|
members: `${API_PREFIX}/members`,
|
||||||
|
admin: `${API_PREFIX}/admin`,
|
||||||
|
};
|
||||||
|
//# sourceMappingURL=api.js.map
|
||||||
1
node_modules/@paperclipai/shared/dist/api.js.map
generated
vendored
Normal file
1
node_modules/@paperclipai/shared/dist/api.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC;AAEjC,MAAM,CAAC,MAAM,GAAG,GAAG;IACjB,MAAM,EAAE,GAAG,UAAU,SAAS;IAC9B,SAAS,EAAE,GAAG,UAAU,YAAY;IACpC,MAAM,EAAE,GAAG,UAAU,SAAS;IAC9B,QAAQ,EAAE,GAAG,UAAU,WAAW;IAClC,MAAM,EAAE,GAAG,UAAU,SAAS;IAC9B,KAAK,EAAE,GAAG,UAAU,QAAQ;IAC5B,SAAS,EAAE,GAAG,UAAU,YAAY;IACpC,OAAO,EAAE,GAAG,UAAU,UAAU;IAChC,KAAK,EAAE,GAAG,UAAU,QAAQ;IAC5B,QAAQ,EAAE,GAAG,UAAU,WAAW;IAClC,SAAS,EAAE,GAAG,UAAU,YAAY;IACpC,aAAa,EAAE,GAAG,UAAU,iBAAiB;IAC7C,OAAO,EAAE,GAAG,UAAU,UAAU;IAChC,YAAY,EAAE,GAAG,UAAU,gBAAgB;IAC3C,OAAO,EAAE,GAAG,UAAU,UAAU;IAChC,KAAK,EAAE,GAAG,UAAU,QAAQ;CACpB,CAAC"}
|
||||||
678
node_modules/@paperclipai/shared/dist/config-schema.d.ts
generated
vendored
Normal file
678
node_modules/@paperclipai/shared/dist/config-schema.d.ts
generated
vendored
Normal file
@@ -0,0 +1,678 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
export declare const configMetaSchema: z.ZodObject<{
|
||||||
|
version: z.ZodLiteral<1>;
|
||||||
|
updatedAt: z.ZodString;
|
||||||
|
source: z.ZodEnum<["onboard", "configure", "doctor"]>;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
version: 1;
|
||||||
|
updatedAt: string;
|
||||||
|
source: "onboard" | "configure" | "doctor";
|
||||||
|
}, {
|
||||||
|
version: 1;
|
||||||
|
updatedAt: string;
|
||||||
|
source: "onboard" | "configure" | "doctor";
|
||||||
|
}>;
|
||||||
|
export declare const llmConfigSchema: z.ZodObject<{
|
||||||
|
provider: z.ZodEnum<["claude", "openai"]>;
|
||||||
|
apiKey: z.ZodOptional<z.ZodString>;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
provider: "claude" | "openai";
|
||||||
|
apiKey?: string | undefined;
|
||||||
|
}, {
|
||||||
|
provider: "claude" | "openai";
|
||||||
|
apiKey?: string | undefined;
|
||||||
|
}>;
|
||||||
|
export declare const databaseBackupConfigSchema: z.ZodObject<{
|
||||||
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
||||||
|
intervalMinutes: z.ZodDefault<z.ZodNumber>;
|
||||||
|
retentionDays: z.ZodDefault<z.ZodNumber>;
|
||||||
|
dir: z.ZodDefault<z.ZodString>;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
enabled: boolean;
|
||||||
|
intervalMinutes: number;
|
||||||
|
retentionDays: number;
|
||||||
|
dir: string;
|
||||||
|
}, {
|
||||||
|
enabled?: boolean | undefined;
|
||||||
|
intervalMinutes?: number | undefined;
|
||||||
|
retentionDays?: number | undefined;
|
||||||
|
dir?: string | undefined;
|
||||||
|
}>;
|
||||||
|
export declare const databaseConfigSchema: z.ZodObject<{
|
||||||
|
mode: z.ZodDefault<z.ZodEnum<["embedded-postgres", "postgres"]>>;
|
||||||
|
connectionString: z.ZodOptional<z.ZodString>;
|
||||||
|
embeddedPostgresDataDir: z.ZodDefault<z.ZodString>;
|
||||||
|
embeddedPostgresPort: z.ZodDefault<z.ZodNumber>;
|
||||||
|
backup: z.ZodDefault<z.ZodObject<{
|
||||||
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
||||||
|
intervalMinutes: z.ZodDefault<z.ZodNumber>;
|
||||||
|
retentionDays: z.ZodDefault<z.ZodNumber>;
|
||||||
|
dir: z.ZodDefault<z.ZodString>;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
enabled: boolean;
|
||||||
|
intervalMinutes: number;
|
||||||
|
retentionDays: number;
|
||||||
|
dir: string;
|
||||||
|
}, {
|
||||||
|
enabled?: boolean | undefined;
|
||||||
|
intervalMinutes?: number | undefined;
|
||||||
|
retentionDays?: number | undefined;
|
||||||
|
dir?: string | undefined;
|
||||||
|
}>>;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
mode: "embedded-postgres" | "postgres";
|
||||||
|
embeddedPostgresDataDir: string;
|
||||||
|
embeddedPostgresPort: number;
|
||||||
|
backup: {
|
||||||
|
enabled: boolean;
|
||||||
|
intervalMinutes: number;
|
||||||
|
retentionDays: number;
|
||||||
|
dir: string;
|
||||||
|
};
|
||||||
|
connectionString?: string | undefined;
|
||||||
|
}, {
|
||||||
|
mode?: "embedded-postgres" | "postgres" | undefined;
|
||||||
|
connectionString?: string | undefined;
|
||||||
|
embeddedPostgresDataDir?: string | undefined;
|
||||||
|
embeddedPostgresPort?: number | undefined;
|
||||||
|
backup?: {
|
||||||
|
enabled?: boolean | undefined;
|
||||||
|
intervalMinutes?: number | undefined;
|
||||||
|
retentionDays?: number | undefined;
|
||||||
|
dir?: string | undefined;
|
||||||
|
} | undefined;
|
||||||
|
}>;
|
||||||
|
export declare const loggingConfigSchema: z.ZodObject<{
|
||||||
|
mode: z.ZodEnum<["file", "cloud"]>;
|
||||||
|
logDir: z.ZodDefault<z.ZodString>;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
mode: "file" | "cloud";
|
||||||
|
logDir: string;
|
||||||
|
}, {
|
||||||
|
mode: "file" | "cloud";
|
||||||
|
logDir?: string | undefined;
|
||||||
|
}>;
|
||||||
|
export declare const serverConfigSchema: z.ZodObject<{
|
||||||
|
deploymentMode: z.ZodDefault<z.ZodEnum<["local_trusted", "authenticated"]>>;
|
||||||
|
exposure: z.ZodDefault<z.ZodEnum<["private", "public"]>>;
|
||||||
|
host: z.ZodDefault<z.ZodString>;
|
||||||
|
port: z.ZodDefault<z.ZodNumber>;
|
||||||
|
allowedHostnames: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
||||||
|
serveUi: z.ZodDefault<z.ZodBoolean>;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
deploymentMode: "local_trusted" | "authenticated";
|
||||||
|
exposure: "private" | "public";
|
||||||
|
host: string;
|
||||||
|
port: number;
|
||||||
|
allowedHostnames: string[];
|
||||||
|
serveUi: boolean;
|
||||||
|
}, {
|
||||||
|
deploymentMode?: "local_trusted" | "authenticated" | undefined;
|
||||||
|
exposure?: "private" | "public" | undefined;
|
||||||
|
host?: string | undefined;
|
||||||
|
port?: number | undefined;
|
||||||
|
allowedHostnames?: string[] | undefined;
|
||||||
|
serveUi?: boolean | undefined;
|
||||||
|
}>;
|
||||||
|
export declare const authConfigSchema: z.ZodObject<{
|
||||||
|
baseUrlMode: z.ZodDefault<z.ZodEnum<["auto", "explicit"]>>;
|
||||||
|
publicBaseUrl: z.ZodOptional<z.ZodString>;
|
||||||
|
disableSignUp: z.ZodDefault<z.ZodBoolean>;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
baseUrlMode: "auto" | "explicit";
|
||||||
|
disableSignUp: boolean;
|
||||||
|
publicBaseUrl?: string | undefined;
|
||||||
|
}, {
|
||||||
|
baseUrlMode?: "auto" | "explicit" | undefined;
|
||||||
|
publicBaseUrl?: string | undefined;
|
||||||
|
disableSignUp?: boolean | undefined;
|
||||||
|
}>;
|
||||||
|
export declare const storageLocalDiskConfigSchema: z.ZodObject<{
|
||||||
|
baseDir: z.ZodDefault<z.ZodString>;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
baseDir: string;
|
||||||
|
}, {
|
||||||
|
baseDir?: string | undefined;
|
||||||
|
}>;
|
||||||
|
export declare const storageS3ConfigSchema: z.ZodObject<{
|
||||||
|
bucket: z.ZodDefault<z.ZodString>;
|
||||||
|
region: z.ZodDefault<z.ZodString>;
|
||||||
|
endpoint: z.ZodOptional<z.ZodString>;
|
||||||
|
prefix: z.ZodDefault<z.ZodString>;
|
||||||
|
forcePathStyle: z.ZodDefault<z.ZodBoolean>;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
bucket: string;
|
||||||
|
region: string;
|
||||||
|
prefix: string;
|
||||||
|
forcePathStyle: boolean;
|
||||||
|
endpoint?: string | undefined;
|
||||||
|
}, {
|
||||||
|
bucket?: string | undefined;
|
||||||
|
region?: string | undefined;
|
||||||
|
endpoint?: string | undefined;
|
||||||
|
prefix?: string | undefined;
|
||||||
|
forcePathStyle?: boolean | undefined;
|
||||||
|
}>;
|
||||||
|
export declare const storageConfigSchema: z.ZodObject<{
|
||||||
|
provider: z.ZodDefault<z.ZodEnum<["local_disk", "s3"]>>;
|
||||||
|
localDisk: z.ZodDefault<z.ZodObject<{
|
||||||
|
baseDir: z.ZodDefault<z.ZodString>;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
baseDir: string;
|
||||||
|
}, {
|
||||||
|
baseDir?: string | undefined;
|
||||||
|
}>>;
|
||||||
|
s3: z.ZodDefault<z.ZodObject<{
|
||||||
|
bucket: z.ZodDefault<z.ZodString>;
|
||||||
|
region: z.ZodDefault<z.ZodString>;
|
||||||
|
endpoint: z.ZodOptional<z.ZodString>;
|
||||||
|
prefix: z.ZodDefault<z.ZodString>;
|
||||||
|
forcePathStyle: z.ZodDefault<z.ZodBoolean>;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
bucket: string;
|
||||||
|
region: string;
|
||||||
|
prefix: string;
|
||||||
|
forcePathStyle: boolean;
|
||||||
|
endpoint?: string | undefined;
|
||||||
|
}, {
|
||||||
|
bucket?: string | undefined;
|
||||||
|
region?: string | undefined;
|
||||||
|
endpoint?: string | undefined;
|
||||||
|
prefix?: string | undefined;
|
||||||
|
forcePathStyle?: boolean | undefined;
|
||||||
|
}>>;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
s3: {
|
||||||
|
bucket: string;
|
||||||
|
region: string;
|
||||||
|
prefix: string;
|
||||||
|
forcePathStyle: boolean;
|
||||||
|
endpoint?: string | undefined;
|
||||||
|
};
|
||||||
|
provider: "local_disk" | "s3";
|
||||||
|
localDisk: {
|
||||||
|
baseDir: string;
|
||||||
|
};
|
||||||
|
}, {
|
||||||
|
s3?: {
|
||||||
|
bucket?: string | undefined;
|
||||||
|
region?: string | undefined;
|
||||||
|
endpoint?: string | undefined;
|
||||||
|
prefix?: string | undefined;
|
||||||
|
forcePathStyle?: boolean | undefined;
|
||||||
|
} | undefined;
|
||||||
|
provider?: "local_disk" | "s3" | undefined;
|
||||||
|
localDisk?: {
|
||||||
|
baseDir?: string | undefined;
|
||||||
|
} | undefined;
|
||||||
|
}>;
|
||||||
|
export declare const secretsLocalEncryptedConfigSchema: z.ZodObject<{
|
||||||
|
keyFilePath: z.ZodDefault<z.ZodString>;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
keyFilePath: string;
|
||||||
|
}, {
|
||||||
|
keyFilePath?: string | undefined;
|
||||||
|
}>;
|
||||||
|
export declare const secretsConfigSchema: z.ZodObject<{
|
||||||
|
provider: z.ZodDefault<z.ZodEnum<["local_encrypted", "aws_secrets_manager", "gcp_secret_manager", "vault"]>>;
|
||||||
|
strictMode: z.ZodDefault<z.ZodBoolean>;
|
||||||
|
localEncrypted: z.ZodDefault<z.ZodObject<{
|
||||||
|
keyFilePath: z.ZodDefault<z.ZodString>;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
keyFilePath: string;
|
||||||
|
}, {
|
||||||
|
keyFilePath?: string | undefined;
|
||||||
|
}>>;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
provider: "local_encrypted" | "aws_secrets_manager" | "gcp_secret_manager" | "vault";
|
||||||
|
strictMode: boolean;
|
||||||
|
localEncrypted: {
|
||||||
|
keyFilePath: string;
|
||||||
|
};
|
||||||
|
}, {
|
||||||
|
provider?: "local_encrypted" | "aws_secrets_manager" | "gcp_secret_manager" | "vault" | undefined;
|
||||||
|
strictMode?: boolean | undefined;
|
||||||
|
localEncrypted?: {
|
||||||
|
keyFilePath?: string | undefined;
|
||||||
|
} | undefined;
|
||||||
|
}>;
|
||||||
|
export declare const paperclipConfigSchema: z.ZodEffects<z.ZodObject<{
|
||||||
|
$meta: z.ZodObject<{
|
||||||
|
version: z.ZodLiteral<1>;
|
||||||
|
updatedAt: z.ZodString;
|
||||||
|
source: z.ZodEnum<["onboard", "configure", "doctor"]>;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
version: 1;
|
||||||
|
updatedAt: string;
|
||||||
|
source: "onboard" | "configure" | "doctor";
|
||||||
|
}, {
|
||||||
|
version: 1;
|
||||||
|
updatedAt: string;
|
||||||
|
source: "onboard" | "configure" | "doctor";
|
||||||
|
}>;
|
||||||
|
llm: z.ZodOptional<z.ZodObject<{
|
||||||
|
provider: z.ZodEnum<["claude", "openai"]>;
|
||||||
|
apiKey: z.ZodOptional<z.ZodString>;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
provider: "claude" | "openai";
|
||||||
|
apiKey?: string | undefined;
|
||||||
|
}, {
|
||||||
|
provider: "claude" | "openai";
|
||||||
|
apiKey?: string | undefined;
|
||||||
|
}>>;
|
||||||
|
database: z.ZodObject<{
|
||||||
|
mode: z.ZodDefault<z.ZodEnum<["embedded-postgres", "postgres"]>>;
|
||||||
|
connectionString: z.ZodOptional<z.ZodString>;
|
||||||
|
embeddedPostgresDataDir: z.ZodDefault<z.ZodString>;
|
||||||
|
embeddedPostgresPort: z.ZodDefault<z.ZodNumber>;
|
||||||
|
backup: z.ZodDefault<z.ZodObject<{
|
||||||
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
||||||
|
intervalMinutes: z.ZodDefault<z.ZodNumber>;
|
||||||
|
retentionDays: z.ZodDefault<z.ZodNumber>;
|
||||||
|
dir: z.ZodDefault<z.ZodString>;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
enabled: boolean;
|
||||||
|
intervalMinutes: number;
|
||||||
|
retentionDays: number;
|
||||||
|
dir: string;
|
||||||
|
}, {
|
||||||
|
enabled?: boolean | undefined;
|
||||||
|
intervalMinutes?: number | undefined;
|
||||||
|
retentionDays?: number | undefined;
|
||||||
|
dir?: string | undefined;
|
||||||
|
}>>;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
mode: "embedded-postgres" | "postgres";
|
||||||
|
embeddedPostgresDataDir: string;
|
||||||
|
embeddedPostgresPort: number;
|
||||||
|
backup: {
|
||||||
|
enabled: boolean;
|
||||||
|
intervalMinutes: number;
|
||||||
|
retentionDays: number;
|
||||||
|
dir: string;
|
||||||
|
};
|
||||||
|
connectionString?: string | undefined;
|
||||||
|
}, {
|
||||||
|
mode?: "embedded-postgres" | "postgres" | undefined;
|
||||||
|
connectionString?: string | undefined;
|
||||||
|
embeddedPostgresDataDir?: string | undefined;
|
||||||
|
embeddedPostgresPort?: number | undefined;
|
||||||
|
backup?: {
|
||||||
|
enabled?: boolean | undefined;
|
||||||
|
intervalMinutes?: number | undefined;
|
||||||
|
retentionDays?: number | undefined;
|
||||||
|
dir?: string | undefined;
|
||||||
|
} | undefined;
|
||||||
|
}>;
|
||||||
|
logging: z.ZodObject<{
|
||||||
|
mode: z.ZodEnum<["file", "cloud"]>;
|
||||||
|
logDir: z.ZodDefault<z.ZodString>;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
mode: "file" | "cloud";
|
||||||
|
logDir: string;
|
||||||
|
}, {
|
||||||
|
mode: "file" | "cloud";
|
||||||
|
logDir?: string | undefined;
|
||||||
|
}>;
|
||||||
|
server: z.ZodObject<{
|
||||||
|
deploymentMode: z.ZodDefault<z.ZodEnum<["local_trusted", "authenticated"]>>;
|
||||||
|
exposure: z.ZodDefault<z.ZodEnum<["private", "public"]>>;
|
||||||
|
host: z.ZodDefault<z.ZodString>;
|
||||||
|
port: z.ZodDefault<z.ZodNumber>;
|
||||||
|
allowedHostnames: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
||||||
|
serveUi: z.ZodDefault<z.ZodBoolean>;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
deploymentMode: "local_trusted" | "authenticated";
|
||||||
|
exposure: "private" | "public";
|
||||||
|
host: string;
|
||||||
|
port: number;
|
||||||
|
allowedHostnames: string[];
|
||||||
|
serveUi: boolean;
|
||||||
|
}, {
|
||||||
|
deploymentMode?: "local_trusted" | "authenticated" | undefined;
|
||||||
|
exposure?: "private" | "public" | undefined;
|
||||||
|
host?: string | undefined;
|
||||||
|
port?: number | undefined;
|
||||||
|
allowedHostnames?: string[] | undefined;
|
||||||
|
serveUi?: boolean | undefined;
|
||||||
|
}>;
|
||||||
|
auth: z.ZodDefault<z.ZodObject<{
|
||||||
|
baseUrlMode: z.ZodDefault<z.ZodEnum<["auto", "explicit"]>>;
|
||||||
|
publicBaseUrl: z.ZodOptional<z.ZodString>;
|
||||||
|
disableSignUp: z.ZodDefault<z.ZodBoolean>;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
baseUrlMode: "auto" | "explicit";
|
||||||
|
disableSignUp: boolean;
|
||||||
|
publicBaseUrl?: string | undefined;
|
||||||
|
}, {
|
||||||
|
baseUrlMode?: "auto" | "explicit" | undefined;
|
||||||
|
publicBaseUrl?: string | undefined;
|
||||||
|
disableSignUp?: boolean | undefined;
|
||||||
|
}>>;
|
||||||
|
storage: z.ZodDefault<z.ZodObject<{
|
||||||
|
provider: z.ZodDefault<z.ZodEnum<["local_disk", "s3"]>>;
|
||||||
|
localDisk: z.ZodDefault<z.ZodObject<{
|
||||||
|
baseDir: z.ZodDefault<z.ZodString>;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
baseDir: string;
|
||||||
|
}, {
|
||||||
|
baseDir?: string | undefined;
|
||||||
|
}>>;
|
||||||
|
s3: z.ZodDefault<z.ZodObject<{
|
||||||
|
bucket: z.ZodDefault<z.ZodString>;
|
||||||
|
region: z.ZodDefault<z.ZodString>;
|
||||||
|
endpoint: z.ZodOptional<z.ZodString>;
|
||||||
|
prefix: z.ZodDefault<z.ZodString>;
|
||||||
|
forcePathStyle: z.ZodDefault<z.ZodBoolean>;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
bucket: string;
|
||||||
|
region: string;
|
||||||
|
prefix: string;
|
||||||
|
forcePathStyle: boolean;
|
||||||
|
endpoint?: string | undefined;
|
||||||
|
}, {
|
||||||
|
bucket?: string | undefined;
|
||||||
|
region?: string | undefined;
|
||||||
|
endpoint?: string | undefined;
|
||||||
|
prefix?: string | undefined;
|
||||||
|
forcePathStyle?: boolean | undefined;
|
||||||
|
}>>;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
s3: {
|
||||||
|
bucket: string;
|
||||||
|
region: string;
|
||||||
|
prefix: string;
|
||||||
|
forcePathStyle: boolean;
|
||||||
|
endpoint?: string | undefined;
|
||||||
|
};
|
||||||
|
provider: "local_disk" | "s3";
|
||||||
|
localDisk: {
|
||||||
|
baseDir: string;
|
||||||
|
};
|
||||||
|
}, {
|
||||||
|
s3?: {
|
||||||
|
bucket?: string | undefined;
|
||||||
|
region?: string | undefined;
|
||||||
|
endpoint?: string | undefined;
|
||||||
|
prefix?: string | undefined;
|
||||||
|
forcePathStyle?: boolean | undefined;
|
||||||
|
} | undefined;
|
||||||
|
provider?: "local_disk" | "s3" | undefined;
|
||||||
|
localDisk?: {
|
||||||
|
baseDir?: string | undefined;
|
||||||
|
} | undefined;
|
||||||
|
}>>;
|
||||||
|
secrets: z.ZodDefault<z.ZodObject<{
|
||||||
|
provider: z.ZodDefault<z.ZodEnum<["local_encrypted", "aws_secrets_manager", "gcp_secret_manager", "vault"]>>;
|
||||||
|
strictMode: z.ZodDefault<z.ZodBoolean>;
|
||||||
|
localEncrypted: z.ZodDefault<z.ZodObject<{
|
||||||
|
keyFilePath: z.ZodDefault<z.ZodString>;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
keyFilePath: string;
|
||||||
|
}, {
|
||||||
|
keyFilePath?: string | undefined;
|
||||||
|
}>>;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
provider: "local_encrypted" | "aws_secrets_manager" | "gcp_secret_manager" | "vault";
|
||||||
|
strictMode: boolean;
|
||||||
|
localEncrypted: {
|
||||||
|
keyFilePath: string;
|
||||||
|
};
|
||||||
|
}, {
|
||||||
|
provider?: "local_encrypted" | "aws_secrets_manager" | "gcp_secret_manager" | "vault" | undefined;
|
||||||
|
strictMode?: boolean | undefined;
|
||||||
|
localEncrypted?: {
|
||||||
|
keyFilePath?: string | undefined;
|
||||||
|
} | undefined;
|
||||||
|
}>>;
|
||||||
|
}, "strip", z.ZodTypeAny, {
|
||||||
|
database: {
|
||||||
|
mode: "embedded-postgres" | "postgres";
|
||||||
|
embeddedPostgresDataDir: string;
|
||||||
|
embeddedPostgresPort: number;
|
||||||
|
backup: {
|
||||||
|
enabled: boolean;
|
||||||
|
intervalMinutes: number;
|
||||||
|
retentionDays: number;
|
||||||
|
dir: string;
|
||||||
|
};
|
||||||
|
connectionString?: string | undefined;
|
||||||
|
};
|
||||||
|
$meta: {
|
||||||
|
version: 1;
|
||||||
|
updatedAt: string;
|
||||||
|
source: "onboard" | "configure" | "doctor";
|
||||||
|
};
|
||||||
|
logging: {
|
||||||
|
mode: "file" | "cloud";
|
||||||
|
logDir: string;
|
||||||
|
};
|
||||||
|
server: {
|
||||||
|
deploymentMode: "local_trusted" | "authenticated";
|
||||||
|
exposure: "private" | "public";
|
||||||
|
host: string;
|
||||||
|
port: number;
|
||||||
|
allowedHostnames: string[];
|
||||||
|
serveUi: boolean;
|
||||||
|
};
|
||||||
|
auth: {
|
||||||
|
baseUrlMode: "auto" | "explicit";
|
||||||
|
disableSignUp: boolean;
|
||||||
|
publicBaseUrl?: string | undefined;
|
||||||
|
};
|
||||||
|
storage: {
|
||||||
|
s3: {
|
||||||
|
bucket: string;
|
||||||
|
region: string;
|
||||||
|
prefix: string;
|
||||||
|
forcePathStyle: boolean;
|
||||||
|
endpoint?: string | undefined;
|
||||||
|
};
|
||||||
|
provider: "local_disk" | "s3";
|
||||||
|
localDisk: {
|
||||||
|
baseDir: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
secrets: {
|
||||||
|
provider: "local_encrypted" | "aws_secrets_manager" | "gcp_secret_manager" | "vault";
|
||||||
|
strictMode: boolean;
|
||||||
|
localEncrypted: {
|
||||||
|
keyFilePath: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
llm?: {
|
||||||
|
provider: "claude" | "openai";
|
||||||
|
apiKey?: string | undefined;
|
||||||
|
} | undefined;
|
||||||
|
}, {
|
||||||
|
database: {
|
||||||
|
mode?: "embedded-postgres" | "postgres" | undefined;
|
||||||
|
connectionString?: string | undefined;
|
||||||
|
embeddedPostgresDataDir?: string | undefined;
|
||||||
|
embeddedPostgresPort?: number | undefined;
|
||||||
|
backup?: {
|
||||||
|
enabled?: boolean | undefined;
|
||||||
|
intervalMinutes?: number | undefined;
|
||||||
|
retentionDays?: number | undefined;
|
||||||
|
dir?: string | undefined;
|
||||||
|
} | undefined;
|
||||||
|
};
|
||||||
|
$meta: {
|
||||||
|
version: 1;
|
||||||
|
updatedAt: string;
|
||||||
|
source: "onboard" | "configure" | "doctor";
|
||||||
|
};
|
||||||
|
logging: {
|
||||||
|
mode: "file" | "cloud";
|
||||||
|
logDir?: string | undefined;
|
||||||
|
};
|
||||||
|
server: {
|
||||||
|
deploymentMode?: "local_trusted" | "authenticated" | undefined;
|
||||||
|
exposure?: "private" | "public" | undefined;
|
||||||
|
host?: string | undefined;
|
||||||
|
port?: number | undefined;
|
||||||
|
allowedHostnames?: string[] | undefined;
|
||||||
|
serveUi?: boolean | undefined;
|
||||||
|
};
|
||||||
|
llm?: {
|
||||||
|
provider: "claude" | "openai";
|
||||||
|
apiKey?: string | undefined;
|
||||||
|
} | undefined;
|
||||||
|
auth?: {
|
||||||
|
baseUrlMode?: "auto" | "explicit" | undefined;
|
||||||
|
publicBaseUrl?: string | undefined;
|
||||||
|
disableSignUp?: boolean | undefined;
|
||||||
|
} | undefined;
|
||||||
|
storage?: {
|
||||||
|
s3?: {
|
||||||
|
bucket?: string | undefined;
|
||||||
|
region?: string | undefined;
|
||||||
|
endpoint?: string | undefined;
|
||||||
|
prefix?: string | undefined;
|
||||||
|
forcePathStyle?: boolean | undefined;
|
||||||
|
} | undefined;
|
||||||
|
provider?: "local_disk" | "s3" | undefined;
|
||||||
|
localDisk?: {
|
||||||
|
baseDir?: string | undefined;
|
||||||
|
} | undefined;
|
||||||
|
} | undefined;
|
||||||
|
secrets?: {
|
||||||
|
provider?: "local_encrypted" | "aws_secrets_manager" | "gcp_secret_manager" | "vault" | undefined;
|
||||||
|
strictMode?: boolean | undefined;
|
||||||
|
localEncrypted?: {
|
||||||
|
keyFilePath?: string | undefined;
|
||||||
|
} | undefined;
|
||||||
|
} | undefined;
|
||||||
|
}>, {
|
||||||
|
database: {
|
||||||
|
mode: "embedded-postgres" | "postgres";
|
||||||
|
embeddedPostgresDataDir: string;
|
||||||
|
embeddedPostgresPort: number;
|
||||||
|
backup: {
|
||||||
|
enabled: boolean;
|
||||||
|
intervalMinutes: number;
|
||||||
|
retentionDays: number;
|
||||||
|
dir: string;
|
||||||
|
};
|
||||||
|
connectionString?: string | undefined;
|
||||||
|
};
|
||||||
|
$meta: {
|
||||||
|
version: 1;
|
||||||
|
updatedAt: string;
|
||||||
|
source: "onboard" | "configure" | "doctor";
|
||||||
|
};
|
||||||
|
logging: {
|
||||||
|
mode: "file" | "cloud";
|
||||||
|
logDir: string;
|
||||||
|
};
|
||||||
|
server: {
|
||||||
|
deploymentMode: "local_trusted" | "authenticated";
|
||||||
|
exposure: "private" | "public";
|
||||||
|
host: string;
|
||||||
|
port: number;
|
||||||
|
allowedHostnames: string[];
|
||||||
|
serveUi: boolean;
|
||||||
|
};
|
||||||
|
auth: {
|
||||||
|
baseUrlMode: "auto" | "explicit";
|
||||||
|
disableSignUp: boolean;
|
||||||
|
publicBaseUrl?: string | undefined;
|
||||||
|
};
|
||||||
|
storage: {
|
||||||
|
s3: {
|
||||||
|
bucket: string;
|
||||||
|
region: string;
|
||||||
|
prefix: string;
|
||||||
|
forcePathStyle: boolean;
|
||||||
|
endpoint?: string | undefined;
|
||||||
|
};
|
||||||
|
provider: "local_disk" | "s3";
|
||||||
|
localDisk: {
|
||||||
|
baseDir: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
secrets: {
|
||||||
|
provider: "local_encrypted" | "aws_secrets_manager" | "gcp_secret_manager" | "vault";
|
||||||
|
strictMode: boolean;
|
||||||
|
localEncrypted: {
|
||||||
|
keyFilePath: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
llm?: {
|
||||||
|
provider: "claude" | "openai";
|
||||||
|
apiKey?: string | undefined;
|
||||||
|
} | undefined;
|
||||||
|
}, {
|
||||||
|
database: {
|
||||||
|
mode?: "embedded-postgres" | "postgres" | undefined;
|
||||||
|
connectionString?: string | undefined;
|
||||||
|
embeddedPostgresDataDir?: string | undefined;
|
||||||
|
embeddedPostgresPort?: number | undefined;
|
||||||
|
backup?: {
|
||||||
|
enabled?: boolean | undefined;
|
||||||
|
intervalMinutes?: number | undefined;
|
||||||
|
retentionDays?: number | undefined;
|
||||||
|
dir?: string | undefined;
|
||||||
|
} | undefined;
|
||||||
|
};
|
||||||
|
$meta: {
|
||||||
|
version: 1;
|
||||||
|
updatedAt: string;
|
||||||
|
source: "onboard" | "configure" | "doctor";
|
||||||
|
};
|
||||||
|
logging: {
|
||||||
|
mode: "file" | "cloud";
|
||||||
|
logDir?: string | undefined;
|
||||||
|
};
|
||||||
|
server: {
|
||||||
|
deploymentMode?: "local_trusted" | "authenticated" | undefined;
|
||||||
|
exposure?: "private" | "public" | undefined;
|
||||||
|
host?: string | undefined;
|
||||||
|
port?: number | undefined;
|
||||||
|
allowedHostnames?: string[] | undefined;
|
||||||
|
serveUi?: boolean | undefined;
|
||||||
|
};
|
||||||
|
llm?: {
|
||||||
|
provider: "claude" | "openai";
|
||||||
|
apiKey?: string | undefined;
|
||||||
|
} | undefined;
|
||||||
|
auth?: {
|
||||||
|
baseUrlMode?: "auto" | "explicit" | undefined;
|
||||||
|
publicBaseUrl?: string | undefined;
|
||||||
|
disableSignUp?: boolean | undefined;
|
||||||
|
} | undefined;
|
||||||
|
storage?: {
|
||||||
|
s3?: {
|
||||||
|
bucket?: string | undefined;
|
||||||
|
region?: string | undefined;
|
||||||
|
endpoint?: string | undefined;
|
||||||
|
prefix?: string | undefined;
|
||||||
|
forcePathStyle?: boolean | undefined;
|
||||||
|
} | undefined;
|
||||||
|
provider?: "local_disk" | "s3" | undefined;
|
||||||
|
localDisk?: {
|
||||||
|
baseDir?: string | undefined;
|
||||||
|
} | undefined;
|
||||||
|
} | undefined;
|
||||||
|
secrets?: {
|
||||||
|
provider?: "local_encrypted" | "aws_secrets_manager" | "gcp_secret_manager" | "vault" | undefined;
|
||||||
|
strictMode?: boolean | undefined;
|
||||||
|
localEncrypted?: {
|
||||||
|
keyFilePath?: string | undefined;
|
||||||
|
} | undefined;
|
||||||
|
} | undefined;
|
||||||
|
}>;
|
||||||
|
export type PaperclipConfig = z.infer<typeof paperclipConfigSchema>;
|
||||||
|
export type LlmConfig = z.infer<typeof llmConfigSchema>;
|
||||||
|
export type DatabaseConfig = z.infer<typeof databaseConfigSchema>;
|
||||||
|
export type LoggingConfig = z.infer<typeof loggingConfigSchema>;
|
||||||
|
export type ServerConfig = z.infer<typeof serverConfigSchema>;
|
||||||
|
export type StorageConfig = z.infer<typeof storageConfigSchema>;
|
||||||
|
export type StorageLocalDiskConfig = z.infer<typeof storageLocalDiskConfigSchema>;
|
||||||
|
export type StorageS3Config = z.infer<typeof storageS3ConfigSchema>;
|
||||||
|
export type SecretsConfig = z.infer<typeof secretsConfigSchema>;
|
||||||
|
export type SecretsLocalEncryptedConfig = z.infer<typeof secretsLocalEncryptedConfigSchema>;
|
||||||
|
export type AuthConfig = z.infer<typeof authConfigSchema>;
|
||||||
|
export type ConfigMeta = z.infer<typeof configMetaSchema>;
|
||||||
|
export type DatabaseBackupConfig = z.infer<typeof databaseBackupConfigSchema>;
|
||||||
|
//# sourceMappingURL=config-schema.d.ts.map
|
||||||
1
node_modules/@paperclipai/shared/dist/config-schema.d.ts.map
generated
vendored
Normal file
1
node_modules/@paperclipai/shared/dist/config-schema.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"config-schema.d.ts","sourceRoot":"","sources":["../src/config-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AASxB,eAAO,MAAM,gBAAgB;;;;;;;;;;;;EAI3B,CAAC;AAEH,eAAO,MAAM,eAAe;;;;;;;;;EAG1B,CAAC;AAEH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;EAKrC,CAAC;AAEH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAW/B,CAAC;AAEH,eAAO,MAAM,mBAAmB;;;;;;;;;EAG9B,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;EAO7B,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;EAI3B,CAAC;AAEH,eAAO,MAAM,4BAA4B;;;;;;EAEvC,CAAC;AAEH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;EAMhC,CAAC;AAEH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAW9B,CAAC;AAEH,eAAO,MAAM,iCAAiC;;;;;;EAE5C,CAAC;AAEH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;EAM9B,CAAC;AAEH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkE9B,CAAC;AAEL,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACpE,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AACxD,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAClE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAChE,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAC9D,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAChE,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAClF,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACpE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAChE,MAAM,MAAM,2BAA2B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iCAAiC,CAAC,CAAC;AAC5F,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC1D,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC1D,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC"}
|
||||||
143
node_modules/@paperclipai/shared/dist/config-schema.js
generated
vendored
Normal file
143
node_modules/@paperclipai/shared/dist/config-schema.js
generated
vendored
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
import { AUTH_BASE_URL_MODES, DEPLOYMENT_EXPOSURES, DEPLOYMENT_MODES, SECRET_PROVIDERS, STORAGE_PROVIDERS, } from "./constants.js";
|
||||||
|
export const configMetaSchema = z.object({
|
||||||
|
version: z.literal(1),
|
||||||
|
updatedAt: z.string(),
|
||||||
|
source: z.enum(["onboard", "configure", "doctor"]),
|
||||||
|
});
|
||||||
|
export const llmConfigSchema = z.object({
|
||||||
|
provider: z.enum(["claude", "openai"]),
|
||||||
|
apiKey: z.string().optional(),
|
||||||
|
});
|
||||||
|
export const databaseBackupConfigSchema = z.object({
|
||||||
|
enabled: z.boolean().default(true),
|
||||||
|
intervalMinutes: z.number().int().min(1).max(7 * 24 * 60).default(60),
|
||||||
|
retentionDays: z.number().int().min(1).max(3650).default(30),
|
||||||
|
dir: z.string().default("~/.paperclip/instances/default/data/backups"),
|
||||||
|
});
|
||||||
|
export const databaseConfigSchema = z.object({
|
||||||
|
mode: z.enum(["embedded-postgres", "postgres"]).default("embedded-postgres"),
|
||||||
|
connectionString: z.string().optional(),
|
||||||
|
embeddedPostgresDataDir: z.string().default("~/.paperclip/instances/default/db"),
|
||||||
|
embeddedPostgresPort: z.number().int().min(1).max(65535).default(54329),
|
||||||
|
backup: databaseBackupConfigSchema.default({
|
||||||
|
enabled: true,
|
||||||
|
intervalMinutes: 60,
|
||||||
|
retentionDays: 30,
|
||||||
|
dir: "~/.paperclip/instances/default/data/backups",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
export const loggingConfigSchema = z.object({
|
||||||
|
mode: z.enum(["file", "cloud"]),
|
||||||
|
logDir: z.string().default("~/.paperclip/instances/default/logs"),
|
||||||
|
});
|
||||||
|
export const serverConfigSchema = z.object({
|
||||||
|
deploymentMode: z.enum(DEPLOYMENT_MODES).default("local_trusted"),
|
||||||
|
exposure: z.enum(DEPLOYMENT_EXPOSURES).default("private"),
|
||||||
|
host: z.string().default("127.0.0.1"),
|
||||||
|
port: z.number().int().min(1).max(65535).default(3100),
|
||||||
|
allowedHostnames: z.array(z.string().min(1)).default([]),
|
||||||
|
serveUi: z.boolean().default(true),
|
||||||
|
});
|
||||||
|
export const authConfigSchema = z.object({
|
||||||
|
baseUrlMode: z.enum(AUTH_BASE_URL_MODES).default("auto"),
|
||||||
|
publicBaseUrl: z.string().url().optional(),
|
||||||
|
disableSignUp: z.boolean().default(false),
|
||||||
|
});
|
||||||
|
export const storageLocalDiskConfigSchema = z.object({
|
||||||
|
baseDir: z.string().default("~/.paperclip/instances/default/data/storage"),
|
||||||
|
});
|
||||||
|
export const storageS3ConfigSchema = z.object({
|
||||||
|
bucket: z.string().min(1).default("paperclip"),
|
||||||
|
region: z.string().min(1).default("us-east-1"),
|
||||||
|
endpoint: z.string().optional(),
|
||||||
|
prefix: z.string().default(""),
|
||||||
|
forcePathStyle: z.boolean().default(false),
|
||||||
|
});
|
||||||
|
export const storageConfigSchema = z.object({
|
||||||
|
provider: z.enum(STORAGE_PROVIDERS).default("local_disk"),
|
||||||
|
localDisk: storageLocalDiskConfigSchema.default({
|
||||||
|
baseDir: "~/.paperclip/instances/default/data/storage",
|
||||||
|
}),
|
||||||
|
s3: storageS3ConfigSchema.default({
|
||||||
|
bucket: "paperclip",
|
||||||
|
region: "us-east-1",
|
||||||
|
prefix: "",
|
||||||
|
forcePathStyle: false,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
export const secretsLocalEncryptedConfigSchema = z.object({
|
||||||
|
keyFilePath: z.string().default("~/.paperclip/instances/default/secrets/master.key"),
|
||||||
|
});
|
||||||
|
export const secretsConfigSchema = z.object({
|
||||||
|
provider: z.enum(SECRET_PROVIDERS).default("local_encrypted"),
|
||||||
|
strictMode: z.boolean().default(false),
|
||||||
|
localEncrypted: secretsLocalEncryptedConfigSchema.default({
|
||||||
|
keyFilePath: "~/.paperclip/instances/default/secrets/master.key",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
export const paperclipConfigSchema = z
|
||||||
|
.object({
|
||||||
|
$meta: configMetaSchema,
|
||||||
|
llm: llmConfigSchema.optional(),
|
||||||
|
database: databaseConfigSchema,
|
||||||
|
logging: loggingConfigSchema,
|
||||||
|
server: serverConfigSchema,
|
||||||
|
auth: authConfigSchema.default({
|
||||||
|
baseUrlMode: "auto",
|
||||||
|
disableSignUp: false,
|
||||||
|
}),
|
||||||
|
storage: storageConfigSchema.default({
|
||||||
|
provider: "local_disk",
|
||||||
|
localDisk: {
|
||||||
|
baseDir: "~/.paperclip/instances/default/data/storage",
|
||||||
|
},
|
||||||
|
s3: {
|
||||||
|
bucket: "paperclip",
|
||||||
|
region: "us-east-1",
|
||||||
|
prefix: "",
|
||||||
|
forcePathStyle: false,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
secrets: secretsConfigSchema.default({
|
||||||
|
provider: "local_encrypted",
|
||||||
|
strictMode: false,
|
||||||
|
localEncrypted: {
|
||||||
|
keyFilePath: "~/.paperclip/instances/default/secrets/master.key",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.superRefine((value, ctx) => {
|
||||||
|
if (value.server.deploymentMode === "local_trusted") {
|
||||||
|
if (value.server.exposure !== "private") {
|
||||||
|
ctx.addIssue({
|
||||||
|
code: z.ZodIssueCode.custom,
|
||||||
|
message: "server.exposure must be private when deploymentMode is local_trusted",
|
||||||
|
path: ["server", "exposure"],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (value.auth.baseUrlMode === "explicit" && !value.auth.publicBaseUrl) {
|
||||||
|
ctx.addIssue({
|
||||||
|
code: z.ZodIssueCode.custom,
|
||||||
|
message: "auth.publicBaseUrl is required when auth.baseUrlMode is explicit",
|
||||||
|
path: ["auth", "publicBaseUrl"],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (value.server.exposure === "public" && value.auth.baseUrlMode !== "explicit") {
|
||||||
|
ctx.addIssue({
|
||||||
|
code: z.ZodIssueCode.custom,
|
||||||
|
message: "auth.baseUrlMode must be explicit when deploymentMode=authenticated and exposure=public",
|
||||||
|
path: ["auth", "baseUrlMode"],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (value.server.exposure === "public" && !value.auth.publicBaseUrl) {
|
||||||
|
ctx.addIssue({
|
||||||
|
code: z.ZodIssueCode.custom,
|
||||||
|
message: "auth.publicBaseUrl is required when deploymentMode=authenticated and exposure=public",
|
||||||
|
path: ["auth", "publicBaseUrl"],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
//# sourceMappingURL=config-schema.js.map
|
||||||
1
node_modules/@paperclipai/shared/dist/config-schema.js.map
generated
vendored
Normal file
1
node_modules/@paperclipai/shared/dist/config-schema.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
224
node_modules/@paperclipai/shared/dist/constants.d.ts
generated
vendored
Normal file
224
node_modules/@paperclipai/shared/dist/constants.d.ts
generated
vendored
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
export declare const COMPANY_STATUSES: readonly ["active", "paused", "archived"];
|
||||||
|
export type CompanyStatus = (typeof COMPANY_STATUSES)[number];
|
||||||
|
export declare const DEPLOYMENT_MODES: readonly ["local_trusted", "authenticated"];
|
||||||
|
export type DeploymentMode = (typeof DEPLOYMENT_MODES)[number];
|
||||||
|
export declare const DEPLOYMENT_EXPOSURES: readonly ["private", "public"];
|
||||||
|
export type DeploymentExposure = (typeof DEPLOYMENT_EXPOSURES)[number];
|
||||||
|
export declare const AUTH_BASE_URL_MODES: readonly ["auto", "explicit"];
|
||||||
|
export type AuthBaseUrlMode = (typeof AUTH_BASE_URL_MODES)[number];
|
||||||
|
export declare const AGENT_STATUSES: readonly ["active", "paused", "idle", "running", "error", "pending_approval", "terminated"];
|
||||||
|
export type AgentStatus = (typeof AGENT_STATUSES)[number];
|
||||||
|
export declare const AGENT_ADAPTER_TYPES: readonly ["process", "http", "claude_local", "codex_local", "opencode_local", "pi_local", "cursor", "openclaw_gateway", "hermes_local"];
|
||||||
|
export type AgentAdapterType = (typeof AGENT_ADAPTER_TYPES)[number];
|
||||||
|
export declare const AGENT_ROLES: readonly ["ceo", "cto", "cmo", "cfo", "engineer", "designer", "pm", "qa", "devops", "researcher", "general"];
|
||||||
|
export type AgentRole = (typeof AGENT_ROLES)[number];
|
||||||
|
export declare const AGENT_ROLE_LABELS: Record<AgentRole, string>;
|
||||||
|
export declare const AGENT_ICON_NAMES: readonly ["bot", "cpu", "brain", "zap", "rocket", "code", "terminal", "shield", "eye", "search", "wrench", "hammer", "lightbulb", "sparkles", "star", "heart", "flame", "bug", "cog", "database", "globe", "lock", "mail", "message-square", "file-code", "git-branch", "package", "puzzle", "target", "wand", "atom", "circuit-board", "radar", "swords", "telescope", "microscope", "crown", "gem", "hexagon", "pentagon", "fingerprint"];
|
||||||
|
export type AgentIconName = (typeof AGENT_ICON_NAMES)[number];
|
||||||
|
export declare const ISSUE_STATUSES: readonly ["backlog", "todo", "in_progress", "in_review", "done", "blocked", "cancelled"];
|
||||||
|
export type IssueStatus = (typeof ISSUE_STATUSES)[number];
|
||||||
|
export declare const ISSUE_PRIORITIES: readonly ["critical", "high", "medium", "low"];
|
||||||
|
export type IssuePriority = (typeof ISSUE_PRIORITIES)[number];
|
||||||
|
export declare const ISSUE_ORIGIN_KINDS: readonly ["manual", "routine_execution"];
|
||||||
|
export type IssueOriginKind = (typeof ISSUE_ORIGIN_KINDS)[number];
|
||||||
|
export declare const GOAL_LEVELS: readonly ["company", "team", "agent", "task"];
|
||||||
|
export type GoalLevel = (typeof GOAL_LEVELS)[number];
|
||||||
|
export declare const GOAL_STATUSES: readonly ["planned", "active", "achieved", "cancelled"];
|
||||||
|
export type GoalStatus = (typeof GOAL_STATUSES)[number];
|
||||||
|
export declare const PROJECT_STATUSES: readonly ["backlog", "planned", "in_progress", "completed", "cancelled"];
|
||||||
|
export type ProjectStatus = (typeof PROJECT_STATUSES)[number];
|
||||||
|
export declare const ROUTINE_STATUSES: readonly ["active", "paused", "archived"];
|
||||||
|
export type RoutineStatus = (typeof ROUTINE_STATUSES)[number];
|
||||||
|
export declare const ROUTINE_CONCURRENCY_POLICIES: readonly ["coalesce_if_active", "always_enqueue", "skip_if_active"];
|
||||||
|
export type RoutineConcurrencyPolicy = (typeof ROUTINE_CONCURRENCY_POLICIES)[number];
|
||||||
|
export declare const ROUTINE_CATCH_UP_POLICIES: readonly ["skip_missed", "enqueue_missed_with_cap"];
|
||||||
|
export type RoutineCatchUpPolicy = (typeof ROUTINE_CATCH_UP_POLICIES)[number];
|
||||||
|
export declare const ROUTINE_TRIGGER_KINDS: readonly ["schedule", "webhook", "api"];
|
||||||
|
export type RoutineTriggerKind = (typeof ROUTINE_TRIGGER_KINDS)[number];
|
||||||
|
export declare const ROUTINE_TRIGGER_SIGNING_MODES: readonly ["bearer", "hmac_sha256"];
|
||||||
|
export type RoutineTriggerSigningMode = (typeof ROUTINE_TRIGGER_SIGNING_MODES)[number];
|
||||||
|
export declare const ROUTINE_RUN_STATUSES: readonly ["received", "coalesced", "skipped", "issue_created", "completed", "failed"];
|
||||||
|
export type RoutineRunStatus = (typeof ROUTINE_RUN_STATUSES)[number];
|
||||||
|
export declare const ROUTINE_RUN_SOURCES: readonly ["schedule", "manual", "api", "webhook"];
|
||||||
|
export type RoutineRunSource = (typeof ROUTINE_RUN_SOURCES)[number];
|
||||||
|
export declare const PAUSE_REASONS: readonly ["manual", "budget", "system"];
|
||||||
|
export type PauseReason = (typeof PAUSE_REASONS)[number];
|
||||||
|
export declare const PROJECT_COLORS: readonly ["#6366f1", "#8b5cf6", "#ec4899", "#ef4444", "#f97316", "#eab308", "#22c55e", "#14b8a6", "#06b6d4", "#3b82f6"];
|
||||||
|
export declare const APPROVAL_TYPES: readonly ["hire_agent", "approve_ceo_strategy", "budget_override_required"];
|
||||||
|
export type ApprovalType = (typeof APPROVAL_TYPES)[number];
|
||||||
|
export declare const APPROVAL_STATUSES: readonly ["pending", "revision_requested", "approved", "rejected", "cancelled"];
|
||||||
|
export type ApprovalStatus = (typeof APPROVAL_STATUSES)[number];
|
||||||
|
export declare const SECRET_PROVIDERS: readonly ["local_encrypted", "aws_secrets_manager", "gcp_secret_manager", "vault"];
|
||||||
|
export type SecretProvider = (typeof SECRET_PROVIDERS)[number];
|
||||||
|
export declare const STORAGE_PROVIDERS: readonly ["local_disk", "s3"];
|
||||||
|
export type StorageProvider = (typeof STORAGE_PROVIDERS)[number];
|
||||||
|
export declare const BILLING_TYPES: readonly ["metered_api", "subscription_included", "subscription_overage", "credits", "fixed", "unknown"];
|
||||||
|
export type BillingType = (typeof BILLING_TYPES)[number];
|
||||||
|
export declare const FINANCE_EVENT_KINDS: readonly ["inference_charge", "platform_fee", "credit_purchase", "credit_refund", "credit_expiry", "byok_fee", "gateway_overhead", "log_storage_charge", "logpush_charge", "provisioned_capacity_charge", "training_charge", "custom_model_import_charge", "custom_model_storage_charge", "manual_adjustment"];
|
||||||
|
export type FinanceEventKind = (typeof FINANCE_EVENT_KINDS)[number];
|
||||||
|
export declare const FINANCE_DIRECTIONS: readonly ["debit", "credit"];
|
||||||
|
export type FinanceDirection = (typeof FINANCE_DIRECTIONS)[number];
|
||||||
|
export declare const FINANCE_UNITS: readonly ["input_token", "output_token", "cached_input_token", "request", "credit_usd", "credit_unit", "model_unit_minute", "model_unit_hour", "gb_month", "train_token", "unknown"];
|
||||||
|
export type FinanceUnit = (typeof FINANCE_UNITS)[number];
|
||||||
|
export declare const BUDGET_SCOPE_TYPES: readonly ["company", "agent", "project"];
|
||||||
|
export type BudgetScopeType = (typeof BUDGET_SCOPE_TYPES)[number];
|
||||||
|
export declare const BUDGET_METRICS: readonly ["billed_cents"];
|
||||||
|
export type BudgetMetric = (typeof BUDGET_METRICS)[number];
|
||||||
|
export declare const BUDGET_WINDOW_KINDS: readonly ["calendar_month_utc", "lifetime"];
|
||||||
|
export type BudgetWindowKind = (typeof BUDGET_WINDOW_KINDS)[number];
|
||||||
|
export declare const BUDGET_THRESHOLD_TYPES: readonly ["soft", "hard"];
|
||||||
|
export type BudgetThresholdType = (typeof BUDGET_THRESHOLD_TYPES)[number];
|
||||||
|
export declare const BUDGET_INCIDENT_STATUSES: readonly ["open", "resolved", "dismissed"];
|
||||||
|
export type BudgetIncidentStatus = (typeof BUDGET_INCIDENT_STATUSES)[number];
|
||||||
|
export declare const BUDGET_INCIDENT_RESOLUTION_ACTIONS: readonly ["keep_paused", "raise_budget_and_resume"];
|
||||||
|
export type BudgetIncidentResolutionAction = (typeof BUDGET_INCIDENT_RESOLUTION_ACTIONS)[number];
|
||||||
|
export declare const HEARTBEAT_INVOCATION_SOURCES: readonly ["timer", "assignment", "on_demand", "automation"];
|
||||||
|
export type HeartbeatInvocationSource = (typeof HEARTBEAT_INVOCATION_SOURCES)[number];
|
||||||
|
export declare const WAKEUP_TRIGGER_DETAILS: readonly ["manual", "ping", "callback", "system"];
|
||||||
|
export type WakeupTriggerDetail = (typeof WAKEUP_TRIGGER_DETAILS)[number];
|
||||||
|
export declare const WAKEUP_REQUEST_STATUSES: readonly ["queued", "deferred_issue_execution", "claimed", "coalesced", "skipped", "completed", "failed", "cancelled"];
|
||||||
|
export type WakeupRequestStatus = (typeof WAKEUP_REQUEST_STATUSES)[number];
|
||||||
|
export declare const HEARTBEAT_RUN_STATUSES: readonly ["queued", "running", "succeeded", "failed", "cancelled", "timed_out"];
|
||||||
|
export type HeartbeatRunStatus = (typeof HEARTBEAT_RUN_STATUSES)[number];
|
||||||
|
export declare const LIVE_EVENT_TYPES: readonly ["heartbeat.run.queued", "heartbeat.run.status", "heartbeat.run.event", "heartbeat.run.log", "agent.status", "activity.logged", "plugin.ui.updated", "plugin.worker.crashed", "plugin.worker.restarted"];
|
||||||
|
export type LiveEventType = (typeof LIVE_EVENT_TYPES)[number];
|
||||||
|
export declare const PRINCIPAL_TYPES: readonly ["user", "agent"];
|
||||||
|
export type PrincipalType = (typeof PRINCIPAL_TYPES)[number];
|
||||||
|
export declare const MEMBERSHIP_STATUSES: readonly ["pending", "active", "suspended"];
|
||||||
|
export type MembershipStatus = (typeof MEMBERSHIP_STATUSES)[number];
|
||||||
|
export declare const INSTANCE_USER_ROLES: readonly ["instance_admin"];
|
||||||
|
export type InstanceUserRole = (typeof INSTANCE_USER_ROLES)[number];
|
||||||
|
export declare const INVITE_TYPES: readonly ["company_join", "bootstrap_ceo"];
|
||||||
|
export type InviteType = (typeof INVITE_TYPES)[number];
|
||||||
|
export declare const INVITE_JOIN_TYPES: readonly ["human", "agent", "both"];
|
||||||
|
export type InviteJoinType = (typeof INVITE_JOIN_TYPES)[number];
|
||||||
|
export declare const JOIN_REQUEST_TYPES: readonly ["human", "agent"];
|
||||||
|
export type JoinRequestType = (typeof JOIN_REQUEST_TYPES)[number];
|
||||||
|
export declare const JOIN_REQUEST_STATUSES: readonly ["pending_approval", "approved", "rejected"];
|
||||||
|
export type JoinRequestStatus = (typeof JOIN_REQUEST_STATUSES)[number];
|
||||||
|
export declare const PERMISSION_KEYS: readonly ["agents:create", "users:invite", "users:manage_permissions", "tasks:assign", "tasks:assign_scope", "joins:approve"];
|
||||||
|
export type PermissionKey = (typeof PERMISSION_KEYS)[number];
|
||||||
|
/**
|
||||||
|
* The current version of the Plugin API contract.
|
||||||
|
*
|
||||||
|
* Increment this value whenever a breaking change is made to the plugin API
|
||||||
|
* so that the host can reject incompatible plugin manifests.
|
||||||
|
*
|
||||||
|
* @see PLUGIN_SPEC.md §4 — Versioning
|
||||||
|
*/
|
||||||
|
export declare const PLUGIN_API_VERSION: 1;
|
||||||
|
/**
|
||||||
|
* Lifecycle statuses for an installed plugin.
|
||||||
|
*
|
||||||
|
* State machine: installed → ready | error, ready → disabled | error | upgrade_pending | uninstalled,
|
||||||
|
* disabled → ready | uninstalled, error → ready | uninstalled,
|
||||||
|
* upgrade_pending → ready | error | uninstalled, uninstalled → installed (reinstall).
|
||||||
|
*
|
||||||
|
* @see {@link PluginStatus} — inferred union type
|
||||||
|
* @see PLUGIN_SPEC.md §21.3 `plugins.status`
|
||||||
|
*/
|
||||||
|
export declare const PLUGIN_STATUSES: readonly ["installed", "ready", "disabled", "error", "upgrade_pending", "uninstalled"];
|
||||||
|
export type PluginStatus = (typeof PLUGIN_STATUSES)[number];
|
||||||
|
/**
|
||||||
|
* Plugin classification categories. A plugin declares one or more categories
|
||||||
|
* in its manifest to describe its primary purpose.
|
||||||
|
*
|
||||||
|
* @see PLUGIN_SPEC.md §6.2
|
||||||
|
*/
|
||||||
|
export declare const PLUGIN_CATEGORIES: readonly ["connector", "workspace", "automation", "ui"];
|
||||||
|
export type PluginCategory = (typeof PLUGIN_CATEGORIES)[number];
|
||||||
|
/**
|
||||||
|
* Named permissions the host grants to a plugin. Plugins declare required
|
||||||
|
* capabilities in their manifest; the host enforces them at runtime via the
|
||||||
|
* plugin capability validator.
|
||||||
|
*
|
||||||
|
* Grouped into: Data Read, Data Write, Plugin State, Runtime/Integration,
|
||||||
|
* Agent Tools, and UI.
|
||||||
|
*
|
||||||
|
* @see PLUGIN_SPEC.md §15 — Capability Model
|
||||||
|
*/
|
||||||
|
export declare const PLUGIN_CAPABILITIES: readonly ["companies.read", "projects.read", "project.workspaces.read", "issues.read", "issue.comments.read", "issue.documents.read", "agents.read", "goals.read", "goals.create", "goals.update", "activity.read", "costs.read", "issues.create", "issues.update", "issue.comments.create", "issue.documents.write", "agents.pause", "agents.resume", "agents.invoke", "agent.sessions.create", "agent.sessions.list", "agent.sessions.send", "agent.sessions.close", "activity.log.write", "metrics.write", "plugin.state.read", "plugin.state.write", "events.subscribe", "events.emit", "jobs.schedule", "webhooks.receive", "http.outbound", "secrets.read-ref", "agent.tools.register", "instance.settings.register", "ui.sidebar.register", "ui.page.register", "ui.detailTab.register", "ui.dashboardWidget.register", "ui.commentAnnotation.register", "ui.action.register"];
|
||||||
|
export type PluginCapability = (typeof PLUGIN_CAPABILITIES)[number];
|
||||||
|
/**
|
||||||
|
* UI extension slot types. Each slot type corresponds to a mount point in the
|
||||||
|
* Paperclip UI where plugin components can be rendered.
|
||||||
|
*
|
||||||
|
* @see PLUGIN_SPEC.md §19 — UI Extension Model
|
||||||
|
*/
|
||||||
|
export declare const PLUGIN_UI_SLOT_TYPES: readonly ["page", "detailTab", "taskDetailView", "dashboardWidget", "sidebar", "sidebarPanel", "projectSidebarItem", "globalToolbarButton", "toolbarButton", "contextMenuItem", "commentAnnotation", "commentContextMenuItem", "settingsPage"];
|
||||||
|
export type PluginUiSlotType = (typeof PLUGIN_UI_SLOT_TYPES)[number];
|
||||||
|
/**
|
||||||
|
* Reserved company-scoped route segments that plugin page routes may not claim.
|
||||||
|
*
|
||||||
|
* These map to first-class host pages under `/:companyPrefix/...`.
|
||||||
|
*/
|
||||||
|
export declare const PLUGIN_RESERVED_COMPANY_ROUTE_SEGMENTS: readonly ["dashboard", "onboarding", "companies", "company", "settings", "plugins", "org", "agents", "projects", "issues", "goals", "approvals", "costs", "activity", "inbox", "design-guide", "tests"];
|
||||||
|
export type PluginReservedCompanyRouteSegment = (typeof PLUGIN_RESERVED_COMPANY_ROUTE_SEGMENTS)[number];
|
||||||
|
/**
|
||||||
|
* Launcher placement zones describe where a plugin-owned launcher can appear
|
||||||
|
* in the host UI. These are intentionally aligned with current slot surfaces
|
||||||
|
* so manifest authors can describe launch intent without coupling to a single
|
||||||
|
* component implementation detail.
|
||||||
|
*/
|
||||||
|
export declare const PLUGIN_LAUNCHER_PLACEMENT_ZONES: readonly ["page", "detailTab", "taskDetailView", "dashboardWidget", "sidebar", "sidebarPanel", "projectSidebarItem", "globalToolbarButton", "toolbarButton", "contextMenuItem", "commentAnnotation", "commentContextMenuItem", "settingsPage"];
|
||||||
|
export type PluginLauncherPlacementZone = (typeof PLUGIN_LAUNCHER_PLACEMENT_ZONES)[number];
|
||||||
|
/**
|
||||||
|
* Launcher action kinds describe what the launcher does when activated.
|
||||||
|
*/
|
||||||
|
export declare const PLUGIN_LAUNCHER_ACTIONS: readonly ["navigate", "openModal", "openDrawer", "openPopover", "performAction", "deepLink"];
|
||||||
|
export type PluginLauncherAction = (typeof PLUGIN_LAUNCHER_ACTIONS)[number];
|
||||||
|
/**
|
||||||
|
* Optional size hints the host can use when rendering plugin-owned launcher
|
||||||
|
* destinations such as overlays, drawers, or full page handoffs.
|
||||||
|
*/
|
||||||
|
export declare const PLUGIN_LAUNCHER_BOUNDS: readonly ["inline", "compact", "default", "wide", "full"];
|
||||||
|
export type PluginLauncherBounds = (typeof PLUGIN_LAUNCHER_BOUNDS)[number];
|
||||||
|
/**
|
||||||
|
* Render environments describe the container a launcher expects after it is
|
||||||
|
* activated. The current host may map these to concrete UI primitives.
|
||||||
|
*/
|
||||||
|
export declare const PLUGIN_LAUNCHER_RENDER_ENVIRONMENTS: readonly ["hostInline", "hostOverlay", "hostRoute", "external", "iframe"];
|
||||||
|
export type PluginLauncherRenderEnvironment = (typeof PLUGIN_LAUNCHER_RENDER_ENVIRONMENTS)[number];
|
||||||
|
/**
|
||||||
|
* Entity types that a `detailTab` UI slot can attach to.
|
||||||
|
*
|
||||||
|
* @see PLUGIN_SPEC.md §19.3 — Detail Tabs
|
||||||
|
*/
|
||||||
|
export declare const PLUGIN_UI_SLOT_ENTITY_TYPES: readonly ["project", "issue", "agent", "goal", "run", "comment"];
|
||||||
|
export type PluginUiSlotEntityType = (typeof PLUGIN_UI_SLOT_ENTITY_TYPES)[number];
|
||||||
|
/**
|
||||||
|
* Scope kinds for plugin state storage. Determines the granularity at which
|
||||||
|
* a plugin stores key-value state data.
|
||||||
|
*
|
||||||
|
* @see PLUGIN_SPEC.md §21.3 `plugin_state.scope_kind`
|
||||||
|
*/
|
||||||
|
export declare const PLUGIN_STATE_SCOPE_KINDS: readonly ["instance", "company", "project", "project_workspace", "agent", "issue", "goal", "run"];
|
||||||
|
export type PluginStateScopeKind = (typeof PLUGIN_STATE_SCOPE_KINDS)[number];
|
||||||
|
/** Statuses for a plugin's scheduled job definition. */
|
||||||
|
export declare const PLUGIN_JOB_STATUSES: readonly ["active", "paused", "failed"];
|
||||||
|
export type PluginJobStatus = (typeof PLUGIN_JOB_STATUSES)[number];
|
||||||
|
/** Statuses for individual job run executions. */
|
||||||
|
export declare const PLUGIN_JOB_RUN_STATUSES: readonly ["pending", "queued", "running", "succeeded", "failed", "cancelled"];
|
||||||
|
export type PluginJobRunStatus = (typeof PLUGIN_JOB_RUN_STATUSES)[number];
|
||||||
|
/** What triggered a particular job run. */
|
||||||
|
export declare const PLUGIN_JOB_RUN_TRIGGERS: readonly ["schedule", "manual", "retry"];
|
||||||
|
export type PluginJobRunTrigger = (typeof PLUGIN_JOB_RUN_TRIGGERS)[number];
|
||||||
|
/** Statuses for inbound webhook deliveries. */
|
||||||
|
export declare const PLUGIN_WEBHOOK_DELIVERY_STATUSES: readonly ["pending", "success", "failed"];
|
||||||
|
export type PluginWebhookDeliveryStatus = (typeof PLUGIN_WEBHOOK_DELIVERY_STATUSES)[number];
|
||||||
|
/**
|
||||||
|
* Core domain event types that plugins can subscribe to via the
|
||||||
|
* `events.subscribe` capability.
|
||||||
|
*
|
||||||
|
* @see PLUGIN_SPEC.md §16 — Event System
|
||||||
|
*/
|
||||||
|
export declare const PLUGIN_EVENT_TYPES: readonly ["company.created", "company.updated", "project.created", "project.updated", "project.workspace_created", "project.workspace_updated", "project.workspace_deleted", "issue.created", "issue.updated", "issue.comment.created", "agent.created", "agent.updated", "agent.status_changed", "agent.run.started", "agent.run.finished", "agent.run.failed", "agent.run.cancelled", "goal.created", "goal.updated", "approval.created", "approval.decided", "cost_event.created", "activity.logged"];
|
||||||
|
export type PluginEventType = (typeof PLUGIN_EVENT_TYPES)[number];
|
||||||
|
/**
|
||||||
|
* Error codes returned by the plugin bridge when a UI → worker call fails.
|
||||||
|
*
|
||||||
|
* @see PLUGIN_SPEC.md §19.7 — Error Propagation Through The Bridge
|
||||||
|
*/
|
||||||
|
export declare const PLUGIN_BRIDGE_ERROR_CODES: readonly ["WORKER_UNAVAILABLE", "CAPABILITY_DENIED", "WORKER_ERROR", "TIMEOUT", "UNKNOWN"];
|
||||||
|
export type PluginBridgeErrorCode = (typeof PLUGIN_BRIDGE_ERROR_CODES)[number];
|
||||||
|
//# sourceMappingURL=constants.d.ts.map
|
||||||
1
node_modules/@paperclipai/shared/dist/constants.d.ts.map
generated
vendored
Normal file
1
node_modules/@paperclipai/shared/dist/constants.d.ts.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
553
node_modules/@paperclipai/shared/dist/constants.js
generated
vendored
Normal file
553
node_modules/@paperclipai/shared/dist/constants.js
generated
vendored
Normal file
@@ -0,0 +1,553 @@
|
|||||||
|
export const COMPANY_STATUSES = ["active", "paused", "archived"];
|
||||||
|
export const DEPLOYMENT_MODES = ["local_trusted", "authenticated"];
|
||||||
|
export const DEPLOYMENT_EXPOSURES = ["private", "public"];
|
||||||
|
export const AUTH_BASE_URL_MODES = ["auto", "explicit"];
|
||||||
|
export const AGENT_STATUSES = [
|
||||||
|
"active",
|
||||||
|
"paused",
|
||||||
|
"idle",
|
||||||
|
"running",
|
||||||
|
"error",
|
||||||
|
"pending_approval",
|
||||||
|
"terminated",
|
||||||
|
];
|
||||||
|
export const AGENT_ADAPTER_TYPES = [
|
||||||
|
"process",
|
||||||
|
"http",
|
||||||
|
"claude_local",
|
||||||
|
"codex_local",
|
||||||
|
"opencode_local",
|
||||||
|
"pi_local",
|
||||||
|
"cursor",
|
||||||
|
"openclaw_gateway",
|
||||||
|
"hermes_local",
|
||||||
|
];
|
||||||
|
export const AGENT_ROLES = [
|
||||||
|
"ceo",
|
||||||
|
"cto",
|
||||||
|
"cmo",
|
||||||
|
"cfo",
|
||||||
|
"engineer",
|
||||||
|
"designer",
|
||||||
|
"pm",
|
||||||
|
"qa",
|
||||||
|
"devops",
|
||||||
|
"researcher",
|
||||||
|
"general",
|
||||||
|
];
|
||||||
|
export const AGENT_ROLE_LABELS = {
|
||||||
|
ceo: "CEO",
|
||||||
|
cto: "CTO",
|
||||||
|
cmo: "CMO",
|
||||||
|
cfo: "CFO",
|
||||||
|
engineer: "Engineer",
|
||||||
|
designer: "Designer",
|
||||||
|
pm: "PM",
|
||||||
|
qa: "QA",
|
||||||
|
devops: "DevOps",
|
||||||
|
researcher: "Researcher",
|
||||||
|
general: "General",
|
||||||
|
};
|
||||||
|
export const AGENT_ICON_NAMES = [
|
||||||
|
"bot",
|
||||||
|
"cpu",
|
||||||
|
"brain",
|
||||||
|
"zap",
|
||||||
|
"rocket",
|
||||||
|
"code",
|
||||||
|
"terminal",
|
||||||
|
"shield",
|
||||||
|
"eye",
|
||||||
|
"search",
|
||||||
|
"wrench",
|
||||||
|
"hammer",
|
||||||
|
"lightbulb",
|
||||||
|
"sparkles",
|
||||||
|
"star",
|
||||||
|
"heart",
|
||||||
|
"flame",
|
||||||
|
"bug",
|
||||||
|
"cog",
|
||||||
|
"database",
|
||||||
|
"globe",
|
||||||
|
"lock",
|
||||||
|
"mail",
|
||||||
|
"message-square",
|
||||||
|
"file-code",
|
||||||
|
"git-branch",
|
||||||
|
"package",
|
||||||
|
"puzzle",
|
||||||
|
"target",
|
||||||
|
"wand",
|
||||||
|
"atom",
|
||||||
|
"circuit-board",
|
||||||
|
"radar",
|
||||||
|
"swords",
|
||||||
|
"telescope",
|
||||||
|
"microscope",
|
||||||
|
"crown",
|
||||||
|
"gem",
|
||||||
|
"hexagon",
|
||||||
|
"pentagon",
|
||||||
|
"fingerprint",
|
||||||
|
];
|
||||||
|
export const ISSUE_STATUSES = [
|
||||||
|
"backlog",
|
||||||
|
"todo",
|
||||||
|
"in_progress",
|
||||||
|
"in_review",
|
||||||
|
"done",
|
||||||
|
"blocked",
|
||||||
|
"cancelled",
|
||||||
|
];
|
||||||
|
export const ISSUE_PRIORITIES = ["critical", "high", "medium", "low"];
|
||||||
|
export const ISSUE_ORIGIN_KINDS = ["manual", "routine_execution"];
|
||||||
|
export const GOAL_LEVELS = ["company", "team", "agent", "task"];
|
||||||
|
export const GOAL_STATUSES = ["planned", "active", "achieved", "cancelled"];
|
||||||
|
export const PROJECT_STATUSES = [
|
||||||
|
"backlog",
|
||||||
|
"planned",
|
||||||
|
"in_progress",
|
||||||
|
"completed",
|
||||||
|
"cancelled",
|
||||||
|
];
|
||||||
|
export const ROUTINE_STATUSES = ["active", "paused", "archived"];
|
||||||
|
export const ROUTINE_CONCURRENCY_POLICIES = ["coalesce_if_active", "always_enqueue", "skip_if_active"];
|
||||||
|
export const ROUTINE_CATCH_UP_POLICIES = ["skip_missed", "enqueue_missed_with_cap"];
|
||||||
|
export const ROUTINE_TRIGGER_KINDS = ["schedule", "webhook", "api"];
|
||||||
|
export const ROUTINE_TRIGGER_SIGNING_MODES = ["bearer", "hmac_sha256"];
|
||||||
|
export const ROUTINE_RUN_STATUSES = [
|
||||||
|
"received",
|
||||||
|
"coalesced",
|
||||||
|
"skipped",
|
||||||
|
"issue_created",
|
||||||
|
"completed",
|
||||||
|
"failed",
|
||||||
|
];
|
||||||
|
export const ROUTINE_RUN_SOURCES = ["schedule", "manual", "api", "webhook"];
|
||||||
|
export const PAUSE_REASONS = ["manual", "budget", "system"];
|
||||||
|
export const PROJECT_COLORS = [
|
||||||
|
"#6366f1", // indigo
|
||||||
|
"#8b5cf6", // violet
|
||||||
|
"#ec4899", // pink
|
||||||
|
"#ef4444", // red
|
||||||
|
"#f97316", // orange
|
||||||
|
"#eab308", // yellow
|
||||||
|
"#22c55e", // green
|
||||||
|
"#14b8a6", // teal
|
||||||
|
"#06b6d4", // cyan
|
||||||
|
"#3b82f6", // blue
|
||||||
|
];
|
||||||
|
export const APPROVAL_TYPES = ["hire_agent", "approve_ceo_strategy", "budget_override_required"];
|
||||||
|
export const APPROVAL_STATUSES = [
|
||||||
|
"pending",
|
||||||
|
"revision_requested",
|
||||||
|
"approved",
|
||||||
|
"rejected",
|
||||||
|
"cancelled",
|
||||||
|
];
|
||||||
|
export const SECRET_PROVIDERS = [
|
||||||
|
"local_encrypted",
|
||||||
|
"aws_secrets_manager",
|
||||||
|
"gcp_secret_manager",
|
||||||
|
"vault",
|
||||||
|
];
|
||||||
|
export const STORAGE_PROVIDERS = ["local_disk", "s3"];
|
||||||
|
export const BILLING_TYPES = [
|
||||||
|
"metered_api",
|
||||||
|
"subscription_included",
|
||||||
|
"subscription_overage",
|
||||||
|
"credits",
|
||||||
|
"fixed",
|
||||||
|
"unknown",
|
||||||
|
];
|
||||||
|
export const FINANCE_EVENT_KINDS = [
|
||||||
|
"inference_charge",
|
||||||
|
"platform_fee",
|
||||||
|
"credit_purchase",
|
||||||
|
"credit_refund",
|
||||||
|
"credit_expiry",
|
||||||
|
"byok_fee",
|
||||||
|
"gateway_overhead",
|
||||||
|
"log_storage_charge",
|
||||||
|
"logpush_charge",
|
||||||
|
"provisioned_capacity_charge",
|
||||||
|
"training_charge",
|
||||||
|
"custom_model_import_charge",
|
||||||
|
"custom_model_storage_charge",
|
||||||
|
"manual_adjustment",
|
||||||
|
];
|
||||||
|
export const FINANCE_DIRECTIONS = ["debit", "credit"];
|
||||||
|
export const FINANCE_UNITS = [
|
||||||
|
"input_token",
|
||||||
|
"output_token",
|
||||||
|
"cached_input_token",
|
||||||
|
"request",
|
||||||
|
"credit_usd",
|
||||||
|
"credit_unit",
|
||||||
|
"model_unit_minute",
|
||||||
|
"model_unit_hour",
|
||||||
|
"gb_month",
|
||||||
|
"train_token",
|
||||||
|
"unknown",
|
||||||
|
];
|
||||||
|
export const BUDGET_SCOPE_TYPES = ["company", "agent", "project"];
|
||||||
|
export const BUDGET_METRICS = ["billed_cents"];
|
||||||
|
export const BUDGET_WINDOW_KINDS = ["calendar_month_utc", "lifetime"];
|
||||||
|
export const BUDGET_THRESHOLD_TYPES = ["soft", "hard"];
|
||||||
|
export const BUDGET_INCIDENT_STATUSES = ["open", "resolved", "dismissed"];
|
||||||
|
export const BUDGET_INCIDENT_RESOLUTION_ACTIONS = [
|
||||||
|
"keep_paused",
|
||||||
|
"raise_budget_and_resume",
|
||||||
|
];
|
||||||
|
export const HEARTBEAT_INVOCATION_SOURCES = [
|
||||||
|
"timer",
|
||||||
|
"assignment",
|
||||||
|
"on_demand",
|
||||||
|
"automation",
|
||||||
|
];
|
||||||
|
export const WAKEUP_TRIGGER_DETAILS = ["manual", "ping", "callback", "system"];
|
||||||
|
export const WAKEUP_REQUEST_STATUSES = [
|
||||||
|
"queued",
|
||||||
|
"deferred_issue_execution",
|
||||||
|
"claimed",
|
||||||
|
"coalesced",
|
||||||
|
"skipped",
|
||||||
|
"completed",
|
||||||
|
"failed",
|
||||||
|
"cancelled",
|
||||||
|
];
|
||||||
|
export const HEARTBEAT_RUN_STATUSES = [
|
||||||
|
"queued",
|
||||||
|
"running",
|
||||||
|
"succeeded",
|
||||||
|
"failed",
|
||||||
|
"cancelled",
|
||||||
|
"timed_out",
|
||||||
|
];
|
||||||
|
export const LIVE_EVENT_TYPES = [
|
||||||
|
"heartbeat.run.queued",
|
||||||
|
"heartbeat.run.status",
|
||||||
|
"heartbeat.run.event",
|
||||||
|
"heartbeat.run.log",
|
||||||
|
"agent.status",
|
||||||
|
"activity.logged",
|
||||||
|
"plugin.ui.updated",
|
||||||
|
"plugin.worker.crashed",
|
||||||
|
"plugin.worker.restarted",
|
||||||
|
];
|
||||||
|
export const PRINCIPAL_TYPES = ["user", "agent"];
|
||||||
|
export const MEMBERSHIP_STATUSES = ["pending", "active", "suspended"];
|
||||||
|
export const INSTANCE_USER_ROLES = ["instance_admin"];
|
||||||
|
export const INVITE_TYPES = ["company_join", "bootstrap_ceo"];
|
||||||
|
export const INVITE_JOIN_TYPES = ["human", "agent", "both"];
|
||||||
|
export const JOIN_REQUEST_TYPES = ["human", "agent"];
|
||||||
|
export const JOIN_REQUEST_STATUSES = ["pending_approval", "approved", "rejected"];
|
||||||
|
export const PERMISSION_KEYS = [
|
||||||
|
"agents:create",
|
||||||
|
"users:invite",
|
||||||
|
"users:manage_permissions",
|
||||||
|
"tasks:assign",
|
||||||
|
"tasks:assign_scope",
|
||||||
|
"joins:approve",
|
||||||
|
];
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Plugin System — see doc/plugins/PLUGIN_SPEC.md for the full specification
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* The current version of the Plugin API contract.
|
||||||
|
*
|
||||||
|
* Increment this value whenever a breaking change is made to the plugin API
|
||||||
|
* so that the host can reject incompatible plugin manifests.
|
||||||
|
*
|
||||||
|
* @see PLUGIN_SPEC.md §4 — Versioning
|
||||||
|
*/
|
||||||
|
export const PLUGIN_API_VERSION = 1;
|
||||||
|
/**
|
||||||
|
* Lifecycle statuses for an installed plugin.
|
||||||
|
*
|
||||||
|
* State machine: installed → ready | error, ready → disabled | error | upgrade_pending | uninstalled,
|
||||||
|
* disabled → ready | uninstalled, error → ready | uninstalled,
|
||||||
|
* upgrade_pending → ready | error | uninstalled, uninstalled → installed (reinstall).
|
||||||
|
*
|
||||||
|
* @see {@link PluginStatus} — inferred union type
|
||||||
|
* @see PLUGIN_SPEC.md §21.3 `plugins.status`
|
||||||
|
*/
|
||||||
|
export const PLUGIN_STATUSES = [
|
||||||
|
"installed",
|
||||||
|
"ready",
|
||||||
|
"disabled",
|
||||||
|
"error",
|
||||||
|
"upgrade_pending",
|
||||||
|
"uninstalled",
|
||||||
|
];
|
||||||
|
/**
|
||||||
|
* Plugin classification categories. A plugin declares one or more categories
|
||||||
|
* in its manifest to describe its primary purpose.
|
||||||
|
*
|
||||||
|
* @see PLUGIN_SPEC.md §6.2
|
||||||
|
*/
|
||||||
|
export const PLUGIN_CATEGORIES = [
|
||||||
|
"connector",
|
||||||
|
"workspace",
|
||||||
|
"automation",
|
||||||
|
"ui",
|
||||||
|
];
|
||||||
|
/**
|
||||||
|
* Named permissions the host grants to a plugin. Plugins declare required
|
||||||
|
* capabilities in their manifest; the host enforces them at runtime via the
|
||||||
|
* plugin capability validator.
|
||||||
|
*
|
||||||
|
* Grouped into: Data Read, Data Write, Plugin State, Runtime/Integration,
|
||||||
|
* Agent Tools, and UI.
|
||||||
|
*
|
||||||
|
* @see PLUGIN_SPEC.md §15 — Capability Model
|
||||||
|
*/
|
||||||
|
export const PLUGIN_CAPABILITIES = [
|
||||||
|
// Data Read
|
||||||
|
"companies.read",
|
||||||
|
"projects.read",
|
||||||
|
"project.workspaces.read",
|
||||||
|
"issues.read",
|
||||||
|
"issue.comments.read",
|
||||||
|
"issue.documents.read",
|
||||||
|
"agents.read",
|
||||||
|
"goals.read",
|
||||||
|
"goals.create",
|
||||||
|
"goals.update",
|
||||||
|
"activity.read",
|
||||||
|
"costs.read",
|
||||||
|
// Data Write
|
||||||
|
"issues.create",
|
||||||
|
"issues.update",
|
||||||
|
"issue.comments.create",
|
||||||
|
"issue.documents.write",
|
||||||
|
"agents.pause",
|
||||||
|
"agents.resume",
|
||||||
|
"agents.invoke",
|
||||||
|
"agent.sessions.create",
|
||||||
|
"agent.sessions.list",
|
||||||
|
"agent.sessions.send",
|
||||||
|
"agent.sessions.close",
|
||||||
|
"activity.log.write",
|
||||||
|
"metrics.write",
|
||||||
|
// Plugin State
|
||||||
|
"plugin.state.read",
|
||||||
|
"plugin.state.write",
|
||||||
|
// Runtime / Integration
|
||||||
|
"events.subscribe",
|
||||||
|
"events.emit",
|
||||||
|
"jobs.schedule",
|
||||||
|
"webhooks.receive",
|
||||||
|
"http.outbound",
|
||||||
|
"secrets.read-ref",
|
||||||
|
// Agent Tools
|
||||||
|
"agent.tools.register",
|
||||||
|
// UI
|
||||||
|
"instance.settings.register",
|
||||||
|
"ui.sidebar.register",
|
||||||
|
"ui.page.register",
|
||||||
|
"ui.detailTab.register",
|
||||||
|
"ui.dashboardWidget.register",
|
||||||
|
"ui.commentAnnotation.register",
|
||||||
|
"ui.action.register",
|
||||||
|
];
|
||||||
|
/**
|
||||||
|
* UI extension slot types. Each slot type corresponds to a mount point in the
|
||||||
|
* Paperclip UI where plugin components can be rendered.
|
||||||
|
*
|
||||||
|
* @see PLUGIN_SPEC.md §19 — UI Extension Model
|
||||||
|
*/
|
||||||
|
export const PLUGIN_UI_SLOT_TYPES = [
|
||||||
|
"page",
|
||||||
|
"detailTab",
|
||||||
|
"taskDetailView",
|
||||||
|
"dashboardWidget",
|
||||||
|
"sidebar",
|
||||||
|
"sidebarPanel",
|
||||||
|
"projectSidebarItem",
|
||||||
|
"globalToolbarButton",
|
||||||
|
"toolbarButton",
|
||||||
|
"contextMenuItem",
|
||||||
|
"commentAnnotation",
|
||||||
|
"commentContextMenuItem",
|
||||||
|
"settingsPage",
|
||||||
|
];
|
||||||
|
/**
|
||||||
|
* Reserved company-scoped route segments that plugin page routes may not claim.
|
||||||
|
*
|
||||||
|
* These map to first-class host pages under `/:companyPrefix/...`.
|
||||||
|
*/
|
||||||
|
export const PLUGIN_RESERVED_COMPANY_ROUTE_SEGMENTS = [
|
||||||
|
"dashboard",
|
||||||
|
"onboarding",
|
||||||
|
"companies",
|
||||||
|
"company",
|
||||||
|
"settings",
|
||||||
|
"plugins",
|
||||||
|
"org",
|
||||||
|
"agents",
|
||||||
|
"projects",
|
||||||
|
"issues",
|
||||||
|
"goals",
|
||||||
|
"approvals",
|
||||||
|
"costs",
|
||||||
|
"activity",
|
||||||
|
"inbox",
|
||||||
|
"design-guide",
|
||||||
|
"tests",
|
||||||
|
];
|
||||||
|
/**
|
||||||
|
* Launcher placement zones describe where a plugin-owned launcher can appear
|
||||||
|
* in the host UI. These are intentionally aligned with current slot surfaces
|
||||||
|
* so manifest authors can describe launch intent without coupling to a single
|
||||||
|
* component implementation detail.
|
||||||
|
*/
|
||||||
|
export const PLUGIN_LAUNCHER_PLACEMENT_ZONES = [
|
||||||
|
"page",
|
||||||
|
"detailTab",
|
||||||
|
"taskDetailView",
|
||||||
|
"dashboardWidget",
|
||||||
|
"sidebar",
|
||||||
|
"sidebarPanel",
|
||||||
|
"projectSidebarItem",
|
||||||
|
"globalToolbarButton",
|
||||||
|
"toolbarButton",
|
||||||
|
"contextMenuItem",
|
||||||
|
"commentAnnotation",
|
||||||
|
"commentContextMenuItem",
|
||||||
|
"settingsPage",
|
||||||
|
];
|
||||||
|
/**
|
||||||
|
* Launcher action kinds describe what the launcher does when activated.
|
||||||
|
*/
|
||||||
|
export const PLUGIN_LAUNCHER_ACTIONS = [
|
||||||
|
"navigate",
|
||||||
|
"openModal",
|
||||||
|
"openDrawer",
|
||||||
|
"openPopover",
|
||||||
|
"performAction",
|
||||||
|
"deepLink",
|
||||||
|
];
|
||||||
|
/**
|
||||||
|
* Optional size hints the host can use when rendering plugin-owned launcher
|
||||||
|
* destinations such as overlays, drawers, or full page handoffs.
|
||||||
|
*/
|
||||||
|
export const PLUGIN_LAUNCHER_BOUNDS = [
|
||||||
|
"inline",
|
||||||
|
"compact",
|
||||||
|
"default",
|
||||||
|
"wide",
|
||||||
|
"full",
|
||||||
|
];
|
||||||
|
/**
|
||||||
|
* Render environments describe the container a launcher expects after it is
|
||||||
|
* activated. The current host may map these to concrete UI primitives.
|
||||||
|
*/
|
||||||
|
export const PLUGIN_LAUNCHER_RENDER_ENVIRONMENTS = [
|
||||||
|
"hostInline",
|
||||||
|
"hostOverlay",
|
||||||
|
"hostRoute",
|
||||||
|
"external",
|
||||||
|
"iframe",
|
||||||
|
];
|
||||||
|
/**
|
||||||
|
* Entity types that a `detailTab` UI slot can attach to.
|
||||||
|
*
|
||||||
|
* @see PLUGIN_SPEC.md §19.3 — Detail Tabs
|
||||||
|
*/
|
||||||
|
export const PLUGIN_UI_SLOT_ENTITY_TYPES = [
|
||||||
|
"project",
|
||||||
|
"issue",
|
||||||
|
"agent",
|
||||||
|
"goal",
|
||||||
|
"run",
|
||||||
|
"comment",
|
||||||
|
];
|
||||||
|
/**
|
||||||
|
* Scope kinds for plugin state storage. Determines the granularity at which
|
||||||
|
* a plugin stores key-value state data.
|
||||||
|
*
|
||||||
|
* @see PLUGIN_SPEC.md §21.3 `plugin_state.scope_kind`
|
||||||
|
*/
|
||||||
|
export const PLUGIN_STATE_SCOPE_KINDS = [
|
||||||
|
"instance",
|
||||||
|
"company",
|
||||||
|
"project",
|
||||||
|
"project_workspace",
|
||||||
|
"agent",
|
||||||
|
"issue",
|
||||||
|
"goal",
|
||||||
|
"run",
|
||||||
|
];
|
||||||
|
/** Statuses for a plugin's scheduled job definition. */
|
||||||
|
export const PLUGIN_JOB_STATUSES = [
|
||||||
|
"active",
|
||||||
|
"paused",
|
||||||
|
"failed",
|
||||||
|
];
|
||||||
|
/** Statuses for individual job run executions. */
|
||||||
|
export const PLUGIN_JOB_RUN_STATUSES = [
|
||||||
|
"pending",
|
||||||
|
"queued",
|
||||||
|
"running",
|
||||||
|
"succeeded",
|
||||||
|
"failed",
|
||||||
|
"cancelled",
|
||||||
|
];
|
||||||
|
/** What triggered a particular job run. */
|
||||||
|
export const PLUGIN_JOB_RUN_TRIGGERS = [
|
||||||
|
"schedule",
|
||||||
|
"manual",
|
||||||
|
"retry",
|
||||||
|
];
|
||||||
|
/** Statuses for inbound webhook deliveries. */
|
||||||
|
export const PLUGIN_WEBHOOK_DELIVERY_STATUSES = [
|
||||||
|
"pending",
|
||||||
|
"success",
|
||||||
|
"failed",
|
||||||
|
];
|
||||||
|
/**
|
||||||
|
* Core domain event types that plugins can subscribe to via the
|
||||||
|
* `events.subscribe` capability.
|
||||||
|
*
|
||||||
|
* @see PLUGIN_SPEC.md §16 — Event System
|
||||||
|
*/
|
||||||
|
export const PLUGIN_EVENT_TYPES = [
|
||||||
|
"company.created",
|
||||||
|
"company.updated",
|
||||||
|
"project.created",
|
||||||
|
"project.updated",
|
||||||
|
"project.workspace_created",
|
||||||
|
"project.workspace_updated",
|
||||||
|
"project.workspace_deleted",
|
||||||
|
"issue.created",
|
||||||
|
"issue.updated",
|
||||||
|
"issue.comment.created",
|
||||||
|
"agent.created",
|
||||||
|
"agent.updated",
|
||||||
|
"agent.status_changed",
|
||||||
|
"agent.run.started",
|
||||||
|
"agent.run.finished",
|
||||||
|
"agent.run.failed",
|
||||||
|
"agent.run.cancelled",
|
||||||
|
"goal.created",
|
||||||
|
"goal.updated",
|
||||||
|
"approval.created",
|
||||||
|
"approval.decided",
|
||||||
|
"cost_event.created",
|
||||||
|
"activity.logged",
|
||||||
|
];
|
||||||
|
/**
|
||||||
|
* Error codes returned by the plugin bridge when a UI → worker call fails.
|
||||||
|
*
|
||||||
|
* @see PLUGIN_SPEC.md §19.7 — Error Propagation Through The Bridge
|
||||||
|
*/
|
||||||
|
export const PLUGIN_BRIDGE_ERROR_CODES = [
|
||||||
|
"WORKER_UNAVAILABLE",
|
||||||
|
"CAPABILITY_DENIED",
|
||||||
|
"WORKER_ERROR",
|
||||||
|
"TIMEOUT",
|
||||||
|
"UNKNOWN",
|
||||||
|
];
|
||||||
|
//# sourceMappingURL=constants.js.map
|
||||||
1
node_modules/@paperclipai/shared/dist/constants.js.map
generated
vendored
Normal file
1
node_modules/@paperclipai/shared/dist/constants.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
10
node_modules/@paperclipai/shared/dist/index.d.ts
generated
vendored
Normal file
10
node_modules/@paperclipai/shared/dist/index.d.ts
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
node_modules/@paperclipai/shared/dist/index.d.ts.map
generated
vendored
Normal file
1
node_modules/@paperclipai/shared/dist/index.d.ts.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
9
node_modules/@paperclipai/shared/dist/index.js
generated
vendored
Normal file
9
node_modules/@paperclipai/shared/dist/index.js
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
export { COMPANY_STATUSES, DEPLOYMENT_MODES, DEPLOYMENT_EXPOSURES, AUTH_BASE_URL_MODES, AGENT_STATUSES, AGENT_ADAPTER_TYPES, AGENT_ROLES, AGENT_ROLE_LABELS, AGENT_ICON_NAMES, ISSUE_STATUSES, ISSUE_PRIORITIES, ISSUE_ORIGIN_KINDS, GOAL_LEVELS, GOAL_STATUSES, PROJECT_STATUSES, ROUTINE_STATUSES, ROUTINE_CONCURRENCY_POLICIES, ROUTINE_CATCH_UP_POLICIES, ROUTINE_TRIGGER_KINDS, ROUTINE_TRIGGER_SIGNING_MODES, ROUTINE_RUN_STATUSES, ROUTINE_RUN_SOURCES, PAUSE_REASONS, PROJECT_COLORS, APPROVAL_TYPES, APPROVAL_STATUSES, SECRET_PROVIDERS, STORAGE_PROVIDERS, BILLING_TYPES, FINANCE_EVENT_KINDS, FINANCE_DIRECTIONS, FINANCE_UNITS, BUDGET_SCOPE_TYPES, BUDGET_METRICS, BUDGET_WINDOW_KINDS, BUDGET_THRESHOLD_TYPES, BUDGET_INCIDENT_STATUSES, BUDGET_INCIDENT_RESOLUTION_ACTIONS, HEARTBEAT_INVOCATION_SOURCES, HEARTBEAT_RUN_STATUSES, WAKEUP_TRIGGER_DETAILS, WAKEUP_REQUEST_STATUSES, LIVE_EVENT_TYPES, PRINCIPAL_TYPES, MEMBERSHIP_STATUSES, INSTANCE_USER_ROLES, INVITE_TYPES, INVITE_JOIN_TYPES, JOIN_REQUEST_TYPES, JOIN_REQUEST_STATUSES, PERMISSION_KEYS, PLUGIN_API_VERSION, PLUGIN_STATUSES, PLUGIN_CATEGORIES, PLUGIN_CAPABILITIES, PLUGIN_UI_SLOT_TYPES, PLUGIN_UI_SLOT_ENTITY_TYPES, PLUGIN_LAUNCHER_PLACEMENT_ZONES, PLUGIN_LAUNCHER_ACTIONS, PLUGIN_LAUNCHER_BOUNDS, PLUGIN_LAUNCHER_RENDER_ENVIRONMENTS, 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 "./constants.js";
|
||||||
|
export { instanceGeneralSettingsSchema, patchInstanceGeneralSettingsSchema, instanceExperimentalSettingsSchema, patchInstanceExperimentalSettingsSchema, } from "./validators/index.js";
|
||||||
|
export { createCompanySchema, updateCompanySchema, updateCompanyBrandingSchema, agentSkillStateSchema, agentSkillSyncModeSchema, agentSkillEntrySchema, agentSkillSnapshotSchema, agentSkillSyncSchema, createAgentSchema, createAgentHireSchema, updateAgentSchema, agentInstructionsBundleModeSchema, updateAgentInstructionsBundleSchema, upsertAgentInstructionsFileSchema, updateAgentInstructionsPathSchema, createAgentKeySchema, wakeAgentSchema, resetAgentSessionSchema, testAdapterEnvironmentSchema, agentPermissionsSchema, updateAgentPermissionsSchema, createProjectSchema, updateProjectSchema, createProjectWorkspaceSchema, updateProjectWorkspaceSchema, projectExecutionWorkspacePolicySchema, createIssueSchema, createIssueLabelSchema, updateIssueSchema, issueExecutionWorkspaceSettingsSchema, checkoutIssueSchema, addIssueCommentSchema, linkIssueApprovalSchema, createIssueAttachmentMetadataSchema, createIssueWorkProductSchema, updateIssueWorkProductSchema, issueWorkProductTypeSchema, issueWorkProductStatusSchema, issueWorkProductReviewStateSchema, updateExecutionWorkspaceSchema, executionWorkspaceStatusSchema, issueDocumentFormatSchema, issueDocumentKeySchema, upsertIssueDocumentSchema, createGoalSchema, updateGoalSchema, createApprovalSchema, upsertBudgetPolicySchema, resolveBudgetIncidentSchema, resolveApprovalSchema, requestApprovalRevisionSchema, resubmitApprovalSchema, addApprovalCommentSchema, envBindingPlainSchema, envBindingSecretRefSchema, envBindingSchema, envConfigSchema, createSecretSchema, rotateSecretSchema, updateSecretSchema, createRoutineSchema, updateRoutineSchema, createRoutineTriggerSchema, updateRoutineTriggerSchema, runRoutineSchema, rotateRoutineTriggerSecretSchema, createCostEventSchema, createFinanceEventSchema, updateBudgetSchema, createAssetImageMetadataSchema, createCompanyInviteSchema, createOpenClawInvitePromptSchema, acceptInviteSchema, listJoinRequestsQuerySchema, claimJoinRequestApiKeySchema, boardCliAuthAccessLevelSchema, createCliAuthChallengeSchema, resolveCliAuthChallengeSchema, updateMemberPermissionsSchema, updateUserCompanyAccessSchema, companySkillSourceTypeSchema, companySkillTrustLevelSchema, companySkillCompatibilitySchema, companySkillSourceBadgeSchema, companySkillFileInventoryEntrySchema, companySkillSchema, companySkillListItemSchema, companySkillUsageAgentSchema, companySkillDetailSchema, companySkillUpdateStatusSchema, companySkillImportSchema, companySkillProjectScanRequestSchema, companySkillProjectScanSkippedSchema, companySkillProjectScanConflictSchema, companySkillProjectScanResultSchema, companySkillCreateSchema, companySkillFileDetailSchema, companySkillFileUpdateSchema, portabilityIncludeSchema, portabilityEnvInputSchema, portabilityCompanyManifestEntrySchema, portabilitySidebarOrderSchema, portabilityAgentManifestEntrySchema, portabilityManifestSchema, portabilitySourceSchema, portabilityTargetSchema, portabilityAgentSelectionSchema, portabilityCollisionStrategySchema, companyPortabilityExportSchema, companyPortabilityPreviewSchema, companyPortabilityImportSchema, jsonSchemaSchema, pluginJobDeclarationSchema, pluginWebhookDeclarationSchema, pluginToolDeclarationSchema, pluginUiSlotDeclarationSchema, pluginLauncherActionDeclarationSchema, pluginLauncherRenderDeclarationSchema, pluginLauncherDeclarationSchema, pluginManifestV1Schema, installPluginSchema, upsertPluginConfigSchema, patchPluginConfigSchema, updatePluginStatusSchema, uninstallPluginSchema, pluginStateScopeKeySchema, setPluginStateSchema, listPluginStateSchema, } from "./validators/index.js";
|
||||||
|
export { API_PREFIX, API } from "./api.js";
|
||||||
|
export { normalizeAgentUrlKey, deriveAgentUrlKey, isUuidLike } from "./agent-url-key.js";
|
||||||
|
export { deriveProjectUrlKey, normalizeProjectUrlKey } from "./project-url-key.js";
|
||||||
|
export { AGENT_MENTION_SCHEME, PROJECT_MENTION_SCHEME, buildAgentMentionHref, buildProjectMentionHref, extractAgentMentionIds, parseAgentMentionHref, parseProjectMentionHref, extractProjectMentionIds, } from "./project-mentions.js";
|
||||||
|
export { paperclipConfigSchema, configMetaSchema, llmConfigSchema, databaseBackupConfigSchema, databaseConfigSchema, loggingConfigSchema, serverConfigSchema, authConfigSchema, secretsConfigSchema, storageConfigSchema, storageLocalDiskConfigSchema, storageS3ConfigSchema, secretsLocalEncryptedConfigSchema, } from "./config-schema.js";
|
||||||
|
//# sourceMappingURL=index.js.map
|
||||||
1
node_modules/@paperclipai/shared/dist/index.js.map
generated
vendored
Normal file
1
node_modules/@paperclipai/shared/dist/index.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
||||||
17
node_modules/@paperclipai/shared/dist/project-mentions.d.ts
generated
vendored
Normal file
17
node_modules/@paperclipai/shared/dist/project-mentions.d.ts
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
export declare const PROJECT_MENTION_SCHEME = "project://";
|
||||||
|
export declare const AGENT_MENTION_SCHEME = "agent://";
|
||||||
|
export interface ParsedProjectMention {
|
||||||
|
projectId: string;
|
||||||
|
color: string | null;
|
||||||
|
}
|
||||||
|
export interface ParsedAgentMention {
|
||||||
|
agentId: string;
|
||||||
|
icon: string | null;
|
||||||
|
}
|
||||||
|
export declare function buildProjectMentionHref(projectId: string, color?: string | null): string;
|
||||||
|
export declare function parseProjectMentionHref(href: string): ParsedProjectMention | null;
|
||||||
|
export declare function buildAgentMentionHref(agentId: string, icon?: string | null): string;
|
||||||
|
export declare function parseAgentMentionHref(href: string): ParsedAgentMention | null;
|
||||||
|
export declare function extractProjectMentionIds(markdown: string): string[];
|
||||||
|
export declare function extractAgentMentionIds(markdown: string): string[];
|
||||||
|
//# sourceMappingURL=project-mentions.d.ts.map
|
||||||
1
node_modules/@paperclipai/shared/dist/project-mentions.d.ts.map
generated
vendored
Normal file
1
node_modules/@paperclipai/shared/dist/project-mentions.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"project-mentions.d.ts","sourceRoot":"","sources":["../src/project-mentions.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB,eAAe,CAAC;AACnD,eAAO,MAAM,oBAAoB,aAAa,CAAC;AAU/C,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAwBD,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAOxF;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,GAAG,IAAI,CAqBjF;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAOnF;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CAmB7E;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAUnE;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAUjE"}
|
||||||
123
node_modules/@paperclipai/shared/dist/project-mentions.js
generated
vendored
Normal file
123
node_modules/@paperclipai/shared/dist/project-mentions.js
generated
vendored
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
export const PROJECT_MENTION_SCHEME = "project://";
|
||||||
|
export const AGENT_MENTION_SCHEME = "agent://";
|
||||||
|
const HEX_COLOR_RE = /^[0-9a-f]{6}$/i;
|
||||||
|
const HEX_COLOR_SHORT_RE = /^[0-9a-f]{3}$/i;
|
||||||
|
const HEX_COLOR_WITH_HASH_RE = /^#[0-9a-f]{6}$/i;
|
||||||
|
const HEX_COLOR_SHORT_WITH_HASH_RE = /^#[0-9a-f]{3}$/i;
|
||||||
|
const PROJECT_MENTION_LINK_RE = /\[[^\]]*]\((project:\/\/[^)\s]+)\)/gi;
|
||||||
|
const AGENT_MENTION_LINK_RE = /\[[^\]]*]\((agent:\/\/[^)\s]+)\)/gi;
|
||||||
|
const AGENT_ICON_NAME_RE = /^[a-z0-9-]+$/i;
|
||||||
|
function normalizeHexColor(input) {
|
||||||
|
if (!input)
|
||||||
|
return null;
|
||||||
|
const trimmed = input.trim();
|
||||||
|
if (!trimmed)
|
||||||
|
return null;
|
||||||
|
if (HEX_COLOR_WITH_HASH_RE.test(trimmed)) {
|
||||||
|
return trimmed.toLowerCase();
|
||||||
|
}
|
||||||
|
if (HEX_COLOR_RE.test(trimmed)) {
|
||||||
|
return `#${trimmed.toLowerCase()}`;
|
||||||
|
}
|
||||||
|
if (HEX_COLOR_SHORT_WITH_HASH_RE.test(trimmed)) {
|
||||||
|
const raw = trimmed.slice(1).toLowerCase();
|
||||||
|
return `#${raw[0]}${raw[0]}${raw[1]}${raw[1]}${raw[2]}${raw[2]}`;
|
||||||
|
}
|
||||||
|
if (HEX_COLOR_SHORT_RE.test(trimmed)) {
|
||||||
|
const raw = trimmed.toLowerCase();
|
||||||
|
return `#${raw[0]}${raw[0]}${raw[1]}${raw[1]}${raw[2]}${raw[2]}`;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
export function buildProjectMentionHref(projectId, color) {
|
||||||
|
const trimmedProjectId = projectId.trim();
|
||||||
|
const normalizedColor = normalizeHexColor(color ?? null);
|
||||||
|
if (!normalizedColor) {
|
||||||
|
return `${PROJECT_MENTION_SCHEME}${trimmedProjectId}`;
|
||||||
|
}
|
||||||
|
return `${PROJECT_MENTION_SCHEME}${trimmedProjectId}?c=${encodeURIComponent(normalizedColor.slice(1))}`;
|
||||||
|
}
|
||||||
|
export function parseProjectMentionHref(href) {
|
||||||
|
if (!href.startsWith(PROJECT_MENTION_SCHEME))
|
||||||
|
return null;
|
||||||
|
let url;
|
||||||
|
try {
|
||||||
|
url = new URL(href);
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (url.protocol !== "project:")
|
||||||
|
return null;
|
||||||
|
const projectId = `${url.hostname}${url.pathname}`.replace(/^\/+/, "").trim();
|
||||||
|
if (!projectId)
|
||||||
|
return null;
|
||||||
|
const color = normalizeHexColor(url.searchParams.get("c") ?? url.searchParams.get("color"));
|
||||||
|
return {
|
||||||
|
projectId,
|
||||||
|
color,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export function buildAgentMentionHref(agentId, icon) {
|
||||||
|
const trimmedAgentId = agentId.trim();
|
||||||
|
const normalizedIcon = normalizeAgentIcon(icon ?? null);
|
||||||
|
if (!normalizedIcon) {
|
||||||
|
return `${AGENT_MENTION_SCHEME}${trimmedAgentId}`;
|
||||||
|
}
|
||||||
|
return `${AGENT_MENTION_SCHEME}${trimmedAgentId}?i=${encodeURIComponent(normalizedIcon)}`;
|
||||||
|
}
|
||||||
|
export function parseAgentMentionHref(href) {
|
||||||
|
if (!href.startsWith(AGENT_MENTION_SCHEME))
|
||||||
|
return null;
|
||||||
|
let url;
|
||||||
|
try {
|
||||||
|
url = new URL(href);
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (url.protocol !== "agent:")
|
||||||
|
return null;
|
||||||
|
const agentId = `${url.hostname}${url.pathname}`.replace(/^\/+/, "").trim();
|
||||||
|
if (!agentId)
|
||||||
|
return null;
|
||||||
|
return {
|
||||||
|
agentId,
|
||||||
|
icon: normalizeAgentIcon(url.searchParams.get("i") ?? url.searchParams.get("icon")),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export function extractProjectMentionIds(markdown) {
|
||||||
|
if (!markdown)
|
||||||
|
return [];
|
||||||
|
const ids = new Set();
|
||||||
|
const re = new RegExp(PROJECT_MENTION_LINK_RE);
|
||||||
|
let match;
|
||||||
|
while ((match = re.exec(markdown)) !== null) {
|
||||||
|
const parsed = parseProjectMentionHref(match[1]);
|
||||||
|
if (parsed)
|
||||||
|
ids.add(parsed.projectId);
|
||||||
|
}
|
||||||
|
return [...ids];
|
||||||
|
}
|
||||||
|
export function extractAgentMentionIds(markdown) {
|
||||||
|
if (!markdown)
|
||||||
|
return [];
|
||||||
|
const ids = new Set();
|
||||||
|
const re = new RegExp(AGENT_MENTION_LINK_RE);
|
||||||
|
let match;
|
||||||
|
while ((match = re.exec(markdown)) !== null) {
|
||||||
|
const parsed = parseAgentMentionHref(match[1]);
|
||||||
|
if (parsed)
|
||||||
|
ids.add(parsed.agentId);
|
||||||
|
}
|
||||||
|
return [...ids];
|
||||||
|
}
|
||||||
|
function normalizeAgentIcon(input) {
|
||||||
|
if (!input)
|
||||||
|
return null;
|
||||||
|
const trimmed = input.trim().toLowerCase();
|
||||||
|
if (!trimmed || !AGENT_ICON_NAME_RE.test(trimmed))
|
||||||
|
return null;
|
||||||
|
return trimmed;
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=project-mentions.js.map
|
||||||
1
node_modules/@paperclipai/shared/dist/project-mentions.js.map
generated
vendored
Normal file
1
node_modules/@paperclipai/shared/dist/project-mentions.js.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"project-mentions.js","sourceRoot":"","sources":["../src/project-mentions.ts"],"names":[],"mappings":"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"}
|
||||||
2
node_modules/@paperclipai/shared/dist/project-mentions.test.d.ts
generated
vendored
Normal file
2
node_modules/@paperclipai/shared/dist/project-mentions.test.d.ts
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export {};
|
||||||
|
//# sourceMappingURL=project-mentions.test.d.ts.map
|
||||||
1
node_modules/@paperclipai/shared/dist/project-mentions.test.d.ts.map
generated
vendored
Normal file
1
node_modules/@paperclipai/shared/dist/project-mentions.test.d.ts.map
generated
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"file":"project-mentions.test.d.ts","sourceRoot":"","sources":["../src/project-mentions.test.ts"],"names":[],"mappings":""}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user