@puntoycoma/bulki 3.0.2 → 3.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -22
- package/dist/bin/bulki.js +144 -226
- package/dist/bin/bulki.js.map +1 -1
- package/dist/chunk-YUAKNSBH.js +851 -0
- package/dist/chunk-YUAKNSBH.js.map +1 -0
- package/dist/interactive-LGLWIHMM.js +566 -0
- package/dist/interactive-LGLWIHMM.js.map +1 -0
- package/package.json +12 -11
- package/dist/chunk-7TFBGWBB.js +0 -532
- package/dist/chunk-7TFBGWBB.js.map +0 -1
- package/dist/interactive-HBGX4FD6.js +0 -295
- package/dist/interactive-HBGX4FD6.js.map +0 -1
package/dist/bin/bulki.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/bin/bulki.ts","../../src/commands/compress.ts","../../src/output/progress.ts","../../src/commands/mcp.ts","../../src/commands/pipe.ts"],"sourcesContent":["import { createRequire } from 'node:module'\nimport { Command } from 'commander'\nimport { compressCommand } from '../commands/compress.js'\nimport { hookCommand } from '../commands/hook.js'\nimport { mcpCommand } from '../commands/mcp.js'\nimport { pipeCommand } from '../commands/pipe.js'\nimport { watchCommand } from '../commands/watch.js'\n\nconst require = createRequire(import.meta.url)\nconst { version } = require('../../package.json') as { version: string }\n\nif (process.argv.length <= 2) {\n\tconst { runInteractive } = await import('../interactive/index.js')\n\tawait runInteractive()\n} else {\n\tconst program = new Command()\n\tprogram.name('bulki').description('Ultra-fast CLI image optimizer').version(version)\n\n\tprogram.addCommand(compressCommand)\n\tprogram.addCommand(hookCommand)\n\tprogram.addCommand(mcpCommand)\n\tprogram.addCommand(pipeCommand)\n\tprogram.addCommand(watchCommand)\n\tprogram.parse()\n}\n","import path from 'node:path'\nimport { Command } from 'commander'\nimport { processPool } from '../concurrency/pool.js'\nimport { createProgressBar } from '../output/progress.js'\nimport { buildJsonOutput, formatFileResult, formatSummary } from '../output/results.js'\nimport { collectImageFiles } from '../pipeline/discover.js'\nimport { optimizeImage } from '../pipeline/optimize.js'\nimport type { CompressOptions, FileResult } from '../types/index.js'\nimport { type RawOptions, validateOptions } from '../validation/options.js'\n\nexport const compressCommand = new Command('compress')\n\t.description('Compress images in a file or directory')\n\t.argument('<path>', 'File or directory to process')\n\t.option('-q, --quality <n>', 'Output quality (1-100)', (v) => parseInt(v, 10), 70)\n\t.option('-f, --format <fmt>', 'Output format (jpeg, png, webp, gif, avif)')\n\t.option('-r, --resize <width>', 'Resize to width (px)', (v) => parseInt(v, 10))\n\t.option('-d, --destination <path>', 'Destination directory', '.')\n\t.option('-o, --output-folder <name>', 'Output subfolder name', 'bulki-output')\n\t.option('--concurrency <n>', 'Number of parallel operations', (v) => parseInt(v, 10))\n\t.option('--exclude <patterns...>', 'Directory names to exclude')\n\t.option('--dry-run', 'Show what would be processed without writing files')\n\t.option('--verbose', 'Show per-file output')\n\t.option('--json', 'Output results as JSON')\n\t.option('--max-size <size>', 'Maximum file size budget (e.g., 200kb, 1mb)')\n\t.action(async (inputPath: string, opts: Partial<RawOptions>) => {\n\t\tlet options: CompressOptions\n\t\ttry {\n\t\t\toptions = validateOptions(opts)\n\t\t} catch (err) {\n\t\t\tconsole.error((err as Error).message)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tconst resolvedPath = path.resolve(inputPath)\n\t\tlet filePaths: { path: string; format: string }[]\n\t\tlet isDirectory: boolean\n\t\ttry {\n\t\t\tconst collected = await collectImageFiles(resolvedPath, options.exclude)\n\t\t\tfilePaths = collected.files\n\t\t\tisDirectory = collected.isDirectory\n\t\t} catch {\n\t\t\tconsole.error(`Path not found: ${resolvedPath}`)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tif (filePaths.length === 0) {\n\t\t\tconsole.log('No images found to process.')\n\t\t\treturn\n\t\t}\n\n\t\tif (options.dryRun) {\n\t\t\tconsole.log(`Would process ${filePaths.length} image(s)`)\n\t\t\tfor (const f of filePaths) console.log(` ${path.basename(f.path)}`)\n\t\t\treturn\n\t\t}\n\n\t\tconst sourcePath = isDirectory ? resolvedPath : path.dirname(resolvedPath)\n\t\tconst startTime = performance.now()\n\n\t\tconst progress = !options.verbose && !options.json ? createProgressBar(filePaths.length) : null\n\n\t\tconst errors: Error[] = []\n\t\tconst jsonErrors: { file: string; message: string }[] = []\n\n\t\tconst processedResults = await processPool(\n\t\t\tfilePaths,\n\t\t\tasync (file) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst result = await optimizeImage(file.path, {\n\t\t\t\t\t\t...options,\n\t\t\t\t\t\tsourcePath,\n\t\t\t\t\t\tdetectedFormat: file.format,\n\t\t\t\t\t})\n\t\t\t\t\tif (options.verbose && !options.json) {\n\t\t\t\t\t\tconsole.log(formatFileResult(result))\n\t\t\t\t\t}\n\t\t\t\t\tprogress?.tick()\n\t\t\t\t\treturn result\n\t\t\t\t} catch (err) {\n\t\t\t\t\terrors.push(err as Error)\n\t\t\t\t\tjsonErrors.push({ file: file.path, message: (err as Error).message })\n\t\t\t\t\tprogress?.tick()\n\t\t\t\t\treturn null\n\t\t\t\t}\n\t\t\t},\n\t\t\toptions.concurrency,\n\t\t)\n\n\t\tconst validResults = processedResults.filter((r): r is FileResult => r !== null)\n\t\tconst totalDuration = performance.now() - startTime\n\n\t\tif (options.json) {\n\t\t\tconst output = buildJsonOutput(validResults, jsonErrors, totalDuration)\n\t\t\tif (options.maxSize) {\n\t\t\t\tfor (const file of output.files) {\n\t\t\t\t\tif (file.newSize > options.maxSize) {\n\t\t\t\t\t\tfile.exceeded = true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tconsole.log(JSON.stringify(output))\n\t\t} else {\n\t\t\tconsole.log(formatSummary(validResults, errors, totalDuration))\n\t\t}\n\n\t\tif (validResults.length === 0 && errors.length > 0) {\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tconst maxSize = options.maxSize\n\t\tif (maxSize) {\n\t\t\tconst exceeded = validResults.filter((r) => r.newSize > maxSize)\n\t\t\tif (exceeded.length > 0) {\n\t\t\t\tif (!options.json) {\n\t\t\t\t\tfor (const r of exceeded) {\n\t\t\t\t\t\tconsole.warn(`File exceeds size budget: ${r.outputPath}`)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tprocess.exit(1)\n\t\t\t}\n\t\t}\n\t})\n","export function createProgressBar(total: number) {\n\tlet current = 0\n\n\treturn {\n\t\ttick() {\n\t\t\tcurrent++\n\t\t\tconst percent = Math.round((current / total) * 100)\n\t\t\tconst barWidth = Math.max(0, Math.min(process.stdout.columns || 40, 40) - 20)\n\t\t\tconst filled = Math.round((current / total) * barWidth)\n\t\t\tconst empty = barWidth - filled\n\t\t\tconst bar = `[${'='.repeat(filled)}${' '.repeat(empty)}] ${percent}% ${current}/${total}`\n\t\t\tprocess.stdout.write(`\\r${bar}`)\n\t\t\tif (current === total) process.stdout.write('\\n')\n\t\t},\n\t}\n}\n","import { createRequire } from 'node:module'\nimport { availableParallelism } from 'node:os'\nimport path from 'node:path'\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { Command } from 'commander'\nimport { z } from 'zod'\nimport { processPool } from '../concurrency/pool.js'\nimport { buildJsonOutput } from '../output/results.js'\nimport { collectImageFiles } from '../pipeline/discover.js'\nimport { detectFormat } from '../pipeline/filter.js'\nimport { optimizeImage } from '../pipeline/optimize.js'\nimport type { FileResult } from '../types/index.js'\nimport { parseSize } from '../validation/options.js'\n\nconst require = createRequire(import.meta.url)\nconst { version } = require('../../package.json') as { version: string }\n\nexport const mcpCommand = new Command('mcp')\n\t.description('Start MCP server for AI editor integration (Claude Code, Cursor, etc.)')\n\t.action(async () => {\n\t\tconst server = new McpServer({\n\t\t\tname: 'bulki',\n\t\t\tversion,\n\t\t})\n\n\t\tserver.tool(\n\t\t\t'compress',\n\t\t\t'Compress and optimize images. Supports JPEG, PNG, WebP, GIF, AVIF.',\n\t\t\t{\n\t\t\t\tpath: z.string().describe('File or directory path to compress'),\n\t\t\t\tquality: z.number().min(1).max(100).default(70).describe('Output quality (1-100)'),\n\t\t\t\tformat: z\n\t\t\t\t\t.enum(['jpeg', 'png', 'webp', 'gif', 'avif'])\n\t\t\t\t\t.optional()\n\t\t\t\t\t.describe('Output format (omit to keep original)'),\n\t\t\t\tresize: z.number().positive().optional().describe('Max width in pixels'),\n\t\t\t\tdestination: z.string().default('.').describe('Output directory'),\n\t\t\t\toutputFolder: z.string().default('bulki-output').describe('Output folder name'),\n\t\t\t\tmaxSize: z.string().optional().describe('Max file size budget (e.g., 200kb, 1mb)'),\n\t\t\t},\n\t\t\tasync (params) => {\n\t\t\t\tif (\n\t\t\t\t\tparams.outputFolder.includes('/') ||\n\t\t\t\t\tparams.outputFolder.includes('\\\\') ||\n\t\t\t\t\tparams.outputFolder.includes('..')\n\t\t\t\t) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\t\t\t\terror: 'Invalid outputFolder: must be a simple directory name',\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst resolvedPath = path.resolve(params.path)\n\n\t\t\t\tlet filePaths: { path: string; format: string }[]\n\t\t\t\tlet isDirectory: boolean\n\t\t\t\ttry {\n\t\t\t\t\tconst collected = await collectImageFiles(resolvedPath, [])\n\t\t\t\t\tfilePaths = collected.files\n\t\t\t\t\tisDirectory = collected.isDirectory\n\t\t\t\t} catch {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\t\t\ttext: JSON.stringify({ error: `Path not found: ${resolvedPath}` }),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (filePaths.length === 0) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\t\t\ttext: JSON.stringify({ files: [], summary: { totalFiles: 0 } }),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst sourcePath = isDirectory ? resolvedPath : path.dirname(resolvedPath)\n\t\t\t\tconst startTime = performance.now()\n\t\t\t\tconst errors: { file: string; message: string }[] = []\n\n\t\t\t\tlet maxSizeBytes: number | undefined\n\t\t\t\tif (params.maxSize) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tmaxSizeBytes = parseSize(params.maxSize)\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\t\ttype: 'text' as const,\n\t\t\t\t\t\t\t\t\ttext: JSON.stringify({ error: (err as Error).message }),\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t],\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst processedResults = await processPool(\n\t\t\t\t\tfilePaths,\n\t\t\t\t\tasync (file) => {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst result = await optimizeImage(file.path, {\n\t\t\t\t\t\t\t\tquality: params.quality,\n\t\t\t\t\t\t\t\tformat: params.format,\n\t\t\t\t\t\t\t\tresize: params.resize,\n\t\t\t\t\t\t\t\tdestination: params.destination,\n\t\t\t\t\t\t\t\toutputFolder: params.outputFolder,\n\t\t\t\t\t\t\t\tconcurrency: 1,\n\t\t\t\t\t\t\t\texclude: [],\n\t\t\t\t\t\t\t\tdryRun: false,\n\t\t\t\t\t\t\t\tverbose: false,\n\t\t\t\t\t\t\t\tjson: false,\n\t\t\t\t\t\t\t\tsourcePath,\n\t\t\t\t\t\t\t\tdetectedFormat: file.format,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\treturn result\n\t\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\t\terrors.push({ file: file.path, message: (err as Error).message })\n\t\t\t\t\t\t\treturn null\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\tMath.min(availableParallelism(), 8),\n\t\t\t\t)\n\n\t\t\t\tconst results = processedResults.filter((r): r is FileResult => r !== null)\n\n\t\t\t\tconst totalDuration = performance.now() - startTime\n\t\t\t\tconst output = buildJsonOutput(results, errors, totalDuration)\n\t\t\t\tif (maxSizeBytes !== undefined) {\n\t\t\t\t\tfor (const file of output.files) {\n\t\t\t\t\t\tif (file.newSize > maxSizeBytes) {\n\t\t\t\t\t\t\tfile.exceeded = true\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn { content: [{ type: 'text' as const, text: JSON.stringify(output, null, 2) }] }\n\t\t\t},\n\t\t)\n\n\t\tserver.tool(\n\t\t\t'detect',\n\t\t\t'Detect if a file is an image and identify its format',\n\t\t\t{\n\t\t\t\tpath: z.string().describe('Path to the file'),\n\t\t\t},\n\t\t\tasync (params) => {\n\t\t\t\tconst resolvedPath = path.resolve(params.path)\n\t\t\t\tconst format = await detectFormat(resolvedPath)\n\t\t\t\tconst result = format ? { format, isImage: true } : { format: null, isImage: false }\n\t\t\t\treturn { content: [{ type: 'text' as const, text: JSON.stringify(result) }] }\n\t\t\t},\n\t\t)\n\n\t\tconst transport = new StdioServerTransport()\n\t\tawait server.connect(transport)\n\t})\n","import { Command } from 'commander'\nimport type { FormatEnum } from 'sharp'\nimport sharp from 'sharp'\nimport { getFormatOptions } from '../pipeline/optimize.js'\nimport { VALID_FORMATS } from '../validation/options.js'\n\nexport const pipeCommand = new Command('pipe')\n\t.description('Read image from stdin, write optimized image to stdout')\n\t.requiredOption('-f, --format <fmt>', 'Output format (jpeg, png, webp, gif, avif)')\n\t.option('-q, --quality <n>', 'Quality (1-100)', (v) => parseInt(v, 10), 70)\n\t.option('-r, --resize <width>', 'Resize to width (px)', (v) => parseInt(v, 10))\n\t.action(async (opts) => {\n\t\tconst MAX_INPUT_SIZE = 100 * 1024 * 1024 // 100 MB\n\t\tconst chunks: Buffer[] = []\n\t\tlet totalSize = 0\n\t\tfor await (const chunk of process.stdin) {\n\t\t\ttotalSize += chunk.length\n\t\t\tif (totalSize > MAX_INPUT_SIZE) {\n\t\t\t\tprocess.stderr.write('Error: input exceeds 100 MB limit\\n')\n\t\t\t\tprocess.exit(1)\n\t\t\t}\n\t\t\tchunks.push(chunk)\n\t\t}\n\t\tconst input = Buffer.concat(chunks)\n\n\t\tif (input.length === 0) {\n\t\t\tprocess.stderr.write('Error: no input received on stdin\\n')\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tconst format = opts.format\n\t\tconst quality = opts.quality\n\t\tconst resize = opts.resize\n\n\t\tif (!VALID_FORMATS.includes(format)) {\n\t\t\tprocess.stderr.write(`Error: invalid format \"${format}\"\\n`)\n\t\t\tprocess.exit(1)\n\t\t}\n\n\t\tconst outputFormat = format === 'jpg' ? 'jpeg' : format\n\n\t\tconst formatOptions = getFormatOptions(quality)\n\n\t\ttry {\n\t\t\tlet pipeline = sharp(input)\n\n\t\t\tif (resize) {\n\t\t\t\tpipeline = pipeline.resize(resize)\n\t\t\t}\n\n\t\t\tpipeline = pipeline.toFormat(\n\t\t\t\toutputFormat as keyof FormatEnum,\n\t\t\t\tformatOptions[outputFormat] ?? {},\n\t\t\t)\n\n\t\t\tconst outputBuffer = await pipeline.toBuffer()\n\t\t\tprocess.stdout.write(outputBuffer)\n\t\t} catch (err) {\n\t\t\tprocess.stderr.write(`Error: ${(err as Error).message}\\n`)\n\t\t\tprocess.exit(1)\n\t\t}\n\t})\n"],"mappings":"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","names":["createRequire","Command","path","Command","require","Command","path","Command","Command","require","createRequire","version","Command"]}
|
|
1
|
+
{"version":3,"sources":["../../src/bin/bulki.ts","../../src/commands/compress.ts","../../src/output/progress.ts","../../src/commands/mcp.ts","../../src/commands/pipe.ts"],"sourcesContent":["import { createRequire } from 'node:module'\nimport { Command } from 'commander'\nimport { compressCommand } from '../commands/compress.js'\nimport { hookCommand } from '../commands/hook.js'\nimport { mcpCommand } from '../commands/mcp.js'\nimport { pipeCommand } from '../commands/pipe.js'\nimport { watchCommand } from '../commands/watch.js'\nimport { parsePathInput } from '../pipeline/paths.js'\n\nconst require = createRequire(import.meta.url)\nconst { version } = require('../../package.json') as { version: string }\n\nconst args = process.argv.slice(2)\nconst COMMANDS = new Set(['compress', 'hook', 'mcp', 'pipe', 'watch', 'help'])\n\n// `bulki` alone, or `bulki <folder/images>` (e.g. dragged onto the terminal) → guided mode\nconst droppedPaths =\n\targs.length > 0 && !args[0].startsWith('-') && !COMMANDS.has(args[0])\n\t\t? parsePathInput(args.join(' '))\n\t\t: null\n\nif (args.length === 0 || droppedPaths?.ok) {\n\tconst { runInteractive } = await import('../interactive/index.js')\n\tawait runInteractive(droppedPaths?.ok ? droppedPaths.paths : undefined)\n} else {\n\tconst program = new Command()\n\tprogram\n\t\t.name('bulki')\n\t\t.description('Fast image optimizer. Run `bulki` with no arguments for the guided mode.')\n\t\t.version(version)\n\t\t.showSuggestionAfterError()\n\n\tprogram.addCommand(compressCommand)\n\tprogram.addCommand(hookCommand)\n\tprogram.addCommand(mcpCommand)\n\tprogram.addCommand(pipeCommand)\n\tprogram.addCommand(watchCommand)\n\tawait program.parseAsync()\n}\n","import { Command } from 'commander'\nimport { createProgressBar } from '../output/progress.js'\nimport { buildJsonOutput, formatFileResult, formatSummary } from '../output/results.js'\nimport { runBatch } from '../pipeline/optimize.js'\nimport { displayPath, resolveUserPath } from '../pipeline/paths.js'\nimport { PlanError, planJobs } from '../pipeline/plan.js'\nimport type { CompressOptions } from '../types/index.js'\nimport { DEFAULTS, parseInteger, type RawOptions, validateOptions } from '../validation/options.js'\n\nexport const compressCommand = new Command('compress')\n\t.description(\n\t\t'Compress images. Originals are never modified; optimized copies go to the output folder.',\n\t)\n\t.argument('<paths...>', 'Images and/or folders to optimize (quote paths with spaces)')\n\t.option('-o, --output <dir>', 'Folder where optimized images are written', DEFAULTS.output)\n\t.option('-q, --quality <n>', 'Output quality (1-100)', parseInteger, DEFAULTS.quality)\n\t.option('-f, --format <fmt>', 'Convert to jpeg, png, webp, gif or avif (default: keep format)')\n\t.option(\n\t\t'-r, --resize <width>',\n\t\t'Max width in px (smaller images are never enlarged)',\n\t\tparseInteger,\n\t)\n\t.option('--concurrency <n>', 'Images processed in parallel (1-32)', parseInteger)\n\t.option('--exclude <names...>', 'Folder names to skip')\n\t.option('--dry-run', 'Show what would be processed without writing files')\n\t.option('--verbose', 'Show one line per file')\n\t.option('--json', 'Output results as JSON')\n\t.option('--max-size <size>', 'Fail if any output is bigger than this (e.g. 200kb, 1mb)')\n\t// Legacy flags — kept so existing scripts and the GitHub Action keep working\n\t.option('-d, --destination <dir>', 'Deprecated: base folder; output goes to <dir>/bulki-output')\n\t.option('--output-folder <name>', 'Deprecated: subfolder name inside --destination')\n\t.addHelpText(\n\t\t'after',\n\t\t`\nExamples:\n bulki compress ./photos → ./bulki-output\n bulki compress ./photos -o ./optimized -f webp\n bulki compress \"My Photos\" -o \"Web Ready\" -r 1920\n bulki compress *.png -f avif`,\n\t)\n\t.action(async (inputPaths: string[], opts: Partial<RawOptions>, cmd: Command) => {\n\t\t// `-o` used to be the subfolder name; keep `-d x -o name` meaning `x/name`\n\t\tconst outputFromCli = cmd.getOptionValueSource('output') === 'cli'\n\t\tlet options: CompressOptions\n\t\ttry {\n\t\t\toptions = validateOptions({ ...opts, output: outputFromCli ? opts.output : undefined })\n\t\t} catch (err) {\n\t\t\tfail((err as Error).message)\n\t\t}\n\n\t\tconst inputs = inputPaths.map((p) => resolveUserPath(p))\n\t\tlet plan: Awaited<ReturnType<typeof planJobs>>\n\t\ttry {\n\t\t\tplan = await planJobs({\n\t\t\t\tinputs,\n\t\t\t\toutput: options.output,\n\t\t\t\tformat: options.format,\n\t\t\t\texclude: options.exclude,\n\t\t\t})\n\t\t} catch (err) {\n\t\t\tif (err instanceof PlanError) fail(err.message)\n\t\t\tthrow err\n\t\t}\n\n\t\tconst log = options.json ? () => {} : console.log\n\t\tfor (const s of plan.skipped) console.error(`Skipped ${s.file}: ${s.reason}`)\n\n\t\tif (plan.jobs.length === 0) {\n\t\t\tif (options.json) console.log(JSON.stringify(buildJsonOutput([], [], 0, options.output)))\n\t\t\telse log('No images found to process.')\n\t\t\treturn\n\t\t}\n\n\t\tif (options.dryRun) {\n\t\t\tlog(`Would optimize ${plan.jobs.length} image(s) into ${displayPath(options.output)}`)\n\t\t\tfor (const job of plan.jobs) log(` ${displayPath(job.input)} → ${displayPath(job.output)}`)\n\t\t\treturn\n\t\t}\n\n\t\tconst showBar = !options.verbose && !options.json && process.stdout.isTTY\n\t\tconst progress = showBar ? createProgressBar(plan.jobs.length) : null\n\n\t\tconst batch = await runBatch(plan.jobs, options, options.concurrency, (job, result, error) => {\n\t\t\tprogress?.tick()\n\t\t\tif (!options.verbose || options.json) return\n\t\t\tif (result) console.log(formatFileResult(result))\n\t\t\telse console.error(`✗ ${displayPath(job.input)}: ${error?.message}`)\n\t\t})\n\n\t\tconst maxSize = options.maxSize\n\t\tconst exceeded = maxSize ? batch.results.filter((r) => r.newSize > maxSize) : []\n\n\t\tif (options.json) {\n\t\t\tconst output = buildJsonOutput(batch.results, batch.errors, batch.duration, options.output)\n\t\t\tif (maxSize) for (const f of output.files) if (f.newSize > maxSize) f.exceeded = true\n\t\t\tconsole.log(JSON.stringify(output))\n\t\t} else {\n\t\t\tconsole.log(formatSummary(batch.results, batch.errors, batch.duration))\n\t\t\tif (!options.verbose) {\n\t\t\t\tfor (const e of batch.errors) console.error(`✗ ${displayPath(e.file)}: ${e.message}`)\n\t\t\t}\n\t\t\tif (batch.results.length > 0) console.log(`Saved to ${displayPath(options.output)}`)\n\t\t\tfor (const r of exceeded) console.warn(`File exceeds size budget: ${r.outputPath}`)\n\t\t}\n\n\t\tif (batch.errors.length > 0 || exceeded.length > 0) process.exitCode = 1\n\t})\n\nfunction fail(message: string): never {\n\tconsole.error(`error: ${message}`)\n\tprocess.exit(1)\n}\n","export function createProgressBar(total: number) {\n\tlet current = 0\n\n\treturn {\n\t\ttick() {\n\t\t\tcurrent++\n\t\t\tconst percent = Math.round((current / total) * 100)\n\t\t\tconst barWidth = Math.max(0, Math.min(process.stdout.columns || 40, 40) - 20)\n\t\t\tconst filled = Math.round((current / total) * barWidth)\n\t\t\tconst empty = barWidth - filled\n\t\t\tconst bar = `[${'='.repeat(filled)}${' '.repeat(empty)}] ${percent}% ${current}/${total}`\n\t\t\tprocess.stdout.write(`\\r${bar}`)\n\t\t\tif (current === total) process.stdout.write('\\n')\n\t\t},\n\t}\n}\n","import { createRequire } from 'node:module'\nimport path from 'node:path'\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'\nimport { Command } from 'commander'\nimport { z } from 'zod'\nimport { buildJsonOutput } from '../output/results.js'\nimport { detectFormat, UNSUPPORTED } from '../pipeline/filter.js'\nimport { runBatch } from '../pipeline/optimize.js'\nimport { resolveUserPath } from '../pipeline/paths.js'\nimport { planJobs } from '../pipeline/plan.js'\nimport { defaultConcurrency, parseSize, resolveOutputDir } from '../validation/options.js'\n\nconst require = createRequire(import.meta.url)\nconst { version } = require('../../package.json') as { version: string }\n\nfunction text(payload: unknown) {\n\treturn { content: [{ type: 'text' as const, text: JSON.stringify(payload, null, 2) }] }\n}\n\nexport const mcpCommand = new Command('mcp')\n\t.description('Start MCP server for AI editor integration (Claude Code, Cursor, etc.)')\n\t.action(async () => {\n\t\tconst server = new McpServer({ name: 'bulki', version })\n\n\t\tserver.tool(\n\t\t\t'compress',\n\t\t\t'Compress and optimize images (JPEG, PNG, WebP, GIF, AVIF). Originals are never modified; optimized copies are written to `output`.',\n\t\t\t{\n\t\t\t\tpath: z.string().describe('File or directory path to compress'),\n\t\t\t\tquality: z.number().int().min(1).max(100).default(70).describe('Output quality (1-100)'),\n\t\t\t\tformat: z\n\t\t\t\t\t.enum(['jpeg', 'png', 'webp', 'gif', 'avif'])\n\t\t\t\t\t.optional()\n\t\t\t\t\t.describe('Output format (omit to keep original)'),\n\t\t\t\tresize: z\n\t\t\t\t\t.number()\n\t\t\t\t\t.int()\n\t\t\t\t\t.positive()\n\t\t\t\t\t.optional()\n\t\t\t\t\t.describe('Max width in pixels (never enlarges)'),\n\t\t\t\toutput: z\n\t\t\t\t\t.string()\n\t\t\t\t\t.optional()\n\t\t\t\t\t.describe('Folder where optimized images are written (default: ./bulki-output)'),\n\t\t\t\tdestination: z.string().optional().describe('Deprecated: base folder, use `output`'),\n\t\t\t\toutputFolder: z.string().optional().describe('Deprecated: subfolder name, use `output`'),\n\t\t\t\tmaxSize: z.string().optional().describe('Max file size budget (e.g., 200kb, 1mb)'),\n\t\t\t},\n\t\t\tasync (params) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst maxSizeBytes = params.maxSize ? parseSize(params.maxSize) : undefined\n\t\t\t\t\tconst output = resolveOutputDir(params)\n\t\t\t\t\tconst plan = await planJobs({\n\t\t\t\t\t\tinputs: [resolveUserPath(params.path)],\n\t\t\t\t\t\toutput,\n\t\t\t\t\t\tformat: params.format,\n\t\t\t\t\t})\n\t\t\t\t\tconst settings = { quality: params.quality, format: params.format, resize: params.resize }\n\t\t\t\t\tconst batch = await runBatch(plan.jobs, settings, defaultConcurrency())\n\t\t\t\t\tconst result = buildJsonOutput(batch.results, batch.errors, batch.duration, output)\n\t\t\t\t\tif (maxSizeBytes !== undefined) {\n\t\t\t\t\t\tfor (const file of result.files) if (file.newSize > maxSizeBytes) file.exceeded = true\n\t\t\t\t\t}\n\t\t\t\t\treturn text(result)\n\t\t\t\t} catch (err) {\n\t\t\t\t\treturn text({ error: (err as Error).message })\n\t\t\t\t}\n\t\t\t},\n\t\t)\n\n\t\tserver.tool(\n\t\t\t'detect',\n\t\t\t'Detect if a file is an image and identify its format',\n\t\t\t{ path: z.string().describe('Path to the file') },\n\t\t\tasync (params) => {\n\t\t\t\tconst format = await detectFormat(path.resolve(resolveUserPath(params.path)))\n\t\t\t\tif (format === UNSUPPORTED) return text({ format: 'heic', isImage: true, supported: false })\n\t\t\t\treturn text(format ? { format, isImage: true } : { format: null, isImage: false })\n\t\t\t},\n\t\t)\n\n\t\tawait server.connect(new StdioServerTransport())\n\t})\n","import { Command } from 'commander'\nimport { encodeImage } from '../pipeline/encode.js'\nimport { UNSUPPORTED } from '../pipeline/filter.js'\nimport { DEFAULTS, parseInteger, validateEncodeSettings } from '../validation/options.js'\n\nconst MAX_INPUT_SIZE = 100 * 1024 * 1024 // 100 MB\n\nexport const pipeCommand = new Command('pipe')\n\t.description('Read image from stdin, write optimized image to stdout')\n\t.requiredOption('-f, --format <fmt>', 'Output format (jpeg, png, webp, gif, avif)')\n\t.option('-q, --quality <n>', 'Quality (1-100)', parseInteger, DEFAULTS.quality)\n\t.option('-r, --resize <width>', 'Max width in px (never enlarges)', parseInteger)\n\t.action(async (opts) => {\n\t\tlet settings: ReturnType<typeof validateEncodeSettings>\n\t\ttry {\n\t\t\tsettings = validateEncodeSettings(opts)\n\t\t} catch (err) {\n\t\t\treturn fail((err as Error).message)\n\t\t}\n\n\t\tconst chunks: Buffer[] = []\n\t\tlet totalSize = 0\n\t\tfor await (const chunk of process.stdin) {\n\t\t\ttotalSize += chunk.length\n\t\t\tif (totalSize > MAX_INPUT_SIZE) return fail('input exceeds 100 MB limit')\n\t\t\tchunks.push(chunk)\n\t\t}\n\t\tconst input = Buffer.concat(chunks)\n\t\tif (input.length === 0) return fail('no input received on stdin')\n\n\t\ttry {\n\t\t\tconst detected = sniff(input)\n\t\t\tconst { data } = await encodeImage(input, detected, settings)\n\t\t\tprocess.stdout.write(data)\n\t\t} catch (err) {\n\t\t\tfail((err as Error).message)\n\t\t}\n\t})\n\n/** Same magic-byte rules as detectFormat, for an in-memory buffer. */\nfunction sniff(buf: Buffer): string {\n\tif (buf[0] === 0xff && buf[1] === 0xd8) return 'jpeg'\n\tif (buf[0] === 0x89 && buf.toString('latin1', 1, 4) === 'PNG') return 'png'\n\tif (buf.toString('latin1', 0, 4) === 'RIFF' && buf.toString('latin1', 8, 12) === 'WEBP')\n\t\treturn 'webp'\n\tif (buf.toString('latin1', 0, 3) === 'GIF') return 'gif'\n\tif (buf.toString('latin1', 4, 8) === 'ftyp') {\n\t\tconst brand = buf.toString('latin1', 8, 12)\n\t\treturn brand === 'avif' || brand === 'avis' ? 'avif' : UNSUPPORTED\n\t}\n\treturn 'unknown'\n}\n\nfunction fail(message: string): never {\n\tprocess.stderr.write(`Error: ${message}\\n`)\n\tprocess.exit(1)\n}\n"],"mappings":"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","names":["createRequire","Command","Command","require","Command","Command","Command","fail","require","createRequire","version","Command"]}
|