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
166 lines (146 loc) • 6.34 kB
JavaScript
// 正确的透视执行器 - 基于用户提供的工作代码实现
import { exec } from 'child_process';
import { promisify } from 'util';
const execAsync = promisify(exec);
/**
* 透视任务获取器
* 基于用户提供的正确代码实现,直接切换透视并从内容树收集任务
*/
export class CorrectPerspectiveExecutor {
/**
* 获取透视任务的正确方法
* 基于用户提供的工作代码,直接切换透视并从内容树收集任务
*/
static async getPerspectiveTasks(perspectiveName, options = {}) {
const { hideCompleted = true, limit = 0 } = options;
const script = `
(() => {
try {
console.log("开始获取透视: ${perspectiveName}");
// 获取自定义透视
let perspective = Perspective.Custom.byName("${perspectiveName}");
if (!perspective) {
return {
success: false,
error: "未找到透视: ${perspectiveName}"
};
}
console.log("成功找到透视:", perspective.name);
// 切换到目标透视
document.windows[0].perspective = perspective;
// 用于存储所有任务,key为任务ID
let taskMap = {};
// 遍历内容树,收集任务信息(含层级关系)
let rootNode = document.windows[0].content.rootNode;
function collectTasks(node, parentId) {
if (node.object && node.object instanceof Task) {
let t = node.object;
let id = t.id.primaryKey;
// 应用 hideCompleted 筛选
if (${hideCompleted} && (t.completed || t.dropped)) {
return;
}
// 记录任务信息
taskMap[id] = {
id: id,
name: t.name,
note: t.note,
project: t.project ? t.project.name : null,
tags: t.tags ? t.tags.map(tag => tag.name) : [],
dueDate: t.dueDate ? t.dueDate.toISOString() : null,
deferDate: t.deferDate ? t.deferDate.toISOString() : null,
completed: t.completed,
dropped: t.dropped,
flagged: t.flagged,
estimatedMinutes: t.estimatedMinutes,
repetitionRule: t.repetitionRule ? t.repetitionRule.toString() : null,
parent: parentId, // 父任务ID
children: [], // 子任务ID列表,后面补充
creationDate: t.added ? t.added.toISOString() : null,
completionDate: t.completionDate ? t.completionDate.toISOString() : null
};
// 递归收集子任务
node.children.forEach(childNode => {
if (childNode.object && childNode.object instanceof Task) {
let childId = childNode.object.id.primaryKey;
taskMap[id].children.push(childId);
collectTasks(childNode, id);
} else {
collectTasks(childNode, id);
}
});
} else {
// 不是任务节点,递归子节点
node.children.forEach(childNode => collectTasks(childNode, parentId));
}
}
// 开始收集任务
rootNode.children.forEach(node => collectTasks(node, null));
// 转换为数组格式(保持向后兼容)
let tasks = Object.values(taskMap);
// 应用 limit 限制
if (${limit} > 0 && tasks.length > ${limit}) {
tasks = tasks.slice(0, ${limit});
}
console.log("透视 '" + perspective.name + "' 收集到 " + tasks.length + " 个任务");
const result = {
success: true,
perspectiveName: "${perspectiveName}",
taskCount: tasks.length,
tasks: tasks,
taskMap: taskMap, // 包含层级关系的完整数据
totalTasks: tasks.length
};
return result;
} catch (error) {
console.log("透视获取失败:", error);
return {
success: false,
error: error.toString()
};
}
})()
`;
try {
console.log('[CorrectPerspectiveExecutor] 开始执行透视查询...');
// 转义脚本中的特殊字符
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: 10000 });
if (stderr) {
console.error('[CorrectPerspectiveExecutor] 执行错误:', stderr);
throw new Error(`JXA执行错误: ${stderr}`);
}
console.log('[CorrectPerspectiveExecutor] 原始输出:', stdout);
// 解析返回结果
try {
const result = JSON.parse(stdout);
console.log('[CorrectPerspectiveExecutor] 执行成功,结果:', result);
return result;
}
catch (parseError) {
// 如果不是JSON,直接返回字符串
console.log('[CorrectPerspectiveExecutor] 返回非JSON结果:', stdout);
return stdout.trim();
}
}
catch (error) {
console.error('[CorrectPerspectiveExecutor] 执行失败:', error);
throw new Error(`透视查询失败: ${error}`);
}
}
}