@mazees/mark 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +29 -0
- package/README.md +209 -0
- package/bin/mark.js +27 -0
- package/out/renderer/assets/icon-DHo2CYph.svg +1 -0
- package/out/renderer/assets/index-G2fADWO-.js +4768 -0
- package/out/renderer/assets/index-f3mVse3c.css +1 -0
- package/out/renderer/assets/music-cover-CG4vi3kZ.png +0 -0
- package/out/renderer/assets/poppins-devanagari-400-normal-CJDn6rn8.woff2 +0 -0
- package/out/renderer/assets/poppins-devanagari-400-normal-CqVvlrh5.woff +0 -0
- package/out/renderer/assets/poppins-devanagari-500-normal-BIdkeU1p.woff2 +0 -0
- package/out/renderer/assets/poppins-devanagari-500-normal-DMPDjHtT.woff +0 -0
- package/out/renderer/assets/poppins-devanagari-600-normal-ClASKHrr.woff +0 -0
- package/out/renderer/assets/poppins-devanagari-600-normal-STEjXBNN.woff2 +0 -0
- package/out/renderer/assets/poppins-devanagari-700-normal-O-jipLrW.woff2 +0 -0
- package/out/renderer/assets/poppins-devanagari-700-normal-fHs-vx92.woff +0 -0
- package/out/renderer/assets/poppins-latin-400-normal-BOb3E3N0.woff +0 -0
- package/out/renderer/assets/poppins-latin-400-normal-cpxAROuN.woff2 +0 -0
- package/out/renderer/assets/poppins-latin-500-normal-C8OXljZJ.woff2 +0 -0
- package/out/renderer/assets/poppins-latin-500-normal-DGXqpDMm.woff +0 -0
- package/out/renderer/assets/poppins-latin-600-normal-BJdTmd5m.woff +0 -0
- package/out/renderer/assets/poppins-latin-600-normal-zEkxB9Mr.woff2 +0 -0
- package/out/renderer/assets/poppins-latin-700-normal-BVuQR_eA.woff +0 -0
- package/out/renderer/assets/poppins-latin-700-normal-Qrb0O0WB.woff2 +0 -0
- package/out/renderer/assets/poppins-latin-ext-400-normal-DaBSavcJ.woff +0 -0
- package/out/renderer/assets/poppins-latin-ext-400-normal-by3JarPu.woff2 +0 -0
- package/out/renderer/assets/poppins-latin-ext-500-normal-CK-6C4Hw.woff2 +0 -0
- package/out/renderer/assets/poppins-latin-ext-500-normal-CgAe2rWW.woff +0 -0
- package/out/renderer/assets/poppins-latin-ext-600-normal-CAhIAdZj.woff2 +0 -0
- package/out/renderer/assets/poppins-latin-ext-600-normal-Df5ffKXP.woff +0 -0
- package/out/renderer/assets/poppins-latin-ext-700-normal-DctTR6Tg.woff +0 -0
- package/out/renderer/assets/poppins-latin-ext-700-normal-cby-RkWa.woff2 +0 -0
- package/out/renderer/favicon.ico +0 -0
- package/out/renderer/icon-256.png +0 -0
- package/out/renderer/icon.svg +1 -0
- package/out/renderer/index.html +25 -0
- package/out/renderer/manifest.json +25 -0
- package/out/renderer/robot-simulator.html +1640 -0
- package/package.json +106 -0
- package/src/cli/index.js +332 -0
- package/src/cli/monitor.js +413 -0
- package/src/cli/theme.js +347 -0
- package/src/launcher/MarkTray.cs +227 -0
- package/src/main/browser-agent.js +1132 -0
- package/src/main/git-service.js +90 -0
- package/src/main/google/google-calendar.js +69 -0
- package/src/main/google/google-drive.js +263 -0
- package/src/main/google/google-gmail.js +131 -0
- package/src/main/google/google-service.js +241 -0
- package/src/main/node-tools.js +23 -0
- package/src/main/pc-agent-scripts/mouse-locker.ps1 +76 -0
- package/src/main/pc-agent-scripts/ocr-region.ps1 +185 -0
- package/src/main/pc-agent-scripts/pc-daemon.ps1 +726 -0
- package/src/main/pc-agent-scripts/pc-overlay.ps1 +253 -0
- package/src/main/pc-agent-scripts/pick-folder.ps1 +33 -0
- package/src/main/pc-agent-scripts/read-ui.ps1 +181 -0
- package/src/main/pc-agent-scripts/take-screenshot.ps1 +24 -0
- package/src/main/pc-agent-scripts/win-action.ps1 +380 -0
- package/src/main/plugins/plugin-loader.js +306 -0
- package/src/main/skills/skill-manager.js +265 -0
- package/src/main/syntax-validator.js +455 -0
- package/src/main/task-daemon.js +117 -0
- package/src/main/telegram/telegram-service.js +835 -0
- package/src/main/tools/browser-tools.js +516 -0
- package/src/main/tools/file-tools.js +1126 -0
- package/src/main/tools/git-tools.js +79 -0
- package/src/main/tools/google-tools.js +566 -0
- package/src/main/tools/index.js +36 -0
- package/src/main/tools/media-tools.js +42 -0
- package/src/main/tools/system-tools.js +295 -0
- package/src/main/tools/task-tools.js +73 -0
- package/src/main/tools/telegram-tools.js +101 -0
- package/src/renderer/index.html +24 -0
- package/src/renderer/public/favicon.ico +0 -0
- package/src/renderer/public/icon-256.png +0 -0
- package/src/renderer/public/icon.svg +1 -0
- package/src/renderer/public/manifest.json +25 -0
- package/src/renderer/public/robot-simulator.html +1640 -0
- package/src/renderer/src/App.jsx +304 -0
- package/src/renderer/src/api/ai/awareness.js +294 -0
- package/src/renderer/src/api/ai/chatSummarizer.js +79 -0
- package/src/renderer/src/api/ai/contextCompactor.js +129 -0
- package/src/renderer/src/api/ai/contextManager.js +1130 -0
- package/src/renderer/src/api/ai/core.js +167 -0
- package/src/renderer/src/api/ai/index.js +4 -0
- package/src/renderer/src/api/ai/memoryGroomer.js +101 -0
- package/src/renderer/src/api/ai/persona.js +115 -0
- package/src/renderer/src/api/ai/planning.js +317 -0
- package/src/renderer/src/api/ai/relationship.js +177 -0
- package/src/renderer/src/api/ai/skillSynthesizer.js +168 -0
- package/src/renderer/src/api/ai/tools.js +167 -0
- package/src/renderer/src/api/ai/utils.js +359 -0
- package/src/renderer/src/api/db.js +1013 -0
- package/src/renderer/src/api/oramaStore.js +592 -0
- package/src/renderer/src/api/ragPipeline.js +87 -0
- package/src/renderer/src/api/subagent/subagentExecutor.js +780 -0
- package/src/renderer/src/api/subagent/subagentPrompt.js +44 -0
- package/src/renderer/src/api/subagent/subagentStore.js +173 -0
- package/src/renderer/src/api/taskExecutor.js +18 -0
- package/src/renderer/src/api/taskStore.js +315 -0
- package/src/renderer/src/api/tools/core-tools.js +707 -0
- package/src/renderer/src/api/tools/index.js +151 -0
- package/src/renderer/src/api/turnPairMigrator.js +200 -0
- package/src/renderer/src/api/vectorMemory.js +136 -0
- package/src/renderer/src/api/wakeWord.js +101 -0
- package/src/renderer/src/api/web-bridge.js +885 -0
- package/src/renderer/src/api/webSpeech.js +178 -0
- package/src/renderer/src/api/workspaceRag.js +76 -0
- package/src/renderer/src/assets/electron.svg +10 -0
- package/src/renderer/src/assets/icon.svg +1 -0
- package/src/renderer/src/assets/main.css +589 -0
- package/src/renderer/src/assets/music-cover.png +0 -0
- package/src/renderer/src/assets/wavy-lines.svg +25 -0
- package/src/renderer/src/components/Chat/ApprovalBubble.jsx +127 -0
- package/src/renderer/src/components/Chat/CodeBlock.jsx +83 -0
- package/src/renderer/src/components/Chat/DurableTaskBubble.jsx +455 -0
- package/src/renderer/src/components/Chat/FileDiffModal.jsx +328 -0
- package/src/renderer/src/components/Chat/GeminiWidgets.jsx +82 -0
- package/src/renderer/src/components/Chat/MemoryFooterBubble.jsx +34 -0
- package/src/renderer/src/components/Chat/MessageBubble.jsx +459 -0
- package/src/renderer/src/components/Chat/PluginExecutionBubble.jsx +41 -0
- package/src/renderer/src/components/Chat/ThinkingBubble.jsx +316 -0
- package/src/renderer/src/components/Chat/YoutubeSearchBubble.jsx +43 -0
- package/src/renderer/src/components/Chat/YoutubeSummaryBubble.jsx +40 -0
- package/src/renderer/src/components/Chat/index.js +11 -0
- package/src/renderer/src/components/ChatList.jsx +434 -0
- package/src/renderer/src/components/GlobalCameraManager.jsx +67 -0
- package/src/renderer/src/components/Versions.jsx +15 -0
- package/src/renderer/src/components/YoutubeMusicPlayer.jsx +375 -0
- package/src/renderer/src/components/camera/CameraPreview.jsx +236 -0
- package/src/renderer/src/components/config/AiEngineSection.jsx +363 -0
- package/src/renderer/src/components/config/CameraSection.jsx +100 -0
- package/src/renderer/src/components/config/PermissionsSection.jsx +130 -0
- package/src/renderer/src/components/config/ShortcutSection.jsx +102 -0
- package/src/renderer/src/components/config/VoiceSection.jsx +110 -0
- package/src/renderer/src/components/config/index.js +5 -0
- package/src/renderer/src/components/core/Avatar.jsx +2197 -0
- package/src/renderer/src/components/core/ConfirmModal.jsx +80 -0
- package/src/renderer/src/components/core/DraggableHoloCard.jsx +125 -0
- package/src/renderer/src/components/core/FloatingMenu.jsx +272 -0
- package/src/renderer/src/components/core/HoloCard.jsx +64 -0
- package/src/renderer/src/components/core/InputBar.jsx +1346 -0
- package/src/renderer/src/components/core/ProcessPanel.jsx +216 -0
- package/src/renderer/src/components/core/ResponseArea.jsx +149 -0
- package/src/renderer/src/components/core/StatusIndicator.jsx +59 -0
- package/src/renderer/src/components/core/ThoughtNeuralFlow.jsx +168 -0
- package/src/renderer/src/components/core/ToolClustersDeck.jsx +210 -0
- package/src/renderer/src/components/core/native-skills.js +43 -0
- package/src/renderer/src/components/neural-core/EmotionalCortexLobe.jsx +359 -0
- package/src/renderer/src/components/neural-core/NodeDissectionModal.jsx +57 -0
- package/src/renderer/src/components/neural-core/ProceduralMatrixLobe.jsx +193 -0
- package/src/renderer/src/components/neural-core/ProceduralSkillModal.jsx +53 -0
- package/src/renderer/src/components/neural-core/ResetAiModal.jsx +198 -0
- package/src/renderer/src/components/neural-core/SynapticMemoryLobe.jsx +204 -0
- package/src/renderer/src/components/neural-core/traitMeta.js +102 -0
- package/src/renderer/src/components/subagent/AgentChatFeed.jsx +603 -0
- package/src/renderer/src/components/subagent/AgentInspector.jsx +224 -0
- package/src/renderer/src/components/subagent/AgentSidebar.jsx +216 -0
- package/src/renderer/src/components/subagent/SubagentIntercom.jsx +572 -0
- package/src/renderer/src/components/subagent/SubagentTopologyMap.jsx +423 -0
- package/src/renderer/src/contexts/ApprovalContext.jsx +425 -0
- package/src/renderer/src/contexts/ChatContext.jsx +61 -0
- package/src/renderer/src/contexts/YoutubeMusicContext.jsx +333 -0
- package/src/renderer/src/data/faqData.js +202 -0
- package/src/renderer/src/hooks/agent/executeAgentTool.js +895 -0
- package/src/renderer/src/hooks/agent/index.js +4 -0
- package/src/renderer/src/hooks/agent/useMarkMusic.js +75 -0
- package/src/renderer/src/hooks/agent/useMarkPlan.js +2739 -0
- package/src/renderer/src/hooks/agent/useMarkState.js +173 -0
- package/src/renderer/src/hooks/agent/useMarkYoutube.js +82 -0
- package/src/renderer/src/hooks/agent/useRelationalGrowth.js +201 -0
- package/src/renderer/src/hooks/telegram/useTelegramBot.js +63 -0
- package/src/renderer/src/hooks/useAwareness.js +289 -0
- package/src/renderer/src/hooks/useChatArchiver.js +52 -0
- package/src/renderer/src/hooks/useConfirm.jsx +68 -0
- package/src/renderer/src/hooks/useMarkAgent.js +454 -0
- package/src/renderer/src/hooks/useMemoryGroomer.js +152 -0
- package/src/renderer/src/hooks/useVAD.js +511 -0
- package/src/renderer/src/hooks/useWebSpeechWakeWord.js +122 -0
- package/src/renderer/src/main.jsx +12 -0
- package/src/renderer/src/pages/ChatStudio.jsx +922 -0
- package/src/renderer/src/pages/Configuration.jsx +360 -0
- package/src/renderer/src/pages/GoogleWorkspace.jsx +293 -0
- package/src/renderer/src/pages/Guidebook.jsx +917 -0
- package/src/renderer/src/pages/Knowledge.jsx +241 -0
- package/src/renderer/src/pages/MarkHome.jsx +491 -0
- package/src/renderer/src/pages/NeuralCore.jsx +786 -0
- package/src/renderer/src/pages/Plugins.jsx +630 -0
- package/src/renderer/src/pages/SkillEditor.jsx +380 -0
- package/src/renderer/src/pages/Skills.jsx +219 -0
- package/src/renderer/src/pages/Subagents.jsx +342 -0
- package/src/renderer/src/pages/TelegramBot.jsx +343 -0
- package/src/renderer/src/utils/chatContentParser.js +203 -0
- package/src/renderer/src/utils/toolClusters.js +201 -0
- package/src/server/bin/sha3_wasm_bg.wasm +0 -0
- package/src/server/config-manager.js +57 -0
- package/src/server/index.js +195 -0
- package/src/server/launcher.js +87 -0
- package/src/server/memory/db-store.js +758 -0
- package/src/server/memory/orama-store.js +177 -0
- package/src/server/memory/vector-engine.js +109 -0
- package/src/server/routes/ai.routes.js +280 -0
- package/src/server/routes/awareness.routes.js +62 -0
- package/src/server/routes/chat.routes.js +61 -0
- package/src/server/routes/config.routes.js +41 -0
- package/src/server/routes/index.js +35 -0
- package/src/server/routes/integrations.routes.js +116 -0
- package/src/server/routes/memory.routes.js +256 -0
- package/src/server/routes/plugins.routes.js +72 -0
- package/src/server/routes/skills.routes.js +139 -0
- package/src/server/routes/subagents.routes.js +70 -0
- package/src/server/routes/tasks.routes.js +175 -0
- package/src/server/routes/workspace.routes.js +127 -0
- package/src/server/services/ai/ai-utils.js +161 -0
- package/src/server/services/ai/openai-provider.js +757 -0
- package/src/server/services/ai/web-provider.js +729 -0
- package/src/server/services/ai-bridge.js +46 -0
- package/src/server/services/deepseek-web.js +956 -0
- package/src/server/services/gemini-web.js +170 -0
- package/src/server/services/notification-service.js +91 -0
- package/src/server/services/updater.js +125 -0
- package/src/server/tools/awareness-tracker.js +292 -0
- package/src/server/tools/group-tools.js +1164 -0
- package/src/server/tools/media-tools.js +244 -0
- package/src/server/tools/pc-agent.js +327 -0
- package/src/server/tools/screen-service.js +51 -0
- package/src/server/ws-hub.js +168 -0
- package/src/shared/parsers/mark-tag-parser.js +275 -0
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
# win-action.ps1
|
|
2
|
+
# Native Windows Win32 action executor for MARK PC Automation
|
|
3
|
+
# Supports: click, type, key, scroll, open, list-windows, focus-window
|
|
4
|
+
|
|
5
|
+
param(
|
|
6
|
+
[string]$Action,
|
|
7
|
+
[int]$X = 0,
|
|
8
|
+
[int]$Y = 0,
|
|
9
|
+
[string]$Text = "",
|
|
10
|
+
[string]$Combo = "",
|
|
11
|
+
[string]$Direction = "down",
|
|
12
|
+
[int]$Amount = 3,
|
|
13
|
+
[string]$Target = ""
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
$ErrorActionPreference = "Continue"
|
|
17
|
+
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
|
18
|
+
|
|
19
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
20
|
+
Add-Type -AssemblyName System.Drawing
|
|
21
|
+
|
|
22
|
+
$code = @"
|
|
23
|
+
using System;
|
|
24
|
+
using System.Runtime.InteropServices;
|
|
25
|
+
using System.Text;
|
|
26
|
+
|
|
27
|
+
public class Win32 {
|
|
28
|
+
[DllImport("user32.dll")]
|
|
29
|
+
public static extern bool SetCursorPos(int X, int Y);
|
|
30
|
+
|
|
31
|
+
[DllImport("user32.dll")]
|
|
32
|
+
public static extern void mouse_event(uint dwFlags, uint dx, uint dy, int dwData, int dwExtraInfo);
|
|
33
|
+
|
|
34
|
+
[DllImport("user32.dll")]
|
|
35
|
+
public static extern IntPtr GetForegroundWindow();
|
|
36
|
+
|
|
37
|
+
[DllImport("user32.dll")]
|
|
38
|
+
public static extern bool SetForegroundWindow(IntPtr hWnd);
|
|
39
|
+
|
|
40
|
+
[DllImport("user32.dll")]
|
|
41
|
+
public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
|
|
42
|
+
|
|
43
|
+
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
|
|
44
|
+
public static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
|
|
45
|
+
|
|
46
|
+
[DllImport("user32.dll")]
|
|
47
|
+
public static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam);
|
|
48
|
+
|
|
49
|
+
[DllImport("user32.dll")]
|
|
50
|
+
public static extern bool IsWindowVisible(IntPtr hWnd);
|
|
51
|
+
|
|
52
|
+
[DllImport("user32.dll")]
|
|
53
|
+
public static extern bool IsIconic(IntPtr hWnd);
|
|
54
|
+
|
|
55
|
+
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
|
|
56
|
+
public static extern int GetWindowTextLength(IntPtr hWnd);
|
|
57
|
+
|
|
58
|
+
public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
|
|
59
|
+
|
|
60
|
+
public const uint MOUSEEVENTF_LEFTDOWN = 0x0002;
|
|
61
|
+
public const uint MOUSEEVENTF_LEFTUP = 0x0004;
|
|
62
|
+
public const uint MOUSEEVENTF_RIGHTDOWN = 0x0008;
|
|
63
|
+
public const uint MOUSEEVENTF_RIGHTUP = 0x0010;
|
|
64
|
+
public const uint MOUSEEVENTF_WHEEL = 0x0800;
|
|
65
|
+
|
|
66
|
+
[DllImport("user32.dll")]
|
|
67
|
+
public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);
|
|
68
|
+
public const int KEYEVENTF_EXTENDEDKEY = 0x0001;
|
|
69
|
+
public const byte VK_LWIN = 0x5B;
|
|
70
|
+
|
|
71
|
+
public static string ListWindowsJson() {
|
|
72
|
+
StringBuilder json = new StringBuilder();
|
|
73
|
+
json.Append("[");
|
|
74
|
+
bool first = true;
|
|
75
|
+
EnumWindows(delegate(IntPtr hWnd, IntPtr lParam) {
|
|
76
|
+
int len = GetWindowTextLength(hWnd);
|
|
77
|
+
if (len > 0) {
|
|
78
|
+
StringBuilder sb = new StringBuilder(len + 1);
|
|
79
|
+
GetWindowText(hWnd, sb, sb.Capacity);
|
|
80
|
+
string title = sb.ToString();
|
|
81
|
+
if (!string.IsNullOrWhiteSpace(title) && title != "Program Manager" && title != "Default IME" && !title.StartsWith("MSCTFIME")) {
|
|
82
|
+
if (!first) json.Append(",");
|
|
83
|
+
first = false;
|
|
84
|
+
json.Append("{\"hwnd\":").Append(hWnd.ToInt64()).Append(",\"title\":\"").Append(title.Replace("\\", "\\\\").Replace("\"", "\\\"")).Append("\"}");
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return true;
|
|
88
|
+
}, IntPtr.Zero);
|
|
89
|
+
json.Append("]");
|
|
90
|
+
return json.ToString();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
public static string FocusWindowByTitle(string target) {
|
|
94
|
+
string lowerTarget = target.ToLower();
|
|
95
|
+
bool found = false;
|
|
96
|
+
string foundTitle = "";
|
|
97
|
+
EnumWindows(delegate(IntPtr hWnd, IntPtr lParam) {
|
|
98
|
+
int len = GetWindowTextLength(hWnd);
|
|
99
|
+
if (len > 0) {
|
|
100
|
+
StringBuilder sb = new StringBuilder(len + 1);
|
|
101
|
+
GetWindowText(hWnd, sb, sb.Capacity);
|
|
102
|
+
string title = sb.ToString();
|
|
103
|
+
if (title.ToLower().Contains(lowerTarget)) {
|
|
104
|
+
ShowWindow(hWnd, 9); // SW_RESTORE = 9
|
|
105
|
+
SetForegroundWindow(hWnd);
|
|
106
|
+
found = true;
|
|
107
|
+
foundTitle = title;
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return true;
|
|
112
|
+
}, IntPtr.Zero);
|
|
113
|
+
if (found) {
|
|
114
|
+
return "{\"status\":\"success\",\"action\":\"focus-window\",\"title\":\"" + foundTitle.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\"}";
|
|
115
|
+
}
|
|
116
|
+
return "{\"status\":\"error\",\"message\":\"Window not found: " + target.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\"}";
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
public static bool IsMarkWindow(string title) {
|
|
120
|
+
if (string.IsNullOrWhiteSpace(title)) return true;
|
|
121
|
+
string t = title.Trim();
|
|
122
|
+
if (t == "Program Manager" || t == "Default IME" || t.StartsWith("MSCTFIME")) return true;
|
|
123
|
+
string lower = t.ToLower();
|
|
124
|
+
if (lower == "mark" || lower.StartsWith("mark -") || lower.StartsWith("mark_pc_stop") || lower.StartsWith("mark_unblock") || lower.Contains("mark agent") || lower.Contains("mark pc automation")) {
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
public static void EnsureTargetWindowFocused() {
|
|
131
|
+
IntPtr fg = GetForegroundWindow();
|
|
132
|
+
string fgTitle = "";
|
|
133
|
+
int fgLen = GetWindowTextLength(fg);
|
|
134
|
+
if (fgLen > 0) {
|
|
135
|
+
StringBuilder fgSb = new StringBuilder(fgLen + 1);
|
|
136
|
+
GetWindowText(fg, fgSb, fgSb.Capacity);
|
|
137
|
+
fgTitle = fgSb.ToString();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (!IsMarkWindow(fgTitle)) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
IntPtr target = IntPtr.Zero;
|
|
145
|
+
EnumWindows(delegate(IntPtr hWnd, IntPtr lParam) {
|
|
146
|
+
if (!IsWindowVisible(hWnd) || IsIconic(hWnd)) return true;
|
|
147
|
+
int len = GetWindowTextLength(hWnd);
|
|
148
|
+
if (len <= 0) return true;
|
|
149
|
+
StringBuilder sb = new StringBuilder(len + 1);
|
|
150
|
+
GetWindowText(hWnd, sb, sb.Capacity);
|
|
151
|
+
string title = sb.ToString();
|
|
152
|
+
|
|
153
|
+
if (!IsMarkWindow(title)) {
|
|
154
|
+
target = hWnd;
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
return true;
|
|
158
|
+
}, IntPtr.Zero);
|
|
159
|
+
|
|
160
|
+
if (target != IntPtr.Zero) {
|
|
161
|
+
SetForegroundWindow(target);
|
|
162
|
+
System.Threading.Thread.Sleep(200);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
167
|
+
public struct KEYBDINPUT {
|
|
168
|
+
public ushort wVk;
|
|
169
|
+
public ushort wScan;
|
|
170
|
+
public uint dwFlags;
|
|
171
|
+
public uint time;
|
|
172
|
+
public IntPtr dwExtraInfo;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
[StructLayout(LayoutKind.Sequential)]
|
|
176
|
+
public struct INPUT {
|
|
177
|
+
public int type;
|
|
178
|
+
public KEYBDINPUT ki;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
[DllImport("user32.dll", SetLastError = true)]
|
|
182
|
+
public static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
|
|
183
|
+
|
|
184
|
+
public const int INPUT_KEYBOARD = 1;
|
|
185
|
+
public const uint KEYEVENTF_KEYUP = 0x0002;
|
|
186
|
+
public const uint KEYEVENTF_UNICODE = 0x0004;
|
|
187
|
+
public const ushort VK_RETURN = 0x0D;
|
|
188
|
+
|
|
189
|
+
public static string TypeTextUnicode(string text) {
|
|
190
|
+
string normalized = text.Replace("\r\n", "\n").Replace("\r", "\n");
|
|
191
|
+
foreach (char c in normalized) {
|
|
192
|
+
if (c == '\n') {
|
|
193
|
+
INPUT[] inputs = new INPUT[2];
|
|
194
|
+
inputs[0].type = INPUT_KEYBOARD;
|
|
195
|
+
inputs[0].ki.wVk = VK_RETURN;
|
|
196
|
+
inputs[0].ki.wScan = 0;
|
|
197
|
+
inputs[0].ki.dwFlags = 0;
|
|
198
|
+
|
|
199
|
+
inputs[1].type = INPUT_KEYBOARD;
|
|
200
|
+
inputs[1].ki.wVk = VK_RETURN;
|
|
201
|
+
inputs[1].ki.wScan = 0;
|
|
202
|
+
inputs[1].ki.dwFlags = KEYEVENTF_KEYUP;
|
|
203
|
+
|
|
204
|
+
SendInput(2, inputs, Marshal.SizeOf(typeof(INPUT)));
|
|
205
|
+
System.Threading.Thread.Sleep(15);
|
|
206
|
+
} else {
|
|
207
|
+
INPUT[] inputs = new INPUT[2];
|
|
208
|
+
inputs[0].type = INPUT_KEYBOARD;
|
|
209
|
+
inputs[0].ki.wVk = 0;
|
|
210
|
+
inputs[0].ki.wScan = (ushort)c;
|
|
211
|
+
inputs[0].ki.dwFlags = KEYEVENTF_UNICODE;
|
|
212
|
+
|
|
213
|
+
inputs[1].type = INPUT_KEYBOARD;
|
|
214
|
+
inputs[1].ki.wVk = 0;
|
|
215
|
+
inputs[1].ki.wScan = (ushort)c;
|
|
216
|
+
inputs[1].ki.dwFlags = KEYEVENTF_UNICODE | KEYEVENTF_KEYUP;
|
|
217
|
+
|
|
218
|
+
SendInput(2, inputs, Marshal.SizeOf(typeof(INPUT)));
|
|
219
|
+
System.Threading.Thread.Sleep(8);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return "{\"status\":\"success\",\"action\":\"type\",\"text\":\"" + text.Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("\r", "\\r").Replace("\n", "\\n") + "\"}";
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
"@
|
|
226
|
+
Add-Type -TypeDefinition $code -Language CSharp
|
|
227
|
+
|
|
228
|
+
function Execute-Click {
|
|
229
|
+
[Win32]::SetCursorPos($X, $Y) | Out-Null
|
|
230
|
+
Start-Sleep -Milliseconds 50
|
|
231
|
+
[Win32]::mouse_event([Win32]::MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0)
|
|
232
|
+
Start-Sleep -Milliseconds 30
|
|
233
|
+
[Win32]::mouse_event([Win32]::MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)
|
|
234
|
+
Write-Output "({`"status`":`"success`",`"action`":`"click`",`"x`":$X,`"y`":$Y})".Trim("()")
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function Execute-DoubleClick {
|
|
238
|
+
[Win32]::SetCursorPos($X, $Y) | Out-Null
|
|
239
|
+
Start-Sleep -Milliseconds 50
|
|
240
|
+
[Win32]::mouse_event([Win32]::MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0)
|
|
241
|
+
Start-Sleep -Milliseconds 30
|
|
242
|
+
[Win32]::mouse_event([Win32]::MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)
|
|
243
|
+
Start-Sleep -Milliseconds 50
|
|
244
|
+
[Win32]::mouse_event([Win32]::MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0)
|
|
245
|
+
Start-Sleep -Milliseconds 30
|
|
246
|
+
[Win32]::mouse_event([Win32]::MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)
|
|
247
|
+
Write-Output "({`"status`":`"success`",`"action`":`"doubleclick`",`"x`":$X,`"y`":$Y})".Trim("()")
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function Escape-SendKeys {
|
|
251
|
+
param([string]$str)
|
|
252
|
+
if ([string]::IsNullOrEmpty($str)) { return "" }
|
|
253
|
+
$res = ""
|
|
254
|
+
foreach ($char in $str.ToCharArray()) {
|
|
255
|
+
if ("+^%~(){}[]".Contains($char.ToString())) {
|
|
256
|
+
$res += "{$char}"
|
|
257
|
+
} elseif ($char -eq "`n" -or $char -eq "`r") {
|
|
258
|
+
$res += "{ENTER}"
|
|
259
|
+
} else {
|
|
260
|
+
$res += $char
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return $res
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function Execute-Type {
|
|
267
|
+
[Win32]::EnsureTargetWindowFocused()
|
|
268
|
+
if (-not [string]::IsNullOrEmpty($Text)) {
|
|
269
|
+
$escaped = Escape-SendKeys -str $Text
|
|
270
|
+
try {
|
|
271
|
+
[System.Windows.Forms.SendKeys]::SendWait($escaped)
|
|
272
|
+
Write-Output "({`"status`":`"success`",`"action`":`"type`",`"text`":`"$Text`"})".Trim("()")
|
|
273
|
+
} catch {
|
|
274
|
+
Write-Output '{"status":"error","message":"SendKeys failed for type"}'
|
|
275
|
+
}
|
|
276
|
+
} else {
|
|
277
|
+
Write-Output '{"status":"error","message":"Empty text"}'
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function Execute-Key {
|
|
282
|
+
[Win32]::EnsureTargetWindowFocused()
|
|
283
|
+
if (-not [string]::IsNullOrEmpty($Combo)) {
|
|
284
|
+
$keys = $Combo.ToLower().Trim()
|
|
285
|
+
$sendStr = ""
|
|
286
|
+
|
|
287
|
+
# Convert common key combos to SendKeys format
|
|
288
|
+
$keys = $keys -replace 'ctrl\+', '^'
|
|
289
|
+
$keys = $keys -replace 'alt\+', '%'
|
|
290
|
+
$keys = $keys -replace 'shift\+', '+'
|
|
291
|
+
|
|
292
|
+
$specialMap = @{
|
|
293
|
+
"enter" = "{ENTER}"
|
|
294
|
+
"tab" = "{TAB}"
|
|
295
|
+
"esc" = "{ESC}"
|
|
296
|
+
"escape" = "{ESC}"
|
|
297
|
+
"backspace" = "{BACKSPACE}"
|
|
298
|
+
"del" = "{DELETE}"
|
|
299
|
+
"delete" = "{DELETE}"
|
|
300
|
+
"up" = "{UP}"
|
|
301
|
+
"down" = "{DOWN}"
|
|
302
|
+
"left" = "{LEFT}"
|
|
303
|
+
"right" = "{RIGHT}"
|
|
304
|
+
"home" = "{HOME}"
|
|
305
|
+
"end" = "{END}"
|
|
306
|
+
"space" = " "
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if ($keys -eq "win") {
|
|
310
|
+
[Win32]::keybd_event([Win32]::VK_LWIN, 0, 0, 0)
|
|
311
|
+
Start-Sleep -Milliseconds 20
|
|
312
|
+
[Win32]::keybd_event([Win32]::VK_LWIN, 0, [Win32]::KEYEVENTF_KEYUP, 0)
|
|
313
|
+
Write-Output "({`"status`":`"success`",`"action`":`"key`",`"combo`":`"$Combo`"})".Trim("()")
|
|
314
|
+
return
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if ($specialMap.ContainsKey($keys)) {
|
|
318
|
+
$sendStr = $specialMap[$keys]
|
|
319
|
+
} else {
|
|
320
|
+
$sendStr = $keys
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
try {
|
|
324
|
+
[System.Windows.Forms.SendKeys]::SendWait($sendStr)
|
|
325
|
+
Write-Output "({`"status`":`"success`",`"action`":`"key`",`"combo`":`"$Combo`"})".Trim("()")
|
|
326
|
+
} catch {
|
|
327
|
+
Write-Output "({`"status`":`"error`",`"message`":`"Invalid key combo: $Combo`"})".Trim("()")
|
|
328
|
+
}
|
|
329
|
+
} else {
|
|
330
|
+
Write-Output '{"status":"error","message":"Empty combo"}'
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function Execute-Scroll {
|
|
335
|
+
$delta = 120 * $Amount
|
|
336
|
+
if ($Direction -eq "down") {
|
|
337
|
+
$delta = -$delta
|
|
338
|
+
}
|
|
339
|
+
[Win32]::mouse_event([Win32]::MOUSEEVENTF_WHEEL, 0, 0, $delta, 0)
|
|
340
|
+
Write-Output "({`"status`":`"success`",`"action`":`"scroll`",`"direction`":`"$Direction`",`"amount`":$Amount})".Trim("()")
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
function Execute-Open {
|
|
344
|
+
if (-not [string]::IsNullOrEmpty($Target)) {
|
|
345
|
+
try {
|
|
346
|
+
Start-Process -FilePath $Target
|
|
347
|
+
Write-Output "({`"status`":`"success`",`"action`":`"open`",`"target`":`"$Target`"})".Trim("()")
|
|
348
|
+
} catch {
|
|
349
|
+
Write-Output "({`"status`":`"error`",`"message`":`"Failed to open app: $Target`"})".Trim("()")
|
|
350
|
+
}
|
|
351
|
+
} else {
|
|
352
|
+
Write-Output '{"status":"error","message":"Empty target"}'
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function Execute-ListWindows {
|
|
357
|
+
$res = [Win32]::ListWindowsJson()
|
|
358
|
+
Write-Output $res
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function Execute-FocusWindow {
|
|
362
|
+
if (-not [string]::IsNullOrEmpty($Target)) {
|
|
363
|
+
$res = [Win32]::FocusWindowByTitle($Target)
|
|
364
|
+
Write-Output $res
|
|
365
|
+
} else {
|
|
366
|
+
Write-Output '{"status":"error","message":"Empty target"}'
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
switch ($Action.ToLower()) {
|
|
371
|
+
"click" { Execute-Click }
|
|
372
|
+
"doubleclick" { Execute-DoubleClick }
|
|
373
|
+
"type" { Execute-Type }
|
|
374
|
+
"key" { Execute-Key }
|
|
375
|
+
"scroll" { Execute-Scroll }
|
|
376
|
+
"open" { Execute-Open }
|
|
377
|
+
"list-windows" { Execute-ListWindows }
|
|
378
|
+
"focus-window" { Execute-FocusWindow }
|
|
379
|
+
default { Write-Output '{"status":"error","message":"Unknown action: ' + $Action + '"}' }
|
|
380
|
+
}
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import os from 'os'
|
|
4
|
+
import { pathToFileURL } from 'url'
|
|
5
|
+
import { execSync } from 'child_process'
|
|
6
|
+
|
|
7
|
+
let loadedPlugins = []
|
|
8
|
+
let pluginActionHandlers = new Map()
|
|
9
|
+
|
|
10
|
+
export function getPluginsDirectory() {
|
|
11
|
+
const dir = path.join(os.homedir(), 'Documents', 'Mark Plugins')
|
|
12
|
+
if (!fs.existsSync(dir)) {
|
|
13
|
+
fs.mkdirSync(dir, { recursive: true })
|
|
14
|
+
}
|
|
15
|
+
return dir
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const getPluginsDir = getPluginsDirectory
|
|
19
|
+
|
|
20
|
+
export async function loadAllPlugins() {
|
|
21
|
+
const dir = getPluginsDirectory()
|
|
22
|
+
loadedPlugins = []
|
|
23
|
+
pluginActionHandlers.clear()
|
|
24
|
+
|
|
25
|
+
const entries = await fs.promises.readdir(dir, { withFileTypes: true })
|
|
26
|
+
for (const entry of entries) {
|
|
27
|
+
if (entry.isDirectory()) {
|
|
28
|
+
const pluginDir = path.join(dir, entry.name)
|
|
29
|
+
const manifestPath = path.join(pluginDir, 'plugin.json')
|
|
30
|
+
const indexPath = path.join(pluginDir, 'index.js')
|
|
31
|
+
|
|
32
|
+
if (fs.existsSync(manifestPath) && fs.existsSync(indexPath)) {
|
|
33
|
+
try {
|
|
34
|
+
const manifestRaw = await fs.promises.readFile(manifestPath, 'utf-8')
|
|
35
|
+
const manifest = JSON.parse(manifestRaw)
|
|
36
|
+
manifest.folderName = entry.name
|
|
37
|
+
manifest.folderPath = pluginDir
|
|
38
|
+
|
|
39
|
+
const moduleUrl = `${pathToFileURL(indexPath).href}?t=${Date.now()}`
|
|
40
|
+
const module = await import(moduleUrl)
|
|
41
|
+
const handlerInstance = module.default || module
|
|
42
|
+
|
|
43
|
+
const indexContent = await fs.promises.readFile(indexPath, 'utf-8')
|
|
44
|
+
|
|
45
|
+
if (manifest.isEnabled !== false && Array.isArray(manifest.actions)) {
|
|
46
|
+
for (const act of manifest.actions) {
|
|
47
|
+
const actHandler =
|
|
48
|
+
handlerInstance[act.name] ||
|
|
49
|
+
(typeof handlerInstance === 'function' ? handlerInstance : null)
|
|
50
|
+
if (typeof actHandler === 'function') {
|
|
51
|
+
pluginActionHandlers.set(act.name, {
|
|
52
|
+
handler: actHandler,
|
|
53
|
+
pluginName: manifest.name,
|
|
54
|
+
actionName: act.name
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Extract function body for Monaco editor
|
|
59
|
+
const searchPatterns = [
|
|
60
|
+
`'${act.name}': async ({ query }) => {`,
|
|
61
|
+
`"${act.name}": async ({ query }) => {`,
|
|
62
|
+
`${act.name}: async ({ query }) => {`,
|
|
63
|
+
`'${act.name}': async (query) => {`,
|
|
64
|
+
`"${act.name}": async (query) => {`,
|
|
65
|
+
`${act.name}: async (query) => {`
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
let startIdx = -1
|
|
69
|
+
let matchedPatternLen = 0
|
|
70
|
+
for (const pat of searchPatterns) {
|
|
71
|
+
const idx = indexContent.indexOf(pat)
|
|
72
|
+
if (idx !== -1) {
|
|
73
|
+
startIdx = idx
|
|
74
|
+
matchedPatternLen = pat.length
|
|
75
|
+
break
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (startIdx !== -1) {
|
|
80
|
+
let i = startIdx + matchedPatternLen
|
|
81
|
+
let openBrackets = 1
|
|
82
|
+
for (; i < indexContent.length; i++) {
|
|
83
|
+
if (indexContent[i] === '{') openBrackets++
|
|
84
|
+
if (indexContent[i] === '}') {
|
|
85
|
+
openBrackets--
|
|
86
|
+
if (openBrackets === 0) break
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const rawCode = indexContent.substring(startIdx + matchedPatternLen, i)
|
|
90
|
+
act.code = rawCode
|
|
91
|
+
.split('\n')
|
|
92
|
+
.map((l) =>
|
|
93
|
+
l.startsWith(' ') ? l.substring(4) : l.startsWith(' ') ? l.substring(2) : l
|
|
94
|
+
)
|
|
95
|
+
.join('\n')
|
|
96
|
+
.trim()
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
loadedPlugins.push(manifest)
|
|
102
|
+
} catch (err) {
|
|
103
|
+
console.error(`[Plugin Loader] Gagal memuat plugin ${entry.name}:`, err)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return loadedPlugins
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export const loadPlugins = loadAllPlugins
|
|
112
|
+
export const getLoadedPlugins = () => loadedPlugins
|
|
113
|
+
export const getPluginHandlers = () => Object.fromEntries(pluginActionHandlers.entries())
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Normalisasi query input dari AI/UI menjadi string tunggal bersih
|
|
117
|
+
*/
|
|
118
|
+
export function normalizePluginQuery(rawQuery) {
|
|
119
|
+
if (rawQuery === null || rawQuery === undefined) return ''
|
|
120
|
+
if (typeof rawQuery === 'string') return rawQuery
|
|
121
|
+
if (typeof rawQuery === 'number' || typeof rawQuery === 'boolean') return String(rawQuery)
|
|
122
|
+
if (typeof rawQuery === 'object') {
|
|
123
|
+
if (rawQuery.query !== undefined) {
|
|
124
|
+
return typeof rawQuery.query === 'object'
|
|
125
|
+
? JSON.stringify(rawQuery.query)
|
|
126
|
+
: String(rawQuery.query)
|
|
127
|
+
}
|
|
128
|
+
if (rawQuery.value !== undefined) return String(rawQuery.value)
|
|
129
|
+
if (rawQuery.volume !== undefined) return String(rawQuery.volume)
|
|
130
|
+
if (rawQuery.text !== undefined) return String(rawQuery.text)
|
|
131
|
+
if (rawQuery.prompt !== undefined) return String(rawQuery.prompt)
|
|
132
|
+
if (rawQuery.target !== undefined) return String(rawQuery.target)
|
|
133
|
+
if (rawQuery.input !== undefined) return String(rawQuery.input)
|
|
134
|
+
const vals = Object.values(rawQuery)
|
|
135
|
+
if (vals.length === 1 && typeof vals[0] !== 'object') {
|
|
136
|
+
return String(vals[0])
|
|
137
|
+
}
|
|
138
|
+
return JSON.stringify(rawQuery)
|
|
139
|
+
}
|
|
140
|
+
return String(rawQuery)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function findRegisteredAction(actionName) {
|
|
144
|
+
let registered = pluginActionHandlers.get(actionName)
|
|
145
|
+
if (!registered && typeof actionName === 'string') {
|
|
146
|
+
const lowerName = actionName.toLowerCase()
|
|
147
|
+
for (const [key, value] of pluginActionHandlers.entries()) {
|
|
148
|
+
const fullKey = `plugin-${value.pluginName}-${value.actionName}`.toLowerCase()
|
|
149
|
+
if (
|
|
150
|
+
key.toLowerCase() === lowerName ||
|
|
151
|
+
fullKey === lowerName ||
|
|
152
|
+
lowerName.endsWith(`-${key.toLowerCase()}`) ||
|
|
153
|
+
lowerName.endsWith(`_${key.toLowerCase()}`)
|
|
154
|
+
) {
|
|
155
|
+
return value
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return registered
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export async function executePluginAction(actionName, query) {
|
|
163
|
+
let registered = findRegisteredAction(actionName)
|
|
164
|
+
|
|
165
|
+
// Lazy reload on cache miss jika plugin baru ditambah ke disk saat runtime
|
|
166
|
+
if (!registered) {
|
|
167
|
+
await loadAllPlugins()
|
|
168
|
+
registered = findRegisteredAction(actionName)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (!registered) {
|
|
172
|
+
return {
|
|
173
|
+
success: false,
|
|
174
|
+
error: `Action plugin '${actionName}' tidak ditemukan atau sedang dinonaktifkan.`
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
const cleanQuery = normalizePluginQuery(query)
|
|
180
|
+
|
|
181
|
+
// Buat polymorphic payload agar kompatibel dengan handler `{ query }` maupun `(query)` atau direct template literals
|
|
182
|
+
const payload = {
|
|
183
|
+
query: cleanQuery,
|
|
184
|
+
...(typeof query === 'object' && query !== null ? query : {}),
|
|
185
|
+
toString: () => cleanQuery,
|
|
186
|
+
valueOf: () => cleanQuery,
|
|
187
|
+
[Symbol.toPrimitive]: () => cleanQuery
|
|
188
|
+
}
|
|
189
|
+
payload.query = cleanQuery
|
|
190
|
+
|
|
191
|
+
const result = await registered.handler(payload)
|
|
192
|
+
return { success: true, data: result }
|
|
193
|
+
} catch (err) {
|
|
194
|
+
console.error(`[Plugin Execution Error] ${actionName}:`, err)
|
|
195
|
+
return { success: false, error: err.message }
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export async function savePluginDefinition(payload) {
|
|
200
|
+
const {
|
|
201
|
+
name,
|
|
202
|
+
displayName,
|
|
203
|
+
description,
|
|
204
|
+
actions = [],
|
|
205
|
+
dependencies = [],
|
|
206
|
+
isEdit = false
|
|
207
|
+
} = payload
|
|
208
|
+
const cleanName = (name || '').replace(/[^a-zA-Z0-9-_]/g, '-').toLowerCase()
|
|
209
|
+
if (!cleanName) {
|
|
210
|
+
throw new Error('Nama plugin wajib diisi.')
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const dir = getPluginsDirectory()
|
|
214
|
+
const pluginDir = path.join(dir, cleanName)
|
|
215
|
+
|
|
216
|
+
if (!isEdit && fs.existsSync(pluginDir)) {
|
|
217
|
+
throw new Error(`Plugin dengan nama '${cleanName}' sudah ada.`)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (!fs.existsSync(pluginDir)) {
|
|
221
|
+
fs.mkdirSync(pluginDir, { recursive: true })
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const depsList = Array.isArray(dependencies)
|
|
225
|
+
? dependencies
|
|
226
|
+
: typeof dependencies === 'string'
|
|
227
|
+
? dependencies
|
|
228
|
+
.split(',')
|
|
229
|
+
.map((d) => d.trim())
|
|
230
|
+
.filter(Boolean)
|
|
231
|
+
: []
|
|
232
|
+
|
|
233
|
+
const manifestActions = actions.map((act) => ({
|
|
234
|
+
name: (act.name || '').trim().replace(/\s+/g, '_').toLowerCase(),
|
|
235
|
+
description: act.description || '',
|
|
236
|
+
triggerHint: act.triggerHint || '',
|
|
237
|
+
code: act.code || 'return null;'
|
|
238
|
+
}))
|
|
239
|
+
|
|
240
|
+
const manifest = {
|
|
241
|
+
name: cleanName,
|
|
242
|
+
displayName: displayName || cleanName,
|
|
243
|
+
description: description || '',
|
|
244
|
+
version: '1.0.0',
|
|
245
|
+
isEnabled: true,
|
|
246
|
+
dependencies: depsList,
|
|
247
|
+
actions: manifestActions
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
let jsCode = `// Auto-generated Plugin Module for MARK V5\n// Plugin: ${cleanName}\n\nexport default {\n`
|
|
251
|
+
manifest.actions.forEach((act, idx) => {
|
|
252
|
+
jsCode += ` '${act.name}': async ({ query }) => {\n`
|
|
253
|
+
const lines = (act.code || 'return null;').split('\n')
|
|
254
|
+
for (const l of lines) {
|
|
255
|
+
jsCode += ` ${l}\n`
|
|
256
|
+
}
|
|
257
|
+
jsCode += ` }${idx < manifest.actions.length - 1 ? ',' : ''}\n\n`
|
|
258
|
+
})
|
|
259
|
+
jsCode += `};\n`
|
|
260
|
+
|
|
261
|
+
await fs.promises.writeFile(
|
|
262
|
+
path.join(pluginDir, 'plugin.json'),
|
|
263
|
+
JSON.stringify(manifest, null, 2),
|
|
264
|
+
'utf-8'
|
|
265
|
+
)
|
|
266
|
+
await fs.promises.writeFile(path.join(pluginDir, 'index.js'), jsCode, 'utf-8')
|
|
267
|
+
|
|
268
|
+
if (depsList.length > 0) {
|
|
269
|
+
try {
|
|
270
|
+
if (!fs.existsSync(path.join(pluginDir, 'package.json'))) {
|
|
271
|
+
execSync('npm init -y', { cwd: pluginDir, stdio: 'ignore' })
|
|
272
|
+
}
|
|
273
|
+
execSync(`npm install ${depsList.join(' ')}`, { cwd: pluginDir, stdio: 'ignore' })
|
|
274
|
+
} catch (npmErr) {
|
|
275
|
+
console.error('[Plugin Dependencies] Gagal install npm dependencies:', npmErr)
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
await loadAllPlugins()
|
|
280
|
+
return manifest
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export async function togglePluginState(pluginName, isEnabled) {
|
|
284
|
+
const dir = getPluginsDirectory()
|
|
285
|
+
const manifestPath = path.join(dir, pluginName, 'plugin.json')
|
|
286
|
+
if (fs.existsSync(manifestPath)) {
|
|
287
|
+
const raw = await fs.promises.readFile(manifestPath, 'utf-8')
|
|
288
|
+
const manifest = JSON.parse(raw)
|
|
289
|
+
manifest.isEnabled = isEnabled
|
|
290
|
+
await fs.promises.writeFile(manifestPath, JSON.stringify(manifest, null, 2), 'utf-8')
|
|
291
|
+
await loadAllPlugins()
|
|
292
|
+
return true
|
|
293
|
+
}
|
|
294
|
+
return false
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export async function deletePlugin(pluginName) {
|
|
298
|
+
const dir = getPluginsDirectory()
|
|
299
|
+
const pluginDir = path.join(dir, pluginName)
|
|
300
|
+
if (fs.existsSync(pluginDir)) {
|
|
301
|
+
await fs.promises.rm(pluginDir, { recursive: true, force: true })
|
|
302
|
+
await loadAllPlugins()
|
|
303
|
+
return true
|
|
304
|
+
}
|
|
305
|
+
return false
|
|
306
|
+
}
|