fazee-vision-mcp 0.1.0 → 0.1.1
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 +58 -5
- package/dist/index.js +98 -15
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ Production-grade MCP server for advanced image and screenshot analysis with a Ge
|
|
|
7
7
|
- Gemini as primary/default provider
|
|
8
8
|
- Provider fallback chain: Gemini -> OpenRouter -> Ollama
|
|
9
9
|
- `vision_analyze` MCP tool with strict Zod validation
|
|
10
|
-
- Secure image ingestion: URL, local path, base64, data URL
|
|
10
|
+
- Secure image ingestion: URL, local path, `file://` URL, base64, data URL
|
|
11
11
|
- Security protections: SSRF defense, private IP blocking, safe local path allowlist, size/pixel limits
|
|
12
12
|
- Sharp-based preprocessing: EXIF fix, resize, compression, normalization, metadata extraction, hashing
|
|
13
13
|
- Typed errors and stable error codes
|
|
@@ -189,7 +189,7 @@ npm publish --access public
|
|
|
189
189
|
Tool: `vision_analyze`
|
|
190
190
|
|
|
191
191
|
Input:
|
|
192
|
-
- `imageSource`: URL | local path | base64 | data URL
|
|
192
|
+
- `imageSource`: URL | local path | `file://` URL | base64 | data URL
|
|
193
193
|
- `prompt`: analysis instruction
|
|
194
194
|
- `mode`: `general | palette | hierarchy | components | ocr | ui_analysis | code_screenshot`
|
|
195
195
|
- `options`: `temperature`, `maxTokens`, `outputFormat`, `provider`, `model`, `enableCache`, `timeoutMs`
|
|
@@ -201,17 +201,70 @@ Input:
|
|
|
201
201
|
- `INPUT_ERROR`: invalid image source or limits exceeded
|
|
202
202
|
- `TIMEOUT_ERROR` / `PROVIDER_ERROR`: provider request exceeded timeout or failed
|
|
203
203
|
|
|
204
|
+
For local files, set both:
|
|
205
|
+
|
|
206
|
+
```env
|
|
207
|
+
VISION_ENABLE_LOCAL_PATH_INPUT=true
|
|
208
|
+
VISION_ALLOWED_LOCAL_ROOTS=C:\Users\name\Pictures,C:\Users\name\AppData\Local\Temp
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Use `VISION_ALLOWED_LOCAL_ROOTS=*` only for a fully trusted local setup.
|
|
212
|
+
|
|
204
213
|
## Security Notes
|
|
205
214
|
|
|
206
215
|
- Blocks localhost/private/metadata targets for URL image fetching
|
|
207
|
-
- Enforces local path allowlist for filesystem image access
|
|
216
|
+
- Enforces local path allowlist for filesystem image access unless `VISION_ALLOWED_LOCAL_ROOTS=*`
|
|
208
217
|
- Enforces image byte and pixel limits
|
|
209
218
|
- Prevents secret leakage by avoiding raw secret logging
|
|
210
219
|
|
|
211
220
|
## Windows Setup
|
|
212
221
|
|
|
213
|
-
|
|
214
|
-
|
|
222
|
+
Do not set `command` to a `.js` file directly on Windows.
|
|
223
|
+
If `command` is a `.js` path, Windows will show "Select an app to open this .js file".
|
|
224
|
+
|
|
225
|
+
Use one of these two safe options:
|
|
226
|
+
|
|
227
|
+
1) `command: "node"` + JS path in `args` (recommended)
|
|
228
|
+
2) `command: "cmd"` + the included launcher script (`scripts\\run-vision-mcp.cmd`)
|
|
229
|
+
|
|
230
|
+
Example (local project):
|
|
231
|
+
|
|
232
|
+
```json
|
|
233
|
+
{
|
|
234
|
+
"mcpServers": {
|
|
235
|
+
"vision": {
|
|
236
|
+
"command": "node",
|
|
237
|
+
"args": ["C:\\\\Users\\\\name\\\\project\\\\dist\\\\index.js"],
|
|
238
|
+
"env": {
|
|
239
|
+
"VISION_PROVIDER": "openrouter",
|
|
240
|
+
"OPENROUTER_API_KEY": "your_key",
|
|
241
|
+
"OPENROUTER_MODEL": "gemini-2.5-flash-lite"
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
If Node is not in PATH, use full path:
|
|
249
|
+
`C:\\Program Files\\nodejs\\node.exe`
|
|
250
|
+
|
|
251
|
+
Alternative launcher config (avoids `.js` association issues in strict clients):
|
|
252
|
+
|
|
253
|
+
```json
|
|
254
|
+
{
|
|
255
|
+
"mcpServers": {
|
|
256
|
+
"vision": {
|
|
257
|
+
"command": "cmd",
|
|
258
|
+
"args": ["/d", "/c", "C:\\\\Users\\\\name\\\\project\\\\scripts\\\\run-vision-mcp.cmd"],
|
|
259
|
+
"env": {
|
|
260
|
+
"VISION_PROVIDER": "openrouter",
|
|
261
|
+
"OPENROUTER_API_KEY": "your_key",
|
|
262
|
+
"OPENROUTER_MODEL": "gemini-2.5-flash-lite"
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
```
|
|
215
268
|
|
|
216
269
|
## Linux Setup
|
|
217
270
|
|
package/dist/index.js
CHANGED
|
@@ -141,7 +141,7 @@ function parseNumber(input, fallback) {
|
|
|
141
141
|
}
|
|
142
142
|
function parseRoots(input) {
|
|
143
143
|
if (!input) return [];
|
|
144
|
-
return input.split(",").map((x) => x.trim()).filter(Boolean);
|
|
144
|
+
return input.split(",").map((x) => x.trim().replace(/^['"]|['"]$/g, "")).filter(Boolean);
|
|
145
145
|
}
|
|
146
146
|
function loadConfig(env = process.env) {
|
|
147
147
|
const parsed = ConfigSchema.parse({
|
|
@@ -403,6 +403,7 @@ async function normalizeImage(input, sourceKind, config) {
|
|
|
403
403
|
var import_promises = __toESM(require("dns/promises"));
|
|
404
404
|
var import_node_net = __toESM(require("net"));
|
|
405
405
|
var import_node_path = __toESM(require("path"));
|
|
406
|
+
var import_node_os = __toESM(require("os"));
|
|
406
407
|
var import_node_url = require("url");
|
|
407
408
|
var BLOCKED_HOSTNAMES = /* @__PURE__ */ new Set(["localhost", "metadata.google.internal"]);
|
|
408
409
|
var PRIVATE_CIDR_CHECKS = [
|
|
@@ -449,10 +450,14 @@ async function validateRemoteUrl(rawUrl) {
|
|
|
449
450
|
return parsed;
|
|
450
451
|
}
|
|
451
452
|
function validateLocalPath(inputPath, allowedRoots) {
|
|
452
|
-
const
|
|
453
|
+
const normalizedPath = normalizeLocalPathInput(inputPath);
|
|
454
|
+
const resolved = import_node_path.default.resolve(normalizedPath);
|
|
453
455
|
if (allowedRoots.length === 0) {
|
|
454
456
|
throw new VisionError("SECURITY_ERROR", "Local file access disabled: allowlist is empty.", 403);
|
|
455
457
|
}
|
|
458
|
+
if (allowedRoots.includes("*")) {
|
|
459
|
+
return resolved;
|
|
460
|
+
}
|
|
456
461
|
const isAllowed = allowedRoots.some((root) => {
|
|
457
462
|
const resolvedRoot = import_node_path.default.resolve(root);
|
|
458
463
|
const relative = import_node_path.default.relative(resolvedRoot, resolved);
|
|
@@ -463,13 +468,39 @@ function validateLocalPath(inputPath, allowedRoots) {
|
|
|
463
468
|
}
|
|
464
469
|
return resolved;
|
|
465
470
|
}
|
|
471
|
+
function normalizeLocalPathInput(inputPath) {
|
|
472
|
+
const trimmed = inputPath.trim().replace(/^['"]|['"]$/g, "");
|
|
473
|
+
if (/^file:\/\//i.test(trimmed)) {
|
|
474
|
+
try {
|
|
475
|
+
return (0, import_node_url.fileURLToPath)(trimmed);
|
|
476
|
+
} catch {
|
|
477
|
+
throw new VisionError("INPUT_ERROR", "Invalid file URL.", 400);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
if (trimmed === "~") {
|
|
481
|
+
return import_node_os.default.homedir();
|
|
482
|
+
}
|
|
483
|
+
if (trimmed.startsWith(`~${import_node_path.default.sep}`) || trimmed.startsWith("~/")) {
|
|
484
|
+
return import_node_path.default.join(import_node_os.default.homedir(), trimmed.slice(2));
|
|
485
|
+
}
|
|
486
|
+
return trimmed;
|
|
487
|
+
}
|
|
466
488
|
|
|
467
489
|
// src/image/loadImage.ts
|
|
468
490
|
function isDataUrl(input) {
|
|
469
491
|
return input.startsWith("data:image/");
|
|
470
492
|
}
|
|
471
493
|
function isBase64(input) {
|
|
472
|
-
|
|
494
|
+
const cleaned = input.replace(/\s/g, "");
|
|
495
|
+
if (cleaned.length < 32 || cleaned.length % 4 !== 0 || !/^[A-Za-z0-9+/]+={0,2}$/.test(cleaned)) {
|
|
496
|
+
return false;
|
|
497
|
+
}
|
|
498
|
+
const decoded = Buffer.from(cleaned, "base64");
|
|
499
|
+
return hasImageMagicNumber(decoded);
|
|
500
|
+
}
|
|
501
|
+
function hasImageMagicNumber(input) {
|
|
502
|
+
if (input.length < 4) return false;
|
|
503
|
+
return input.subarray(0, 3).equals(Buffer.from([255, 216, 255])) || input.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10])) || input.subarray(0, 4).toString("ascii") === "RIFF" || input.subarray(0, 4).toString("ascii") === "GIF8" || input.subarray(0, 4).toString("ascii") === "%PDF" || input.subarray(0, 4).toString("ascii") === "MM\0*" || input.subarray(0, 4).toString("ascii") === "II*\0" || input.subarray(4, 12).toString("ascii").startsWith("ftyp");
|
|
473
504
|
}
|
|
474
505
|
function parseDataUrl(input) {
|
|
475
506
|
const match = /^data:(image\/[\w+.-]+);base64,(.+)$/i.exec(input);
|
|
@@ -505,6 +536,13 @@ async function loadImageFromSource(imageSource, config, timeoutMs) {
|
|
|
505
536
|
if (isDataUrl(imageSource)) {
|
|
506
537
|
raw = parseDataUrl(imageSource);
|
|
507
538
|
sourceKind = "data-url";
|
|
539
|
+
} else if (isBase64(imageSource)) {
|
|
540
|
+
try {
|
|
541
|
+
raw = Buffer.from(imageSource, "base64");
|
|
542
|
+
sourceKind = "base64";
|
|
543
|
+
} catch {
|
|
544
|
+
throw new VisionError("INPUT_ERROR", "Invalid base64 image input.", 400);
|
|
545
|
+
}
|
|
508
546
|
} else if (/^https?:\/\//i.test(imageSource)) {
|
|
509
547
|
if (!config.enableUrlInput) {
|
|
510
548
|
throw new VisionError("SECURITY_ERROR", "URL input is disabled.", 403);
|
|
@@ -516,13 +554,6 @@ async function loadImageFromSource(imageSource, config, timeoutMs) {
|
|
|
516
554
|
const localPath = validateLocalPath(imageSource, config.allowedLocalRoots);
|
|
517
555
|
raw = await import_promises2.default.readFile(localPath);
|
|
518
556
|
sourceKind = "local";
|
|
519
|
-
} else if (isBase64(imageSource)) {
|
|
520
|
-
try {
|
|
521
|
-
raw = Buffer.from(imageSource, "base64");
|
|
522
|
-
sourceKind = "base64";
|
|
523
|
-
} catch {
|
|
524
|
-
throw new VisionError("INPUT_ERROR", "Invalid base64 image input.", 400);
|
|
525
|
-
}
|
|
526
557
|
} else {
|
|
527
558
|
throw new VisionError("INPUT_ERROR", "Unsupported image source. Use URL, local path, base64, or data URL.", 400);
|
|
528
559
|
}
|
|
@@ -675,24 +706,28 @@ var VisionService = class {
|
|
|
675
706
|
timeoutMs,
|
|
676
707
|
() => new VisionError("TIMEOUT_ERROR", `${providerName} OCR pass timed out`, 504)
|
|
677
708
|
);
|
|
709
|
+
const normalizedOcrText = this.normalizeOcrAnchors(ocrPass.text);
|
|
678
710
|
return withTimeout(
|
|
679
711
|
provider.analyze({
|
|
680
712
|
...analyzeRequest,
|
|
681
713
|
outputFormat: "json",
|
|
682
|
-
prompt: buildUiStructuredJsonPrompt(input.prompt,
|
|
714
|
+
prompt: buildUiStructuredJsonPrompt(input.prompt, normalizedOcrText)
|
|
683
715
|
}),
|
|
684
716
|
timeoutMs,
|
|
685
717
|
() => new VisionError("TIMEOUT_ERROR", `${providerName} UI pass timed out`, 504)
|
|
686
718
|
);
|
|
687
719
|
});
|
|
688
|
-
const
|
|
720
|
+
const outputFormat = options.outputFormat ?? "text";
|
|
721
|
+
const uiAnalysis = this.tryParseUiAnalysis(value, input.mode, outputFormat);
|
|
722
|
+
const normalizedUiAnalysis = uiAnalysis ? this.normalizeUiAnalysis(uiAnalysis) : void 0;
|
|
723
|
+
const analysisText = input.mode === "ui_analysis" && outputFormat === "json" && normalizedUiAnalysis ? JSON.stringify(normalizedUiAnalysis, null, 2) : value.text;
|
|
689
724
|
const result = VisionAnalyzeResponseSchema.parse({
|
|
690
725
|
provider: value.provider,
|
|
691
726
|
model: value.model,
|
|
692
727
|
mode: input.mode,
|
|
693
|
-
outputFormat
|
|
694
|
-
analysis:
|
|
695
|
-
uiAnalysis,
|
|
728
|
+
outputFormat,
|
|
729
|
+
analysis: analysisText,
|
|
730
|
+
uiAnalysis: normalizedUiAnalysis,
|
|
696
731
|
cached: false,
|
|
697
732
|
image: {
|
|
698
733
|
width: image.width,
|
|
@@ -723,6 +758,54 @@ var VisionService = class {
|
|
|
723
758
|
return void 0;
|
|
724
759
|
}
|
|
725
760
|
}
|
|
761
|
+
normalizeOcrAnchors(rawText) {
|
|
762
|
+
const text = rawText.trim();
|
|
763
|
+
if (!text) return text;
|
|
764
|
+
const hasAiContext = /\b(ui\/ux|design|model|assistant|automation|research|testing|wireframing)\b/i.test(text);
|
|
765
|
+
if (!hasAiContext) return text;
|
|
766
|
+
const lines = text.split(/\r?\n/);
|
|
767
|
+
const normalizedLines = lines.map((line) => {
|
|
768
|
+
let next = line;
|
|
769
|
+
next = next.replace(/\bAl\b/g, "AI");
|
|
770
|
+
next = next.replace(/\bA1\b/g, "AI");
|
|
771
|
+
return next;
|
|
772
|
+
});
|
|
773
|
+
return normalizedLines.join("\n");
|
|
774
|
+
}
|
|
775
|
+
normalizeUiAnalysis(uiAnalysis) {
|
|
776
|
+
const normalizedAnchors = uiAnalysis.ocr_anchors.map((anchor) => this.normalizeOcrAnchors(anchor));
|
|
777
|
+
const sectionBackfill = this.buildSectionsFromAnchorsIfEmpty(uiAnalysis.sections, normalizedAnchors);
|
|
778
|
+
return {
|
|
779
|
+
...uiAnalysis,
|
|
780
|
+
ocr_anchors: normalizedAnchors,
|
|
781
|
+
sections: sectionBackfill
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
buildSectionsFromAnchorsIfEmpty(existing, anchors) {
|
|
785
|
+
if (existing.length > 0) return existing;
|
|
786
|
+
if (anchors.length < 4) return existing;
|
|
787
|
+
const titleLike = anchors.filter((a) => /[A-Z][a-z]+(?:\s+[A-Z][a-z]+){0,3}/.test(a));
|
|
788
|
+
const hintLike = anchors.filter((a) => /(testing|automation|qa|ai|service|platform|tools|visual|agent)/i.test(a));
|
|
789
|
+
if (titleLike.length < 2 || hintLike.length < 2) return existing;
|
|
790
|
+
const grouped = [];
|
|
791
|
+
let current;
|
|
792
|
+
for (const anchor of anchors) {
|
|
793
|
+
const isCandidateName = /^[A-Z][A-Za-z0-9&+.-]*(?:\s+[A-Z][A-Za-z0-9&+.-]*){0,3}$/.test(anchor) && !/(FOR SOFTWARE|LEADING|IN \d{4}|TESTING IN)/i.test(anchor);
|
|
794
|
+
if (isCandidateName) {
|
|
795
|
+
if (current) grouped.push(current);
|
|
796
|
+
current = { name: anchor, observations: [] };
|
|
797
|
+
} else if (current) {
|
|
798
|
+
current.observations.push(anchor);
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
if (current) grouped.push(current);
|
|
802
|
+
const filtered = grouped.filter((g) => g.name.length >= 3).slice(0, 10).map((g) => ({
|
|
803
|
+
name: g.name,
|
|
804
|
+
observations: g.observations.slice(0, 3),
|
|
805
|
+
confidence: 0.74
|
|
806
|
+
}));
|
|
807
|
+
return filtered.length > 0 ? filtered : existing;
|
|
808
|
+
}
|
|
726
809
|
};
|
|
727
810
|
|
|
728
811
|
// src/utils/logger.ts
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/server.ts","../src/config.ts","../src/schemas.ts","../src/errors.ts","../src/cache/memoryCache.ts","../src/providers/gemini.ts","../src/providers/ollama.ts","../src/providers/openrouter.ts","../src/services/visionService.ts","../src/image/loadImage.ts","../src/image/normalize.ts","../src/image/security.ts","../src/utils/timing.ts","../src/services/prompts.ts","../src/utils/logger.ts"],"sourcesContent":["import dotenv from \"dotenv\";\n\nimport { runServer } from \"./server\";\n\ndotenv.config();\n\nrunServer().catch((error) => {\n console.error(\"[vision-mcp] fatal error\", error);\n process.exit(1);\n});\r\n","import { z } from \"zod\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\n\nimport { loadConfig } from \"./config\";\nimport { VisionError, toVisionError } from \"./errors\";\nimport { MemoryCache } from \"./cache/memoryCache\";\nimport { GeminiProvider } from \"./providers/gemini\";\nimport { OllamaProvider } from \"./providers/ollama\";\nimport { OpenRouterProvider } from \"./providers/openrouter\";\nimport { VisionAnalyzeInputSchema, VisionAnalyzeResponseSchema } from \"./schemas\";\nimport { VisionService } from \"./services/visionService\";\nimport { Logger } from \"./utils/logger\";\n\nexport function createVisionServer() {\n const config = loadConfig();\n const logger = new Logger(config.logLevel);\n\n const providers: Record<string, { analyze: VisionService[\"analyze\"] } | any> = {};\n\n if (config.geminiApiKey) {\n providers.gemini = new GeminiProvider(config.geminiApiKey, config.geminiModel);\n }\n if (config.openrouterApiKey) {\n providers.openrouter = new OpenRouterProvider(config.openrouterApiKey, config.openrouterModel);\n }\n providers.ollama = new OllamaProvider(config.ollamaBaseUrl, config.ollamaModel);\n\n if (!providers.gemini && !providers.openrouter && !providers.ollama) {\n throw new VisionError(\"CONFIG_ERROR\", \"No provider configured.\", 500);\n }\n\n const service = new VisionService(\n config,\n logger,\n providers,\n [\"gemini\", \"openrouter\", \"ollama\"],\n new MemoryCache(config.cacheTtlSeconds),\n );\n\n const server = new McpServer({\n name: \"vision-mcp\",\n version: \"0.1.0\",\n });\n\n server.registerTool(\n \"vision_analyze\",\n {\n description: \"Analyze images and screenshots with Gemini-first vision pipeline.\",\n inputSchema: {\n imageSource: z.string(),\n prompt: z.string(),\n mode: z.enum([\"general\", \"palette\", \"hierarchy\", \"components\", \"ocr\", \"ui_analysis\", \"code_screenshot\"]).optional(),\n options: z\n .object({\n temperature: z.number().min(0).max(2).optional(),\n maxTokens: z.number().int().min(16).max(8192).optional(),\n outputFormat: z.enum([\"text\", \"json\"]).optional(),\n provider: z.enum([\"gemini\", \"openrouter\", \"ollama\"]).optional(),\n model: z.string().optional(),\n enableCache: z.boolean().optional(),\n timeoutMs: z.number().int().min(500).max(120000).optional(),\n })\n .optional(),\n },\n outputSchema: VisionAnalyzeResponseSchema.shape,\n },\n async (args) => {\n try {\n const input = VisionAnalyzeInputSchema.parse(args);\n const result = await service.analyze(input);\n\n return {\n content: [\n {\n type: \"text\",\n text: result.outputFormat === \"json\" ? JSON.stringify(result, null, 2) : result.analysis,\n },\n ],\n structuredContent: result,\n };\n } catch (error) {\n const vError = toVisionError(error);\n return {\n isError: true,\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n code: vError.code,\n message: vError.message,\n },\n null,\n 2,\n ),\n },\n ],\n };\n }\n },\n );\n\n return { server, config };\n}\n\nexport async function runServer(): Promise<void> {\n const { server, config } = createVisionServer();\n const transport = new StdioServerTransport();\n await server.connect(transport);\n console.error(`[vision-mcp] running on stdio with provider=${config.visionProvider}`);\n}\r\n","import { createHash } from \"node:crypto\";\nimport { z } from \"zod\";\n\nimport { ProviderNameSchema, type ProviderName } from \"./schemas\";\n\nconst ConfigSchema = z.object({\n visionProvider: ProviderNameSchema.default(\"gemini\"),\n geminiApiKey: z.string().optional(),\n geminiModel: z.string().default(\"gemini-2.5-flash\"),\n openrouterApiKey: z.string().optional(),\n openrouterModel: z.string().default(\"google/gemini-2.5-flash\"),\n ollamaBaseUrl: z.string().url().default(\"http://127.0.0.1:11434\"),\n ollamaModel: z.string().default(\"llava\"),\n maxImageMb: z.number().default(12),\n maxPixels: z.number().int().default(40000000),\n allowedLocalRoots: z.array(z.string()).default([]),\n enableUrlInput: z.boolean().default(true),\n enableLocalPathInput: z.boolean().default(false),\n cacheEnabled: z.boolean().default(true),\n cacheTtlSeconds: z.number().int().default(300),\n logLevel: z.enum([\"debug\", \"info\", \"warn\", \"error\"]).default(\"info\"),\n requestTimeoutMs: z.number().int().default(30000),\n});\n\nexport type AppConfig = z.infer<typeof ConfigSchema>;\n\nfunction parseBool(input: string | undefined, fallback: boolean): boolean {\n if (!input) return fallback;\n return [\"1\", \"true\", \"yes\", \"on\"].includes(input.toLowerCase());\n}\n\nfunction parseNumber(input: string | undefined, fallback: number): number {\n if (!input) return fallback;\n const value = Number(input);\n return Number.isFinite(value) ? value : fallback;\n}\n\nfunction parseRoots(input: string | undefined): string[] {\n if (!input) return [];\n return input.split(\",\").map((x) => x.trim()).filter(Boolean);\n}\n\nexport function loadConfig(env: NodeJS.ProcessEnv = process.env): AppConfig {\n const parsed = ConfigSchema.parse({\n visionProvider: (env.VISION_PROVIDER as ProviderName | undefined) ?? \"gemini\",\n geminiApiKey: env.GEMINI_API_KEY,\n geminiModel: env.GEMINI_MODEL ?? \"gemini-2.5-flash\",\n openrouterApiKey: env.OPENROUTER_API_KEY,\n openrouterModel: env.OPENROUTER_MODEL ?? \"google/gemini-2.5-flash\",\n ollamaBaseUrl: env.OLLAMA_BASE_URL ?? \"http://127.0.0.1:11434\",\n ollamaModel: env.OLLAMA_MODEL ?? \"llava\",\n maxImageMb: parseNumber(env.VISION_MAX_IMAGE_MB, 12),\n maxPixels: parseNumber(env.VISION_MAX_PIXELS, 40000000),\n allowedLocalRoots: parseRoots(env.VISION_ALLOWED_LOCAL_ROOTS),\n enableUrlInput: parseBool(env.VISION_ENABLE_URL_INPUT, true),\n enableLocalPathInput: parseBool(env.VISION_ENABLE_LOCAL_PATH_INPUT, false),\n cacheEnabled: parseBool(env.VISION_CACHE_ENABLED, true),\n cacheTtlSeconds: parseNumber(env.VISION_CACHE_TTL_SECONDS, 300),\n logLevel: (env.VISION_LOG_LEVEL as AppConfig[\"logLevel\"] | undefined) ?? \"info\",\n requestTimeoutMs: parseNumber(env.VISION_REQUEST_TIMEOUT_MS, 30000),\n });\n\n return parsed;\n}\n\nexport function maskSecret(secret: string | undefined): string {\n if (!secret) return \"<unset>\";\n return `${createHash(\"sha256\").update(secret).digest(\"hex\").slice(0, 8)}...`;\n}\r\n","import { z } from \"zod\";\n\nexport const OutputFormatSchema = z.enum([\"text\", \"json\"]);\nexport type OutputFormat = z.infer<typeof OutputFormatSchema>;\n\nexport const VisionModeSchema = z.enum([\n \"general\",\n \"palette\",\n \"hierarchy\",\n \"components\",\n \"ocr\",\n \"ui_analysis\",\n \"code_screenshot\",\n]);\nexport type VisionMode = z.infer<typeof VisionModeSchema>;\n\nexport const ProviderNameSchema = z.enum([\"gemini\", \"openrouter\", \"ollama\"]);\nexport type ProviderName = z.infer<typeof ProviderNameSchema>;\n\nexport const ToolOptionsSchema = z\n .object({\n temperature: z.number().min(0).max(2).optional(),\n maxTokens: z.number().int().min(16).max(8192).optional(),\n outputFormat: OutputFormatSchema.optional(),\n provider: ProviderNameSchema.optional(),\n model: z.string().min(1).max(200).optional(),\n enableCache: z.boolean().optional(),\n timeoutMs: z.number().int().min(500).max(120000).optional(),\n })\n .default({});\n\nexport const VisionAnalyzeInputSchema = z.object({\n imageSource: z.string().min(1),\n prompt: z.string().min(1).max(8000),\n mode: VisionModeSchema.default(\"general\"),\n options: ToolOptionsSchema.optional(),\n});\n\nexport type VisionAnalyzeInput = z.infer<typeof VisionAnalyzeInputSchema>;\n\nexport const VisionAnalyzeResponseSchema = z.object({\n provider: ProviderNameSchema,\n model: z.string(),\n mode: VisionModeSchema,\n outputFormat: OutputFormatSchema,\n analysis: z.string(),\n uiAnalysis: z\n .object({\n page_context: z.object({\n value: z.string(),\n confidence: z.number().min(0).max(1),\n }),\n sections: z.array(\n z.object({\n name: z.string(),\n observations: z.array(z.string()),\n confidence: z.number().min(0).max(1),\n }),\n ),\n controls: z.array(\n z.object({\n label: z.string(),\n type: z.string(),\n state: z.string().optional(),\n confidence: z.number().min(0).max(1),\n }),\n ),\n status_indicators: z.array(\n z.object({\n label: z.string(),\n value: z.string(),\n confidence: z.number().min(0).max(1),\n }),\n ),\n ocr_anchors: z.array(z.string()),\n uncertainties: z.array(z.string()),\n inferred_interpretations: z.array(z.string()),\n })\n .optional(),\n cached: z.boolean(),\n image: z.object({\n width: z.number().int(),\n height: z.number().int(),\n format: z.string(),\n mimeType: z.string(),\n hash: z.string(),\n bytes: z.number().int(),\n }),\n timingMs: z.number().int(),\n});\n\nexport type VisionAnalyzeResponse = z.infer<typeof VisionAnalyzeResponseSchema>;\r\n","export type ErrorCode =\n | \"CONFIG_ERROR\"\n | \"VALIDATION_ERROR\"\n | \"SECURITY_ERROR\"\n | \"INPUT_ERROR\"\n | \"TIMEOUT_ERROR\"\n | \"RATE_LIMITED\"\n | \"PROVIDER_ERROR\"\n | \"INTERNAL_ERROR\";\n\nexport class VisionError extends Error {\n readonly code: ErrorCode;\n readonly status: number;\n readonly details?: unknown;\n\n constructor(code: ErrorCode, message: string, status = 500, details?: unknown) {\n super(message);\n this.name = \"VisionError\";\n this.code = code;\n this.status = status;\n this.details = details;\n }\n}\n\nexport function toVisionError(error: unknown): VisionError {\n if (error instanceof VisionError) return error;\n if (error instanceof Error) {\n return new VisionError(\"INTERNAL_ERROR\", error.message, 500);\n }\n return new VisionError(\"INTERNAL_ERROR\", \"Unknown error\", 500, error);\n}\r\n","export class MemoryCache<T> {\n private readonly store = new Map<string, { expiresAt: number; value: T }>();\n\n constructor(private readonly ttlSeconds: number) {}\n\n get(key: string): T | undefined {\n const existing = this.store.get(key);\n if (!existing) return undefined;\n if (Date.now() > existing.expiresAt) {\n this.store.delete(key);\n return undefined;\n }\n return existing.value;\n }\n\n set(key: string, value: T): void {\n this.store.set(key, {\n value,\n expiresAt: Date.now() + this.ttlSeconds * 1000,\n });\n }\n}\r\n","import { GoogleGenAI } from \"@google/genai\";\n\nimport { VisionError } from \"../errors\";\nimport type { AnalyzeRequest } from \"../image/types\";\nimport type { VisionProvider, ProviderResult } from \"./base\";\n\nexport class GeminiProvider implements VisionProvider {\n readonly name = \"gemini\" as const;\n private readonly client: GoogleGenAI;\n\n constructor(private readonly apiKey: string, private readonly defaultModel: string) {\n this.client = new GoogleGenAI({ apiKey });\n }\n\n async analyze(input: AnalyzeRequest): Promise<ProviderResult> {\n const model = input.model ?? this.defaultModel;\n\n try {\n const response = await this.client.models.generateContent({\n model,\n contents: [\n {\n role: \"user\",\n parts: [\n { text: input.prompt },\n {\n inlineData: {\n mimeType: input.image.mimeType,\n data: input.image.data.toString(\"base64\"),\n },\n },\n ],\n },\n ],\n config: {\n maxOutputTokens: input.maxTokens,\n temperature: input.temperature,\n responseMimeType: input.outputFormat === \"json\" ? \"application/json\" : \"text/plain\",\n },\n });\n\n const text = response.text?.trim();\n if (!text) {\n throw new VisionError(\"PROVIDER_ERROR\", \"Gemini returned an empty response.\", 502);\n }\n\n return { text, model, provider: this.name };\n } catch (error) {\n if (error instanceof VisionError) throw error;\n const msg = error instanceof Error ? error.message : \"Gemini request failed\";\n if (/429|rate/i.test(msg)) {\n throw new VisionError(\"RATE_LIMITED\", `Gemini rate limited: ${msg}`, 429);\n }\n throw new VisionError(\"PROVIDER_ERROR\", `Gemini provider failed: ${msg}`, 502);\n }\n }\n}\r\n","import { VisionError } from \"../errors\";\nimport type { AnalyzeRequest } from \"../image/types\";\nimport type { VisionProvider, ProviderResult } from \"./base\";\n\nexport class OllamaProvider implements VisionProvider {\n readonly name = \"ollama\" as const;\n\n constructor(private readonly baseUrl: string, private readonly defaultModel: string) {}\n\n async analyze(input: AnalyzeRequest): Promise<ProviderResult> {\n const model = input.model ?? this.defaultModel;\n const response = await fetch(`${this.baseUrl}/api/generate`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n model,\n prompt: input.prompt,\n images: [input.image.data.toString(\"base64\")],\n stream: false,\n options: {\n temperature: input.temperature,\n num_predict: input.maxTokens,\n },\n }),\n });\n\n if (!response.ok) {\n throw new VisionError(\"PROVIDER_ERROR\", `Ollama failed: HTTP ${response.status}`, 502);\n }\n\n const json = (await response.json()) as { response?: string };\n const text = json.response?.trim();\n if (!text) {\n throw new VisionError(\"PROVIDER_ERROR\", \"Ollama returned empty response.\", 502);\n }\n\n return { text, model, provider: this.name };\n }\n}\r\n","import { VisionError } from \"../errors\";\nimport type { AnalyzeRequest } from \"../image/types\";\nimport type { VisionProvider, ProviderResult } from \"./base\";\n\nexport class OpenRouterProvider implements VisionProvider {\n readonly name = \"openrouter\" as const;\n\n constructor(private readonly apiKey: string, private readonly defaultModel: string) {}\n\n async analyze(input: AnalyzeRequest): Promise<ProviderResult> {\n const model = input.model ?? this.defaultModel;\n const response = await fetch(\"https://openrouter.ai/api/v1/chat/completions\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify({\n model,\n temperature: input.temperature,\n max_tokens: input.maxTokens,\n messages: [\n {\n role: \"user\",\n content: [\n { type: \"text\", text: input.prompt },\n {\n type: \"image_url\",\n image_url: {\n url: `data:${input.image.mimeType};base64,${input.image.data.toString(\"base64\")}`,\n },\n },\n ],\n },\n ],\n }),\n });\n\n if (!response.ok) {\n throw new VisionError(\"PROVIDER_ERROR\", `OpenRouter failed: HTTP ${response.status}`, 502);\n }\n\n const json = (await response.json()) as {\n choices?: Array<{ message?: { content?: string } }>;\n };\n\n const text = json.choices?.[0]?.message?.content?.trim();\n if (!text) {\n throw new VisionError(\"PROVIDER_ERROR\", \"OpenRouter returned empty content.\", 502);\n }\n\n return { text, model, provider: this.name };\n }\n}\r\n","import { createHash } from \"node:crypto\";\n\nimport type { AppConfig } from \"../config\";\nimport { VisionError } from \"../errors\";\nimport { loadImageFromSource } from \"../image/loadImage\";\nimport type { AnalyzeRequest } from \"../image/types\";\nimport { type VisionAnalyzeInput, VisionAnalyzeResponseSchema, type VisionAnalyzeResponse } from \"../schemas\";\nimport type { Logger } from \"../utils/logger\";\nimport { timed, withTimeout } from \"../utils/timing\";\nimport { MemoryCache } from \"../cache/memoryCache\";\nimport type { VisionProvider, ProviderResult } from \"../providers/base\";\nimport { buildPrompt, buildUiOcrPrompt, buildUiStructuredJsonPrompt } from \"./prompts\";\n\nexport class VisionService {\n constructor(\n private readonly config: AppConfig,\n private readonly logger: Logger,\n private readonly providers: Record<string, VisionProvider>,\n private readonly providerOrder: string[],\n private readonly cache: MemoryCache<VisionAnalyzeResponse>,\n ) {}\n\n private getCacheKey(input: {\n hash: string;\n prompt: string;\n mode: string;\n provider: string;\n model: string;\n outputFormat: string;\n }): string {\n return createHash(\"sha256\")\n .update(JSON.stringify(input))\n .digest(\"hex\");\n }\n\n private selectProvider(explicit?: string): string[] {\n if (explicit) return [explicit, ...this.providerOrder.filter((p) => p !== explicit)];\n return [...this.providerOrder];\n }\n\n async analyze(input: VisionAnalyzeInput): Promise<VisionAnalyzeResponse> {\n const options = input.options ?? {};\n const timeoutMs = options.timeoutMs ?? this.config.requestTimeoutMs;\n const image = await loadImageFromSource(input.imageSource, this.config, timeoutMs);\n const prompt = buildPrompt(input.mode, input.prompt, options.outputFormat ?? \"text\");\n\n const orderedProviders = this.selectProvider(options.provider ?? this.config.visionProvider);\n\n const cacheKey = this.getCacheKey({\n hash: image.hash,\n prompt,\n mode: input.mode,\n provider: orderedProviders[0],\n model: options.model ?? \"default\",\n outputFormat: options.outputFormat ?? \"text\",\n });\n\n const cacheEnabled = options.enableCache ?? this.config.cacheEnabled;\n if (cacheEnabled) {\n const cached = this.cache.get(cacheKey);\n if (cached) return { ...cached, cached: true };\n }\n\n const analyzeRequest: AnalyzeRequest = {\n image,\n prompt,\n mode: input.mode,\n outputFormat: options.outputFormat ?? \"text\",\n temperature: options.temperature,\n maxTokens: options.maxTokens,\n timeoutMs,\n model: options.model,\n provider: options.provider,\n };\n\n let lastError: Error | undefined;\n\n for (const providerName of orderedProviders) {\n const provider = this.providers[providerName];\n if (!provider) continue;\n\n try {\n const { value, durationMs } = await timed(async () => {\n if (input.mode !== \"ui_analysis\" || (options.outputFormat ?? \"text\") !== \"json\") {\n return withTimeout(provider.analyze(analyzeRequest), timeoutMs, () => new VisionError(\"TIMEOUT_ERROR\", `${providerName} timed out`, 504));\n }\n\n const ocrPass = await withTimeout(\n provider.analyze({\n ...analyzeRequest,\n mode: \"ocr\",\n outputFormat: \"text\",\n prompt: buildUiOcrPrompt(input.prompt),\n }),\n timeoutMs,\n () => new VisionError(\"TIMEOUT_ERROR\", `${providerName} OCR pass timed out`, 504),\n );\n\n return withTimeout(\n provider.analyze({\n ...analyzeRequest,\n outputFormat: \"json\",\n prompt: buildUiStructuredJsonPrompt(input.prompt, ocrPass.text),\n }),\n timeoutMs,\n () => new VisionError(\"TIMEOUT_ERROR\", `${providerName} UI pass timed out`, 504),\n );\n });\n\n const uiAnalysis = this.tryParseUiAnalysis(value, input.mode, options.outputFormat ?? \"text\");\n\n const result: VisionAnalyzeResponse = VisionAnalyzeResponseSchema.parse({\n provider: value.provider,\n model: value.model,\n mode: input.mode,\n outputFormat: options.outputFormat ?? \"text\",\n analysis: value.text,\n uiAnalysis,\n cached: false,\n image: {\n width: image.width,\n height: image.height,\n format: image.format,\n mimeType: image.mimeType,\n hash: image.hash,\n bytes: image.bytes,\n },\n timingMs: durationMs,\n });\n\n if (cacheEnabled) this.cache.set(cacheKey, result);\n return result;\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n this.logger.warn(`Provider failed: ${providerName}`, { error: lastError.message });\n }\n }\n\n throw new VisionError(\"PROVIDER_ERROR\", `All providers failed. Last error: ${lastError?.message ?? \"unknown\"}`, 502);\n }\n\n private tryParseUiAnalysis(value: ProviderResult, mode: VisionAnalyzeInput[\"mode\"], outputFormat: \"text\" | \"json\") {\n if (mode !== \"ui_analysis\" || outputFormat !== \"json\") return undefined;\n try {\n const cleaned = value.text.trim().replace(/^```json\\s*/i, \"\").replace(/^```\\s*/i, \"\").replace(/\\s*```$/, \"\");\n const parsed = JSON.parse(cleaned) as VisionAnalyzeResponse[\"uiAnalysis\"];\n return parsed;\n } catch {\n return undefined;\n }\n }\n}\n","import fs from \"node:fs/promises\";\n\nimport type { AppConfig } from \"../config\";\nimport { VisionError } from \"../errors\";\nimport { normalizeImage } from \"./normalize\";\nimport { validateLocalPath, validateRemoteUrl } from \"./security\";\nimport type { ImageAsset, ImageSourceKind } from \"./types\";\n\nfunction isDataUrl(input: string): boolean {\n return input.startsWith(\"data:image/\");\n}\n\nfunction isBase64(input: string): boolean {\n return /^[A-Za-z0-9+/=\\r\\n]+$/.test(input) && input.length > 128;\n}\n\nfunction parseDataUrl(input: string): Buffer {\n const match = /^data:(image\\/[\\w+.-]+);base64,(.+)$/i.exec(input);\n if (!match) {\n throw new VisionError(\"INPUT_ERROR\", \"Invalid image data URL.\", 400);\n }\n\n try {\n return Buffer.from(match[2], \"base64\");\n } catch {\n throw new VisionError(\"INPUT_ERROR\", \"Invalid base64 in data URL.\", 400);\n }\n}\n\nasync function loadFromUrl(url: string, timeoutMs: number): Promise<Buffer> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n const response = await fetch(url, { signal: controller.signal });\n if (!response.ok) {\n throw new VisionError(\"INPUT_ERROR\", `Failed to fetch image URL: HTTP ${response.status}`, 400);\n }\n const buf = Buffer.from(await response.arrayBuffer());\n return buf;\n } catch (error) {\n if (error instanceof VisionError) throw error;\n throw new VisionError(\"INPUT_ERROR\", \"Failed to fetch remote image.\", 400, error);\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport async function loadImageFromSource(imageSource: string, config: AppConfig, timeoutMs: number): Promise<ImageAsset> {\n let raw: Buffer;\n let sourceKind: ImageSourceKind;\n\n if (isDataUrl(imageSource)) {\n raw = parseDataUrl(imageSource);\n sourceKind = \"data-url\";\n } else if (/^https?:\\/\\//i.test(imageSource)) {\n if (!config.enableUrlInput) {\n throw new VisionError(\"SECURITY_ERROR\", \"URL input is disabled.\", 403);\n }\n\n const safe = await validateRemoteUrl(imageSource);\n raw = await loadFromUrl(safe.toString(), timeoutMs);\n sourceKind = \"url\";\n } else if (config.enableLocalPathInput) {\n const localPath = validateLocalPath(imageSource, config.allowedLocalRoots);\n raw = await fs.readFile(localPath);\n sourceKind = \"local\";\n } else if (isBase64(imageSource)) {\n try {\n raw = Buffer.from(imageSource, \"base64\");\n sourceKind = \"base64\";\n } catch {\n throw new VisionError(\"INPUT_ERROR\", \"Invalid base64 image input.\", 400);\n }\n } else {\n throw new VisionError(\"INPUT_ERROR\", \"Unsupported image source. Use URL, local path, base64, or data URL.\", 400);\n }\n\n if (!raw || raw.byteLength === 0) {\n throw new VisionError(\"INPUT_ERROR\", \"Empty image data.\", 400);\n }\n\n return normalizeImage(raw, sourceKind, config);\n}\r\n","import { createHash } from \"node:crypto\";\nimport sharp from \"sharp\";\n\nimport { VisionError } from \"../errors\";\nimport type { AppConfig } from \"../config\";\nimport type { ImageAsset, ImageSourceKind } from \"./types\";\n\nconst SUPPORTED_MIME = new Map<string, string>([\n [\"jpeg\", \"image/jpeg\"],\n [\"png\", \"image/png\"],\n [\"webp\", \"image/webp\"],\n [\"gif\", \"image/gif\"],\n]);\n\nexport async function normalizeImage(input: Buffer, sourceKind: ImageSourceKind, config: AppConfig): Promise<ImageAsset> {\n const maxBytes = Math.floor(config.maxImageMb * 1024 * 1024);\n if (input.byteLength > maxBytes) {\n throw new VisionError(\"INPUT_ERROR\", `Image is too large. Max is ${config.maxImageMb}MB.`, 400);\n }\n\n const metadata = await sharp(input, { failOn: \"error\" }).metadata();\n if (!metadata.width || !metadata.height || !metadata.format) {\n throw new VisionError(\"INPUT_ERROR\", \"Unsupported image or corrupted data.\", 400);\n }\n\n if (metadata.width * metadata.height > config.maxPixels) {\n throw new VisionError(\"INPUT_ERROR\", `Image pixel count exceeds limit of ${config.maxPixels}.`, 400);\n }\n\n let pipeline = sharp(input, { failOn: \"error\" }).rotate();\n const shouldResize = metadata.width > 2200 || metadata.height > 2200;\n if (shouldResize) {\n pipeline = pipeline.resize({ width: 2200, height: 2200, fit: \"inside\", withoutEnlargement: true });\n }\n\n const normalizedBuffer = await pipeline\n .webp({ quality: 85, effort: 4 })\n .toBuffer();\n\n const finalMeta = await sharp(normalizedBuffer).metadata();\n const format = finalMeta.format ?? \"webp\";\n const mimeType = SUPPORTED_MIME.get(format) ?? \"image/webp\";\n\n return {\n data: normalizedBuffer,\n format,\n mimeType,\n width: finalMeta.width ?? metadata.width,\n height: finalMeta.height ?? metadata.height,\n bytes: normalizedBuffer.byteLength,\n hash: createHash(\"sha256\").update(normalizedBuffer).digest(\"hex\"),\n sourceKind,\n };\n}\r\n","import dns from \"node:dns/promises\";\nimport net from \"node:net\";\nimport path from \"node:path\";\nimport { URL } from \"node:url\";\n\nimport { VisionError } from \"../errors\";\n\nconst BLOCKED_HOSTNAMES = new Set([\"localhost\", \"metadata.google.internal\"]);\n\nconst PRIVATE_CIDR_CHECKS = [\n /^10\\./,\n /^127\\./,\n /^169\\.254\\./,\n /^172\\.(1[6-9]|2\\d|3[0-1])\\./,\n /^192\\.168\\./,\n /^0\\./,\n];\n\nfunction isPrivateIpv4(ip: string): boolean {\n return PRIVATE_CIDR_CHECKS.some((r) => r.test(ip));\n}\n\nfunction isBlockedIpv6(ip: string): boolean {\n const normalized = ip.toLowerCase();\n return normalized === \"::1\" || normalized.startsWith(\"fc\") || normalized.startsWith(\"fd\") || normalized.startsWith(\"fe80:\");\n}\n\nexport async function validateRemoteUrl(rawUrl: string): Promise<URL> {\n let parsed: URL;\n try {\n parsed = new URL(rawUrl);\n } catch {\n throw new VisionError(\"SECURITY_ERROR\", \"Invalid URL.\", 400);\n }\n\n if (![\"http:\", \"https:\"].includes(parsed.protocol)) {\n throw new VisionError(\"SECURITY_ERROR\", \"Only HTTP/HTTPS URLs are allowed.\", 400);\n }\n\n if (BLOCKED_HOSTNAMES.has(parsed.hostname.toLowerCase())) {\n throw new VisionError(\"SECURITY_ERROR\", \"URL host is blocked.\", 403);\n }\n\n const host = parsed.hostname;\n if (net.isIP(host)) {\n if ((net.isIPv4(host) && isPrivateIpv4(host)) || (net.isIPv6(host) && isBlockedIpv6(host))) {\n throw new VisionError(\"SECURITY_ERROR\", \"Private or loopback IP is blocked.\", 403);\n }\n return parsed;\n }\n\n const resolved = await dns.lookup(host, { all: true });\n for (const addr of resolved) {\n if ((addr.family === 4 && isPrivateIpv4(addr.address)) || (addr.family === 6 && isBlockedIpv6(addr.address))) {\n throw new VisionError(\"SECURITY_ERROR\", \"Resolved IP is private/loopback and blocked.\", 403);\n }\n }\n\n return parsed;\n}\n\nexport function validateLocalPath(inputPath: string, allowedRoots: string[]): string {\n const resolved = path.resolve(inputPath);\n\n if (allowedRoots.length === 0) {\n throw new VisionError(\"SECURITY_ERROR\", \"Local file access disabled: allowlist is empty.\", 403);\n }\n\n const isAllowed = allowedRoots.some((root) => {\n const resolvedRoot = path.resolve(root);\n const relative = path.relative(resolvedRoot, resolved);\n return (relative === \"\" || (!relative.startsWith(\"..\") && !path.isAbsolute(relative)));\n });\n\n if (!isAllowed) {\n throw new VisionError(\"SECURITY_ERROR\", \"Local path is outside allowed roots.\", 403);\n }\n\n return resolved;\n}\r\n","export async function withTimeout<T>(promise: Promise<T>, timeoutMs: number, onTimeout: () => Error): Promise<T> {\n let timer: NodeJS.Timeout | undefined;\n try {\n const timeoutPromise = new Promise<T>((_, reject) => {\n timer = setTimeout(() => reject(onTimeout()), timeoutMs);\n });\n\n return await Promise.race([promise, timeoutPromise]);\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n\nexport async function timed<T>(fn: () => Promise<T>): Promise<{ value: T; durationMs: number }> {\n const start = Date.now();\n const value = await fn();\n return { value, durationMs: Date.now() - start };\n}\r\n","import type { VisionMode } from \"../schemas\";\n\nconst MODE_GUIDANCE: Record<VisionMode, string> = {\n general: \"Provide a clear and accurate description with key details and context.\",\n palette: \"Extract visual design tokens: colors, spacing, typography, shadows, radius, and style patterns.\",\n hierarchy: \"Analyze hierarchy, attention flow, focus areas, layout clarity, and readability.\",\n components: \"Identify UI components, consistency, reuse quality, interaction patterns, and design-system maturity.\",\n ocr: \"Extract all visible text accurately. Preserve ordering and structure where possible.\",\n ui_analysis: \"Analyze the software UI: goals, flows, usability, information architecture, and issues.\",\n code_screenshot: \"Analyze code/terminal screenshot: summarize code intent, errors, stack traces, and likely fixes.\",\n};\n\nexport function buildPrompt(mode: VisionMode, userPrompt: string, outputFormat: \"text\" | \"json\"): string {\n const uiFactPolicy =\n mode === \"ui_analysis\"\n ? \"Strict policy: separate observed facts from inferred interpretations. If uncertain, explicitly list uncertainty instead of asserting.\"\n : \"\";\n\n const format =\n outputFormat === \"json\"\n ? \"Return strict JSON with stable keys and no markdown.\"\n : \"Return concise plain text with headings when useful.\";\n\n return [\n \"You are an expert vision analyst for images and screenshots.\",\n `Mode: ${mode}. ${MODE_GUIDANCE[mode]}`,\n uiFactPolicy,\n format,\n `User request: ${userPrompt}`,\n ].join(\"\\n\\n\");\n}\n\nexport function buildUiOcrPrompt(userPrompt: string): string {\n return [\n \"You are extracting OCR anchors from a software screenshot.\",\n \"Return only visible text anchors and short structural labels.\",\n \"No assumptions. Include exact labels, button text, section titles, and model IDs if visible.\",\n \"Output plain text lines only.\",\n `User request context: ${userPrompt}`,\n ].join(\"\\n\\n\");\n}\n\nexport function buildUiStructuredJsonPrompt(userPrompt: string, ocrAnchors: string): string {\n return [\n \"You are an expert UI screenshot analyst.\",\n \"Strict policy: separate observed facts from inferred interpretations. If uncertain, include the item under uncertainties.\",\n \"Use OCR anchors as grounding evidence and do not invent labels not supported by image evidence.\",\n \"Return strict JSON only with this exact shape:\",\n JSON.stringify(\n {\n page_context: { value: \"string\", confidence: 0.0 },\n sections: [{ name: \"string\", observations: [\"string\"], confidence: 0.0 }],\n controls: [{ label: \"string\", type: \"string\", state: \"optional\", confidence: 0.0 }],\n status_indicators: [{ label: \"string\", value: \"string\", confidence: 0.0 }],\n ocr_anchors: [\"string\"],\n uncertainties: [\"string\"],\n inferred_interpretations: [\"string\"],\n },\n null,\n 2,\n ),\n `OCR anchors:\\n${ocrAnchors || \"<none>\"}`,\n `User request: ${userPrompt}`,\n ].join(\"\\n\\n\");\n}\n","type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\nconst ORDER: Record<LogLevel, number> = {\n debug: 10,\n info: 20,\n warn: 30,\n error: 40,\n};\n\nexport class Logger {\n constructor(private readonly level: LogLevel) {}\n\n private shouldLog(level: LogLevel): boolean {\n return ORDER[level] >= ORDER[this.level];\n }\n\n debug(message: string, context?: unknown): void {\n if (this.shouldLog(\"debug\")) console.debug(`[vision][debug] ${message}`, context ?? \"\");\n }\n\n info(message: string, context?: unknown): void {\n if (this.shouldLog(\"info\")) console.info(`[vision][info] ${message}`, context ?? \"\");\n }\n\n warn(message: string, context?: unknown): void {\n if (this.shouldLog(\"warn\")) console.warn(`[vision][warn] ${message}`, context ?? \"\");\n }\n\n error(message: string, context?: unknown): void {\n if (this.shouldLog(\"error\")) console.error(`[vision][error] ${message}`, context ?? \"\");\n }\n}\r\n"],"mappings":"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","names":["import_zod","import_zod","import_node_crypto","import_promises","import_node_crypto","sharp","net","dns","path","fs","dotenv"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/server.ts","../src/config.ts","../src/schemas.ts","../src/errors.ts","../src/cache/memoryCache.ts","../src/providers/gemini.ts","../src/providers/ollama.ts","../src/providers/openrouter.ts","../src/services/visionService.ts","../src/image/loadImage.ts","../src/image/normalize.ts","../src/image/security.ts","../src/utils/timing.ts","../src/services/prompts.ts","../src/utils/logger.ts"],"sourcesContent":["import dotenv from \"dotenv\";\n\nimport { runServer } from \"./server\";\n\ndotenv.config();\n\nrunServer().catch((error) => {\n console.error(\"[vision-mcp] fatal error\", error);\n process.exit(1);\n});\r\n","import { z } from \"zod\";\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\n\nimport { loadConfig } from \"./config\";\nimport { VisionError, toVisionError } from \"./errors\";\nimport { MemoryCache } from \"./cache/memoryCache\";\nimport { GeminiProvider } from \"./providers/gemini\";\nimport { OllamaProvider } from \"./providers/ollama\";\nimport { OpenRouterProvider } from \"./providers/openrouter\";\nimport { VisionAnalyzeInputSchema, VisionAnalyzeResponseSchema } from \"./schemas\";\nimport { VisionService } from \"./services/visionService\";\nimport { Logger } from \"./utils/logger\";\n\nexport function createVisionServer() {\n const config = loadConfig();\n const logger = new Logger(config.logLevel);\n\n const providers: Record<string, { analyze: VisionService[\"analyze\"] } | any> = {};\n\n if (config.geminiApiKey) {\n providers.gemini = new GeminiProvider(config.geminiApiKey, config.geminiModel);\n }\n if (config.openrouterApiKey) {\n providers.openrouter = new OpenRouterProvider(config.openrouterApiKey, config.openrouterModel);\n }\n providers.ollama = new OllamaProvider(config.ollamaBaseUrl, config.ollamaModel);\n\n if (!providers.gemini && !providers.openrouter && !providers.ollama) {\n throw new VisionError(\"CONFIG_ERROR\", \"No provider configured.\", 500);\n }\n\n const service = new VisionService(\n config,\n logger,\n providers,\n [\"gemini\", \"openrouter\", \"ollama\"],\n new MemoryCache(config.cacheTtlSeconds),\n );\n\n const server = new McpServer({\n name: \"vision-mcp\",\n version: \"0.1.0\",\n });\n\n server.registerTool(\n \"vision_analyze\",\n {\n description: \"Analyze images and screenshots with Gemini-first vision pipeline.\",\n inputSchema: {\n imageSource: z.string(),\n prompt: z.string(),\n mode: z.enum([\"general\", \"palette\", \"hierarchy\", \"components\", \"ocr\", \"ui_analysis\", \"code_screenshot\"]).optional(),\n options: z\n .object({\n temperature: z.number().min(0).max(2).optional(),\n maxTokens: z.number().int().min(16).max(8192).optional(),\n outputFormat: z.enum([\"text\", \"json\"]).optional(),\n provider: z.enum([\"gemini\", \"openrouter\", \"ollama\"]).optional(),\n model: z.string().optional(),\n enableCache: z.boolean().optional(),\n timeoutMs: z.number().int().min(500).max(120000).optional(),\n })\n .optional(),\n },\n outputSchema: VisionAnalyzeResponseSchema.shape,\n },\n async (args) => {\n try {\n const input = VisionAnalyzeInputSchema.parse(args);\n const result = await service.analyze(input);\n\n return {\n content: [\n {\n type: \"text\",\n text: result.outputFormat === \"json\" ? JSON.stringify(result, null, 2) : result.analysis,\n },\n ],\n structuredContent: result,\n };\n } catch (error) {\n const vError = toVisionError(error);\n return {\n isError: true,\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n code: vError.code,\n message: vError.message,\n },\n null,\n 2,\n ),\n },\n ],\n };\n }\n },\n );\n\n return { server, config };\n}\n\nexport async function runServer(): Promise<void> {\n const { server, config } = createVisionServer();\n const transport = new StdioServerTransport();\n await server.connect(transport);\n console.error(`[vision-mcp] running on stdio with provider=${config.visionProvider}`);\n}\r\n","import { createHash } from \"node:crypto\";\nimport { z } from \"zod\";\n\nimport { ProviderNameSchema, type ProviderName } from \"./schemas\";\n\nconst ConfigSchema = z.object({\n visionProvider: ProviderNameSchema.default(\"gemini\"),\n geminiApiKey: z.string().optional(),\n geminiModel: z.string().default(\"gemini-2.5-flash\"),\n openrouterApiKey: z.string().optional(),\n openrouterModel: z.string().default(\"google/gemini-2.5-flash\"),\n ollamaBaseUrl: z.string().url().default(\"http://127.0.0.1:11434\"),\n ollamaModel: z.string().default(\"llava\"),\n maxImageMb: z.number().default(12),\n maxPixels: z.number().int().default(40000000),\n allowedLocalRoots: z.array(z.string()).default([]),\n enableUrlInput: z.boolean().default(true),\n enableLocalPathInput: z.boolean().default(false),\n cacheEnabled: z.boolean().default(true),\n cacheTtlSeconds: z.number().int().default(300),\n logLevel: z.enum([\"debug\", \"info\", \"warn\", \"error\"]).default(\"info\"),\n requestTimeoutMs: z.number().int().default(30000),\n});\n\nexport type AppConfig = z.infer<typeof ConfigSchema>;\n\nfunction parseBool(input: string | undefined, fallback: boolean): boolean {\n if (!input) return fallback;\n return [\"1\", \"true\", \"yes\", \"on\"].includes(input.toLowerCase());\n}\n\nfunction parseNumber(input: string | undefined, fallback: number): number {\n if (!input) return fallback;\n const value = Number(input);\n return Number.isFinite(value) ? value : fallback;\n}\n\nfunction parseRoots(input: string | undefined): string[] {\n if (!input) return [];\n return input\n .split(\",\")\n .map((x) => x.trim().replace(/^['\"]|['\"]$/g, \"\"))\n .filter(Boolean);\n}\n\nexport function loadConfig(env: NodeJS.ProcessEnv = process.env): AppConfig {\n const parsed = ConfigSchema.parse({\n visionProvider: (env.VISION_PROVIDER as ProviderName | undefined) ?? \"gemini\",\n geminiApiKey: env.GEMINI_API_KEY,\n geminiModel: env.GEMINI_MODEL ?? \"gemini-2.5-flash\",\n openrouterApiKey: env.OPENROUTER_API_KEY,\n openrouterModel: env.OPENROUTER_MODEL ?? \"google/gemini-2.5-flash\",\n ollamaBaseUrl: env.OLLAMA_BASE_URL ?? \"http://127.0.0.1:11434\",\n ollamaModel: env.OLLAMA_MODEL ?? \"llava\",\n maxImageMb: parseNumber(env.VISION_MAX_IMAGE_MB, 12),\n maxPixels: parseNumber(env.VISION_MAX_PIXELS, 40000000),\n allowedLocalRoots: parseRoots(env.VISION_ALLOWED_LOCAL_ROOTS),\n enableUrlInput: parseBool(env.VISION_ENABLE_URL_INPUT, true),\n enableLocalPathInput: parseBool(env.VISION_ENABLE_LOCAL_PATH_INPUT, false),\n cacheEnabled: parseBool(env.VISION_CACHE_ENABLED, true),\n cacheTtlSeconds: parseNumber(env.VISION_CACHE_TTL_SECONDS, 300),\n logLevel: (env.VISION_LOG_LEVEL as AppConfig[\"logLevel\"] | undefined) ?? \"info\",\n requestTimeoutMs: parseNumber(env.VISION_REQUEST_TIMEOUT_MS, 30000),\n });\n\n return parsed;\n}\n\nexport function maskSecret(secret: string | undefined): string {\n if (!secret) return \"<unset>\";\n return `${createHash(\"sha256\").update(secret).digest(\"hex\").slice(0, 8)}...`;\n}\r\n","import { z } from \"zod\";\n\nexport const OutputFormatSchema = z.enum([\"text\", \"json\"]);\nexport type OutputFormat = z.infer<typeof OutputFormatSchema>;\n\nexport const VisionModeSchema = z.enum([\n \"general\",\n \"palette\",\n \"hierarchy\",\n \"components\",\n \"ocr\",\n \"ui_analysis\",\n \"code_screenshot\",\n]);\nexport type VisionMode = z.infer<typeof VisionModeSchema>;\n\nexport const ProviderNameSchema = z.enum([\"gemini\", \"openrouter\", \"ollama\"]);\nexport type ProviderName = z.infer<typeof ProviderNameSchema>;\n\nexport const ToolOptionsSchema = z\n .object({\n temperature: z.number().min(0).max(2).optional(),\n maxTokens: z.number().int().min(16).max(8192).optional(),\n outputFormat: OutputFormatSchema.optional(),\n provider: ProviderNameSchema.optional(),\n model: z.string().min(1).max(200).optional(),\n enableCache: z.boolean().optional(),\n timeoutMs: z.number().int().min(500).max(120000).optional(),\n })\n .default({});\n\nexport const VisionAnalyzeInputSchema = z.object({\n imageSource: z.string().min(1),\n prompt: z.string().min(1).max(8000),\n mode: VisionModeSchema.default(\"general\"),\n options: ToolOptionsSchema.optional(),\n});\n\nexport type VisionAnalyzeInput = z.infer<typeof VisionAnalyzeInputSchema>;\n\nexport const VisionAnalyzeResponseSchema = z.object({\n provider: ProviderNameSchema,\n model: z.string(),\n mode: VisionModeSchema,\n outputFormat: OutputFormatSchema,\n analysis: z.string(),\n uiAnalysis: z\n .object({\n page_context: z.object({\n value: z.string(),\n confidence: z.number().min(0).max(1),\n }),\n sections: z.array(\n z.object({\n name: z.string(),\n observations: z.array(z.string()),\n confidence: z.number().min(0).max(1),\n }),\n ),\n controls: z.array(\n z.object({\n label: z.string(),\n type: z.string(),\n state: z.string().optional(),\n confidence: z.number().min(0).max(1),\n }),\n ),\n status_indicators: z.array(\n z.object({\n label: z.string(),\n value: z.string(),\n confidence: z.number().min(0).max(1),\n }),\n ),\n ocr_anchors: z.array(z.string()),\n uncertainties: z.array(z.string()),\n inferred_interpretations: z.array(z.string()),\n })\n .optional(),\n cached: z.boolean(),\n image: z.object({\n width: z.number().int(),\n height: z.number().int(),\n format: z.string(),\n mimeType: z.string(),\n hash: z.string(),\n bytes: z.number().int(),\n }),\n timingMs: z.number().int(),\n});\n\nexport type VisionAnalyzeResponse = z.infer<typeof VisionAnalyzeResponseSchema>;\r\n","export type ErrorCode =\n | \"CONFIG_ERROR\"\n | \"VALIDATION_ERROR\"\n | \"SECURITY_ERROR\"\n | \"INPUT_ERROR\"\n | \"TIMEOUT_ERROR\"\n | \"RATE_LIMITED\"\n | \"PROVIDER_ERROR\"\n | \"INTERNAL_ERROR\";\n\nexport class VisionError extends Error {\n readonly code: ErrorCode;\n readonly status: number;\n readonly details?: unknown;\n\n constructor(code: ErrorCode, message: string, status = 500, details?: unknown) {\n super(message);\n this.name = \"VisionError\";\n this.code = code;\n this.status = status;\n this.details = details;\n }\n}\n\nexport function toVisionError(error: unknown): VisionError {\n if (error instanceof VisionError) return error;\n if (error instanceof Error) {\n return new VisionError(\"INTERNAL_ERROR\", error.message, 500);\n }\n return new VisionError(\"INTERNAL_ERROR\", \"Unknown error\", 500, error);\n}\r\n","export class MemoryCache<T> {\n private readonly store = new Map<string, { expiresAt: number; value: T }>();\n\n constructor(private readonly ttlSeconds: number) {}\n\n get(key: string): T | undefined {\n const existing = this.store.get(key);\n if (!existing) return undefined;\n if (Date.now() > existing.expiresAt) {\n this.store.delete(key);\n return undefined;\n }\n return existing.value;\n }\n\n set(key: string, value: T): void {\n this.store.set(key, {\n value,\n expiresAt: Date.now() + this.ttlSeconds * 1000,\n });\n }\n}\r\n","import { GoogleGenAI } from \"@google/genai\";\n\nimport { VisionError } from \"../errors\";\nimport type { AnalyzeRequest } from \"../image/types\";\nimport type { VisionProvider, ProviderResult } from \"./base\";\n\nexport class GeminiProvider implements VisionProvider {\n readonly name = \"gemini\" as const;\n private readonly client: GoogleGenAI;\n\n constructor(private readonly apiKey: string, private readonly defaultModel: string) {\n this.client = new GoogleGenAI({ apiKey });\n }\n\n async analyze(input: AnalyzeRequest): Promise<ProviderResult> {\n const model = input.model ?? this.defaultModel;\n\n try {\n const response = await this.client.models.generateContent({\n model,\n contents: [\n {\n role: \"user\",\n parts: [\n { text: input.prompt },\n {\n inlineData: {\n mimeType: input.image.mimeType,\n data: input.image.data.toString(\"base64\"),\n },\n },\n ],\n },\n ],\n config: {\n maxOutputTokens: input.maxTokens,\n temperature: input.temperature,\n responseMimeType: input.outputFormat === \"json\" ? \"application/json\" : \"text/plain\",\n },\n });\n\n const text = response.text?.trim();\n if (!text) {\n throw new VisionError(\"PROVIDER_ERROR\", \"Gemini returned an empty response.\", 502);\n }\n\n return { text, model, provider: this.name };\n } catch (error) {\n if (error instanceof VisionError) throw error;\n const msg = error instanceof Error ? error.message : \"Gemini request failed\";\n if (/429|rate/i.test(msg)) {\n throw new VisionError(\"RATE_LIMITED\", `Gemini rate limited: ${msg}`, 429);\n }\n throw new VisionError(\"PROVIDER_ERROR\", `Gemini provider failed: ${msg}`, 502);\n }\n }\n}\r\n","import { VisionError } from \"../errors\";\nimport type { AnalyzeRequest } from \"../image/types\";\nimport type { VisionProvider, ProviderResult } from \"./base\";\n\nexport class OllamaProvider implements VisionProvider {\n readonly name = \"ollama\" as const;\n\n constructor(private readonly baseUrl: string, private readonly defaultModel: string) {}\n\n async analyze(input: AnalyzeRequest): Promise<ProviderResult> {\n const model = input.model ?? this.defaultModel;\n const response = await fetch(`${this.baseUrl}/api/generate`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n model,\n prompt: input.prompt,\n images: [input.image.data.toString(\"base64\")],\n stream: false,\n options: {\n temperature: input.temperature,\n num_predict: input.maxTokens,\n },\n }),\n });\n\n if (!response.ok) {\n throw new VisionError(\"PROVIDER_ERROR\", `Ollama failed: HTTP ${response.status}`, 502);\n }\n\n const json = (await response.json()) as { response?: string };\n const text = json.response?.trim();\n if (!text) {\n throw new VisionError(\"PROVIDER_ERROR\", \"Ollama returned empty response.\", 502);\n }\n\n return { text, model, provider: this.name };\n }\n}\r\n","import { VisionError } from \"../errors\";\nimport type { AnalyzeRequest } from \"../image/types\";\nimport type { VisionProvider, ProviderResult } from \"./base\";\n\nexport class OpenRouterProvider implements VisionProvider {\n readonly name = \"openrouter\" as const;\n\n constructor(private readonly apiKey: string, private readonly defaultModel: string) {}\n\n async analyze(input: AnalyzeRequest): Promise<ProviderResult> {\n const model = input.model ?? this.defaultModel;\n const response = await fetch(\"https://openrouter.ai/api/v1/chat/completions\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify({\n model,\n temperature: input.temperature,\n max_tokens: input.maxTokens,\n messages: [\n {\n role: \"user\",\n content: [\n { type: \"text\", text: input.prompt },\n {\n type: \"image_url\",\n image_url: {\n url: `data:${input.image.mimeType};base64,${input.image.data.toString(\"base64\")}`,\n },\n },\n ],\n },\n ],\n }),\n });\n\n if (!response.ok) {\n throw new VisionError(\"PROVIDER_ERROR\", `OpenRouter failed: HTTP ${response.status}`, 502);\n }\n\n const json = (await response.json()) as {\n choices?: Array<{ message?: { content?: string } }>;\n };\n\n const text = json.choices?.[0]?.message?.content?.trim();\n if (!text) {\n throw new VisionError(\"PROVIDER_ERROR\", \"OpenRouter returned empty content.\", 502);\n }\n\n return { text, model, provider: this.name };\n }\n}\r\n","import { createHash } from \"node:crypto\";\n\nimport type { AppConfig } from \"../config\";\nimport { VisionError } from \"../errors\";\nimport { loadImageFromSource } from \"../image/loadImage\";\nimport type { AnalyzeRequest } from \"../image/types\";\nimport { type VisionAnalyzeInput, VisionAnalyzeResponseSchema, type VisionAnalyzeResponse } from \"../schemas\";\nimport type { Logger } from \"../utils/logger\";\nimport { timed, withTimeout } from \"../utils/timing\";\nimport { MemoryCache } from \"../cache/memoryCache\";\nimport type { VisionProvider, ProviderResult } from \"../providers/base\";\nimport { buildPrompt, buildUiOcrPrompt, buildUiStructuredJsonPrompt } from \"./prompts\";\n\nexport class VisionService {\n constructor(\n private readonly config: AppConfig,\n private readonly logger: Logger,\n private readonly providers: Record<string, VisionProvider>,\n private readonly providerOrder: string[],\n private readonly cache: MemoryCache<VisionAnalyzeResponse>,\n ) {}\n\n private getCacheKey(input: {\n hash: string;\n prompt: string;\n mode: string;\n provider: string;\n model: string;\n outputFormat: string;\n }): string {\n return createHash(\"sha256\")\n .update(JSON.stringify(input))\n .digest(\"hex\");\n }\n\n private selectProvider(explicit?: string): string[] {\n if (explicit) return [explicit, ...this.providerOrder.filter((p) => p !== explicit)];\n return [...this.providerOrder];\n }\n\n async analyze(input: VisionAnalyzeInput): Promise<VisionAnalyzeResponse> {\n const options = input.options ?? {};\n const timeoutMs = options.timeoutMs ?? this.config.requestTimeoutMs;\n const image = await loadImageFromSource(input.imageSource, this.config, timeoutMs);\n const prompt = buildPrompt(input.mode, input.prompt, options.outputFormat ?? \"text\");\n\n const orderedProviders = this.selectProvider(options.provider ?? this.config.visionProvider);\n\n const cacheKey = this.getCacheKey({\n hash: image.hash,\n prompt,\n mode: input.mode,\n provider: orderedProviders[0],\n model: options.model ?? \"default\",\n outputFormat: options.outputFormat ?? \"text\",\n });\n\n const cacheEnabled = options.enableCache ?? this.config.cacheEnabled;\n if (cacheEnabled) {\n const cached = this.cache.get(cacheKey);\n if (cached) return { ...cached, cached: true };\n }\n\n const analyzeRequest: AnalyzeRequest = {\n image,\n prompt,\n mode: input.mode,\n outputFormat: options.outputFormat ?? \"text\",\n temperature: options.temperature,\n maxTokens: options.maxTokens,\n timeoutMs,\n model: options.model,\n provider: options.provider,\n };\n\n let lastError: Error | undefined;\n\n for (const providerName of orderedProviders) {\n const provider = this.providers[providerName];\n if (!provider) continue;\n\n try {\n const { value, durationMs } = await timed(async () => {\n if (input.mode !== \"ui_analysis\" || (options.outputFormat ?? \"text\") !== \"json\") {\n return withTimeout(provider.analyze(analyzeRequest), timeoutMs, () => new VisionError(\"TIMEOUT_ERROR\", `${providerName} timed out`, 504));\n }\n\n const ocrPass = await withTimeout(\n provider.analyze({\n ...analyzeRequest,\n mode: \"ocr\",\n outputFormat: \"text\",\n prompt: buildUiOcrPrompt(input.prompt),\n }),\n timeoutMs,\n () => new VisionError(\"TIMEOUT_ERROR\", `${providerName} OCR pass timed out`, 504),\n );\n const normalizedOcrText = this.normalizeOcrAnchors(ocrPass.text);\n\n return withTimeout(\n provider.analyze({\n ...analyzeRequest,\n outputFormat: \"json\",\n prompt: buildUiStructuredJsonPrompt(input.prompt, normalizedOcrText),\n }),\n timeoutMs,\n () => new VisionError(\"TIMEOUT_ERROR\", `${providerName} UI pass timed out`, 504),\n );\n });\n\n const outputFormat = options.outputFormat ?? \"text\";\n const uiAnalysis = this.tryParseUiAnalysis(value, input.mode, outputFormat);\n const normalizedUiAnalysis = uiAnalysis ? this.normalizeUiAnalysis(uiAnalysis) : undefined;\n const analysisText =\n input.mode === \"ui_analysis\" && outputFormat === \"json\" && normalizedUiAnalysis\n ? JSON.stringify(normalizedUiAnalysis, null, 2)\n : value.text;\n\n const result: VisionAnalyzeResponse = VisionAnalyzeResponseSchema.parse({\n provider: value.provider,\n model: value.model,\n mode: input.mode,\n outputFormat,\n analysis: analysisText,\n uiAnalysis: normalizedUiAnalysis,\n cached: false,\n image: {\n width: image.width,\n height: image.height,\n format: image.format,\n mimeType: image.mimeType,\n hash: image.hash,\n bytes: image.bytes,\n },\n timingMs: durationMs,\n });\n\n if (cacheEnabled) this.cache.set(cacheKey, result);\n return result;\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n this.logger.warn(`Provider failed: ${providerName}`, { error: lastError.message });\n }\n }\n\n throw new VisionError(\"PROVIDER_ERROR\", `All providers failed. Last error: ${lastError?.message ?? \"unknown\"}`, 502);\n }\n\n private tryParseUiAnalysis(value: ProviderResult, mode: VisionAnalyzeInput[\"mode\"], outputFormat: \"text\" | \"json\") {\n if (mode !== \"ui_analysis\" || outputFormat !== \"json\") return undefined;\n try {\n const cleaned = value.text.trim().replace(/^```json\\s*/i, \"\").replace(/^```\\s*/i, \"\").replace(/\\s*```$/, \"\");\n const parsed = JSON.parse(cleaned) as VisionAnalyzeResponse[\"uiAnalysis\"];\n return parsed;\n } catch {\n return undefined;\n }\n }\n\n private normalizeOcrAnchors(rawText: string): string {\n const text = rawText.trim();\n if (!text) return text;\n\n const hasAiContext = /\\b(ui\\/ux|design|model|assistant|automation|research|testing|wireframing)\\b/i.test(text);\n if (!hasAiContext) return text;\n\n const lines = text.split(/\\r?\\n/);\n const normalizedLines = lines.map((line) => {\n let next = line;\n next = next.replace(/\\bAl\\b/g, \"AI\");\n next = next.replace(/\\bA1\\b/g, \"AI\");\n return next;\n });\n\n return normalizedLines.join(\"\\n\");\n }\n\n private normalizeUiAnalysis(uiAnalysis: NonNullable<VisionAnalyzeResponse[\"uiAnalysis\"]>): NonNullable<VisionAnalyzeResponse[\"uiAnalysis\"]> {\n const normalizedAnchors = uiAnalysis.ocr_anchors.map((anchor) => this.normalizeOcrAnchors(anchor));\n const sectionBackfill = this.buildSectionsFromAnchorsIfEmpty(uiAnalysis.sections, normalizedAnchors);\n return {\n ...uiAnalysis,\n ocr_anchors: normalizedAnchors,\n sections: sectionBackfill,\n };\n }\n\n private buildSectionsFromAnchorsIfEmpty(\n existing: NonNullable<VisionAnalyzeResponse[\"uiAnalysis\"]>[\"sections\"],\n anchors: string[],\n ): NonNullable<VisionAnalyzeResponse[\"uiAnalysis\"]>[\"sections\"] {\n if (existing.length > 0) return existing;\n if (anchors.length < 4) return existing;\n\n const titleLike = anchors.filter((a) => /[A-Z][a-z]+(?:\\s+[A-Z][a-z]+){0,3}/.test(a));\n const hintLike = anchors.filter((a) => /(testing|automation|qa|ai|service|platform|tools|visual|agent)/i.test(a));\n if (titleLike.length < 2 || hintLike.length < 2) return existing;\n\n const grouped: Array<{ name: string; observations: string[] }> = [];\n let current: { name: string; observations: string[] } | undefined;\n\n for (const anchor of anchors) {\n const isCandidateName =\n /^[A-Z][A-Za-z0-9&+.-]*(?:\\s+[A-Z][A-Za-z0-9&+.-]*){0,3}$/.test(anchor) &&\n !/(FOR SOFTWARE|LEADING|IN \\d{4}|TESTING IN)/i.test(anchor);\n\n if (isCandidateName) {\n if (current) grouped.push(current);\n current = { name: anchor, observations: [] };\n } else if (current) {\n current.observations.push(anchor);\n }\n }\n if (current) grouped.push(current);\n\n const filtered = grouped\n .filter((g) => g.name.length >= 3)\n .slice(0, 10)\n .map((g) => ({\n name: g.name,\n observations: g.observations.slice(0, 3),\n confidence: 0.74,\n }));\n\n return filtered.length > 0 ? filtered : existing;\n }\n}\n","import fs from \"node:fs/promises\";\n\nimport type { AppConfig } from \"../config\";\nimport { VisionError } from \"../errors\";\nimport { normalizeImage } from \"./normalize\";\nimport { validateLocalPath, validateRemoteUrl } from \"./security\";\nimport type { ImageAsset, ImageSourceKind } from \"./types\";\n\nfunction isDataUrl(input: string): boolean {\n return input.startsWith(\"data:image/\");\n}\n\nfunction isBase64(input: string): boolean {\n const cleaned = input.replace(/\\s/g, \"\");\n if (cleaned.length < 32 || cleaned.length % 4 !== 0 || !/^[A-Za-z0-9+/]+={0,2}$/.test(cleaned)) {\n return false;\n }\n\n const decoded = Buffer.from(cleaned, \"base64\");\n return hasImageMagicNumber(decoded);\n}\n\nfunction hasImageMagicNumber(input: Buffer): boolean {\n if (input.length < 4) return false;\n return (\n input.subarray(0, 3).equals(Buffer.from([0xff, 0xd8, 0xff])) ||\n input.subarray(0, 8).equals(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])) ||\n input.subarray(0, 4).toString(\"ascii\") === \"RIFF\" ||\n input.subarray(0, 4).toString(\"ascii\") === \"GIF8\" ||\n input.subarray(0, 4).toString(\"ascii\") === \"%PDF\" ||\n input.subarray(0, 4).toString(\"ascii\") === \"MM\\x00*\" ||\n input.subarray(0, 4).toString(\"ascii\") === \"II*\\x00\" ||\n input.subarray(4, 12).toString(\"ascii\").startsWith(\"ftyp\")\n );\n}\n\nfunction parseDataUrl(input: string): Buffer {\n const match = /^data:(image\\/[\\w+.-]+);base64,(.+)$/i.exec(input);\n if (!match) {\n throw new VisionError(\"INPUT_ERROR\", \"Invalid image data URL.\", 400);\n }\n\n try {\n return Buffer.from(match[2], \"base64\");\n } catch {\n throw new VisionError(\"INPUT_ERROR\", \"Invalid base64 in data URL.\", 400);\n }\n}\n\nasync function loadFromUrl(url: string, timeoutMs: number): Promise<Buffer> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n const response = await fetch(url, { signal: controller.signal });\n if (!response.ok) {\n throw new VisionError(\"INPUT_ERROR\", `Failed to fetch image URL: HTTP ${response.status}`, 400);\n }\n const buf = Buffer.from(await response.arrayBuffer());\n return buf;\n } catch (error) {\n if (error instanceof VisionError) throw error;\n throw new VisionError(\"INPUT_ERROR\", \"Failed to fetch remote image.\", 400, error);\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport async function loadImageFromSource(imageSource: string, config: AppConfig, timeoutMs: number): Promise<ImageAsset> {\n let raw: Buffer;\n let sourceKind: ImageSourceKind;\n\n if (isDataUrl(imageSource)) {\n raw = parseDataUrl(imageSource);\n sourceKind = \"data-url\";\n } else if (isBase64(imageSource)) {\n try {\n raw = Buffer.from(imageSource, \"base64\");\n sourceKind = \"base64\";\n } catch {\n throw new VisionError(\"INPUT_ERROR\", \"Invalid base64 image input.\", 400);\n }\n } else if (/^https?:\\/\\//i.test(imageSource)) {\n if (!config.enableUrlInput) {\n throw new VisionError(\"SECURITY_ERROR\", \"URL input is disabled.\", 403);\n }\n\n const safe = await validateRemoteUrl(imageSource);\n raw = await loadFromUrl(safe.toString(), timeoutMs);\n sourceKind = \"url\";\n } else if (config.enableLocalPathInput) {\n const localPath = validateLocalPath(imageSource, config.allowedLocalRoots);\n raw = await fs.readFile(localPath);\n sourceKind = \"local\";\n } else {\n throw new VisionError(\"INPUT_ERROR\", \"Unsupported image source. Use URL, local path, base64, or data URL.\", 400);\n }\n\n if (!raw || raw.byteLength === 0) {\n throw new VisionError(\"INPUT_ERROR\", \"Empty image data.\", 400);\n }\n\n return normalizeImage(raw, sourceKind, config);\n}\r\n","import { createHash } from \"node:crypto\";\nimport sharp from \"sharp\";\n\nimport { VisionError } from \"../errors\";\nimport type { AppConfig } from \"../config\";\nimport type { ImageAsset, ImageSourceKind } from \"./types\";\n\nconst SUPPORTED_MIME = new Map<string, string>([\n [\"jpeg\", \"image/jpeg\"],\n [\"png\", \"image/png\"],\n [\"webp\", \"image/webp\"],\n [\"gif\", \"image/gif\"],\n]);\n\nexport async function normalizeImage(input: Buffer, sourceKind: ImageSourceKind, config: AppConfig): Promise<ImageAsset> {\n const maxBytes = Math.floor(config.maxImageMb * 1024 * 1024);\n if (input.byteLength > maxBytes) {\n throw new VisionError(\"INPUT_ERROR\", `Image is too large. Max is ${config.maxImageMb}MB.`, 400);\n }\n\n const metadata = await sharp(input, { failOn: \"error\" }).metadata();\n if (!metadata.width || !metadata.height || !metadata.format) {\n throw new VisionError(\"INPUT_ERROR\", \"Unsupported image or corrupted data.\", 400);\n }\n\n if (metadata.width * metadata.height > config.maxPixels) {\n throw new VisionError(\"INPUT_ERROR\", `Image pixel count exceeds limit of ${config.maxPixels}.`, 400);\n }\n\n let pipeline = sharp(input, { failOn: \"error\" }).rotate();\n const shouldResize = metadata.width > 2200 || metadata.height > 2200;\n if (shouldResize) {\n pipeline = pipeline.resize({ width: 2200, height: 2200, fit: \"inside\", withoutEnlargement: true });\n }\n\n const normalizedBuffer = await pipeline\n .webp({ quality: 85, effort: 4 })\n .toBuffer();\n\n const finalMeta = await sharp(normalizedBuffer).metadata();\n const format = finalMeta.format ?? \"webp\";\n const mimeType = SUPPORTED_MIME.get(format) ?? \"image/webp\";\n\n return {\n data: normalizedBuffer,\n format,\n mimeType,\n width: finalMeta.width ?? metadata.width,\n height: finalMeta.height ?? metadata.height,\n bytes: normalizedBuffer.byteLength,\n hash: createHash(\"sha256\").update(normalizedBuffer).digest(\"hex\"),\n sourceKind,\n };\n}\r\n","import dns from \"node:dns/promises\";\nimport net from \"node:net\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport { fileURLToPath, URL } from \"node:url\";\n\nimport { VisionError } from \"../errors\";\n\nconst BLOCKED_HOSTNAMES = new Set([\"localhost\", \"metadata.google.internal\"]);\n\nconst PRIVATE_CIDR_CHECKS = [\n /^10\\./,\n /^127\\./,\n /^169\\.254\\./,\n /^172\\.(1[6-9]|2\\d|3[0-1])\\./,\n /^192\\.168\\./,\n /^0\\./,\n];\n\nfunction isPrivateIpv4(ip: string): boolean {\n return PRIVATE_CIDR_CHECKS.some((r) => r.test(ip));\n}\n\nfunction isBlockedIpv6(ip: string): boolean {\n const normalized = ip.toLowerCase();\n return normalized === \"::1\" || normalized.startsWith(\"fc\") || normalized.startsWith(\"fd\") || normalized.startsWith(\"fe80:\");\n}\n\nexport async function validateRemoteUrl(rawUrl: string): Promise<URL> {\n let parsed: URL;\n try {\n parsed = new URL(rawUrl);\n } catch {\n throw new VisionError(\"SECURITY_ERROR\", \"Invalid URL.\", 400);\n }\n\n if (![\"http:\", \"https:\"].includes(parsed.protocol)) {\n throw new VisionError(\"SECURITY_ERROR\", \"Only HTTP/HTTPS URLs are allowed.\", 400);\n }\n\n if (BLOCKED_HOSTNAMES.has(parsed.hostname.toLowerCase())) {\n throw new VisionError(\"SECURITY_ERROR\", \"URL host is blocked.\", 403);\n }\n\n const host = parsed.hostname;\n if (net.isIP(host)) {\n if ((net.isIPv4(host) && isPrivateIpv4(host)) || (net.isIPv6(host) && isBlockedIpv6(host))) {\n throw new VisionError(\"SECURITY_ERROR\", \"Private or loopback IP is blocked.\", 403);\n }\n return parsed;\n }\n\n const resolved = await dns.lookup(host, { all: true });\n for (const addr of resolved) {\n if ((addr.family === 4 && isPrivateIpv4(addr.address)) || (addr.family === 6 && isBlockedIpv6(addr.address))) {\n throw new VisionError(\"SECURITY_ERROR\", \"Resolved IP is private/loopback and blocked.\", 403);\n }\n }\n\n return parsed;\n}\n\nexport function validateLocalPath(inputPath: string, allowedRoots: string[]): string {\n const normalizedPath = normalizeLocalPathInput(inputPath);\n const resolved = path.resolve(normalizedPath);\n\n if (allowedRoots.length === 0) {\n throw new VisionError(\"SECURITY_ERROR\", \"Local file access disabled: allowlist is empty.\", 403);\n }\n\n if (allowedRoots.includes(\"*\")) {\n return resolved;\n }\n\n const isAllowed = allowedRoots.some((root) => {\n const resolvedRoot = path.resolve(root);\n const relative = path.relative(resolvedRoot, resolved);\n return (relative === \"\" || (!relative.startsWith(\"..\") && !path.isAbsolute(relative)));\n });\n\n if (!isAllowed) {\n throw new VisionError(\"SECURITY_ERROR\", \"Local path is outside allowed roots.\", 403);\n }\n\n return resolved;\n}\n\nfunction normalizeLocalPathInput(inputPath: string): string {\n const trimmed = inputPath.trim().replace(/^['\"]|['\"]$/g, \"\");\n\n if (/^file:\\/\\//i.test(trimmed)) {\n try {\n return fileURLToPath(trimmed);\n } catch {\n throw new VisionError(\"INPUT_ERROR\", \"Invalid file URL.\", 400);\n }\n }\n\n if (trimmed === \"~\") {\n return os.homedir();\n }\n\n if (trimmed.startsWith(`~${path.sep}`) || trimmed.startsWith(\"~/\")) {\n return path.join(os.homedir(), trimmed.slice(2));\n }\n\n return trimmed;\n}\n","export async function withTimeout<T>(promise: Promise<T>, timeoutMs: number, onTimeout: () => Error): Promise<T> {\n let timer: NodeJS.Timeout | undefined;\n try {\n const timeoutPromise = new Promise<T>((_, reject) => {\n timer = setTimeout(() => reject(onTimeout()), timeoutMs);\n });\n\n return await Promise.race([promise, timeoutPromise]);\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n\nexport async function timed<T>(fn: () => Promise<T>): Promise<{ value: T; durationMs: number }> {\n const start = Date.now();\n const value = await fn();\n return { value, durationMs: Date.now() - start };\n}\r\n","import type { VisionMode } from \"../schemas\";\n\nconst MODE_GUIDANCE: Record<VisionMode, string> = {\n general: \"Provide a clear and accurate description with key details and context.\",\n palette: \"Extract visual design tokens: colors, spacing, typography, shadows, radius, and style patterns.\",\n hierarchy: \"Analyze hierarchy, attention flow, focus areas, layout clarity, and readability.\",\n components: \"Identify UI components, consistency, reuse quality, interaction patterns, and design-system maturity.\",\n ocr: \"Extract all visible text accurately. Preserve ordering and structure where possible.\",\n ui_analysis: \"Analyze the software UI: goals, flows, usability, information architecture, and issues.\",\n code_screenshot: \"Analyze code/terminal screenshot: summarize code intent, errors, stack traces, and likely fixes.\",\n};\n\nexport function buildPrompt(mode: VisionMode, userPrompt: string, outputFormat: \"text\" | \"json\"): string {\n const uiFactPolicy =\n mode === \"ui_analysis\"\n ? \"Strict policy: separate observed facts from inferred interpretations. If uncertain, explicitly list uncertainty instead of asserting.\"\n : \"\";\n\n const format =\n outputFormat === \"json\"\n ? \"Return strict JSON with stable keys and no markdown.\"\n : \"Return concise plain text with headings when useful.\";\n\n return [\n \"You are an expert vision analyst for images and screenshots.\",\n `Mode: ${mode}. ${MODE_GUIDANCE[mode]}`,\n uiFactPolicy,\n format,\n `User request: ${userPrompt}`,\n ].join(\"\\n\\n\");\n}\n\nexport function buildUiOcrPrompt(userPrompt: string): string {\n return [\n \"You are extracting OCR anchors from a software screenshot.\",\n \"Return only visible text anchors and short structural labels.\",\n \"No assumptions. Include exact labels, button text, section titles, and model IDs if visible.\",\n \"Output plain text lines only.\",\n `User request context: ${userPrompt}`,\n ].join(\"\\n\\n\");\n}\n\nexport function buildUiStructuredJsonPrompt(userPrompt: string, ocrAnchors: string): string {\n return [\n \"You are an expert UI screenshot analyst.\",\n \"Strict policy: separate observed facts from inferred interpretations. If uncertain, include the item under uncertainties.\",\n \"Use OCR anchors as grounding evidence and do not invent labels not supported by image evidence.\",\n \"Return strict JSON only with this exact shape:\",\n JSON.stringify(\n {\n page_context: { value: \"string\", confidence: 0.0 },\n sections: [{ name: \"string\", observations: [\"string\"], confidence: 0.0 }],\n controls: [{ label: \"string\", type: \"string\", state: \"optional\", confidence: 0.0 }],\n status_indicators: [{ label: \"string\", value: \"string\", confidence: 0.0 }],\n ocr_anchors: [\"string\"],\n uncertainties: [\"string\"],\n inferred_interpretations: [\"string\"],\n },\n null,\n 2,\n ),\n `OCR anchors:\\n${ocrAnchors || \"<none>\"}`,\n `User request: ${userPrompt}`,\n ].join(\"\\n\\n\");\n}\n","type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\";\n\nconst ORDER: Record<LogLevel, number> = {\n debug: 10,\n info: 20,\n warn: 30,\n error: 40,\n};\n\nexport class Logger {\n constructor(private readonly level: LogLevel) {}\n\n private shouldLog(level: LogLevel): boolean {\n return ORDER[level] >= ORDER[this.level];\n }\n\n debug(message: string, context?: unknown): void {\n if (this.shouldLog(\"debug\")) console.debug(`[vision][debug] ${message}`, context ?? \"\");\n }\n\n info(message: string, context?: unknown): void {\n if (this.shouldLog(\"info\")) console.info(`[vision][info] ${message}`, context ?? \"\");\n }\n\n warn(message: string, context?: unknown): void {\n if (this.shouldLog(\"warn\")) console.warn(`[vision][warn] ${message}`, context ?? \"\");\n }\n\n error(message: string, context?: unknown): void {\n if (this.shouldLog(\"error\")) console.error(`[vision][error] ${message}`, context ?? \"\");\n }\n}\r\n"],"mappings":"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","names":["import_zod","import_zod","import_node_crypto","import_promises","import_node_crypto","sharp","net","dns","path","os","fs","dotenv"]}
|