web-analyzer-mcp
Version:
基于MCP协议的网页界面分析工具,使用OpenAI Vision AI智能解析页面布局和UI元素
666 lines (642 loc) • 23.3 kB
JavaScript
#!/usr/bin/env node
// src/index.ts
import { Command } from "commander";
// src/server.ts
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
CallToolRequestSchema,
ListToolsRequestSchema
} from "@modelcontextprotocol/sdk/types.js";
// src/utils/openai.ts
import OpenAI from "openai";
import { readFileSync } from "fs";
var apiKey = process.env.OPENAI_API_KEY;
var baseURL = process.env.OPENAI_BASE_URL || "https://api.openai.com/v1";
if (!apiKey) {
throw new Error("OPENAI_API_KEY environment variable is required");
}
var openai = new OpenAI({
apiKey,
baseURL
});
function imageToBase64(imagePath) {
try {
const imageBuffer = readFileSync(imagePath);
return imageBuffer.toString("base64");
} catch (error) {
throw new Error(`Failed to read image file: ${error}`);
}
}
function getImageMimeType(imagePath) {
const extension = imagePath.toLowerCase().split(".").pop();
switch (extension) {
case "png":
return "image/png";
case "jpg":
case "jpeg":
return "image/jpeg";
default:
throw new Error(`Unsupported image format: ${extension}`);
}
}
async function analyzeWebpageScreenshot(imagePath, model = "gpt-4o", maxTokens = 2e3, temperature = 0.1) {
try {
const base64Image = imageToBase64(imagePath);
const mimeType = getImageMimeType(imagePath);
const response = await openai.chat.completions.create({
model,
max_tokens: maxTokens,
temperature,
messages: [
{
role: "system",
content: `\u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684\u7F51\u9875UI\u5206\u6790\u4E13\u5BB6\u3002\u8BF7\u5206\u6790\u63D0\u4F9B\u7684\u7F51\u9875\u622A\u56FE\uFF0C\u7528markdown\u683C\u5F0F\u63CF\u8FF0\u9875\u9762\u5E03\u5C40\u548C\u5173\u952E\u8981\u7D20\u3002
\u8BF7\u53C2\u7167\u4EE5\u4E0B\u683C\u5F0F\u8FD4\u56DE\u5206\u6790\u7ED3\u679C\uFF1A
# \u9875\u9762\u5E03\u5C40\u5206\u6790
## 1.\u6574\u4F53\u7ED3\u6784
- **\u9875\u9762\u6807\u9898**: [\u6839\u636E\u622A\u56FE\u5185\u5BB9\u5224\u65AD\uFF0C\u5982\uFF1A\u201C\u767B\u5F55 - \u6211\u7684\u5E94\u7528\u201D]
- **\u9875\u9762\u7C7B\u578B**: [\u5982\uFF1A\u767B\u5F55\u9875\u3001\u4EEA\u8868\u76D8\u3001\u4EA7\u54C1\u5217\u8868\u9875\u7B49]
- **\u5E03\u5C40\u98CE\u683C**: [\u5982\uFF1A\u54CD\u5E94\u5F0F\u3001\u56FA\u5B9A\u5BBD\u5EA6\u3001\u5361\u7247\u5F0F\u5E03\u5C40\u7B49]
- **\u4E3B\u8272\u8C03**: [\u63CF\u8FF0\u4E3B\u8981\u989C\u8272\u65B9\u6848]
## 2.\u4E3B\u8981\u533A\u57DF
<!--\u4EE5\u4E0B\u662F\u63CF\u8FF0\u6837\u4F8B\uFF0C\u5982\u679C\u67D0\u4E2A\u533A\u57DF\u4E0D\u5B58\u5728\u5219\u65E0\u9700\u63CF\u8FF0-->
<#if \u5BFC\u822A\u680F\u5B58\u5728>
### 2.1 \u9876\u90E8\u5BFC\u822A\u680F (Header)
- **\u4F4D\u7F6E**: \u9875\u9762\u9876\u90E8
- **\u529F\u80FD**: [\u5982\uFF1A\u54C1\u724Clogo\u3001\u4E3B\u5BFC\u822A\u83DC\u5355\u3001\u7528\u6237\u64CD\u4F5C\u7B49]
- **\u5173\u952E\u5143\u7D20**:
- Logo/\u54C1\u724C\u6807\u8BC6
- \u5BFC\u822A\u83DC\u5355\u9879
- \u641C\u7D22\u6846
- \u7528\u6237\u5934\u50CF/\u767B\u5F55\u6309\u94AE
</#if>
<#if \u4E3B\u4F53\u5185\u5BB9\u533A\u5B58\u5728>
### 2.2 \u4E3B\u4F53\u5185\u5BB9\u533A (Main Content)
- **\u5E03\u5C40**: [\u5982\uFF1A\u5355\u5217\u3001\u53CC\u5217\u3001\u7F51\u683C\u5E03\u5C40\u7B49]
- **\u4E3B\u8981\u5185\u5BB9**:
- \u6807\u9898\u548C\u526F\u6807\u9898
- \u4E3B\u8981\u64CD\u4F5C\u6309\u94AE
- \u5185\u5BB9\u5361\u7247/\u5217\u8868
- \u8868\u5355\u5143\u7D20
</#if>
<#if \u4FA7\u8FB9\u680F\u5B58\u5728>
### 2.3 \u4FA7\u8FB9\u680F (Sidebar)
- **\u4F4D\u7F6E**: \u5DE6\u4FA7/\u53F3\u4FA7
- **\u529F\u80FD**: [\u5982\uFF1A\u4E8C\u7EA7\u5BFC\u822A\u3001\u8FC7\u6EE4\u5668\u3001\u5DE5\u5177\u9762\u677F\u7B49]
</#if>
<#if \u9875\u811A\u5B58\u5728>
### 2.4 \u9875\u811A (Footer)
- **\u5185\u5BB9**: [\u5982\uFF1A\u7248\u6743\u4FE1\u606F\u3001\u94FE\u63A5\u3001\u793E\u4EA4\u5A92\u4F53\u56FE\u6807\u7B49]
</#if>
## 3. \u5173\u952EUI\u5143\u7D20
### 3.1 \u4EA4\u4E92\u5143\u7D20
- **\u6309\u94AE**: [\u63CF\u8FF0\u4E3B\u8981\u6309\u94AE\u7684\u6837\u5F0F\u3001\u4F4D\u7F6E\u548C\u529F\u80FD]
- **\u8868\u5355\u63A7\u4EF6**: [\u8F93\u5165\u6846\u3001\u4E0B\u62C9\u83DC\u5355\u3001\u590D\u9009\u6846\u7B49]
- **\u5BFC\u822A\u5143\u7D20**: [\u9762\u5305\u5C51\u3001\u5206\u9875\u3001\u6807\u7B7E\u9875\u7B49]
### 3.2 \u5185\u5BB9\u5143\u7D20
- **\u6587\u672C\u5185\u5BB9**: [\u6807\u9898\u5C42\u7EA7\u3001\u6BB5\u843D\u7ED3\u6784\u3001\u91CD\u8981\u6587\u6848]
- **\u56FE\u7247/\u56FE\u6807**: [\u4E3B\u8981\u56FE\u7247\u5185\u5BB9\u3001\u56FE\u6807\u4F7F\u7528]
- **\u6570\u636E\u5C55\u793A**: [\u8868\u683C\u3001\u56FE\u8868\u3001\u7EDF\u8BA1\u6570\u636E\u7B49]
\u8BF7\u786E\u4FDD\u63CF\u8FF0\u51C6\u786E\u3001\u7B80\u6D01\uFF0C\u91CD\u70B9\u5173\u6CE8\u529F\u80FD\u6027\u5143\u7D20\u548C\u5E03\u5C40\u7279\u5F81\u3002`
},
{
role: "user",
content: [
{
type: "image_url",
image_url: {
url: `data:${mimeType};base64,${base64Image}`,
detail: "high"
}
},
{
type: "text",
text: "\u8BF7\u5206\u6790\u8FD9\u4E2A\u7F51\u9875\u622A\u56FE\uFF0C\u7528markdown\u683C\u5F0F\u63CF\u8FF0\u9875\u9762\u5E03\u5C40\u548C\u5173\u952EUI\u5143\u7D20\u3002"
}
]
}
]
});
const content = response.choices[0]?.message?.content;
if (!content) {
throw new Error("No response from OpenAI API");
}
return {
layout: {},
elements: [],
analysis: content,
metadata: {
imageSize: await getImageSize(imagePath),
analyzedAt: (/* @__PURE__ */ new Date()).toISOString(),
model,
confidence: 0.9
}
};
} catch (error) {
if (error instanceof Error) {
throw new Error(`OpenAI API error: ${error.message}`);
}
throw new Error("Unknown error occurred during analysis");
}
}
async function getImageSize(imagePath) {
return {
x: 0,
y: 0,
width: 1920,
height: 1080
};
}
// src/utils/image.ts
import { readFileSync as readFileSync2, existsSync, statSync } from "fs";
import { resolve, extname } from "path";
var SUPPORTED_IMAGE_FORMATS = [".png", ".jpg", ".jpeg"];
function validateImageFile(imagePath) {
const resolvedPath = resolve(imagePath);
if (!existsSync(resolvedPath)) {
throw new Error(`Image file not found: ${resolvedPath}`);
}
const ext = extname(resolvedPath).toLowerCase();
if (!SUPPORTED_IMAGE_FORMATS.includes(ext)) {
throw new Error(
`Unsupported image format: ${ext}. Supported formats: ${SUPPORTED_IMAGE_FORMATS.join(", ")}`
);
}
try {
const stats = statSync(resolvedPath);
const maxSize = 10 * 1024 * 1024;
if (stats.size > maxSize) {
throw new Error(`Image file too large: ${Math.round(stats.size / 1024 / 1024)}MB (max: 10MB)`);
}
} catch (error) {
throw new Error(`Failed to read image file stats: ${error}`);
}
}
function getImageInfo(imagePath) {
const resolvedPath = resolve(imagePath);
validateImageFile(resolvedPath);
const ext = extname(resolvedPath).toLowerCase();
const stats = statSync(resolvedPath);
return {
path: resolvedPath,
format: ext.slice(1),
// 去掉点号
size: stats.size
};
}
// src/types/schema.ts
import { z } from "zod";
var BoundsSchema = z.object({
x: z.number().describe("X\u5750\u6807"),
y: z.number().describe("Y\u5750\u6807"),
width: z.number().describe("\u5BBD\u5EA6"),
height: z.number().describe("\u9AD8\u5EA6")
});
var ElementTypeSchema = z.enum([
"button",
"input",
"text",
"image",
"link",
"navigation",
"form",
"container",
"icon",
"video",
"table",
"list",
"heading",
"paragraph"
]);
var ElementAttributesSchema = z.object({
color: z.string().optional().describe("\u989C\u8272"),
backgroundColor: z.string().optional().describe("\u80CC\u666F\u8272"),
fontSize: z.string().optional().describe("\u5B57\u4F53\u5927\u5C0F"),
fontWeight: z.string().optional().describe("\u5B57\u91CD"),
style: z.string().optional().describe("\u6837\u5F0F\u7C7B\u578B"),
href: z.string().optional().describe("\u94FE\u63A5\u5730\u5740"),
placeholder: z.string().optional().describe("\u8F93\u5165\u6846\u5360\u4F4D\u7B26"),
alt: z.string().optional().describe("\u56FE\u7247alt\u6587\u672C"),
src: z.string().optional().describe("\u56FE\u7247\u6E90\u5730\u5740")
}).passthrough();
var PageElementSchema = z.object({
type: ElementTypeSchema,
text: z.string().optional().describe("\u5143\u7D20\u6587\u672C\u5185\u5BB9"),
content: z.string().optional().describe("\u5143\u7D20\u5185\u5BB9"),
bounds: BoundsSchema,
attributes: ElementAttributesSchema.optional(),
confidence: z.number().min(0).max(1).optional().describe("\u8BC6\u522B\u7F6E\u4FE1\u5EA6")
});
var LayoutTypeSchema = z.enum([
"header",
"navigation",
"sidebar",
"main",
"content",
"footer",
"modal",
"overlay"
]);
var LayoutAreaSchema = z.object({
bounds: BoundsSchema,
type: LayoutTypeSchema,
elements: z.array(PageElementSchema).describe("\u533A\u57DF\u5185\u7684\u5143\u7D20"),
name: z.string().optional().describe("\u533A\u57DF\u540D\u79F0")
});
var AnalysisResultSchema = z.object({
layout: z.record(z.string(), LayoutAreaSchema).describe("\u9875\u9762\u5E03\u5C40\u533A\u57DF").optional(),
elements: z.array(PageElementSchema).describe("\u6240\u6709\u9875\u9762\u5143\u7D20").optional(),
analysis: z.string().optional().describe("markdown\u683C\u5F0F\u7684\u5206\u6790\u7ED3\u679C"),
metadata: z.object({
imageSize: BoundsSchema.describe("\u539F\u59CB\u56FE\u7247\u5C3A\u5BF8"),
analyzedAt: z.string().datetime().describe("\u5206\u6790\u65F6\u95F4"),
model: z.string().describe("\u4F7F\u7528\u7684\u6A21\u578B"),
confidence: z.number().min(0).max(1).describe("\u6574\u4F53\u7F6E\u4FE1\u5EA6")
})
});
var AnalyzeImageInputSchema = z.object({
imagePath: z.string().describe("\u56FE\u7247\u6587\u4EF6\u8DEF\u5F84"),
model: z.string().optional().default("gpt-4-vision-preview").describe("OpenAI\u6A21\u578B"),
maxTokens: z.number().optional().default(2e3).describe("\u6700\u5927token\u6570"),
temperature: z.number().optional().default(0.1).describe("\u6E29\u5EA6\u53C2\u6570")
});
var ExtractDocxImagesInputSchema = z.object({
docxPath: z.string().describe("Word\u6587\u6863\u8DEF\u5F84(.docx)"),
outputDir: z.string().optional().default("./extracted-images").describe("\u56FE\u7247\u8F93\u51FA\u76EE\u5F55"),
prefix: z.string().optional().default("docx-image").describe("\u8F93\u51FA\u6587\u4EF6\u540D\u524D\u7F00")
});
var ExtractDocxImagesResultSchema = z.object({
extractedImages: z.array(z.object({
originalName: z.string(),
savedPath: z.string(),
size: z.number(),
mimeType: z.string()
})),
totalCount: z.number(),
outputDirectory: z.string()
});
// src/tools/analyze.ts
var analyzeImageTool = {
name: "analyze_webpage_screenshot",
description: "\u5206\u6790\u7F51\u9875\u622A\u56FE\uFF0C\u8BC6\u522B\u9875\u9762\u5E03\u5C40\u548CUI\u5143\u7D20",
inputSchema: {
type: "object",
properties: {
imagePath: {
type: "string",
description: "\u56FE\u7247\u6587\u4EF6\u8DEF\u5F84"
},
model: {
type: "string",
description: "OpenAI\u6A21\u578B",
default: "gpt-4o"
},
maxTokens: {
type: "number",
description: "\u6700\u5927token\u6570",
default: 2e3
},
temperature: {
type: "number",
description: "temperature",
default: 0.1
}
},
required: ["imagePath"]
},
/**
* 执行图片分析
*/
handler: async (input) => {
try {
const validatedInput = AnalyzeImageInputSchema.parse(input);
validateImageFile(validatedInput.imagePath);
const imageInfo = getImageInfo(validatedInput.imagePath);
const result = await analyzeWebpageScreenshot(
validatedInput.imagePath,
validatedInput.model,
validatedInput.maxTokens,
validatedInput.temperature
);
const validatedResult = AnalysisResultSchema.parse(result);
if (validatedResult.analysis) {
return {
content: [
{
type: "text",
text: validatedResult.analysis
}
]
};
}
return {
content: [
{
type: "text",
text: JSON.stringify(validatedResult, null, 2)
}
]
};
} catch (error) {
const errorMessage = error instanceof Error ? error.message : "Unknown error";
return {
content: [
{
type: "text",
text: `Error analyzing image: ${errorMessage}`
}
],
isError: true
};
}
}
};
var tools = [analyzeImageTool];
// src/tools/extract-docx-images.ts
import { z as z2 } from "zod";
import * as fs2 from "fs";
import * as path2 from "path";
// src/utils/docx-parser.ts
import * as fs from "fs";
import * as path from "path";
import mammoth from "mammoth";
async function extractDocxImages(docxPath, outputDir, prefix = "docx-image") {
if (!fs.existsSync(docxPath)) {
throw new Error(`DOCX\u6587\u4EF6\u4E0D\u5B58\u5728: ${docxPath}`);
}
if (!docxPath.toLowerCase().endsWith(".docx")) {
throw new Error("\u4EC5\u652F\u6301DOCX\u683C\u5F0F\u6587\u4EF6");
}
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
const extractedImages = [];
let imageIndex = 0;
const imagePromises = [];
const convertImage = mammoth.images.imgElement(async (image) => {
const promise = (async () => {
try {
const extension = getImageExtension(image.contentType);
const originalName = `image${++imageIndex}${extension}`;
const savedName = `${prefix}-${imageIndex}${extension}`;
const savedPath = path.join(outputDir, savedName);
const buffer = Buffer.from(await image.read("base64"), "base64");
fs.writeFileSync(savedPath, buffer);
extractedImages.push({
originalName,
savedPath: path.resolve(savedPath),
size: buffer.length,
mimeType: image.contentType
});
} catch (error) {
console.error("\u63D0\u53D6\u56FE\u7247\u5931\u8D25:", error);
}
})();
imagePromises.push(promise);
return { src: "" };
});
await mammoth.convertToHtml(
{ path: docxPath },
{ convertImage }
);
await Promise.all(imagePromises);
return {
extractedImages,
totalCount: extractedImages.length,
outputDirectory: path.resolve(outputDir)
};
}
function getImageExtension(mimeType) {
const mimeMap = {
"image/jpeg": ".jpg",
"image/jpg": ".jpg",
"image/png": ".png",
"image/gif": ".gif",
"image/bmp": ".bmp",
"image/webp": ".webp",
"image/svg+xml": ".svg"
};
return mimeMap[mimeType.toLowerCase()] || ".png";
}
// src/tools/extract-docx-images.ts
var extractDocxImagesTool = {
name: "extract_docx_images",
description: "\u4ECEWord\u6587\u6863(.docx)\u4E2D\u63D0\u53D6\u6240\u6709\u5D4C\u5165\u7684\u56FE\u7247",
inputSchema: {
type: "object",
properties: {
docxPath: {
type: "string",
description: "Word\u6587\u6863\u8DEF\u5F84(.docx)"
},
outputDir: {
type: "string",
description: "\u56FE\u7247\u8F93\u51FA\u76EE\u5F55\u7684\u7EDD\u5BF9\u8DEF\u5F84\u3002\u5982\u679C\u672A\u6307\u5B9A\uFF0C\u9ED8\u8BA4\u63D0\u53D6\u5230DOCX\u6587\u4EF6\u540C\u7EA7\u7684extracted-images/\u76EE\u5F55\u4E0B",
default: "./extracted-images"
},
prefix: {
type: "string",
description: "\u8F93\u51FA\u6587\u4EF6\u540D\u524D\u7F00\uFF0C\u9ED8\u8BA4\u4E3Adocx-image",
default: "docx-image"
}
},
required: ["docxPath"]
},
handler: async (input) => {
try {
const validatedInput = ExtractDocxImagesInputSchema.parse(input);
if (!fs2.existsSync(validatedInput.docxPath)) {
throw new Error(`DOCX\u6587\u4EF6\u4E0D\u5B58\u5728: ${validatedInput.docxPath}`);
}
let outputDir = validatedInput.outputDir;
if (!outputDir || outputDir === "./extracted-images") {
const docxDir = path2.dirname(validatedInput.docxPath) + path2.sep;
outputDir = docxDir + "extracted-images";
} else {
if (!path2.isAbsolute(outputDir)) {
outputDir = path2.resolve(process.cwd(), outputDir);
}
}
const result = await extractDocxImages(
validatedInput.docxPath,
outputDir,
validatedInput.prefix
);
const validatedResult = ExtractDocxImagesResultSchema.parse(result);
return {
content: [
{
type: "text",
text: JSON.stringify(validatedResult, null, 2)
}
]
};
} catch (error) {
if (error instanceof z2.ZodError) {
throw new Error(`\u53C2\u6570\u9A8C\u8BC1\u5931\u8D25: ${error.errors.map((e) => e.message).join(", ")}`);
}
throw error;
}
}
};
// src/server.ts
var tools2 = [...tools, extractDocxImagesTool];
function createServer() {
const server = new Server({
name: "webpage-screenshot-analyzer",
version: "1.0.0"
});
server.setRequestHandler(ListToolsRequestSchema, async () => {
const toolList = {
tools: tools2.map((tool) => ({
name: tool.name,
description: tool.description,
inputSchema: tool.inputSchema
}))
};
return toolList;
});
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const tool = tools2.find((t) => t.name === request.params.name);
if (!tool) {
const error = `Unknown tool: ${request.params.name}`;
throw new Error(error);
}
if (!request.params.arguments) {
const error = "Tool arguments are required";
throw new Error(error);
}
try {
const result = await tool.handler(request.params.arguments);
return result;
} catch (error) {
const errorMessage = error instanceof Error ? error.message : "Unknown error";
return {
content: [
{
type: "text",
text: `Tool execution failed: ${errorMessage}`
}
],
isError: true
};
}
});
return server;
}
async function startServer() {
const server = createServer();
const transport = new StdioServerTransport();
console.log("Starting webpage screenshot analyzer MCP server...");
try {
await server.connect(transport);
} catch (error) {
console.error("Failed to start MCP server", error);
process.exit(1);
}
}
// src/index.ts
import * as fs3 from "fs";
var program = new Command();
program.name("web-analyzer").description("\u57FA\u4E8EMCP\u534F\u8BAE\u7684\u7F51\u9875\u754C\u9762\u5206\u6790\u5DE5\u5177\uFF0C\u4F7F\u7528AI\u667A\u80FD\u89E3\u6790\u9875\u9762\u5E03\u5C40\u548CUI\u5143\u7D20").version("1.0.0");
program.command("server").description("\u542F\u52A8MCP\u670D\u52A1\u5668").action(async () => {
await startServer();
});
program.command("analyze").description("\u5206\u6790\u7F51\u9875\u622A\u56FE").requiredOption("-i, --image-path <path>", "\u56FE\u7247\u6587\u4EF6\u8DEF\u5F84").option("-m, --model <model>", "OpenAI\u6A21\u578B", "gpt-4o").option("--max-tokens <number>", "\u6700\u5927token\u6570", "2000").option("--temperature <number>", "temperature", "0.1").action(async (options) => {
try {
const input = AnalyzeImageInputSchema.parse({
imagePath: options.imagePath,
model: options.model,
maxTokens: parseInt(options.maxTokens),
temperature: parseFloat(options.temperature)
});
validateImageFile(input.imagePath);
console.log(`\u6B63\u5728\u5206\u6790\u56FE\u7247: ${input.imagePath}`);
console.log(`\u4F7F\u7528\u6A21\u578B: ${input.model}`);
const result = await analyzeWebpageScreenshot(
input.imagePath,
input.model,
input.maxTokens,
input.temperature
);
if (result.analysis) {
console.log("\n\u5206\u6790\u7ED3\u679C:");
console.log(result.analysis);
} else {
console.log("\n\u5206\u6790\u7ED3\u679C:");
console.log(JSON.stringify(result, null, 2));
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF";
console.error("\u5206\u6790\u5931\u8D25:", errorMessage);
process.exit(1);
}
});
program.command("extract-docx-images").description("\u4ECEWord\u6587\u6863\u4E2D\u63D0\u53D6\u6240\u6709\u5D4C\u5165\u7684\u56FE\u7247").requiredOption("-d, --docx-path <path>", "DOCX\u6587\u4EF6\u8DEF\u5F84").option("-o, --output-dir <dir>", "\u56FE\u7247\u8F93\u51FA\u76EE\u5F55", "./extracted-images").option("-p, --prefix <prefix>", "\u8F93\u51FA\u6587\u4EF6\u540D\u524D\u7F00", "docx-image").action(async (options) => {
try {
const input = ExtractDocxImagesInputSchema.parse({
docxPath: options.docxPath,
outputDir: options.outputDir,
prefix: options.prefix
});
console.log("\u5F00\u59CB\u63D0\u53D6DOCX\u56FE\u7247", {
docxPath: input.docxPath,
outputDir: input.outputDir,
prefix: input.prefix
});
if (!fs3.existsSync(input.docxPath)) {
console.error("DOCX\u6587\u4EF6\u4E0D\u5B58\u5728", { docxPath: input.docxPath });
throw new Error(`DOCX\u6587\u4EF6\u4E0D\u5B58\u5728: ${input.docxPath}`);
}
const stats = fs3.statSync(input.docxPath);
console.log("DOCX\u6587\u4EF6\u4FE1\u606F", {
size: stats.size,
lastModified: stats.mtime
});
const result = await extractDocxImages(
input.docxPath,
input.outputDir,
input.prefix
);
console.log(JSON.stringify(result, null, 2));
} catch (error) {
const errorMessage = error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF";
console.error("DOCX\u56FE\u7247\u63D0\u53D6\u5931\u8D25", { error: errorMessage });
console.error("\u63D0\u53D6\u5931\u8D25:", errorMessage);
process.exit(1);
}
});
function validateEnvironment() {
const requiredEnvVars = ["OPENAI_API_KEY"];
const missingVars = requiredEnvVars.filter((envVar) => !process.env[envVar]);
if (missingVars.length > 0) {
console.error("\u7F3A\u5C11\u5FC5\u9700\u7684\u73AF\u5883\u53D8\u91CF:");
missingVars.forEach((envVar) => {
console.error(` - ${envVar}`);
});
console.error("\n\u8BF7\u8BBE\u7F6E\u73AF\u5883\u53D8\u91CF\u540E\u91CD\u8BD5\u3002");
process.exit(1);
}
}
async function main() {
validateEnvironment();
if (process.argv.length <= 2) {
console.log("\u672A\u63D0\u4F9B\u547D\u4EE4\uFF0C\u9ED8\u8BA4\u542F\u52A8MCP\u670D\u52A1\u5668...");
await startServer();
return;
}
await program.parseAsync();
}
process.on("uncaughtException", (error) => {
console.error("\u672A\u6355\u83B7\u7684\u5F02\u5E38:", error);
process.exit(1);
});
process.on("unhandledRejection", (reason, promise) => {
console.error("\u672A\u5904\u7406\u7684Promise\u62D2\u7EDD:", reason);
process.exit(1);
});
main().catch((error) => {
console.error("\u542F\u52A8\u5931\u8D25:", error);
process.exit(1);
});