@mantisware/peepit-mcp
Version:
Give your AI agents super-vision: blazing-fast macOS screenshots, smart window targeting, and local or cloud AI image analysis—all via a friendly Node.js MCP server.
133 lines • 6.26 kB
JavaScript
import * as fs from "fs/promises";
import * as path from "path";
import * as os from "os";
export async function resolveImagePath(input, logger) {
// If input.path is provided, use it directly
if (input.path) {
return { effectivePath: input.path, tempDirUsed: undefined };
}
// Check if a temporary directory is required
// A temp dir is needed if:
// 1. A question is present
// 2. Format is explicitly set to 'data'
const needsTempDir = input.question || input.format === "data";
if (needsTempDir) {
// Create a temporary directory
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "peepit-img-"));
logger.debug({ tempPath: tempDir }, "Created temporary directory for capture");
return { effectivePath: tempDir, tempDirUsed: tempDir };
}
// Check for PEEPIT_DEFAULT_SAVE_PATH environment variable
const defaultSavePath = process.env.PEEPIT_DEFAULT_SAVE_PATH;
if (defaultSavePath) {
return { effectivePath: defaultSavePath, tempDirUsed: undefined };
}
// Final fallback: create a temporary directory
// This happens when: no path, no question, no explicit 'data' format, no env var
const fallbackTempDir = await fs.mkdtemp(path.join(os.tmpdir(), "peepit-img-"));
logger.debug({ tempPath: fallbackTempDir }, "Created fallback temporary directory for capture");
return { effectivePath: fallbackTempDir, tempDirUsed: fallbackTempDir };
}
export function buildSwiftCliArgs(input, effectivePath, swiftFormat, logger) {
const args = ["image"];
// Use provided format or derive from input
// Format validation is already handled by the schema preprocessor
const inputFormat = input.format || "png";
const actualFormat = swiftFormat || (inputFormat === "data" ? "png" : inputFormat);
// Create a logger if not provided (for backward compatibility)
const log = logger || {
warn: (_msg) => { },
error: (_msg) => { },
debug: (_msg) => { },
};
// Parse app_target to determine Swift CLI arguments
if (!input.app_target || input.app_target === "") {
// Omitted/empty: All screens
args.push("--mode", "screen");
}
else if (input.app_target.startsWith("screen:")) {
// 'screen:INDEX': Specific display
const screenIndexStr = input.app_target.substring(7);
const screenIndex = parseInt(screenIndexStr, 10);
if (isNaN(screenIndex) || screenIndex < 0) {
log.warn({ screenIndex: screenIndexStr }, `Invalid screen index '${screenIndexStr}' in app_target, capturing all screens.`);
args.push("--mode", "screen");
}
else {
args.push("--mode", "screen", "--screen-index", screenIndex.toString());
}
}
else if (input.app_target.toLowerCase() === "frontmost") {
// 'frontmost': Capture the frontmost window of the frontmost app
// This requires special handling to first find the frontmost app, then capture its frontmost window
log.debug("Using frontmost mode - will attempt to capture frontmost window");
args.push("--mode", "frontmost");
}
else if (input.app_target.includes(":")) {
// 'AppName:WINDOW_TITLE:Title' or 'AppName:WINDOW_INDEX:Index'
const parts = input.app_target.split(":");
if (parts.length >= 3) {
const appName = parts[0].trim();
const specifierType = parts[1].trim();
const specifierValue = parts.slice(2).join(":"); // Handle colons in window titles
// Validate that we have a non-empty app name
if (!appName) {
log.warn({ app_target: input.app_target }, "Empty app name detected in app_target, treating as malformed");
// Try to find the first non-empty part as the app name
const nonEmptyParts = parts.filter(part => part.trim());
if (nonEmptyParts.length > 0) {
args.push("--app", nonEmptyParts[0].trim());
args.push("--mode", "multi");
}
else {
// All parts are empty, default to screen mode
log.warn("All parts of app_target are empty, defaulting to screen mode");
args.push("--mode", "screen");
}
}
else {
args.push("--app", appName);
args.push("--mode", "window");
if (specifierType.toUpperCase() === "WINDOW_TITLE") {
args.push("--window-title", specifierValue);
}
else if (specifierType.toUpperCase() === "WINDOW_INDEX") {
args.push("--window-index", specifierValue);
}
else {
log.warn({ specifierType }, "Unknown window specifier type, defaulting to main window");
}
}
}
else {
// Malformed: treat as app name, but validate it's not empty
const cleanAppTarget = input.app_target.trim();
if (!cleanAppTarget || cleanAppTarget === ":".repeat(cleanAppTarget.length)) {
log.warn({ app_target: input.app_target }, "Malformed app_target with only colons or empty, defaulting to screen mode");
args.push("--mode", "screen");
}
else {
log.warn({ app_target: input.app_target }, "Malformed window specifier, treating as app name");
// Remove trailing colons from app name
const appName = cleanAppTarget.replace(/:+$/, "");
args.push("--app", appName);
args.push("--mode", "multi");
}
}
}
else {
// 'AppName': All windows of that app
args.push("--app", input.app_target.trim());
args.push("--mode", "multi");
}
// Add path if it was provided
if (effectivePath) {
args.push("--path", effectivePath);
}
// Add format
args.push("--format", actualFormat);
// Add capture focus
args.push("--capture-focus", input.capture_focus || "background");
return args;
}
//# sourceMappingURL=image-cli-args.js.map