From 8cdba3ce1869a265e1e273338cdf11a498e0f3e2 Mon Sep 17 00:00:00 2001 From: dotta Date: Mon, 6 Apr 2026 07:17:48 -0500 Subject: [PATCH 01/10] Add standalone Paperclip MCP server package Co-Authored-By: Paperclip --- packages/mcp-server/README.md | 76 +++++ packages/mcp-server/package.json | 55 ++++ packages/mcp-server/src/client.ts | 114 +++++++ packages/mcp-server/src/config.ts | 39 +++ packages/mcp-server/src/format.ts | 31 ++ packages/mcp-server/src/index.ts | 30 ++ packages/mcp-server/src/stdio.ts | 7 + packages/mcp-server/src/tools.test.ts | 121 ++++++++ packages/mcp-server/src/tools.ts | 413 ++++++++++++++++++++++++++ packages/mcp-server/tsconfig.json | 8 + packages/mcp-server/vitest.config.ts | 7 + tsconfig.json | 1 + 12 files changed, 902 insertions(+) create mode 100644 packages/mcp-server/README.md create mode 100644 packages/mcp-server/package.json create mode 100644 packages/mcp-server/src/client.ts create mode 100644 packages/mcp-server/src/config.ts create mode 100644 packages/mcp-server/src/format.ts create mode 100644 packages/mcp-server/src/index.ts create mode 100644 packages/mcp-server/src/stdio.ts create mode 100644 packages/mcp-server/src/tools.test.ts create mode 100644 packages/mcp-server/src/tools.ts create mode 100644 packages/mcp-server/tsconfig.json create mode 100644 packages/mcp-server/vitest.config.ts diff --git a/packages/mcp-server/README.md b/packages/mcp-server/README.md new file mode 100644 index 00000000..602521e2 --- /dev/null +++ b/packages/mcp-server/README.md @@ -0,0 +1,76 @@ +# Paperclip MCP Server + +Model Context Protocol server for Paperclip. + +This package is a thin MCP wrapper over the existing Paperclip REST API. It does +not talk to the database directly and it does not reimplement business logic. + +## Authentication + +The server reads its configuration from environment variables: + +- `PAPERCLIP_API_URL` - Paperclip base URL, for example `http://localhost:3100` +- `PAPERCLIP_API_KEY` - bearer token used for `/api` requests +- `PAPERCLIP_COMPANY_ID` - optional default company for company-scoped tools +- `PAPERCLIP_AGENT_ID` - optional default agent for checkout helpers +- `PAPERCLIP_RUN_ID` - optional run id forwarded on mutating requests + +## Usage + +```sh +npx -y @paperclipai/mcp-server +``` + +Or locally in this repo: + +```sh +pnpm --filter @paperclipai/mcp-server build +node packages/mcp-server/dist/stdio.js +``` + +## Tool Surface + +Read tools: + +- `paperclipMe` +- `paperclipInboxLite` +- `paperclipListAgents` +- `paperclipGetAgent` +- `paperclipListIssues` +- `paperclipGetIssue` +- `paperclipGetHeartbeatContext` +- `paperclipListComments` +- `paperclipGetComment` +- `paperclipListIssueApprovals` +- `paperclipListDocuments` +- `paperclipGetDocument` +- `paperclipListDocumentRevisions` +- `paperclipListProjects` +- `paperclipGetProject` +- `paperclipListGoals` +- `paperclipGetGoal` +- `paperclipListApprovals` +- `paperclipGetApproval` +- `paperclipGetApprovalIssues` +- `paperclipListApprovalComments` + +Write tools: + +- `paperclipCreateIssue` +- `paperclipUpdateIssue` +- `paperclipCheckoutIssue` +- `paperclipReleaseIssue` +- `paperclipAddComment` +- `paperclipUpsertIssueDocument` +- `paperclipRestoreIssueDocumentRevision` +- `paperclipLinkIssueApproval` +- `paperclipUnlinkIssueApproval` +- `paperclipApprovalDecision` +- `paperclipAddApprovalComment` + +Escape hatch: + +- `paperclipApiRequest` + +`paperclipApiRequest` is limited to paths under `/api` and JSON bodies. It is +meant for endpoints that do not yet have a dedicated MCP tool. diff --git a/packages/mcp-server/package.json b/packages/mcp-server/package.json new file mode 100644 index 00000000..6edba34d --- /dev/null +++ b/packages/mcp-server/package.json @@ -0,0 +1,55 @@ +{ + "name": "@paperclipai/mcp-server", + "version": "0.1.0", + "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/mcp-server" + }, + "type": "module", + "bin": { + "paperclip-mcp-server": "./dist/stdio.js" + }, + "exports": { + ".": "./src/index.ts" + }, + "publishConfig": { + "access": "public", + "bin": { + "paperclip-mcp-server": "./dist/stdio.js" + }, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "main": "./dist/index.js", + "types": "./dist/index.d.ts" + }, + "files": [ + "dist", + "README.md" + ], + "scripts": { + "build": "tsc", + "clean": "rm -rf dist", + "typecheck": "tsc --noEmit", + "test": "vitest run" + }, + "dependencies": { + "@modelcontextprotocol/sdk": "^1.29.0", + "@paperclipai/shared": "workspace:*", + "zod": "^3.24.2" + }, + "devDependencies": { + "@types/node": "^24.6.0", + "typescript": "^5.7.3", + "vitest": "^3.0.5" + } +} diff --git a/packages/mcp-server/src/client.ts b/packages/mcp-server/src/client.ts new file mode 100644 index 00000000..a2fd8e35 --- /dev/null +++ b/packages/mcp-server/src/client.ts @@ -0,0 +1,114 @@ +import type { PaperclipMcpConfig } from "./config.js"; + +export class PaperclipApiError extends Error { + readonly status: number; + readonly method: string; + readonly path: string; + readonly body: unknown; + + constructor(input: { + status: number; + method: string; + path: string; + body: unknown; + message: string; + }) { + super(input.message); + this.name = "PaperclipApiError"; + this.status = input.status; + this.method = input.method; + this.path = input.path; + this.body = input.body; + } +} + +export interface JsonRequestOptions { + body?: unknown; + includeRunId?: boolean; +} + +function isWriteMethod(method: string): boolean { + return !["GET", "HEAD"].includes(method.toUpperCase()); +} + +function buildErrorMessage(method: string, path: string, status: number, body: unknown): string { + if (body && typeof body === "object" && "error" in body && typeof body.error === "string") { + return `${method} ${path} failed with ${status}: ${body.error}`; + } + return `${method} ${path} failed with ${status}`; +} + +async function parseResponseBody(response: Response): Promise { + const text = await response.text(); + if (!text) return null; + try { + return JSON.parse(text) as unknown; + } catch { + return text; + } +} + +export class PaperclipApiClient { + constructor(private readonly config: PaperclipMcpConfig) {} + + get defaults() { + return { + companyId: this.config.companyId, + agentId: this.config.agentId, + runId: this.config.runId, + }; + } + + resolveCompanyId(companyId?: string | null): string { + const resolved = companyId?.trim() || this.config.companyId; + if (!resolved) { + throw new Error("companyId is required because PAPERCLIP_COMPANY_ID is not set"); + } + return resolved; + } + + resolveAgentId(agentId?: string | null): string { + const resolved = agentId?.trim() || this.config.agentId; + if (!resolved) { + throw new Error("agentId is required because PAPERCLIP_AGENT_ID is not set"); + } + return resolved; + } + + async requestJson(method: string, path: string, options: JsonRequestOptions = {}): Promise { + if (!path.startsWith("/")) { + throw new Error(`API path must start with "/": ${path}`); + } + + const url = new URL(path.slice(1), `${this.config.apiUrl}/`); + const headers: Record = { + Authorization: `Bearer ${this.config.apiKey}`, + Accept: "application/json", + }; + if (options.body !== undefined) { + headers["Content-Type"] = "application/json"; + } + if ((options.includeRunId ?? isWriteMethod(method)) && this.config.runId) { + headers["X-Paperclip-Run-Id"] = this.config.runId; + } + + const response = await fetch(url, { + method, + headers, + body: options.body === undefined ? undefined : JSON.stringify(options.body), + }); + const parsedBody = await parseResponseBody(response); + + if (!response.ok) { + throw new PaperclipApiError({ + status: response.status, + method: method.toUpperCase(), + path, + body: parsedBody, + message: buildErrorMessage(method.toUpperCase(), path, response.status, parsedBody), + }); + } + + return parsedBody as T; + } +} diff --git a/packages/mcp-server/src/config.ts b/packages/mcp-server/src/config.ts new file mode 100644 index 00000000..c1de29eb --- /dev/null +++ b/packages/mcp-server/src/config.ts @@ -0,0 +1,39 @@ +export interface PaperclipMcpConfig { + apiUrl: string; + apiKey: string; + companyId: string | null; + agentId: string | null; + runId: string | null; +} + +function nonEmpty(value: string | undefined): string | null { + return typeof value === "string" && value.trim().length > 0 ? value.trim() : null; +} + +function stripTrailingSlash(value: string): string { + return value.replace(/\/+$/, ""); +} + +export function normalizeApiUrl(apiUrl: string): string { + const trimmed = stripTrailingSlash(apiUrl.trim()); + return trimmed.endsWith("/api") ? trimmed : `${trimmed}/api`; +} + +export function readConfigFromEnv(env: NodeJS.ProcessEnv = process.env): PaperclipMcpConfig { + const apiUrl = nonEmpty(env.PAPERCLIP_API_URL); + if (!apiUrl) { + throw new Error("Missing PAPERCLIP_API_URL"); + } + const apiKey = nonEmpty(env.PAPERCLIP_API_KEY); + if (!apiKey) { + throw new Error("Missing PAPERCLIP_API_KEY"); + } + + return { + apiUrl: normalizeApiUrl(apiUrl), + apiKey, + companyId: nonEmpty(env.PAPERCLIP_COMPANY_ID), + agentId: nonEmpty(env.PAPERCLIP_AGENT_ID), + runId: nonEmpty(env.PAPERCLIP_RUN_ID), + }; +} diff --git a/packages/mcp-server/src/format.ts b/packages/mcp-server/src/format.ts new file mode 100644 index 00000000..b2e6a4cd --- /dev/null +++ b/packages/mcp-server/src/format.ts @@ -0,0 +1,31 @@ +import { PaperclipApiError } from "./client.js"; + +type McpTextResponse = { + content: Array<{ type: "text"; text: string }>; +}; + +export function formatTextResponse(value: unknown): McpTextResponse { + return { + content: [ + { + type: "text", + text: typeof value === "string" ? value : JSON.stringify(value, null, 2), + }, + ], + }; +} + +export function formatErrorResponse(error: unknown): McpTextResponse { + if (error instanceof PaperclipApiError) { + return formatTextResponse({ + error: error.message, + status: error.status, + method: error.method, + path: error.path, + body: error.body, + }); + } + return formatTextResponse({ + error: error instanceof Error ? error.message : String(error), + }); +} diff --git a/packages/mcp-server/src/index.ts b/packages/mcp-server/src/index.ts new file mode 100644 index 00000000..dc9fdc5c --- /dev/null +++ b/packages/mcp-server/src/index.ts @@ -0,0 +1,30 @@ +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; +import { PaperclipApiClient } from "./client.js"; +import { readConfigFromEnv, type PaperclipMcpConfig } from "./config.js"; +import { createToolDefinitions } from "./tools.js"; + +export function createPaperclipMcpServer(config: PaperclipMcpConfig = readConfigFromEnv()) { + const server = new McpServer({ + name: "paperclip", + version: "0.1.0", + }); + + const client = new PaperclipApiClient(config); + const tools = createToolDefinitions(client); + for (const tool of tools) { + server.tool(tool.name, tool.description, tool.schema.shape, tool.execute); + } + + return { + server, + tools, + client, + }; +} + +export async function runServer(config: PaperclipMcpConfig = readConfigFromEnv()) { + const { server } = createPaperclipMcpServer(config); + const transport = new StdioServerTransport(); + await server.connect(transport); +} diff --git a/packages/mcp-server/src/stdio.ts b/packages/mcp-server/src/stdio.ts new file mode 100644 index 00000000..145b2362 --- /dev/null +++ b/packages/mcp-server/src/stdio.ts @@ -0,0 +1,7 @@ +#!/usr/bin/env node +import { runServer } from "./index.js"; + +void runServer().catch((error) => { + console.error("Failed to start Paperclip MCP server:", error); + process.exit(1); +}); diff --git a/packages/mcp-server/src/tools.test.ts b/packages/mcp-server/src/tools.test.ts new file mode 100644 index 00000000..f1d22285 --- /dev/null +++ b/packages/mcp-server/src/tools.test.ts @@ -0,0 +1,121 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { PaperclipApiClient } from "./client.js"; +import { createToolDefinitions } from "./tools.js"; + +function makeClient() { + return new PaperclipApiClient({ + apiUrl: "http://localhost:3100/api", + apiKey: "token-123", + companyId: "11111111-1111-1111-1111-111111111111", + agentId: "22222222-2222-2222-2222-222222222222", + runId: "33333333-3333-3333-3333-333333333333", + }); +} + +function getTool(name: string) { + const tool = createToolDefinitions(makeClient()).find((candidate) => candidate.name === name); + if (!tool) throw new Error(`Missing tool ${name}`); + return tool; +} + +function mockJsonResponse(body: unknown, status = 200) { + return new Response(JSON.stringify(body), { + status, + headers: { "Content-Type": "application/json" }, + }); +} + +describe("paperclip MCP tools", () => { + beforeEach(() => { + vi.restoreAllMocks(); + }); + + it("adds auth headers and run id to mutating requests", async () => { + const fetchMock = vi.fn().mockResolvedValue( + mockJsonResponse({ ok: true }), + ); + vi.stubGlobal("fetch", fetchMock); + + const tool = getTool("paperclipUpdateIssue"); + await tool.execute({ + issueId: "PAP-1135", + status: "done", + }); + + expect(fetchMock).toHaveBeenCalledTimes(1); + const [url, init] = fetchMock.mock.calls[0] as [string, RequestInit]; + expect(String(url)).toBe("http://localhost:3100/api/issues/PAP-1135"); + expect(init.method).toBe("PATCH"); + expect((init.headers as Record)["Authorization"]).toBe("Bearer token-123"); + expect((init.headers as Record)["X-Paperclip-Run-Id"]).toBe( + "33333333-3333-3333-3333-333333333333", + ); + }); + + it("uses default company id for company-scoped list tools", async () => { + const fetchMock = vi.fn().mockResolvedValue( + mockJsonResponse([{ id: "issue-1" }]), + ); + vi.stubGlobal("fetch", fetchMock); + + const tool = getTool("paperclipListIssues"); + const response = await tool.execute({}); + + expect(fetchMock).toHaveBeenCalledTimes(1); + const [url] = fetchMock.mock.calls[0] as [string]; + expect(String(url)).toBe( + "http://localhost:3100/api/companies/11111111-1111-1111-1111-111111111111/issues", + ); + expect(response.content[0]?.text).toContain("issue-1"); + }); + + it("uses default agent id for checkout requests", async () => { + const fetchMock = vi.fn().mockResolvedValue( + mockJsonResponse({ id: "PAP-1135", status: "in_progress" }), + ); + vi.stubGlobal("fetch", fetchMock); + + const tool = getTool("paperclipCheckoutIssue"); + await tool.execute({ + issueId: "PAP-1135", + }); + + const [, init] = fetchMock.mock.calls[0] as [string, RequestInit]; + expect(JSON.parse(String(init.body))).toEqual({ + agentId: "22222222-2222-2222-2222-222222222222", + expectedStatuses: ["todo", "backlog", "blocked"], + }); + }); + + it("defaults issue document format to markdown", async () => { + const fetchMock = vi.fn().mockResolvedValue( + mockJsonResponse({ key: "plan", latestRevisionNumber: 2 }), + ); + vi.stubGlobal("fetch", fetchMock); + + const tool = getTool("paperclipUpsertIssueDocument"); + await tool.execute({ + issueId: "PAP-1135", + key: "plan", + body: "# Updated", + }); + + const [, init] = fetchMock.mock.calls[0] as [string, RequestInit]; + expect(JSON.parse(String(init.body))).toEqual({ + format: "markdown", + body: "# Updated", + }); + }); + + it("rejects invalid generic request paths", async () => { + vi.stubGlobal("fetch", vi.fn()); + + const tool = getTool("paperclipApiRequest"); + const response = await tool.execute({ + method: "GET", + path: "issues", + }); + + expect(response.content[0]?.text).toContain("path must start with /"); + }); +}); diff --git a/packages/mcp-server/src/tools.ts b/packages/mcp-server/src/tools.ts new file mode 100644 index 00000000..cfed2f66 --- /dev/null +++ b/packages/mcp-server/src/tools.ts @@ -0,0 +1,413 @@ +import { z } from "zod"; +import { + addIssueCommentSchema, + checkoutIssueSchema, + createIssueSchema, + updateIssueSchema, + upsertIssueDocumentSchema, + linkIssueApprovalSchema, +} from "@paperclipai/shared"; +import { PaperclipApiClient } from "./client.js"; +import { formatErrorResponse, formatTextResponse } from "./format.js"; + +export interface ToolDefinition { + name: string; + description: string; + schema: z.AnyZodObject; + execute: (input: Record) => Promise<{ + content: Array<{ type: "text"; text: string }>; + }>; +} + +function makeTool( + name: string, + description: string, + schema: z.ZodObject, + execute: (input: z.infer) => Promise, +): ToolDefinition { + return { + name, + description, + schema, + execute: async (input) => { + try { + const parsed = schema.parse(input); + return formatTextResponse(await execute(parsed)); + } catch (error) { + return formatErrorResponse(error); + } + }, + }; +} + +function parseOptionalJson(raw: string | undefined | null): unknown { + if (!raw || raw.trim().length === 0) return undefined; + return JSON.parse(raw); +} + +const companyIdOptional = z.string().uuid().optional().nullable(); +const agentIdOptional = z.string().uuid().optional().nullable(); +const issueIdSchema = z.string().min(1); +const projectIdSchema = z.string().min(1); +const goalIdSchema = z.string().uuid(); +const approvalIdSchema = z.string().uuid(); +const documentKeySchema = z.string().trim().min(1).max(64); + +const listIssuesSchema = z.object({ + companyId: companyIdOptional, + status: z.string().optional(), + projectId: z.string().uuid().optional(), + assigneeAgentId: z.string().uuid().optional(), + participantAgentId: z.string().uuid().optional(), + assigneeUserId: z.string().optional(), + touchedByUserId: z.string().optional(), + inboxArchivedByUserId: z.string().optional(), + unreadForUserId: z.string().optional(), + labelId: z.string().uuid().optional(), + executionWorkspaceId: z.string().uuid().optional(), + originKind: z.string().optional(), + originId: z.string().optional(), + includeRoutineExecutions: z.boolean().optional(), + q: z.string().optional(), +}); + +const listCommentsSchema = z.object({ + issueId: issueIdSchema, + after: z.string().uuid().optional(), + order: z.enum(["asc", "desc"]).optional(), + limit: z.number().int().positive().max(500).optional(), +}); + +const upsertDocumentToolSchema = z.object({ + issueId: issueIdSchema, + key: documentKeySchema, + title: z.string().trim().max(200).nullable().optional(), + format: z.enum(["markdown"]).default("markdown"), + body: z.string().max(524288), + changeSummary: z.string().trim().max(500).nullable().optional(), + baseRevisionId: z.string().uuid().nullable().optional(), +}); + +const createIssueToolSchema = z.object({ + companyId: companyIdOptional, +}).merge(createIssueSchema); + +const updateIssueToolSchema = z.object({ + issueId: issueIdSchema, +}).merge(updateIssueSchema); + +const checkoutIssueToolSchema = z.object({ + issueId: issueIdSchema, + agentId: agentIdOptional, + expectedStatuses: checkoutIssueSchema.shape.expectedStatuses.optional(), +}); + +const addCommentToolSchema = z.object({ + issueId: issueIdSchema, +}).merge(addIssueCommentSchema); + +const approvalDecisionSchema = z.object({ + approvalId: approvalIdSchema, + action: z.enum(["approve", "reject", "requestRevision", "resubmit"]), + decisionNote: z.string().optional(), + payloadJson: z.string().optional(), +}); + +const apiRequestSchema = z.object({ + method: z.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]), + path: z.string().min(1), + jsonBody: z.string().optional(), +}); + +export function createToolDefinitions(client: PaperclipApiClient): ToolDefinition[] { + return [ + makeTool( + "paperclipMe", + "Get the current authenticated Paperclip actor details", + z.object({}), + async () => client.requestJson("GET", "/agents/me"), + ), + makeTool( + "paperclipInboxLite", + "Get the current authenticated agent inbox-lite assignment list", + z.object({}), + async () => client.requestJson("GET", "/agents/me/inbox-lite"), + ), + makeTool( + "paperclipListAgents", + "List agents in a company", + z.object({ companyId: companyIdOptional }), + async ({ companyId }) => client.requestJson("GET", `/companies/${client.resolveCompanyId(companyId)}/agents`), + ), + makeTool( + "paperclipGetAgent", + "Get a single agent by id", + z.object({ agentId: z.string().min(1), companyId: companyIdOptional }), + async ({ agentId, companyId }) => { + const qs = companyId ? `?companyId=${encodeURIComponent(companyId)}` : ""; + return client.requestJson("GET", `/agents/${encodeURIComponent(agentId)}${qs}`); + }, + ), + makeTool( + "paperclipListIssues", + "List issues for a company with optional filters", + listIssuesSchema, + async (input) => { + const companyId = client.resolveCompanyId(input.companyId); + const params = new URLSearchParams(); + for (const [key, value] of Object.entries(input)) { + if (key === "companyId" || value === undefined || value === null) continue; + params.set(key, String(value)); + } + const qs = params.toString(); + return client.requestJson("GET", `/companies/${companyId}/issues${qs ? `?${qs}` : ""}`); + }, + ), + makeTool( + "paperclipGetIssue", + "Get a single issue by UUID or identifier", + z.object({ issueId: issueIdSchema }), + async ({ issueId }) => client.requestJson("GET", `/issues/${encodeURIComponent(issueId)}`), + ), + makeTool( + "paperclipGetHeartbeatContext", + "Get compact heartbeat context for an issue", + z.object({ issueId: issueIdSchema, wakeCommentId: z.string().uuid().optional() }), + async ({ issueId, wakeCommentId }) => { + const qs = wakeCommentId ? `?wakeCommentId=${encodeURIComponent(wakeCommentId)}` : ""; + return client.requestJson("GET", `/issues/${encodeURIComponent(issueId)}/heartbeat-context${qs}`); + }, + ), + makeTool( + "paperclipListComments", + "List issue comments with incremental options", + listCommentsSchema, + async ({ issueId, after, order, limit }) => { + const params = new URLSearchParams(); + if (after) params.set("after", after); + if (order) params.set("order", order); + if (limit) params.set("limit", String(limit)); + const qs = params.toString(); + return client.requestJson("GET", `/issues/${encodeURIComponent(issueId)}/comments${qs ? `?${qs}` : ""}`); + }, + ), + makeTool( + "paperclipGetComment", + "Get a specific issue comment by id", + z.object({ issueId: issueIdSchema, commentId: z.string().uuid() }), + async ({ issueId, commentId }) => + client.requestJson("GET", `/issues/${encodeURIComponent(issueId)}/comments/${encodeURIComponent(commentId)}`), + ), + makeTool( + "paperclipListIssueApprovals", + "List approvals linked to an issue", + z.object({ issueId: issueIdSchema }), + async ({ issueId }) => client.requestJson("GET", `/issues/${encodeURIComponent(issueId)}/approvals`), + ), + makeTool( + "paperclipListDocuments", + "List issue documents", + z.object({ issueId: issueIdSchema }), + async ({ issueId }) => client.requestJson("GET", `/issues/${encodeURIComponent(issueId)}/documents`), + ), + makeTool( + "paperclipGetDocument", + "Get one issue document by key", + z.object({ issueId: issueIdSchema, key: documentKeySchema }), + async ({ issueId, key }) => + client.requestJson("GET", `/issues/${encodeURIComponent(issueId)}/documents/${encodeURIComponent(key)}`), + ), + makeTool( + "paperclipListDocumentRevisions", + "List revisions for an issue document", + z.object({ issueId: issueIdSchema, key: documentKeySchema }), + async ({ issueId, key }) => + client.requestJson( + "GET", + `/issues/${encodeURIComponent(issueId)}/documents/${encodeURIComponent(key)}/revisions`, + ), + ), + makeTool( + "paperclipListProjects", + "List projects in a company", + z.object({ companyId: companyIdOptional }), + async ({ companyId }) => client.requestJson("GET", `/companies/${client.resolveCompanyId(companyId)}/projects`), + ), + makeTool( + "paperclipGetProject", + "Get a project by id or company-scoped short reference", + z.object({ projectId: projectIdSchema, companyId: companyIdOptional }), + async ({ projectId, companyId }) => { + const qs = companyId ? `?companyId=${encodeURIComponent(companyId)}` : ""; + return client.requestJson("GET", `/projects/${encodeURIComponent(projectId)}${qs}`); + }, + ), + makeTool( + "paperclipListGoals", + "List goals in a company", + z.object({ companyId: companyIdOptional }), + async ({ companyId }) => client.requestJson("GET", `/companies/${client.resolveCompanyId(companyId)}/goals`), + ), + makeTool( + "paperclipGetGoal", + "Get a goal by id", + z.object({ goalId: goalIdSchema }), + async ({ goalId }) => client.requestJson("GET", `/goals/${encodeURIComponent(goalId)}`), + ), + makeTool( + "paperclipListApprovals", + "List approvals in a company", + z.object({ companyId: companyIdOptional, status: z.string().optional() }), + async ({ companyId, status }) => { + const qs = status ? `?status=${encodeURIComponent(status)}` : ""; + return client.requestJson("GET", `/companies/${client.resolveCompanyId(companyId)}/approvals${qs}`); + }, + ), + makeTool( + "paperclipGetApproval", + "Get an approval by id", + z.object({ approvalId: approvalIdSchema }), + async ({ approvalId }) => client.requestJson("GET", `/approvals/${encodeURIComponent(approvalId)}`), + ), + makeTool( + "paperclipGetApprovalIssues", + "List issues linked to an approval", + z.object({ approvalId: approvalIdSchema }), + async ({ approvalId }) => client.requestJson("GET", `/approvals/${encodeURIComponent(approvalId)}/issues`), + ), + makeTool( + "paperclipListApprovalComments", + "List comments for an approval", + z.object({ approvalId: approvalIdSchema }), + async ({ approvalId }) => client.requestJson("GET", `/approvals/${encodeURIComponent(approvalId)}/comments`), + ), + makeTool( + "paperclipCreateIssue", + "Create a new issue", + createIssueToolSchema, + async ({ companyId, ...body }) => + client.requestJson("POST", `/companies/${client.resolveCompanyId(companyId)}/issues`, { body }), + ), + makeTool( + "paperclipUpdateIssue", + "Patch an issue, optionally including a comment", + updateIssueToolSchema, + async ({ issueId, ...body }) => + client.requestJson("PATCH", `/issues/${encodeURIComponent(issueId)}`, { body }), + ), + makeTool( + "paperclipCheckoutIssue", + "Checkout an issue for an agent", + checkoutIssueToolSchema, + async ({ issueId, agentId, expectedStatuses }) => + client.requestJson("POST", `/issues/${encodeURIComponent(issueId)}/checkout`, { + body: { + agentId: client.resolveAgentId(agentId), + expectedStatuses: expectedStatuses ?? ["todo", "backlog", "blocked"], + }, + }), + ), + makeTool( + "paperclipReleaseIssue", + "Release an issue checkout", + z.object({ issueId: issueIdSchema }), + async ({ issueId }) => client.requestJson("POST", `/issues/${encodeURIComponent(issueId)}/release`, { body: {} }), + ), + makeTool( + "paperclipAddComment", + "Add a comment to an issue", + addCommentToolSchema, + async ({ issueId, ...body }) => + client.requestJson("POST", `/issues/${encodeURIComponent(issueId)}/comments`, { body }), + ), + makeTool( + "paperclipUpsertIssueDocument", + "Create or update an issue document", + upsertDocumentToolSchema, + async ({ issueId, key, ...body }) => + client.requestJson( + "PUT", + `/issues/${encodeURIComponent(issueId)}/documents/${encodeURIComponent(key)}`, + { body }, + ), + ), + makeTool( + "paperclipRestoreIssueDocumentRevision", + "Restore a prior revision of an issue document", + z.object({ + issueId: issueIdSchema, + key: documentKeySchema, + revisionId: z.string().uuid(), + }), + async ({ issueId, key, revisionId }) => + client.requestJson( + "POST", + `/issues/${encodeURIComponent(issueId)}/documents/${encodeURIComponent(key)}/revisions/${encodeURIComponent(revisionId)}/restore`, + { body: {} }, + ), + ), + makeTool( + "paperclipLinkIssueApproval", + "Link an approval to an issue", + z.object({ issueId: issueIdSchema }).merge(linkIssueApprovalSchema), + async ({ issueId, approvalId }) => + client.requestJson("POST", `/issues/${encodeURIComponent(issueId)}/approvals`, { + body: { approvalId }, + }), + ), + makeTool( + "paperclipUnlinkIssueApproval", + "Unlink an approval from an issue", + z.object({ issueId: issueIdSchema, approvalId: approvalIdSchema }), + async ({ issueId, approvalId }) => + client.requestJson( + "DELETE", + `/issues/${encodeURIComponent(issueId)}/approvals/${encodeURIComponent(approvalId)}`, + ), + ), + makeTool( + "paperclipApprovalDecision", + "Approve, reject, request revision, or resubmit an approval", + approvalDecisionSchema, + async ({ approvalId, action, decisionNote, payloadJson }) => { + const path = + action === "approve" + ? `/approvals/${encodeURIComponent(approvalId)}/approve` + : action === "reject" + ? `/approvals/${encodeURIComponent(approvalId)}/reject` + : action === "requestRevision" + ? `/approvals/${encodeURIComponent(approvalId)}/request-revision` + : `/approvals/${encodeURIComponent(approvalId)}/resubmit`; + + const body = + action === "resubmit" + ? { payload: parseOptionalJson(payloadJson) ?? {} } + : { decisionNote }; + + return client.requestJson("POST", path, { body }); + }, + ), + makeTool( + "paperclipAddApprovalComment", + "Add a comment to an approval", + z.object({ approvalId: approvalIdSchema, body: z.string().min(1) }), + async ({ approvalId, body }) => + client.requestJson("POST", `/approvals/${encodeURIComponent(approvalId)}/comments`, { + body: { body }, + }), + ), + makeTool( + "paperclipApiRequest", + "Make a JSON request to an existing Paperclip /api endpoint for unsupported operations", + apiRequestSchema, + async ({ method, path, jsonBody }) => { + if (!path.startsWith("/")) { + throw new Error("path must start with / and be relative to /api"); + } + return client.requestJson(method, path, { + body: parseOptionalJson(jsonBody), + }); + }, + ), + ]; +} diff --git a/packages/mcp-server/tsconfig.json b/packages/mcp-server/tsconfig.json new file mode 100644 index 00000000..5a24989c --- /dev/null +++ b/packages/mcp-server/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src" + }, + "include": ["src"] +} diff --git a/packages/mcp-server/vitest.config.ts b/packages/mcp-server/vitest.config.ts new file mode 100644 index 00000000..f624398e --- /dev/null +++ b/packages/mcp-server/vitest.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + environment: "node", + }, +}); diff --git a/tsconfig.json b/tsconfig.json index 9a5267db..e597c331 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,6 +3,7 @@ "files": [], "references": [ { "path": "./packages/adapter-utils" }, + { "path": "./packages/mcp-server" }, { "path": "./packages/shared" }, { "path": "./packages/db" }, { "path": "./packages/adapters/claude-local" }, From f1bb175584c933934051cb615fd03f16311eb00b Mon Sep 17 00:00:00 2001 From: dotta Date: Mon, 6 Apr 2026 21:25:58 -0500 Subject: [PATCH 02/10] feat(mcp): add approval creation tool Co-Authored-By: Paperclip --- packages/mcp-server/src/tools.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/mcp-server/src/tools.ts b/packages/mcp-server/src/tools.ts index cfed2f66..0945541c 100644 --- a/packages/mcp-server/src/tools.ts +++ b/packages/mcp-server/src/tools.ts @@ -2,6 +2,7 @@ import { z } from "zod"; import { addIssueCommentSchema, checkoutIssueSchema, + createApprovalSchema, createIssueSchema, updateIssueSchema, upsertIssueDocumentSchema, @@ -113,6 +114,10 @@ const approvalDecisionSchema = z.object({ payloadJson: z.string().optional(), }); +const createApprovalToolSchema = z.object({ + companyId: companyIdOptional, +}).merge(createApprovalSchema); + const apiRequestSchema = z.object({ method: z.enum(["GET", "POST", "PUT", "PATCH", "DELETE"]), path: z.string().min(1), @@ -263,6 +268,15 @@ export function createToolDefinitions(client: PaperclipApiClient): ToolDefinitio return client.requestJson("GET", `/companies/${client.resolveCompanyId(companyId)}/approvals${qs}`); }, ), + makeTool( + "paperclipCreateApproval", + "Create a board approval request, optionally linked to one or more issues", + createApprovalToolSchema, + async ({ companyId, ...body }) => + client.requestJson("POST", `/companies/${client.resolveCompanyId(companyId)}/approvals`, { + body, + }), + ), makeTool( "paperclipGetApproval", "Get an approval by id", From 85ca6753113d8be175fa26de06534e7eb668c785 Mon Sep 17 00:00:00 2001 From: dotta Date: Mon, 6 Apr 2026 21:43:19 -0500 Subject: [PATCH 03/10] fix(docker): include mcp server manifest in deps stage Co-Authored-By: Paperclip --- Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile b/Dockerfile index d613c380..36d5acab 100644 --- a/Dockerfile +++ b/Dockerfile @@ -28,6 +28,7 @@ COPY ui/package.json ui/ COPY packages/shared/package.json packages/shared/ COPY packages/db/package.json packages/db/ COPY packages/adapter-utils/package.json packages/adapter-utils/ +COPY packages/mcp-server/package.json packages/mcp-server/ COPY packages/adapters/claude-local/package.json packages/adapters/claude-local/ COPY packages/adapters/codex-local/package.json packages/adapters/codex-local/ COPY packages/adapters/cursor-local/package.json packages/adapters/cursor-local/ From 669e5c87cc69c1dc4c4c30feb861628702cc5917 Mon Sep 17 00:00:00 2001 From: dotta Date: Mon, 6 Apr 2026 21:56:13 -0500 Subject: [PATCH 04/10] fix(mcp): tighten api request validation --- packages/mcp-server/README.md | 1 + packages/mcp-server/src/tools.test.ts | 38 ++++++++ packages/mcp-server/src/tools.ts | 4 +- pnpm-lock.yaml | 130 ++++++++++++++++++++++++++ 4 files changed, 171 insertions(+), 2 deletions(-) diff --git a/packages/mcp-server/README.md b/packages/mcp-server/README.md index 602521e2..fc485f75 100644 --- a/packages/mcp-server/README.md +++ b/packages/mcp-server/README.md @@ -63,6 +63,7 @@ Write tools: - `paperclipAddComment` - `paperclipUpsertIssueDocument` - `paperclipRestoreIssueDocumentRevision` +- `paperclipCreateApproval` - `paperclipLinkIssueApproval` - `paperclipUnlinkIssueApproval` - `paperclipApprovalDecision` diff --git a/packages/mcp-server/src/tools.test.ts b/packages/mcp-server/src/tools.test.ts index f1d22285..d47347f5 100644 --- a/packages/mcp-server/src/tools.test.ts +++ b/packages/mcp-server/src/tools.test.ts @@ -107,6 +107,32 @@ describe("paperclip MCP tools", () => { }); }); + it("creates approvals with the expected company-scoped payload", async () => { + const fetchMock = vi.fn().mockResolvedValue( + mockJsonResponse({ id: "approval-1" }), + ); + vi.stubGlobal("fetch", fetchMock); + + const tool = getTool("paperclipCreateApproval"); + await tool.execute({ + type: "hire_agent", + payload: { branch: "pap-1167" }, + issueIds: ["44444444-4444-4444-4444-444444444444"], + }); + + expect(fetchMock).toHaveBeenCalledTimes(1); + const [url, init] = fetchMock.mock.calls[0] as [string, RequestInit]; + expect(String(url)).toBe( + "http://localhost:3100/api/companies/11111111-1111-1111-1111-111111111111/approvals", + ); + expect(init.method).toBe("POST"); + expect(JSON.parse(String(init.body))).toEqual({ + type: "hire_agent", + payload: { branch: "pap-1167" }, + issueIds: ["44444444-4444-4444-4444-444444444444"], + }); + }); + it("rejects invalid generic request paths", async () => { vi.stubGlobal("fetch", vi.fn()); @@ -118,4 +144,16 @@ describe("paperclip MCP tools", () => { expect(response.content[0]?.text).toContain("path must start with /"); }); + + it("rejects generic request paths that escape /api", async () => { + vi.stubGlobal("fetch", vi.fn()); + + const tool = getTool("paperclipApiRequest"); + const response = await tool.execute({ + method: "GET", + path: "/../../secret", + }); + + expect(response.content[0]?.text).toContain("must not contain '..'"); + }); }); diff --git a/packages/mcp-server/src/tools.ts b/packages/mcp-server/src/tools.ts index 0945541c..b5c18dba 100644 --- a/packages/mcp-server/src/tools.ts +++ b/packages/mcp-server/src/tools.ts @@ -415,8 +415,8 @@ export function createToolDefinitions(client: PaperclipApiClient): ToolDefinitio "Make a JSON request to an existing Paperclip /api endpoint for unsupported operations", apiRequestSchema, async ({ method, path, jsonBody }) => { - if (!path.startsWith("/")) { - throw new Error("path must start with / and be relative to /api"); + if (!path.startsWith("/") || path.includes("..")) { + throw new Error("path must start with / and be relative to /api, and must not contain '..'"); } return client.requestJson(method, path, { body: parseOptionalJson(jsonBody), diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 19c9ffc4..d6dc92a0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -251,6 +251,28 @@ importers: specifier: ^3.0.5 version: 3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.30.2)(tsx@4.21.0) + packages/mcp-server: + dependencies: + '@modelcontextprotocol/sdk': + specifier: ^1.29.0 + version: 1.29.0(zod@3.25.76) + '@paperclipai/shared': + specifier: workspace:* + version: link:../shared + zod: + specifier: ^3.24.2 + version: 3.25.76 + devDependencies: + '@types/node': + specifier: ^24.6.0 + version: 24.12.0 + typescript: + specifier: ^5.7.3 + version: 5.9.3 + vitest: + specifier: ^3.0.5 + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.30.2)(tsx@4.21.0) + packages/plugins/create-paperclip-plugin: dependencies: '@paperclipai/plugin-sdk': @@ -1727,6 +1749,12 @@ packages: '@floating-ui/utils@0.2.10': resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} + '@hono/node-server@1.19.12': + resolution: {integrity: sha512-txsUW4SQ1iilgE0l9/e9VQWmELXifEFvmdA1j6WFh/aFPj99hIntrSsq/if0UWyGVkmrRPKA1wCeP+UCr1B9Uw==} + engines: {node: '>=18.14.1'} + peerDependencies: + hono: ^4 + '@iconify/types@2.0.0': resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} @@ -2028,6 +2056,16 @@ packages: '@mermaid-js/parser@1.0.0': resolution: {integrity: sha512-vvK0Hi/VWndxoh03Mmz6wa1KDriSPjS2XMZL/1l19HFwygiObEEoEwSDxOqyLzzAI6J2PU3261JjTMTO7x+BPw==} + '@modelcontextprotocol/sdk@1.29.0': + resolution: {integrity: sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==} + engines: {node: '>=18'} + peerDependencies: + '@cfworker/json-schema': ^4.1.1 + zod: ^3.25 || ^4.0 + peerDependenciesMeta: + '@cfworker/json-schema': + optional: true + '@noble/ciphers@2.1.1': resolution: {integrity: sha512-bysYuiVfhxNJuldNXlFEitTVdNnYUc+XNJZd7Qm2a5j1vZHgY+fazadNFWFaMK/2vye0JVlxV3gHmC0WDfAOQw==} engines: {node: '>= 20.19.0'} @@ -3830,6 +3868,10 @@ packages: cookiejar@2.1.4: resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} + cors@2.8.6: + resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} + engines: {node: '>= 0.10'} + cose-base@1.0.3: resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==} @@ -4338,10 +4380,24 @@ packages: event-emitter@0.3.5: resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==} + eventsource-parser@3.0.6: + resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==} + engines: {node: '>=18.0.0'} + + eventsource@3.0.7: + resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} + engines: {node: '>=18.0.0'} + expect-type@1.3.0: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} + express-rate-limit@8.3.2: + resolution: {integrity: sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg==} + engines: {node: '>= 16'} + peerDependencies: + express: '>= 4.11' + express@5.2.1: resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} engines: {node: '>= 18'} @@ -4475,6 +4531,10 @@ packages: resolution: {integrity: sha512-6CP5vxfvY4jY9XJK5zu4ZUL9aB7HHNtEMk6q7m1Pu9Gzoby1Vx5VNmVqte3NUO+1cvVK9Arj1f67xLagWkbo5Q==} engines: {node: '>=20.0.0'} + hono@4.12.11: + resolution: {integrity: sha512-r4xbIa3mGGGoH9nN4A14DOg2wx7y2oQyJEb5O57C/xzETG/qx4c7CVDQ5WMeKHZ7ORk2W0hZ/sQKXTav3cmYBA==} + engines: {node: '>=16.9.0'} + html-encoding-sniffer@6.0.0: resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} @@ -4522,6 +4582,10 @@ packages: resolution: {integrity: sha512-fn4bQ0Xq8FTej09YC/jqKZwtijpvARlRp6wxL5WTA6yPe2YWSJ5RJh7Nm79rK2qB0wr6iDQzH60XGq5V/7u8YQ==} deprecated: The Intersection Observer polyfill is no longer needed and can safely be removed. Intersection Observer has been Baseline since 2019. + ip-address@10.1.0: + resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} + engines: {node: '>= 12'} + ipaddr.js@1.9.1: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} @@ -4617,6 +4681,9 @@ packages: json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-schema-typed@8.0.2: + resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==} + json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} @@ -5146,6 +5213,10 @@ packages: resolution: {integrity: sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==} hasBin: true + pkce-challenge@5.0.1: + resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} + engines: {node: '>=16.20.0'} + pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} @@ -5956,6 +6027,11 @@ packages: resolution: {integrity: sha512-kHqDPdltoXH+X4w1lVmMtddE3Oeqq48nM40FD5ojTd8xYhQpzIDcfE2keMSU5bAgRPJBe225WTUdyUgj1DtbiQ==} engines: {node: '>=16.0.0', npm: '>=8.0.0'} + zod-to-json-schema@3.25.2: + resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==} + peerDependencies: + zod: ^3.25.28 || ^4 + zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} @@ -7294,6 +7370,10 @@ snapshots: '@floating-ui/utils@0.2.10': {} + '@hono/node-server@1.19.12(hono@4.12.11)': + dependencies: + hono: 4.12.11 + '@iconify/types@2.0.0': {} '@iconify/utils@3.1.0': @@ -7735,6 +7815,28 @@ snapshots: dependencies: langium: 4.2.1 + '@modelcontextprotocol/sdk@1.29.0(zod@3.25.76)': + dependencies: + '@hono/node-server': 1.19.12(hono@4.12.11) + ajv: 8.18.0 + ajv-formats: 3.0.1(ajv@8.18.0) + content-type: 1.0.5 + cors: 2.8.6 + cross-spawn: 7.0.6 + eventsource: 3.0.7 + eventsource-parser: 3.0.6 + express: 5.2.1 + express-rate-limit: 8.3.2(express@5.2.1) + hono: 4.12.11 + jose: 6.1.3 + json-schema-typed: 8.0.2 + pkce-challenge: 5.0.1 + raw-body: 3.0.2 + zod: 3.25.76 + zod-to-json-schema: 3.25.2(zod@3.25.76) + transitivePeerDependencies: + - supports-color + '@noble/ciphers@2.1.1': {} '@noble/hashes@1.8.0': {} @@ -9645,6 +9747,11 @@ snapshots: cookiejar@2.1.4: {} + cors@2.8.6: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + cose-base@1.0.3: dependencies: layout-base: 1.0.2 @@ -10170,8 +10277,19 @@ snapshots: d: 1.0.2 es5-ext: 0.10.64 + eventsource-parser@3.0.6: {} + + eventsource@3.0.7: + dependencies: + eventsource-parser: 3.0.6 + expect-type@1.3.0: {} + express-rate-limit@8.3.2(express@5.2.1): + dependencies: + express: 5.2.1 + ip-address: 10.1.0 + express@5.2.1: dependencies: accepts: 2.0.0 @@ -10345,6 +10463,8 @@ snapshots: '@paperclipai/adapter-utils': 2026.325.0 picocolors: 1.1.1 + hono@4.12.11: {} + html-encoding-sniffer@6.0.0(@noble/hashes@2.0.1): dependencies: '@exodus/bytes': 1.15.0(@noble/hashes@2.0.1) @@ -10395,6 +10515,8 @@ snapshots: intersection-observer@0.10.0: {} + ip-address@10.1.0: {} + ipaddr.js@1.9.1: {} is-alphabetical@2.0.1: {} @@ -10481,6 +10603,8 @@ snapshots: json-schema-traverse@1.0.0: {} + json-schema-typed@8.0.2: {} + json5@2.2.3: {} katex@0.16.37: @@ -11303,6 +11427,8 @@ snapshots: sonic-boom: 4.2.1 thread-stream: 3.1.0 + pkce-challenge@5.0.1: {} + pkg-types@1.3.1: dependencies: confbox: 0.1.8 @@ -12286,6 +12412,10 @@ snapshots: dependencies: lib0: 0.2.117 + zod-to-json-schema@3.25.2(zod@3.25.76): + dependencies: + zod: 3.25.76 + zod@3.25.76: {} zod@4.3.6: {} From 2c8cb7f519b9c876d881607586fa9c7853d5237e Mon Sep 17 00:00:00 2001 From: dotta Date: Mon, 6 Apr 2026 21:58:29 -0500 Subject: [PATCH 05/10] fix(ci): support manifest changes without lockfile --- .github/workflows/pr.yml | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index a45f392e..c565ddef 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -109,7 +109,14 @@ jobs: cache: pnpm - name: Install dependencies - run: pnpm install --frozen-lockfile + run: | + changed="$(git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}")" + manifest_pattern='(^|/)package\.json$|^pnpm-workspace\.yaml$|^\.npmrc$|^pnpmfile\.(cjs|js|mjs)$' + if printf '%s\n' "$changed" | grep -Eq "$manifest_pattern"; then + pnpm install --no-frozen-lockfile + else + pnpm install --frozen-lockfile + fi - name: Typecheck run: pnpm -r typecheck @@ -147,7 +154,14 @@ jobs: cache: pnpm - name: Install dependencies - run: pnpm install --frozen-lockfile + run: | + changed="$(git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}")" + manifest_pattern='(^|/)package\.json$|^pnpm-workspace\.yaml$|^\.npmrc$|^pnpmfile\.(cjs|js|mjs)$' + if printf '%s\n' "$changed" | grep -Eq "$manifest_pattern"; then + pnpm install --no-frozen-lockfile + else + pnpm install --frozen-lockfile + fi - name: Build run: pnpm build From ac473820a38a27de3a7560b3cf52a156478e7f4a Mon Sep 17 00:00:00 2001 From: dotta Date: Mon, 6 Apr 2026 21:59:28 -0500 Subject: [PATCH 06/10] fix(ci): drop lockfile changes from mcp pr --- pnpm-lock.yaml | 294 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 291 insertions(+), 3 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d6dc92a0..045bab78 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -27,7 +27,7 @@ importers: version: 5.9.3 vitest: specifier: ^3.0.5 - version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.30.2)(tsx@4.21.0) + version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@2.6.1)(jsdom@28.1.0)(lightningcss@1.30.2)(tsx@4.21.0) cli: dependencies: @@ -679,6 +679,9 @@ importers: react: specifier: ^19.0.0 version: 19.2.4 + react-diff-viewer-continued: + specifier: ^4.2.0 + version: 4.2.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) react-dom: specifier: ^19.0.0 version: 19.2.4(react@19.2.4) @@ -721,7 +724,7 @@ importers: version: 6.4.1(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) vitest: specifier: ^3.0.5 - version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.30.2)(tsx@4.21.0) + version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@2.6.1)(jsdom@28.1.0)(lightningcss@1.30.2)(tsx@4.21.0) packages: @@ -1264,6 +1267,50 @@ packages: '@emnapi/runtime@1.9.1': resolution: {integrity: sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==} + '@emotion/babel-plugin@11.13.5': + resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==} + + '@emotion/cache@11.14.0': + resolution: {integrity: sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==} + + '@emotion/css@11.13.5': + resolution: {integrity: sha512-wQdD0Xhkn3Qy2VNcIzbLP9MR8TafI0MJb7BEAXKp+w4+XqErksWR4OXomuDzPsN4InLdGhVe6EYcn2ZIUCpB8w==} + + '@emotion/hash@0.9.2': + resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} + + '@emotion/memoize@0.9.0': + resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} + + '@emotion/react@11.14.0': + resolution: {integrity: sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==} + peerDependencies: + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@emotion/serialize@1.3.3': + resolution: {integrity: sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==} + + '@emotion/sheet@1.4.0': + resolution: {integrity: sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==} + + '@emotion/unitless@0.10.0': + resolution: {integrity: sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==} + + '@emotion/use-insertion-effect-with-fallbacks@1.2.0': + resolution: {integrity: sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==} + peerDependencies: + react: '>=16.8.0' + + '@emotion/utils@1.4.2': + resolution: {integrity: sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==} + + '@emotion/weak-memoize@0.4.0': + resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==} + '@epic-web/invariant@1.0.0': resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==} @@ -3464,6 +3511,9 @@ packages: '@types/node@25.2.3': resolution: {integrity: sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==} + '@types/parse-json@4.0.2': + resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + '@types/qs@6.14.0': resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} @@ -3614,6 +3664,10 @@ packages: resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} engines: {node: '>=8.0.0'} + babel-plugin-macros@3.1.0: + resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} + engines: {node: '>=10', npm: '>=6'} + bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} @@ -3739,6 +3793,10 @@ packages: resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} engines: {node: '>= 0.4'} + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + caniuse-lite@1.0.30001770: resolution: {integrity: sha512-x/2CLQ1jHENRbHg5PSId2sXq1CIO1CISvwWAj027ltMVG2UNgW+w9oH2+HzgEIRFembL8bUlXtfbBHR1fCg2xw==} @@ -3850,6 +3908,9 @@ packages: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} + convert-source-map@1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -3878,6 +3939,10 @@ packages: cose-base@2.2.0: resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==} + cosmiconfig@7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + crelt@1.0.6: resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} @@ -4151,6 +4216,10 @@ packages: resolution: {integrity: sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==} engines: {node: '>=0.3.1'} + diff@8.0.4: + resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==} + engines: {node: '>=0.3.1'} + dompurify@3.3.2: resolution: {integrity: sha512-6obghkliLdmKa56xdbLOpUZ43pAR6xFy1uOrxBaIDjT+yaRuuybLjGS9eVBoSR/UPU5fq3OXClEHLJNGvbxKpQ==} engines: {node: '>=20'} @@ -4293,6 +4362,9 @@ packages: resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} + es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} @@ -4353,6 +4425,10 @@ packages: escape-html@1.0.3: resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + escape-string-regexp@5.0.0: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} @@ -4440,6 +4516,9 @@ packages: resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} engines: {node: '>= 18.0.0'} + find-root@1.1.0: + resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} + form-data@4.0.5: resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} engines: {node: '>= 6'} @@ -4531,6 +4610,9 @@ packages: resolution: {integrity: sha512-6CP5vxfvY4jY9XJK5zu4ZUL9aB7HHNtEMk6q7m1Pu9Gzoby1Vx5VNmVqte3NUO+1cvVK9Arj1f67xLagWkbo5Q==} engines: {node: '>=20.0.0'} + hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + hono@4.12.11: resolution: {integrity: sha512-r4xbIa3mGGGoH9nN4A14DOg2wx7y2oQyJEb5O57C/xzETG/qx4c7CVDQ5WMeKHZ7ORk2W0hZ/sQKXTav3cmYBA==} engines: {node: '>=16.9.0'} @@ -4565,6 +4647,10 @@ packages: ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -4596,6 +4682,9 @@ packages: is-alphanumerical@2.0.1: resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + is-core-module@2.16.1: resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} engines: {node: '>= 0.4'} @@ -4678,6 +4767,9 @@ packages: engines: {node: '>=6'} hasBin: true + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} @@ -4792,6 +4884,9 @@ packages: resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==} engines: {node: '>= 12.0.0'} + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + lodash-es@4.17.23: resolution: {integrity: sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==} @@ -4904,6 +4999,9 @@ packages: resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} engines: {node: '>= 0.8'} + memoize-one@6.0.0: + resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==} + merge-descriptors@2.0.0: resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} engines: {node: '>=18'} @@ -5119,9 +5217,17 @@ packages: package-manager-detector@1.6.0: resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==} + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + parse-entities@4.0.2: resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} @@ -5145,6 +5251,10 @@ packages: path-to-regexp@8.3.0: resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} @@ -5323,6 +5433,13 @@ packages: react-devtools-inline@4.4.0: resolution: {integrity: sha512-ES0GolSrKO8wsKbsEkVeiR/ZAaHQTY4zDh1UW8DImVmm8oaGLl3ijJDvSGe+qDRKPZdPRnDtWWnSvvrgxXdThQ==} + react-diff-viewer-continued@4.2.0: + resolution: {integrity: sha512-KXeevuPpMRNDAtF878G04Yih/01DBBoC+RjDzWiA5S6TPtUzSfqF5XOlEWyXVWvJuz5n+EQ9QdUQd0ffK2By6w==} + engines: {node: '>= 16'} + peerDependencies: + react: ^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom@19.2.4: resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==} peerDependencies: @@ -5435,6 +5552,10 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} @@ -5559,6 +5680,10 @@ packages: source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + source-map@0.5.7: + resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} + engines: {node: '>=0.10.0'} + source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} @@ -5615,6 +5740,9 @@ packages: style-to-object@1.0.14: resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} + stylis@4.2.0: + resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} + stylis@4.3.6: resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==} @@ -6023,6 +6151,10 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yaml@1.10.3: + resolution: {integrity: sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==} + engines: {node: '>= 6'} + yjs@13.6.29: resolution: {integrity: sha512-kHqDPdltoXH+X4w1lVmMtddE3Oeqq48nM40FD5ojTd8xYhQpzIDcfE2keMSU5bAgRPJBe225WTUdyUgj1DtbiQ==} engines: {node: '>=16.0.0', npm: '>=8.0.0'} @@ -7107,6 +7239,80 @@ snapshots: tslib: 2.8.1 optional: true + '@emotion/babel-plugin@11.13.5': + dependencies: + '@babel/helper-module-imports': 7.28.6 + '@babel/runtime': 7.28.6 + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/serialize': 1.3.3 + babel-plugin-macros: 3.1.0 + convert-source-map: 1.9.0 + escape-string-regexp: 4.0.0 + find-root: 1.1.0 + source-map: 0.5.7 + stylis: 4.2.0 + transitivePeerDependencies: + - supports-color + + '@emotion/cache@11.14.0': + dependencies: + '@emotion/memoize': 0.9.0 + '@emotion/sheet': 1.4.0 + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + stylis: 4.2.0 + + '@emotion/css@11.13.5': + dependencies: + '@emotion/babel-plugin': 11.13.5 + '@emotion/cache': 11.14.0 + '@emotion/serialize': 1.3.3 + '@emotion/sheet': 1.4.0 + '@emotion/utils': 1.4.2 + transitivePeerDependencies: + - supports-color + + '@emotion/hash@0.9.2': {} + + '@emotion/memoize@0.9.0': {} + + '@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4)': + dependencies: + '@babel/runtime': 7.28.6 + '@emotion/babel-plugin': 11.13.5 + '@emotion/cache': 11.14.0 + '@emotion/serialize': 1.3.3 + '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.2.4) + '@emotion/utils': 1.4.2 + '@emotion/weak-memoize': 0.4.0 + hoist-non-react-statics: 3.3.2 + react: 19.2.4 + optionalDependencies: + '@types/react': 19.2.14 + transitivePeerDependencies: + - supports-color + + '@emotion/serialize@1.3.3': + dependencies: + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/unitless': 0.10.0 + '@emotion/utils': 1.4.2 + csstype: 3.2.3 + + '@emotion/sheet@1.4.0': {} + + '@emotion/unitless@0.10.0': {} + + '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@19.2.4)': + dependencies: + react: 19.2.4 + + '@emotion/utils@1.4.2': {} + + '@emotion/weak-memoize@0.4.0': {} + '@epic-web/invariant@1.0.0': {} '@esbuild-kit/core-utils@3.3.2': @@ -9371,6 +9577,8 @@ snapshots: dependencies: undici-types: 7.16.0 + '@types/parse-json@4.0.2': {} + '@types/qs@6.14.0': {} '@types/range-parser@1.2.7': {} @@ -9533,6 +9741,12 @@ snapshots: atomic-sleep@1.0.0: {} + babel-plugin-macros@3.1.0: + dependencies: + '@babel/runtime': 7.28.6 + cosmiconfig: 7.1.0 + resolve: 1.22.11 + bail@2.0.2: {} base64-js@1.5.1: {} @@ -9627,6 +9841,8 @@ snapshots: call-bind-apply-helpers: 1.0.2 get-intrinsic: 1.3.0 + callsites@3.1.0: {} + caniuse-lite@1.0.30001770: {} ccount@2.0.1: {} @@ -9737,6 +9953,8 @@ snapshots: content-type@1.0.5: {} + convert-source-map@1.9.0: {} + convert-source-map@2.0.0: {} cookie-signature@1.2.2: {} @@ -9760,6 +9978,14 @@ snapshots: dependencies: layout-base: 2.0.1 + cosmiconfig@7.1.0: + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.1 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.3 + crelt@1.0.6: {} cross-env@10.1.0: @@ -10043,6 +10269,8 @@ snapshots: diff@5.2.2: {} + diff@8.0.4: {} + dompurify@3.3.2: optionalDependencies: '@types/trusted-types': 2.0.7 @@ -10117,6 +10345,10 @@ snapshots: entities@6.0.1: {} + error-ex@1.3.4: + dependencies: + is-arrayish: 0.2.1 + es-define-property@1.0.1: {} es-errors@1.3.0: {} @@ -10248,6 +10480,8 @@ snapshots: escape-html@1.0.3: {} + escape-string-regexp@4.0.0: {} + escape-string-regexp@5.0.0: {} esniff@2.0.1: @@ -10360,6 +10594,8 @@ snapshots: transitivePeerDependencies: - supports-color + find-root@1.1.0: {} + form-data@4.0.5: dependencies: asynckit: 0.4.0 @@ -10463,6 +10699,10 @@ snapshots: '@paperclipai/adapter-utils': 2026.325.0 picocolors: 1.1.1 + hoist-non-react-statics@3.3.2: + dependencies: + react-is: 16.13.1 + hono@4.12.11: {} html-encoding-sniffer@6.0.0(@noble/hashes@2.0.1): @@ -10505,6 +10745,11 @@ snapshots: ieee754@1.2.1: {} + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + inherits@2.0.4: {} inline-style-parser@0.2.7: {} @@ -10526,6 +10771,8 @@ snapshots: is-alphabetical: 2.0.1 is-decimal: 2.0.1 + is-arrayish@0.2.1: {} + is-core-module@2.16.1: dependencies: hasown: 2.0.2 @@ -10601,6 +10848,8 @@ snapshots: jsesc@3.1.0: {} + json-parse-even-better-errors@2.3.1: {} + json-schema-traverse@1.0.0: {} json-schema-typed@8.0.2: {} @@ -10684,6 +10933,8 @@ snapshots: lightningcss-win32-arm64-msvc: 1.30.2 lightningcss-win32-x64-msvc: 1.30.2 + lines-and-columns@1.2.4: {} + lodash-es@4.17.23: {} longest-streak@3.1.0: {} @@ -10914,6 +11165,8 @@ snapshots: media-typer@1.1.0: {} + memoize-one@6.0.0: {} + merge-descriptors@2.0.0: {} mermaid@11.12.3: @@ -11309,6 +11562,10 @@ snapshots: package-manager-detector@1.6.0: {} + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + parse-entities@4.0.2: dependencies: '@types/unist': 2.0.11 @@ -11319,6 +11576,13 @@ snapshots: is-decimal: 2.0.1 is-hexadecimal: 2.0.1 + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.29.0 + error-ex: 1.3.4 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + parse5@7.3.0: dependencies: entities: 6.0.1 @@ -11337,6 +11601,8 @@ snapshots: path-to-regexp@8.3.0: {} + path-type@4.0.0: {} + pathe@2.0.3: {} pathval@2.0.1: {} @@ -11581,6 +11847,20 @@ snapshots: dependencies: es6-symbol: 3.1.4 + react-diff-viewer-continued@4.2.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + dependencies: + '@emotion/css': 11.13.5 + '@emotion/react': 11.14.0(@types/react@19.2.14)(react@19.2.4) + classnames: 2.5.1 + diff: 8.0.4 + js-yaml: 4.1.1 + memoize-one: 6.0.0 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + transitivePeerDependencies: + - '@types/react' + - supports-color + react-dom@19.2.4(react@19.2.4): dependencies: react: 19.2.4 @@ -11708,6 +11988,8 @@ snapshots: require-from-string@2.0.2: {} + resolve-from@4.0.0: {} + resolve-pkg-maps@1.0.0: {} resolve@1.22.11: @@ -11903,6 +12185,8 @@ snapshots: buffer-from: 1.1.2 source-map: 0.6.1 + source-map@0.5.7: {} + source-map@0.6.1: {} space-separated-tokens@2.0.2: {} @@ -11953,6 +12237,8 @@ snapshots: dependencies: inline-style-parser: 0.2.7 + stylis@4.2.0: {} + stylis@4.3.6: {} superagent@10.3.0: @@ -12304,7 +12590,7 @@ snapshots: - tsx - yaml - vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.30.2)(tsx@4.21.0): + vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@2.6.1)(jsdom@28.1.0)(lightningcss@1.30.2)(tsx@4.21.0): dependencies: '@types/chai': 5.2.3 '@vitest/expect': 3.2.4 @@ -12408,6 +12694,8 @@ snapshots: yallist@3.1.1: {} + yaml@1.10.3: {} + yjs@13.6.29: dependencies: lib0: 0.2.117 From 1e4d252661f8acaa6dcb22a97f90c2edc9f35b3c Mon Sep 17 00:00:00 2001 From: dotta Date: Mon, 6 Apr 2026 22:00:13 -0500 Subject: [PATCH 07/10] fix(ci): restore lockfile to pr base --- pnpm-lock.yaml | 424 +------------------------------------------------ 1 file changed, 3 insertions(+), 421 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 045bab78..19c9ffc4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -27,7 +27,7 @@ importers: version: 5.9.3 vitest: specifier: ^3.0.5 - version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@2.6.1)(jsdom@28.1.0)(lightningcss@1.30.2)(tsx@4.21.0) + version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.30.2)(tsx@4.21.0) cli: dependencies: @@ -251,28 +251,6 @@ importers: specifier: ^3.0.5 version: 3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.30.2)(tsx@4.21.0) - packages/mcp-server: - dependencies: - '@modelcontextprotocol/sdk': - specifier: ^1.29.0 - version: 1.29.0(zod@3.25.76) - '@paperclipai/shared': - specifier: workspace:* - version: link:../shared - zod: - specifier: ^3.24.2 - version: 3.25.76 - devDependencies: - '@types/node': - specifier: ^24.6.0 - version: 24.12.0 - typescript: - specifier: ^5.7.3 - version: 5.9.3 - vitest: - specifier: ^3.0.5 - version: 3.2.4(@types/debug@4.1.12)(@types/node@24.12.0)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.30.2)(tsx@4.21.0) - packages/plugins/create-paperclip-plugin: dependencies: '@paperclipai/plugin-sdk': @@ -679,9 +657,6 @@ importers: react: specifier: ^19.0.0 version: 19.2.4 - react-diff-viewer-continued: - specifier: ^4.2.0 - version: 4.2.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) react-dom: specifier: ^19.0.0 version: 19.2.4(react@19.2.4) @@ -724,7 +699,7 @@ importers: version: 6.4.1(@types/node@25.2.3)(jiti@2.6.1)(lightningcss@1.30.2)(tsx@4.21.0) vitest: specifier: ^3.0.5 - version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@2.6.1)(jsdom@28.1.0)(lightningcss@1.30.2)(tsx@4.21.0) + version: 3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.30.2)(tsx@4.21.0) packages: @@ -1267,50 +1242,6 @@ packages: '@emnapi/runtime@1.9.1': resolution: {integrity: sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==} - '@emotion/babel-plugin@11.13.5': - resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==} - - '@emotion/cache@11.14.0': - resolution: {integrity: sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==} - - '@emotion/css@11.13.5': - resolution: {integrity: sha512-wQdD0Xhkn3Qy2VNcIzbLP9MR8TafI0MJb7BEAXKp+w4+XqErksWR4OXomuDzPsN4InLdGhVe6EYcn2ZIUCpB8w==} - - '@emotion/hash@0.9.2': - resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} - - '@emotion/memoize@0.9.0': - resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} - - '@emotion/react@11.14.0': - resolution: {integrity: sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==} - peerDependencies: - '@types/react': '*' - react: '>=16.8.0' - peerDependenciesMeta: - '@types/react': - optional: true - - '@emotion/serialize@1.3.3': - resolution: {integrity: sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==} - - '@emotion/sheet@1.4.0': - resolution: {integrity: sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==} - - '@emotion/unitless@0.10.0': - resolution: {integrity: sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==} - - '@emotion/use-insertion-effect-with-fallbacks@1.2.0': - resolution: {integrity: sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==} - peerDependencies: - react: '>=16.8.0' - - '@emotion/utils@1.4.2': - resolution: {integrity: sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==} - - '@emotion/weak-memoize@0.4.0': - resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==} - '@epic-web/invariant@1.0.0': resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==} @@ -1796,12 +1727,6 @@ packages: '@floating-ui/utils@0.2.10': resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} - '@hono/node-server@1.19.12': - resolution: {integrity: sha512-txsUW4SQ1iilgE0l9/e9VQWmELXifEFvmdA1j6WFh/aFPj99hIntrSsq/if0UWyGVkmrRPKA1wCeP+UCr1B9Uw==} - engines: {node: '>=18.14.1'} - peerDependencies: - hono: ^4 - '@iconify/types@2.0.0': resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} @@ -2103,16 +2028,6 @@ packages: '@mermaid-js/parser@1.0.0': resolution: {integrity: sha512-vvK0Hi/VWndxoh03Mmz6wa1KDriSPjS2XMZL/1l19HFwygiObEEoEwSDxOqyLzzAI6J2PU3261JjTMTO7x+BPw==} - '@modelcontextprotocol/sdk@1.29.0': - resolution: {integrity: sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==} - engines: {node: '>=18'} - peerDependencies: - '@cfworker/json-schema': ^4.1.1 - zod: ^3.25 || ^4.0 - peerDependenciesMeta: - '@cfworker/json-schema': - optional: true - '@noble/ciphers@2.1.1': resolution: {integrity: sha512-bysYuiVfhxNJuldNXlFEitTVdNnYUc+XNJZd7Qm2a5j1vZHgY+fazadNFWFaMK/2vye0JVlxV3gHmC0WDfAOQw==} engines: {node: '>= 20.19.0'} @@ -3511,9 +3426,6 @@ packages: '@types/node@25.2.3': resolution: {integrity: sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==} - '@types/parse-json@4.0.2': - resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} - '@types/qs@6.14.0': resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} @@ -3664,10 +3576,6 @@ packages: resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} engines: {node: '>=8.0.0'} - babel-plugin-macros@3.1.0: - resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} - engines: {node: '>=10', npm: '>=6'} - bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} @@ -3793,10 +3701,6 @@ packages: resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} engines: {node: '>= 0.4'} - callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} - caniuse-lite@1.0.30001770: resolution: {integrity: sha512-x/2CLQ1jHENRbHg5PSId2sXq1CIO1CISvwWAj027ltMVG2UNgW+w9oH2+HzgEIRFembL8bUlXtfbBHR1fCg2xw==} @@ -3908,9 +3812,6 @@ packages: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} - convert-source-map@1.9.0: - resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} - convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -3929,20 +3830,12 @@ packages: cookiejar@2.1.4: resolution: {integrity: sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==} - cors@2.8.6: - resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} - engines: {node: '>= 0.10'} - cose-base@1.0.3: resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==} cose-base@2.2.0: resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==} - cosmiconfig@7.1.0: - resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} - engines: {node: '>=10'} - crelt@1.0.6: resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} @@ -4216,10 +4109,6 @@ packages: resolution: {integrity: sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==} engines: {node: '>=0.3.1'} - diff@8.0.4: - resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==} - engines: {node: '>=0.3.1'} - dompurify@3.3.2: resolution: {integrity: sha512-6obghkliLdmKa56xdbLOpUZ43pAR6xFy1uOrxBaIDjT+yaRuuybLjGS9eVBoSR/UPU5fq3OXClEHLJNGvbxKpQ==} engines: {node: '>=20'} @@ -4362,9 +4251,6 @@ packages: resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} - error-ex@1.3.4: - resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} - es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} @@ -4425,10 +4311,6 @@ packages: escape-html@1.0.3: resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} - escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - escape-string-regexp@5.0.0: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} @@ -4456,24 +4338,10 @@ packages: event-emitter@0.3.5: resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==} - eventsource-parser@3.0.6: - resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==} - engines: {node: '>=18.0.0'} - - eventsource@3.0.7: - resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} - engines: {node: '>=18.0.0'} - expect-type@1.3.0: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} - express-rate-limit@8.3.2: - resolution: {integrity: sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg==} - engines: {node: '>= 16'} - peerDependencies: - express: '>= 4.11' - express@5.2.1: resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} engines: {node: '>= 18'} @@ -4516,9 +4384,6 @@ packages: resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} engines: {node: '>= 18.0.0'} - find-root@1.1.0: - resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} - form-data@4.0.5: resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} engines: {node: '>= 6'} @@ -4610,13 +4475,6 @@ packages: resolution: {integrity: sha512-6CP5vxfvY4jY9XJK5zu4ZUL9aB7HHNtEMk6q7m1Pu9Gzoby1Vx5VNmVqte3NUO+1cvVK9Arj1f67xLagWkbo5Q==} engines: {node: '>=20.0.0'} - hoist-non-react-statics@3.3.2: - resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} - - hono@4.12.11: - resolution: {integrity: sha512-r4xbIa3mGGGoH9nN4A14DOg2wx7y2oQyJEb5O57C/xzETG/qx4c7CVDQ5WMeKHZ7ORk2W0hZ/sQKXTav3cmYBA==} - engines: {node: '>=16.9.0'} - html-encoding-sniffer@6.0.0: resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} @@ -4647,10 +4505,6 @@ packages: ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - import-fresh@3.3.1: - resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} - engines: {node: '>=6'} - inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -4668,10 +4522,6 @@ packages: resolution: {integrity: sha512-fn4bQ0Xq8FTej09YC/jqKZwtijpvARlRp6wxL5WTA6yPe2YWSJ5RJh7Nm79rK2qB0wr6iDQzH60XGq5V/7u8YQ==} deprecated: The Intersection Observer polyfill is no longer needed and can safely be removed. Intersection Observer has been Baseline since 2019. - ip-address@10.1.0: - resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} - engines: {node: '>= 12'} - ipaddr.js@1.9.1: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} @@ -4682,9 +4532,6 @@ packages: is-alphanumerical@2.0.1: resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} - is-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - is-core-module@2.16.1: resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} engines: {node: '>= 0.4'} @@ -4767,15 +4614,9 @@ packages: engines: {node: '>=6'} hasBin: true - json-parse-even-better-errors@2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - json-schema-typed@8.0.2: - resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==} - json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} @@ -4884,9 +4725,6 @@ packages: resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==} engines: {node: '>= 12.0.0'} - lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - lodash-es@4.17.23: resolution: {integrity: sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==} @@ -4999,9 +4837,6 @@ packages: resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} engines: {node: '>= 0.8'} - memoize-one@6.0.0: - resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==} - merge-descriptors@2.0.0: resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} engines: {node: '>=18'} @@ -5217,17 +5052,9 @@ packages: package-manager-detector@1.6.0: resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==} - parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} - parse-entities@4.0.2: resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} - parse-json@5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} - parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} @@ -5251,10 +5078,6 @@ packages: path-to-regexp@8.3.0: resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} - path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} @@ -5323,10 +5146,6 @@ packages: resolution: {integrity: sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==} hasBin: true - pkce-challenge@5.0.1: - resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} - engines: {node: '>=16.20.0'} - pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} @@ -5433,13 +5252,6 @@ packages: react-devtools-inline@4.4.0: resolution: {integrity: sha512-ES0GolSrKO8wsKbsEkVeiR/ZAaHQTY4zDh1UW8DImVmm8oaGLl3ijJDvSGe+qDRKPZdPRnDtWWnSvvrgxXdThQ==} - react-diff-viewer-continued@4.2.0: - resolution: {integrity: sha512-KXeevuPpMRNDAtF878G04Yih/01DBBoC+RjDzWiA5S6TPtUzSfqF5XOlEWyXVWvJuz5n+EQ9QdUQd0ffK2By6w==} - engines: {node: '>= 16'} - peerDependencies: - react: ^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom@19.2.4: resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==} peerDependencies: @@ -5552,10 +5364,6 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} - resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} - resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} @@ -5680,10 +5488,6 @@ packages: source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} - source-map@0.5.7: - resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} - engines: {node: '>=0.10.0'} - source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} @@ -5740,9 +5544,6 @@ packages: style-to-object@1.0.14: resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} - stylis@4.2.0: - resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} - stylis@4.3.6: resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==} @@ -6151,19 +5952,10 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yaml@1.10.3: - resolution: {integrity: sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==} - engines: {node: '>= 6'} - yjs@13.6.29: resolution: {integrity: sha512-kHqDPdltoXH+X4w1lVmMtddE3Oeqq48nM40FD5ojTd8xYhQpzIDcfE2keMSU5bAgRPJBe225WTUdyUgj1DtbiQ==} engines: {node: '>=16.0.0', npm: '>=8.0.0'} - zod-to-json-schema@3.25.2: - resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==} - peerDependencies: - zod: ^3.25.28 || ^4 - zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} @@ -7239,80 +7031,6 @@ snapshots: tslib: 2.8.1 optional: true - '@emotion/babel-plugin@11.13.5': - dependencies: - '@babel/helper-module-imports': 7.28.6 - '@babel/runtime': 7.28.6 - '@emotion/hash': 0.9.2 - '@emotion/memoize': 0.9.0 - '@emotion/serialize': 1.3.3 - babel-plugin-macros: 3.1.0 - convert-source-map: 1.9.0 - escape-string-regexp: 4.0.0 - find-root: 1.1.0 - source-map: 0.5.7 - stylis: 4.2.0 - transitivePeerDependencies: - - supports-color - - '@emotion/cache@11.14.0': - dependencies: - '@emotion/memoize': 0.9.0 - '@emotion/sheet': 1.4.0 - '@emotion/utils': 1.4.2 - '@emotion/weak-memoize': 0.4.0 - stylis: 4.2.0 - - '@emotion/css@11.13.5': - dependencies: - '@emotion/babel-plugin': 11.13.5 - '@emotion/cache': 11.14.0 - '@emotion/serialize': 1.3.3 - '@emotion/sheet': 1.4.0 - '@emotion/utils': 1.4.2 - transitivePeerDependencies: - - supports-color - - '@emotion/hash@0.9.2': {} - - '@emotion/memoize@0.9.0': {} - - '@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4)': - dependencies: - '@babel/runtime': 7.28.6 - '@emotion/babel-plugin': 11.13.5 - '@emotion/cache': 11.14.0 - '@emotion/serialize': 1.3.3 - '@emotion/use-insertion-effect-with-fallbacks': 1.2.0(react@19.2.4) - '@emotion/utils': 1.4.2 - '@emotion/weak-memoize': 0.4.0 - hoist-non-react-statics: 3.3.2 - react: 19.2.4 - optionalDependencies: - '@types/react': 19.2.14 - transitivePeerDependencies: - - supports-color - - '@emotion/serialize@1.3.3': - dependencies: - '@emotion/hash': 0.9.2 - '@emotion/memoize': 0.9.0 - '@emotion/unitless': 0.10.0 - '@emotion/utils': 1.4.2 - csstype: 3.2.3 - - '@emotion/sheet@1.4.0': {} - - '@emotion/unitless@0.10.0': {} - - '@emotion/use-insertion-effect-with-fallbacks@1.2.0(react@19.2.4)': - dependencies: - react: 19.2.4 - - '@emotion/utils@1.4.2': {} - - '@emotion/weak-memoize@0.4.0': {} - '@epic-web/invariant@1.0.0': {} '@esbuild-kit/core-utils@3.3.2': @@ -7576,10 +7294,6 @@ snapshots: '@floating-ui/utils@0.2.10': {} - '@hono/node-server@1.19.12(hono@4.12.11)': - dependencies: - hono: 4.12.11 - '@iconify/types@2.0.0': {} '@iconify/utils@3.1.0': @@ -8021,28 +7735,6 @@ snapshots: dependencies: langium: 4.2.1 - '@modelcontextprotocol/sdk@1.29.0(zod@3.25.76)': - dependencies: - '@hono/node-server': 1.19.12(hono@4.12.11) - ajv: 8.18.0 - ajv-formats: 3.0.1(ajv@8.18.0) - content-type: 1.0.5 - cors: 2.8.6 - cross-spawn: 7.0.6 - eventsource: 3.0.7 - eventsource-parser: 3.0.6 - express: 5.2.1 - express-rate-limit: 8.3.2(express@5.2.1) - hono: 4.12.11 - jose: 6.1.3 - json-schema-typed: 8.0.2 - pkce-challenge: 5.0.1 - raw-body: 3.0.2 - zod: 3.25.76 - zod-to-json-schema: 3.25.2(zod@3.25.76) - transitivePeerDependencies: - - supports-color - '@noble/ciphers@2.1.1': {} '@noble/hashes@1.8.0': {} @@ -9577,8 +9269,6 @@ snapshots: dependencies: undici-types: 7.16.0 - '@types/parse-json@4.0.2': {} - '@types/qs@6.14.0': {} '@types/range-parser@1.2.7': {} @@ -9741,12 +9431,6 @@ snapshots: atomic-sleep@1.0.0: {} - babel-plugin-macros@3.1.0: - dependencies: - '@babel/runtime': 7.28.6 - cosmiconfig: 7.1.0 - resolve: 1.22.11 - bail@2.0.2: {} base64-js@1.5.1: {} @@ -9841,8 +9525,6 @@ snapshots: call-bind-apply-helpers: 1.0.2 get-intrinsic: 1.3.0 - callsites@3.1.0: {} - caniuse-lite@1.0.30001770: {} ccount@2.0.1: {} @@ -9953,8 +9635,6 @@ snapshots: content-type@1.0.5: {} - convert-source-map@1.9.0: {} - convert-source-map@2.0.0: {} cookie-signature@1.2.2: {} @@ -9965,11 +9645,6 @@ snapshots: cookiejar@2.1.4: {} - cors@2.8.6: - dependencies: - object-assign: 4.1.1 - vary: 1.1.2 - cose-base@1.0.3: dependencies: layout-base: 1.0.2 @@ -9978,14 +9653,6 @@ snapshots: dependencies: layout-base: 2.0.1 - cosmiconfig@7.1.0: - dependencies: - '@types/parse-json': 4.0.2 - import-fresh: 3.3.1 - parse-json: 5.2.0 - path-type: 4.0.0 - yaml: 1.10.3 - crelt@1.0.6: {} cross-env@10.1.0: @@ -10269,8 +9936,6 @@ snapshots: diff@5.2.2: {} - diff@8.0.4: {} - dompurify@3.3.2: optionalDependencies: '@types/trusted-types': 2.0.7 @@ -10345,10 +10010,6 @@ snapshots: entities@6.0.1: {} - error-ex@1.3.4: - dependencies: - is-arrayish: 0.2.1 - es-define-property@1.0.1: {} es-errors@1.3.0: {} @@ -10480,8 +10141,6 @@ snapshots: escape-html@1.0.3: {} - escape-string-regexp@4.0.0: {} - escape-string-regexp@5.0.0: {} esniff@2.0.1: @@ -10511,19 +10170,8 @@ snapshots: d: 1.0.2 es5-ext: 0.10.64 - eventsource-parser@3.0.6: {} - - eventsource@3.0.7: - dependencies: - eventsource-parser: 3.0.6 - expect-type@1.3.0: {} - express-rate-limit@8.3.2(express@5.2.1): - dependencies: - express: 5.2.1 - ip-address: 10.1.0 - express@5.2.1: dependencies: accepts: 2.0.0 @@ -10594,8 +10242,6 @@ snapshots: transitivePeerDependencies: - supports-color - find-root@1.1.0: {} - form-data@4.0.5: dependencies: asynckit: 0.4.0 @@ -10699,12 +10345,6 @@ snapshots: '@paperclipai/adapter-utils': 2026.325.0 picocolors: 1.1.1 - hoist-non-react-statics@3.3.2: - dependencies: - react-is: 16.13.1 - - hono@4.12.11: {} - html-encoding-sniffer@6.0.0(@noble/hashes@2.0.1): dependencies: '@exodus/bytes': 1.15.0(@noble/hashes@2.0.1) @@ -10745,11 +10385,6 @@ snapshots: ieee754@1.2.1: {} - import-fresh@3.3.1: - dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 - inherits@2.0.4: {} inline-style-parser@0.2.7: {} @@ -10760,8 +10395,6 @@ snapshots: intersection-observer@0.10.0: {} - ip-address@10.1.0: {} - ipaddr.js@1.9.1: {} is-alphabetical@2.0.1: {} @@ -10771,8 +10404,6 @@ snapshots: is-alphabetical: 2.0.1 is-decimal: 2.0.1 - is-arrayish@0.2.1: {} - is-core-module@2.16.1: dependencies: hasown: 2.0.2 @@ -10848,12 +10479,8 @@ snapshots: jsesc@3.1.0: {} - json-parse-even-better-errors@2.3.1: {} - json-schema-traverse@1.0.0: {} - json-schema-typed@8.0.2: {} - json5@2.2.3: {} katex@0.16.37: @@ -10933,8 +10560,6 @@ snapshots: lightningcss-win32-arm64-msvc: 1.30.2 lightningcss-win32-x64-msvc: 1.30.2 - lines-and-columns@1.2.4: {} - lodash-es@4.17.23: {} longest-streak@3.1.0: {} @@ -11165,8 +10790,6 @@ snapshots: media-typer@1.1.0: {} - memoize-one@6.0.0: {} - merge-descriptors@2.0.0: {} mermaid@11.12.3: @@ -11562,10 +11185,6 @@ snapshots: package-manager-detector@1.6.0: {} - parent-module@1.0.1: - dependencies: - callsites: 3.1.0 - parse-entities@4.0.2: dependencies: '@types/unist': 2.0.11 @@ -11576,13 +11195,6 @@ snapshots: is-decimal: 2.0.1 is-hexadecimal: 2.0.1 - parse-json@5.2.0: - dependencies: - '@babel/code-frame': 7.29.0 - error-ex: 1.3.4 - json-parse-even-better-errors: 2.3.1 - lines-and-columns: 1.2.4 - parse5@7.3.0: dependencies: entities: 6.0.1 @@ -11601,8 +11213,6 @@ snapshots: path-to-regexp@8.3.0: {} - path-type@4.0.0: {} - pathe@2.0.3: {} pathval@2.0.1: {} @@ -11693,8 +11303,6 @@ snapshots: sonic-boom: 4.2.1 thread-stream: 3.1.0 - pkce-challenge@5.0.1: {} - pkg-types@1.3.1: dependencies: confbox: 0.1.8 @@ -11847,20 +11455,6 @@ snapshots: dependencies: es6-symbol: 3.1.4 - react-diff-viewer-continued@4.2.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): - dependencies: - '@emotion/css': 11.13.5 - '@emotion/react': 11.14.0(@types/react@19.2.14)(react@19.2.4) - classnames: 2.5.1 - diff: 8.0.4 - js-yaml: 4.1.1 - memoize-one: 6.0.0 - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) - transitivePeerDependencies: - - '@types/react' - - supports-color - react-dom@19.2.4(react@19.2.4): dependencies: react: 19.2.4 @@ -11988,8 +11582,6 @@ snapshots: require-from-string@2.0.2: {} - resolve-from@4.0.0: {} - resolve-pkg-maps@1.0.0: {} resolve@1.22.11: @@ -12185,8 +11777,6 @@ snapshots: buffer-from: 1.1.2 source-map: 0.6.1 - source-map@0.5.7: {} - source-map@0.6.1: {} space-separated-tokens@2.0.2: {} @@ -12237,8 +11827,6 @@ snapshots: dependencies: inline-style-parser: 0.2.7 - stylis@4.2.0: {} - stylis@4.3.6: {} superagent@10.3.0: @@ -12590,7 +12178,7 @@ snapshots: - tsx - yaml - vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@2.6.1)(jsdom@28.1.0)(lightningcss@1.30.2)(tsx@4.21.0): + vitest@3.2.4(@types/debug@4.1.12)(@types/node@25.2.3)(jiti@2.6.1)(jsdom@28.1.0(@noble/hashes@2.0.1))(lightningcss@1.30.2)(tsx@4.21.0): dependencies: '@types/chai': 5.2.3 '@vitest/expect': 3.2.4 @@ -12694,16 +12282,10 @@ snapshots: yallist@3.1.1: {} - yaml@1.10.3: {} - yjs@13.6.29: dependencies: lib0: 0.2.117 - zod-to-json-schema@3.25.2(zod@3.25.76): - dependencies: - zod: 3.25.76 - zod@3.25.76: {} zod@4.3.6: {} From bb980bfb335aea2b6b470a4a89d5844782140d94 Mon Sep 17 00:00:00 2001 From: dotta Date: Mon, 6 Apr 2026 22:01:49 -0500 Subject: [PATCH 08/10] fix(ci): fetch base sha in pr jobs --- .github/workflows/pr.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index c565ddef..48200a49 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -19,6 +19,8 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 + with: + fetch-depth: 0 - name: Block manual lockfile edits if: github.head_ref != 'chore/refresh-lockfile' @@ -96,6 +98,8 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Setup pnpm uses: pnpm/action-setup@v4 From 448e9f2be3a603785aefaf7341d3d0bbcea32d24 Mon Sep 17 00:00:00 2001 From: dotta Date: Tue, 7 Apr 2026 06:32:52 -0500 Subject: [PATCH 09/10] revert(ci): drop pr workflow changes from mcp pr --- .github/workflows/pr.yml | 204 --------------------------------------- 1 file changed, 204 deletions(-) delete mode 100644 .github/workflows/pr.yml diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml deleted file mode 100644 index 48200a49..00000000 --- a/.github/workflows/pr.yml +++ /dev/null @@ -1,204 +0,0 @@ -name: PR - -on: - pull_request: - branches: - - master - -concurrency: - group: pr-${{ github.event.pull_request.number }} - cancel-in-progress: true - -jobs: - policy: - runs-on: ubuntu-latest - timeout-minutes: 5 - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - with: - fetch-depth: 0 - - - name: Block manual lockfile edits - if: github.head_ref != 'chore/refresh-lockfile' - run: | - changed="$(git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}")" - if printf '%s\n' "$changed" | grep -qx 'pnpm-lock.yaml'; then - echo "Do not commit pnpm-lock.yaml in pull requests. CI owns lockfile updates." - exit 1 - fi - - - name: Setup pnpm - uses: pnpm/action-setup@v4 - with: - version: 9.15.4 - run_install: false - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 24 - - - name: Validate Dockerfile deps stage - run: | - missing=0 - - # Extract only the deps stage from the Dockerfile - deps_stage="$(awk '/^FROM .* AS deps$/{found=1; next} found && /^FROM /{exit} found{print}' Dockerfile)" - - if [ -z "$deps_stage" ]; then - echo "::error::Could not extract deps stage from Dockerfile (expected 'FROM ... AS deps')" - exit 1 - fi - - # Derive workspace search roots from pnpm-workspace.yaml (exclude dev-only packages) - search_roots="$(grep '^ *- ' pnpm-workspace.yaml | sed 's/^ *- //' | sed 's/\*$//' | grep -v 'examples' | grep -v 'create-paperclip-plugin' | tr '\n' ' ')" - - if [ -z "$search_roots" ]; then - echo "::error::Could not derive workspace roots from pnpm-workspace.yaml" - exit 1 - fi - - # Check all workspace package.json files are copied in the deps stage - for pkg in $(find $search_roots -maxdepth 2 -name package.json -not -path '*/examples/*' -not -path '*/create-paperclip-plugin/*' -not -path '*/node_modules/*' 2>/dev/null | sort -u); do - dir="$(dirname "$pkg")" - if ! echo "$deps_stage" | grep -q "^COPY ${dir}/package.json"; then - echo "::error::Dockerfile deps stage missing: COPY ${pkg} ${dir}/" - missing=1 - fi - done - - # Check patches directory is copied if it exists - if [ -d patches ] && ! echo "$deps_stage" | grep -q '^COPY patches/'; then - echo "::error::Dockerfile deps stage missing: COPY patches/ patches/" - missing=1 - fi - - if [ "$missing" -eq 1 ]; then - echo "Dockerfile deps stage is out of sync. Update it to include the missing files." - exit 1 - fi - - - name: Validate dependency resolution when manifests change - run: | - changed="$(git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}")" - manifest_pattern='(^|/)package\.json$|^pnpm-workspace\.yaml$|^\.npmrc$|^pnpmfile\.(cjs|js|mjs)$' - if printf '%s\n' "$changed" | grep -Eq "$manifest_pattern"; then - pnpm install --lockfile-only --ignore-scripts --no-frozen-lockfile - fi - - verify: - needs: [policy] - runs-on: ubuntu-latest - timeout-minutes: 20 - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Setup pnpm - uses: pnpm/action-setup@v4 - with: - version: 9.15.4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 24 - cache: pnpm - - - name: Install dependencies - run: | - changed="$(git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}")" - manifest_pattern='(^|/)package\.json$|^pnpm-workspace\.yaml$|^\.npmrc$|^pnpmfile\.(cjs|js|mjs)$' - if printf '%s\n' "$changed" | grep -Eq "$manifest_pattern"; then - pnpm install --no-frozen-lockfile - else - pnpm install --frozen-lockfile - fi - - - name: Typecheck - run: pnpm -r typecheck - - - name: Run tests - run: pnpm test:run - - - name: Build - run: pnpm build - - - name: Release canary dry run - run: | - git checkout -B master HEAD - git checkout -- pnpm-lock.yaml - ./scripts/release.sh canary --skip-verify --dry-run - - e2e: - needs: [policy] - runs-on: ubuntu-latest - timeout-minutes: 30 - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Setup pnpm - uses: pnpm/action-setup@v4 - with: - version: 9.15.4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 24 - cache: pnpm - - - name: Install dependencies - run: | - changed="$(git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}")" - manifest_pattern='(^|/)package\.json$|^pnpm-workspace\.yaml$|^\.npmrc$|^pnpmfile\.(cjs|js|mjs)$' - if printf '%s\n' "$changed" | grep -Eq "$manifest_pattern"; then - pnpm install --no-frozen-lockfile - else - pnpm install --frozen-lockfile - fi - - - name: Build - run: pnpm build - - - name: Install Playwright - run: npx playwright install --with-deps chromium - - - name: Generate Paperclip config - run: | - mkdir -p ~/.paperclip/instances/default - cat > ~/.paperclip/instances/default/config.json << 'CONF' - { - "$meta": { "version": 1, "updatedAt": "2026-01-01T00:00:00.000Z", "source": "onboard" }, - "database": { "mode": "embedded-postgres" }, - "logging": { "mode": "file" }, - "server": { "deploymentMode": "local_trusted", "host": "127.0.0.1", "port": 3100 }, - "auth": { "baseUrlMode": "auto" }, - "storage": { "provider": "local_disk" }, - "secrets": { "provider": "local_encrypted", "strictMode": false } - } - CONF - - - name: Run e2e tests - env: - PAPERCLIP_E2E_SKIP_LLM: "true" - run: pnpm run test:e2e - - - name: Upload Playwright report - uses: actions/upload-artifact@v4 - if: always() - with: - name: playwright-report - path: | - tests/e2e/playwright-report/ - tests/e2e/test-results/ - retention-days: 14 From 74481b1d1e68145215f4d9f2bf48d9d077848ffd Mon Sep 17 00:00:00 2001 From: dotta Date: Tue, 7 Apr 2026 07:33:32 -0500 Subject: [PATCH 10/10] fix(ci): restore pr workflow from master --- .github/workflows/pr.yml | 186 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 .github/workflows/pr.yml diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 00000000..a45f392e --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,186 @@ +name: PR + +on: + pull_request: + branches: + - master + +concurrency: + group: pr-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + policy: + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Block manual lockfile edits + if: github.head_ref != 'chore/refresh-lockfile' + run: | + changed="$(git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}")" + if printf '%s\n' "$changed" | grep -qx 'pnpm-lock.yaml'; then + echo "Do not commit pnpm-lock.yaml in pull requests. CI owns lockfile updates." + exit 1 + fi + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 9.15.4 + run_install: false + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 24 + + - name: Validate Dockerfile deps stage + run: | + missing=0 + + # Extract only the deps stage from the Dockerfile + deps_stage="$(awk '/^FROM .* AS deps$/{found=1; next} found && /^FROM /{exit} found{print}' Dockerfile)" + + if [ -z "$deps_stage" ]; then + echo "::error::Could not extract deps stage from Dockerfile (expected 'FROM ... AS deps')" + exit 1 + fi + + # Derive workspace search roots from pnpm-workspace.yaml (exclude dev-only packages) + search_roots="$(grep '^ *- ' pnpm-workspace.yaml | sed 's/^ *- //' | sed 's/\*$//' | grep -v 'examples' | grep -v 'create-paperclip-plugin' | tr '\n' ' ')" + + if [ -z "$search_roots" ]; then + echo "::error::Could not derive workspace roots from pnpm-workspace.yaml" + exit 1 + fi + + # Check all workspace package.json files are copied in the deps stage + for pkg in $(find $search_roots -maxdepth 2 -name package.json -not -path '*/examples/*' -not -path '*/create-paperclip-plugin/*' -not -path '*/node_modules/*' 2>/dev/null | sort -u); do + dir="$(dirname "$pkg")" + if ! echo "$deps_stage" | grep -q "^COPY ${dir}/package.json"; then + echo "::error::Dockerfile deps stage missing: COPY ${pkg} ${dir}/" + missing=1 + fi + done + + # Check patches directory is copied if it exists + if [ -d patches ] && ! echo "$deps_stage" | grep -q '^COPY patches/'; then + echo "::error::Dockerfile deps stage missing: COPY patches/ patches/" + missing=1 + fi + + if [ "$missing" -eq 1 ]; then + echo "Dockerfile deps stage is out of sync. Update it to include the missing files." + exit 1 + fi + + - name: Validate dependency resolution when manifests change + run: | + changed="$(git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.event.pull_request.head.sha }}")" + manifest_pattern='(^|/)package\.json$|^pnpm-workspace\.yaml$|^\.npmrc$|^pnpmfile\.(cjs|js|mjs)$' + if printf '%s\n' "$changed" | grep -Eq "$manifest_pattern"; then + pnpm install --lockfile-only --ignore-scripts --no-frozen-lockfile + fi + + verify: + needs: [policy] + runs-on: ubuntu-latest + timeout-minutes: 20 + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 9.15.4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 24 + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Typecheck + run: pnpm -r typecheck + + - name: Run tests + run: pnpm test:run + + - name: Build + run: pnpm build + + - name: Release canary dry run + run: | + git checkout -B master HEAD + git checkout -- pnpm-lock.yaml + ./scripts/release.sh canary --skip-verify --dry-run + + e2e: + needs: [policy] + runs-on: ubuntu-latest + timeout-minutes: 30 + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 9.15.4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 24 + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build + run: pnpm build + + - name: Install Playwright + run: npx playwright install --with-deps chromium + + - name: Generate Paperclip config + run: | + mkdir -p ~/.paperclip/instances/default + cat > ~/.paperclip/instances/default/config.json << 'CONF' + { + "$meta": { "version": 1, "updatedAt": "2026-01-01T00:00:00.000Z", "source": "onboard" }, + "database": { "mode": "embedded-postgres" }, + "logging": { "mode": "file" }, + "server": { "deploymentMode": "local_trusted", "host": "127.0.0.1", "port": 3100 }, + "auth": { "baseUrlMode": "auto" }, + "storage": { "provider": "local_disk" }, + "secrets": { "provider": "local_encrypted", "strictMode": false } + } + CONF + + - name: Run e2e tests + env: + PAPERCLIP_E2E_SKIP_LLM: "true" + run: pnpm run test:e2e + + - name: Upload Playwright report + uses: actions/upload-artifact@v4 + if: always() + with: + name: playwright-report + path: | + tests/e2e/playwright-report/ + tests/e2e/test-results/ + retention-days: 14