omnifocus-mcp-enhanced
Version:
🚀 NEW: Native Custom Perspective Access! Enhanced MCP server with OmniFocus custom perspective support, hierarchical task display, AI-optimized tool selection, and comprehensive task management
244 lines (212 loc) • 8.29 kB
JavaScript
// OmniJS执行器 - 通过JXA直接执行OmniJS代码
import { exec } from 'child_process';
import { promisify } from 'util';
const execAsync = promisify(exec);
/**
* 执行OmniJS代码的正确方法
* 通过JXA直接执行OmniJS,能够获取返回值
*/
export class OmniJSExecutor {
/**
* 执行OmniJS代码并获取返回值
* @param script - 纯OmniJS代码字符串
* @param timeout - 超时时间(毫秒)
* @returns Promise<any> - 执行结果
*/
static async execute(script, timeout = 10000) {
try {
console.log('[OmniJSExecutor] 开始执行OmniJS代码...');
// 转义脚本中的特殊字符
const escapedScript = script.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$/g, '\\$');
// 使用JXA直接执行OmniJS
const jxaScript = `
function run() {
try {
const app = Application('OmniFocus');
app.includeStandardAdditions = true;
// 执行OmniJS代码并获取结果
const result = app.evaluateJavascript(\`${escapedScript}\`);
return result;
} catch (e) {
return JSON.stringify({ error: e.message });
}
}
`;
// 执行JXA脚本
const { stdout, stderr } = await execAsync(`osascript -l JavaScript -e '${jxaScript}'`, { timeout });
if (stderr) {
console.error('[OmniJSExecutor] 执行错误:', stderr);
throw new Error(`JXA执行错误: ${stderr}`);
}
console.log('[OmniJSExecutor] 原始输出:', stdout);
// 解析返回结果
try {
const result = JSON.parse(stdout);
console.log('[OmniJSExecutor] 执行成功,结果:', result);
return result;
}
catch (parseError) {
// 如果不是JSON,直接返回字符串
console.log('[OmniJSExecutor] 返回非JSON结果:', stdout);
return stdout.trim();
}
}
catch (error) {
console.error('[OmniJSExecutor] 执行失败:', error);
throw new Error(`OmniJS执行失败: ${error}`);
}
}
}
/**
* 透视访问专用的OmniJS执行器
*/
export class PerspectiveOmniJSExecutor {
/**
* 获取透视任务的正确方法
*/
static async getPerspectiveTasks(perspectiveName, options = {}) {
const { hideCompleted = true, limit = 0 } = options;
const script = `
(() => {
try {
console.log("开始获取透视: ${perspectiveName}");
// 使用正确的document全局变量
const doc = document;
// 使用正确的API查找透视
let targetPerspective = null;
try {
// 方法1: 直接通过名称查找
targetPerspective = Perspective.Custom.byName("${perspectiveName}");
console.log("通过byName找到透视");
} catch (byNameError) {
console.log(\`byName失败: \${byNameError.message}\`);
// 方法2: 遍历所有自定义透视
try {
const allCustomPerspectives = Perspective.Custom.all;
for (let perspective of allCustomPerspectives) {
if (perspective.name === "${perspectiveName}") {
targetPerspective = perspective;
console.log("通过遍历找到透视");
break;
}
}
} catch (allError) {
console.log(\`遍历失败: \${allError.message}\`);
}
}
if (!targetPerspective) {
return { success: false, error: "未找到透视: ${perspectiveName}" };
}
console.log("成功找到透视");
// 切换透视
let window = doc.windows[0];
if (!window) {
return { success: false, error: "没有可用窗口" };
}
window.perspective = targetPerspective;
console.log("切换透视成功");
// 计数任务
let taskCount = 0;
let projectCount = 0;
let tasks = [];
function collectTasks(node) {
try {
const obj = node.object;
if (obj instanceof Task) {
const completed = obj.completed;
const dropped = obj.dropped;
// 应用hideCompleted筛选
if (${hideCompleted} && (completed || dropped)) {
return;
}
taskCount++;
// 收集任务信息
const taskData = {
id: obj.id.primaryKey,
name: obj.name,
completed: completed,
dropped: dropped,
flagged: obj.flagged,
projectName: obj.containingProject ? obj.containingProject.name : null
};
tasks.push(taskData);
// 应用limit限制
if (${limit} > 0 && tasks.length >= ${limit}) {
return false;
}
} else if (obj instanceof Project) {
projectCount++;
}
// 递归处理子节点
if (node.children && node.children.length > 0) {
for (let child of node.children) {
if (collectTasks(child) === false) {
return false;
}
}
}
} catch (nodeError) {
console.log(\`节点处理错误: \${nodeError.message}\`);
}
}
const rootNode = window.content.rootNode;
collectTasks(rootNode);
const result = {
success: true,
perspectiveName: "${perspectiveName}",
taskCount: taskCount,
projectCount: projectCount,
tasks: tasks
};
console.log(\`透视 "\${perspectiveName}" 包含 \${taskCount} 个任务\`);
return result;
} catch (error) {
return { success: false, error: error.toString() };
}
})()
`;
// 执行OmniJS脚本
return await OmniJSExecutor.execute(script);
}
/**
* 测试透视查找功能
*/
static async testPerspectiveAccess(perspectiveName) {
const script = `
(() => {
try {
console.log("测试透视访问:", "${perspectiveName}");
// 查找透视
let targetPerspective = null;
try {
targetPerspective = Perspective.Custom.byName("${perspectiveName}");
console.log("找到透视:", targetPerspective.name);
} catch (error) {
console.log("byName失败:", error.message);
// 列出所有自定义透视
const allCustomPerspectives = Perspective.Custom.all;
const perspectiveNames = allCustomPerspectives.map(p => p.name);
return JSON.stringify({
success: false,
error: \`未找到透视 "${perspectiveName}"\`,
availablePerspectives: perspectiveNames
});
}
// 获取透视信息
const perspectiveInfo = {
name: targetPerspective.name,
hasRules: !!targetPerspective.archivedFilterRules,
rulesCount: targetPerspective.archivedFilterRules?.length || 0
};
return JSON.stringify({
success: true,
perspectiveInfo: perspectiveInfo
});
} catch (error) {
return JSON.stringify({ success: false, error: error.toString() });
}
})()
`;
return await OmniJSExecutor.execute(script);
}
}