mirror of
https://github.com/penpot/penpot-mcp.git
synced 2026-04-25 11:18:37 +00:00
209 lines
5.6 KiB
TypeScript
209 lines
5.6 KiB
TypeScript
import { Task, TaskHandler } from "../TaskHandler";
|
|
import { ExecuteCodeTaskParams } from "../../../common/src";
|
|
|
|
/**
|
|
* Console implementation that captures all log output for code execution.
|
|
*
|
|
* Provides the same interface as the native console object but appends
|
|
* all output to an internal log string that can be retrieved.
|
|
*/
|
|
class ExecuteCodeTaskConsole {
|
|
/**
|
|
* Accumulated log output from all console method calls.
|
|
*/
|
|
private logOutput: string = "";
|
|
|
|
/**
|
|
* Resets the accumulated log output to empty string.
|
|
* Should be called before each code execution to start with clean logs.
|
|
*/
|
|
resetLog(): void {
|
|
this.logOutput = "";
|
|
}
|
|
|
|
/**
|
|
* Gets the accumulated log output from all console method calls.
|
|
* @returns The complete log output as a string
|
|
*/
|
|
getLog(): string {
|
|
return this.logOutput;
|
|
}
|
|
|
|
/**
|
|
* Appends a formatted message to the log output.
|
|
* @param level - Log level prefix (e.g., "LOG", "WARN", "ERROR")
|
|
* @param args - Arguments to log, will be stringified and joined
|
|
*/
|
|
private appendToLog(level: string, ...args: any[]): void {
|
|
const message = args
|
|
.map((arg) => (typeof arg === "object" ? JSON.stringify(arg, null, 2) : String(arg)))
|
|
.join(" ");
|
|
this.logOutput += `[${level}] ${message}\n`;
|
|
}
|
|
|
|
/**
|
|
* Logs a message to the captured output.
|
|
*/
|
|
log(...args: any[]): void {
|
|
this.appendToLog("LOG", ...args);
|
|
}
|
|
|
|
/**
|
|
* Logs a warning message to the captured output.
|
|
*/
|
|
warn(...args: any[]): void {
|
|
this.appendToLog("WARN", ...args);
|
|
}
|
|
|
|
/**
|
|
* Logs an error message to the captured output.
|
|
*/
|
|
error(...args: any[]): void {
|
|
this.appendToLog("ERROR", ...args);
|
|
}
|
|
|
|
/**
|
|
* Logs an informational message to the captured output.
|
|
*/
|
|
info(...args: any[]): void {
|
|
this.appendToLog("INFO", ...args);
|
|
}
|
|
|
|
/**
|
|
* Logs a debug message to the captured output.
|
|
*/
|
|
debug(...args: any[]): void {
|
|
this.appendToLog("DEBUG", ...args);
|
|
}
|
|
|
|
/**
|
|
* Logs a message with trace information to the captured output.
|
|
*/
|
|
trace(...args: any[]): void {
|
|
this.appendToLog("TRACE", ...args);
|
|
}
|
|
|
|
/**
|
|
* Logs a table to the captured output (simplified as JSON).
|
|
*/
|
|
table(data: any): void {
|
|
this.appendToLog("TABLE", data);
|
|
}
|
|
|
|
/**
|
|
* Starts a timer (simplified implementation that just logs).
|
|
*/
|
|
time(label?: string): void {
|
|
this.appendToLog("TIME", `Timer started: ${label || "default"}`);
|
|
}
|
|
|
|
/**
|
|
* Ends a timer (simplified implementation that just logs).
|
|
*/
|
|
timeEnd(label?: string): void {
|
|
this.appendToLog("TIME_END", `Timer ended: ${label || "default"}`);
|
|
}
|
|
|
|
/**
|
|
* Logs messages in a group (simplified to just log the label).
|
|
*/
|
|
group(label?: string): void {
|
|
this.appendToLog("GROUP", label || "");
|
|
}
|
|
|
|
/**
|
|
* Logs messages in a collapsed group (simplified to just log the label).
|
|
*/
|
|
groupCollapsed(label?: string): void {
|
|
this.appendToLog("GROUP_COLLAPSED", label || "");
|
|
}
|
|
|
|
/**
|
|
* Ends the current group (simplified implementation).
|
|
*/
|
|
groupEnd(): void {
|
|
this.appendToLog("GROUP_END", "");
|
|
}
|
|
|
|
/**
|
|
* Clears the console (no-op in this implementation since we want to capture logs).
|
|
*/
|
|
clear(): void {
|
|
// intentionally empty - we don't want to clear captured logs
|
|
}
|
|
|
|
/**
|
|
* Counts occurrences of calls with the same label (simplified implementation).
|
|
*/
|
|
count(label?: string): void {
|
|
this.appendToLog("COUNT", label || "default");
|
|
}
|
|
|
|
/**
|
|
* Resets the count for a label (simplified implementation).
|
|
*/
|
|
countReset(label?: string): void {
|
|
this.appendToLog("COUNT_RESET", label || "default");
|
|
}
|
|
|
|
/**
|
|
* Logs an assertion (simplified to just log if condition is false).
|
|
*/
|
|
assert(condition: boolean, ...args: any[]): void {
|
|
if (!condition) {
|
|
this.appendToLog("ASSERT", ...args);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Task handler for executing JavaScript code in the plugin context.
|
|
*
|
|
* Maintains a persistent context object that preserves state between code executions
|
|
* and captures all console output during execution.
|
|
*/
|
|
export class ExecuteCodeTaskHandler extends TaskHandler<ExecuteCodeTaskParams> {
|
|
readonly taskType = "executeCode";
|
|
|
|
/**
|
|
* Persistent context object that maintains state between code executions.
|
|
* Contains the penpot API, storage object, and custom console implementation.
|
|
*/
|
|
private readonly context: any;
|
|
|
|
constructor() {
|
|
super();
|
|
|
|
// initialize context, making penpot object available with custom console
|
|
this.context = {
|
|
penpot: penpot,
|
|
storage: {},
|
|
console: new ExecuteCodeTaskConsole(),
|
|
};
|
|
}
|
|
|
|
handle(task: Task<ExecuteCodeTaskParams>): void {
|
|
if (!task.params.code) {
|
|
task.sendError("executeCode task requires 'code' parameter");
|
|
return;
|
|
}
|
|
|
|
this.context.console.resetLog();
|
|
|
|
const context = this.context;
|
|
const code = task.params.code;
|
|
|
|
const result = (function (ctx) {
|
|
return Function(...Object.keys(ctx), code)(...Object.values(ctx));
|
|
})(context);
|
|
|
|
console.log("Code execution result:", result);
|
|
|
|
// return both result and captured log
|
|
task.sendSuccess({
|
|
result: result,
|
|
log: this.context.console.getLog(),
|
|
});
|
|
}
|
|
}
|