From e26c64782869731903a657f58a1a06e4a5077940 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 20 Jan 2026 08:20:07 +0000 Subject: [PATCH] fix: defer pdf deps and profile flag detection --- src/cli/command-format.ts | 4 ++-- src/gateway/openresponses-http.ts | 34 ++++++++++++++++++++++++++----- src/types/napi-rs-canvas.d.ts | 7 +++++++ src/types/pdfjs-dist-legacy.d.ts | 34 +++++++++++++++++++++++++++++++ 4 files changed, 72 insertions(+), 7 deletions(-) create mode 100644 src/types/napi-rs-canvas.d.ts create mode 100644 src/types/pdfjs-dist-legacy.d.ts diff --git a/src/cli/command-format.ts b/src/cli/command-format.ts index 81e791b67..635ee8a75 100644 --- a/src/cli/command-format.ts +++ b/src/cli/command-format.ts @@ -1,8 +1,8 @@ import { normalizeProfileName } from "./profile-utils.js"; const CLI_PREFIX_RE = /^(?:pnpm|npm|bunx|npx)\s+clawdbot\b|^clawdbot\b/; -const PROFILE_FLAG_RE = /\b--profile\b/; -const DEV_FLAG_RE = /\b--dev\b/; +const PROFILE_FLAG_RE = /(^|\s)--profile(\s|$)/; +const DEV_FLAG_RE = /(^|\s)--dev(\s|$)/; export function formatCliCommand( command: string, diff --git a/src/gateway/openresponses-http.ts b/src/gateway/openresponses-http.ts index af2a44a28..c7dd4e9de 100644 --- a/src/gateway/openresponses-http.ts +++ b/src/gateway/openresponses-http.ts @@ -10,7 +10,6 @@ import { randomUUID } from "node:crypto"; import { lookup } from "node:dns/promises"; import type { IncomingMessage, ServerResponse } from "node:http"; -import { createCanvas } from "@napi-rs/canvas"; import { buildHistoryContextFromEntries, type HistoryEntry } from "../auto-reply/reply/history.js"; import { createDefaultDeps } from "../cli/deps.js"; import { agentCommand } from "../commands/agent.js"; @@ -32,7 +31,6 @@ import { import type { GatewayHttpResponsesConfig } from "../config/types.gateway.js"; import type { ClientToolDefinition } from "../agents/pi-embedded-runner/run/params.js"; import type { ImageContent } from "../commands/agent/types.js"; -import { getDocument } from "pdfjs-dist/legacy/build/pdf.mjs"; type OpenResponsesHttpOptions = { auth: ResolvedGatewayAuth; @@ -40,6 +38,25 @@ type OpenResponsesHttpOptions = { config?: GatewayHttpResponsesConfig; }; +type CanvasModule = typeof import("@napi-rs/canvas"); +type PdfJsModule = typeof import("pdfjs-dist/legacy/build/pdf.mjs"); + +async function loadCanvasModule(): Promise { + try { + return await import("@napi-rs/canvas"); + } catch { + return null; + } +} + +async function loadPdfJsModule(): Promise { + try { + return await import("pdfjs-dist/legacy/build/pdf.mjs"); + } catch { + return null; + } +} + function sendJson(res: ServerResponse, status: number, body: unknown) { res.statusCode = status; res.setHeader("Content-Type", "application/json; charset=utf-8"); @@ -305,9 +322,12 @@ async function extractPdfContent(params: { limits: ResolvedResponsesLimits; }): Promise<{ text: string; images: ImageContent[] }> { const { buffer, limits } = params; - const pdf = await getDocument({ + const pdfjs = await loadPdfJsModule(); + if (!pdfjs) { + throw new Error("PDF parsing requires pdfjs-dist; install it to enable PDF support."); + } + const pdf = await pdfjs.getDocument({ data: new Uint8Array(buffer), - // @ts-expect-error pdfjs-dist legacy option not in current type defs. disableWorker: true, }).promise; const maxPages = Math.min(pdf.numPages, limits.files.pdf.maxPages); @@ -329,6 +349,10 @@ async function extractPdfContent(params: { } const images: ImageContent[] = []; + const canvasModule = await loadCanvasModule(); + if (!canvasModule) { + throw new Error("PDF image extraction requires @napi-rs/canvas; install it to enable images."); + } for (let pageNum = 1; pageNum <= maxPages; pageNum += 1) { const page = await pdf.getPage(pageNum); const viewport = page.getViewport({ scale: 1 }); @@ -337,7 +361,7 @@ async function extractPdfContent(params: { const pagePixels = viewport.width * viewport.height; const scale = Math.min(1, Math.sqrt(pixelBudget / pagePixels)); const scaled = page.getViewport({ scale: Math.max(0.1, scale) }); - const canvas = createCanvas(Math.ceil(scaled.width), Math.ceil(scaled.height)); + const canvas = canvasModule.createCanvas(Math.ceil(scaled.width), Math.ceil(scaled.height)); await page.render({ canvas: canvas as unknown as HTMLCanvasElement, viewport: scaled, diff --git a/src/types/napi-rs-canvas.d.ts b/src/types/napi-rs-canvas.d.ts new file mode 100644 index 000000000..ab856f1a9 --- /dev/null +++ b/src/types/napi-rs-canvas.d.ts @@ -0,0 +1,7 @@ +declare module "@napi-rs/canvas" { + export type Canvas = { + toBuffer(type?: string): Buffer; + }; + + export function createCanvas(width: number, height: number): Canvas; +} diff --git a/src/types/pdfjs-dist-legacy.d.ts b/src/types/pdfjs-dist-legacy.d.ts new file mode 100644 index 000000000..b88ec386b --- /dev/null +++ b/src/types/pdfjs-dist-legacy.d.ts @@ -0,0 +1,34 @@ +declare module "pdfjs-dist/legacy/build/pdf.mjs" { + export type TextItem = { + str: string; + }; + + export type TextMarkedContent = { + type?: string; + }; + + export type TextContent = { + items: Array; + }; + + export type Viewport = { + width: number; + height: number; + }; + + export type PDFPageProxy = { + getTextContent(): Promise; + getViewport(params: { scale: number }): Viewport; + render(params: { canvas: unknown; viewport: Viewport }): { promise: Promise }; + }; + + export type PDFDocumentProxy = { + numPages: number; + getPage(pageNumber: number): Promise; + }; + + export function getDocument(params: { + data: Uint8Array; + disableWorker?: boolean; + }): { promise: Promise }; +}