UNPKG

@notbnull/mcp-cursor-taskmaster

Version:

A project-scoped MCP server requiring explicit --project-dir configuration for task management, memory retrieval, and agentic workflows

202 lines 7.65 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ApprovalUIServer = void 0; exports.getApprovalUIServer = getApprovalUIServer; const express_1 = __importDefault(require("express")); const socket_io_1 = require("socket.io"); const http_1 = require("http"); const path_1 = __importDefault(require("path")); const fs_1 = require("fs"); const timers_1 = require("timers"); class ApprovalUIServer { app; server; io; port; publicPath; pendingApprovals = new Map(); isRunning = false; constructor(port = 3456) { this.port = port; this.app = (0, express_1.default)(); this.server = (0, http_1.createServer)(this.app); this.io = new socket_io_1.Server(this.server, { cors: { origin: "*", methods: ["GET", "POST"], }, }); // Determine the correct path to the public folder this.publicPath = this.findPublicPath(); this.setupRoutes(); this.setupSocketHandlers(); } findPublicPath() { // Try different possible locations for the public folder const possiblePaths = [ path_1.default.join(__dirname, "../public"), // When running from dist/ path_1.default.join(__dirname, "../../public"), // When running from dist/src/ path_1.default.join(process.cwd(), "public"), // From project root ]; for (const publicPath of possiblePaths) { const approvalHtmlPath = path_1.default.join(publicPath, "approval.html"); if ((0, fs_1.existsSync)(approvalHtmlPath)) { console.error(`Found public folder at: ${publicPath}`); return publicPath; } } // Fallback to default if nothing found console.error("Warning: Could not find public folder, using default path"); return path_1.default.join(__dirname, "../public"); } setupRoutes() { // Serve static files from the determined public path this.app.use(express_1.default.static(this.publicPath)); this.app.use(express_1.default.json()); // Serve the approval UI this.app.get("/", (req, res) => { const approvalHtmlPath = path_1.default.join(this.publicPath, "approval.html"); if ((0, fs_1.existsSync)(approvalHtmlPath)) { res.sendFile(approvalHtmlPath); } else { res.status(404).send(` <html> <head><title>Taskmaster Approval UI</title></head> <body> <h1>Approval UI Not Found</h1> <p>The approval.html file could not be found.</p> <p>Searched in: ${this.publicPath}</p> <p>Please ensure the public folder is available.</p> </body> </html> `); } }); // API endpoint to get pending approvals this.app.get("/api/pending", (req, res) => { const pending = Array.from(this.pendingApprovals.entries()).map(([id, data]) => ({ id, request: data.request, })); res.json(pending); }); // Debug endpoint to check paths this.app.get("/debug/paths", (req, res) => { res.json({ __dirname, publicPath: this.publicPath, cwd: process.cwd(), exists: (0, fs_1.existsSync)(path_1.default.join(this.publicPath, "approval.html")), }); }); } setupSocketHandlers() { this.io.on("connection", (socket) => { console.error("Approval UI: Client connected"); // Send all pending approvals to newly connected client const pending = Array.from(this.pendingApprovals.entries()).map(([id, data]) => ({ id, request: data.request, })); socket.emit("pending-approvals", pending); // Handle approval response socket.on("approval-response", (response) => { const pending = this.pendingApprovals.get(response.taskId); if (pending) { pending.resolve(response); this.pendingApprovals.delete(response.taskId); // Notify all clients that this approval is resolved this.io.emit("approval-resolved", response.taskId); } }); socket.on("disconnect", () => { console.error("Approval UI: Client disconnected"); }); }); } async start() { if (this.isRunning) return; return new Promise((resolve, reject) => { this.server .listen(this.port, () => { this.isRunning = true; console.error(`Approval UI server running at http://localhost:${this.port}`); resolve(); }) .on("error", (err) => { if (err.code === "EADDRINUSE") { console.error(`Port ${this.port} is already in use, assuming UI is already running`); this.isRunning = true; resolve(); } else { reject(err); } }); }); } async stop() { if (!this.isRunning) return; return new Promise((resolve) => { this.server.close(() => { this.isRunning = false; resolve(); }); }); } async requestApproval(request) { // Ensure server is running await this.start(); return new Promise((resolve, reject) => { // Store the pending approval this.pendingApprovals.set(request.taskId, { request, resolve, reject, }); // Notify all connected clients this.io.emit("new-approval", { id: request.taskId, request, }); // Open the UI in browser if not already open if (this.pendingApprovals.size === 1) { import("open") .then(({ default: open }) => { open(`http://localhost:${this.port}`).catch(() => { console.error("Failed to open browser automatically. Please open http://localhost:" + this.port); }); }) .catch(() => { console.error("Failed to load open module. Please open http://localhost:" + this.port + " manually"); }); } // Set a timeout for the approval (5 minutes) (0, timers_1.setTimeout)(() => { if (this.pendingApprovals.has(request.taskId)) { this.pendingApprovals.delete(request.taskId); reject(new Error("Approval timeout")); } }, 5 * 60 * 1000); }); } } exports.ApprovalUIServer = ApprovalUIServer; // Singleton instance let approvalUIServer = null; function getApprovalUIServer() { if (!approvalUIServer) { approvalUIServer = new ApprovalUIServer(); } return approvalUIServer; } //# sourceMappingURL=approval-ui.js.map