@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.
Files changed (227) hide show
  1. package/LICENSE +29 -0
  2. package/README.md +209 -0
  3. package/bin/mark.js +27 -0
  4. package/out/renderer/assets/icon-DHo2CYph.svg +1 -0
  5. package/out/renderer/assets/index-G2fADWO-.js +4768 -0
  6. package/out/renderer/assets/index-f3mVse3c.css +1 -0
  7. package/out/renderer/assets/music-cover-CG4vi3kZ.png +0 -0
  8. package/out/renderer/assets/poppins-devanagari-400-normal-CJDn6rn8.woff2 +0 -0
  9. package/out/renderer/assets/poppins-devanagari-400-normal-CqVvlrh5.woff +0 -0
  10. package/out/renderer/assets/poppins-devanagari-500-normal-BIdkeU1p.woff2 +0 -0
  11. package/out/renderer/assets/poppins-devanagari-500-normal-DMPDjHtT.woff +0 -0
  12. package/out/renderer/assets/poppins-devanagari-600-normal-ClASKHrr.woff +0 -0
  13. package/out/renderer/assets/poppins-devanagari-600-normal-STEjXBNN.woff2 +0 -0
  14. package/out/renderer/assets/poppins-devanagari-700-normal-O-jipLrW.woff2 +0 -0
  15. package/out/renderer/assets/poppins-devanagari-700-normal-fHs-vx92.woff +0 -0
  16. package/out/renderer/assets/poppins-latin-400-normal-BOb3E3N0.woff +0 -0
  17. package/out/renderer/assets/poppins-latin-400-normal-cpxAROuN.woff2 +0 -0
  18. package/out/renderer/assets/poppins-latin-500-normal-C8OXljZJ.woff2 +0 -0
  19. package/out/renderer/assets/poppins-latin-500-normal-DGXqpDMm.woff +0 -0
  20. package/out/renderer/assets/poppins-latin-600-normal-BJdTmd5m.woff +0 -0
  21. package/out/renderer/assets/poppins-latin-600-normal-zEkxB9Mr.woff2 +0 -0
  22. package/out/renderer/assets/poppins-latin-700-normal-BVuQR_eA.woff +0 -0
  23. package/out/renderer/assets/poppins-latin-700-normal-Qrb0O0WB.woff2 +0 -0
  24. package/out/renderer/assets/poppins-latin-ext-400-normal-DaBSavcJ.woff +0 -0
  25. package/out/renderer/assets/poppins-latin-ext-400-normal-by3JarPu.woff2 +0 -0
  26. package/out/renderer/assets/poppins-latin-ext-500-normal-CK-6C4Hw.woff2 +0 -0
  27. package/out/renderer/assets/poppins-latin-ext-500-normal-CgAe2rWW.woff +0 -0
  28. package/out/renderer/assets/poppins-latin-ext-600-normal-CAhIAdZj.woff2 +0 -0
  29. package/out/renderer/assets/poppins-latin-ext-600-normal-Df5ffKXP.woff +0 -0
  30. package/out/renderer/assets/poppins-latin-ext-700-normal-DctTR6Tg.woff +0 -0
  31. package/out/renderer/assets/poppins-latin-ext-700-normal-cby-RkWa.woff2 +0 -0
  32. package/out/renderer/favicon.ico +0 -0
  33. package/out/renderer/icon-256.png +0 -0
  34. package/out/renderer/icon.svg +1 -0
  35. package/out/renderer/index.html +25 -0
  36. package/out/renderer/manifest.json +25 -0
  37. package/out/renderer/robot-simulator.html +1640 -0
  38. package/package.json +106 -0
  39. package/src/cli/index.js +332 -0
  40. package/src/cli/monitor.js +413 -0
  41. package/src/cli/theme.js +347 -0
  42. package/src/launcher/MarkTray.cs +227 -0
  43. package/src/main/browser-agent.js +1132 -0
  44. package/src/main/git-service.js +90 -0
  45. package/src/main/google/google-calendar.js +69 -0
  46. package/src/main/google/google-drive.js +263 -0
  47. package/src/main/google/google-gmail.js +131 -0
  48. package/src/main/google/google-service.js +241 -0
  49. package/src/main/node-tools.js +23 -0
  50. package/src/main/pc-agent-scripts/mouse-locker.ps1 +76 -0
  51. package/src/main/pc-agent-scripts/ocr-region.ps1 +185 -0
  52. package/src/main/pc-agent-scripts/pc-daemon.ps1 +726 -0
  53. package/src/main/pc-agent-scripts/pc-overlay.ps1 +253 -0
  54. package/src/main/pc-agent-scripts/pick-folder.ps1 +33 -0
  55. package/src/main/pc-agent-scripts/read-ui.ps1 +181 -0
  56. package/src/main/pc-agent-scripts/take-screenshot.ps1 +24 -0
  57. package/src/main/pc-agent-scripts/win-action.ps1 +380 -0
  58. package/src/main/plugins/plugin-loader.js +306 -0
  59. package/src/main/skills/skill-manager.js +265 -0
  60. package/src/main/syntax-validator.js +455 -0
  61. package/src/main/task-daemon.js +117 -0
  62. package/src/main/telegram/telegram-service.js +835 -0
  63. package/src/main/tools/browser-tools.js +516 -0
  64. package/src/main/tools/file-tools.js +1126 -0
  65. package/src/main/tools/git-tools.js +79 -0
  66. package/src/main/tools/google-tools.js +566 -0
  67. package/src/main/tools/index.js +36 -0
  68. package/src/main/tools/media-tools.js +42 -0
  69. package/src/main/tools/system-tools.js +295 -0
  70. package/src/main/tools/task-tools.js +73 -0
  71. package/src/main/tools/telegram-tools.js +101 -0
  72. package/src/renderer/index.html +24 -0
  73. package/src/renderer/public/favicon.ico +0 -0
  74. package/src/renderer/public/icon-256.png +0 -0
  75. package/src/renderer/public/icon.svg +1 -0
  76. package/src/renderer/public/manifest.json +25 -0
  77. package/src/renderer/public/robot-simulator.html +1640 -0
  78. package/src/renderer/src/App.jsx +304 -0
  79. package/src/renderer/src/api/ai/awareness.js +294 -0
  80. package/src/renderer/src/api/ai/chatSummarizer.js +79 -0
  81. package/src/renderer/src/api/ai/contextCompactor.js +129 -0
  82. package/src/renderer/src/api/ai/contextManager.js +1130 -0
  83. package/src/renderer/src/api/ai/core.js +167 -0
  84. package/src/renderer/src/api/ai/index.js +4 -0
  85. package/src/renderer/src/api/ai/memoryGroomer.js +101 -0
  86. package/src/renderer/src/api/ai/persona.js +115 -0
  87. package/src/renderer/src/api/ai/planning.js +317 -0
  88. package/src/renderer/src/api/ai/relationship.js +177 -0
  89. package/src/renderer/src/api/ai/skillSynthesizer.js +168 -0
  90. package/src/renderer/src/api/ai/tools.js +167 -0
  91. package/src/renderer/src/api/ai/utils.js +359 -0
  92. package/src/renderer/src/api/db.js +1013 -0
  93. package/src/renderer/src/api/oramaStore.js +592 -0
  94. package/src/renderer/src/api/ragPipeline.js +87 -0
  95. package/src/renderer/src/api/subagent/subagentExecutor.js +780 -0
  96. package/src/renderer/src/api/subagent/subagentPrompt.js +44 -0
  97. package/src/renderer/src/api/subagent/subagentStore.js +173 -0
  98. package/src/renderer/src/api/taskExecutor.js +18 -0
  99. package/src/renderer/src/api/taskStore.js +315 -0
  100. package/src/renderer/src/api/tools/core-tools.js +707 -0
  101. package/src/renderer/src/api/tools/index.js +151 -0
  102. package/src/renderer/src/api/turnPairMigrator.js +200 -0
  103. package/src/renderer/src/api/vectorMemory.js +136 -0
  104. package/src/renderer/src/api/wakeWord.js +101 -0
  105. package/src/renderer/src/api/web-bridge.js +885 -0
  106. package/src/renderer/src/api/webSpeech.js +178 -0
  107. package/src/renderer/src/api/workspaceRag.js +76 -0
  108. package/src/renderer/src/assets/electron.svg +10 -0
  109. package/src/renderer/src/assets/icon.svg +1 -0
  110. package/src/renderer/src/assets/main.css +589 -0
  111. package/src/renderer/src/assets/music-cover.png +0 -0
  112. package/src/renderer/src/assets/wavy-lines.svg +25 -0
  113. package/src/renderer/src/components/Chat/ApprovalBubble.jsx +127 -0
  114. package/src/renderer/src/components/Chat/CodeBlock.jsx +83 -0
  115. package/src/renderer/src/components/Chat/DurableTaskBubble.jsx +455 -0
  116. package/src/renderer/src/components/Chat/FileDiffModal.jsx +328 -0
  117. package/src/renderer/src/components/Chat/GeminiWidgets.jsx +82 -0
  118. package/src/renderer/src/components/Chat/MemoryFooterBubble.jsx +34 -0
  119. package/src/renderer/src/components/Chat/MessageBubble.jsx +459 -0
  120. package/src/renderer/src/components/Chat/PluginExecutionBubble.jsx +41 -0
  121. package/src/renderer/src/components/Chat/ThinkingBubble.jsx +316 -0
  122. package/src/renderer/src/components/Chat/YoutubeSearchBubble.jsx +43 -0
  123. package/src/renderer/src/components/Chat/YoutubeSummaryBubble.jsx +40 -0
  124. package/src/renderer/src/components/Chat/index.js +11 -0
  125. package/src/renderer/src/components/ChatList.jsx +434 -0
  126. package/src/renderer/src/components/GlobalCameraManager.jsx +67 -0
  127. package/src/renderer/src/components/Versions.jsx +15 -0
  128. package/src/renderer/src/components/YoutubeMusicPlayer.jsx +375 -0
  129. package/src/renderer/src/components/camera/CameraPreview.jsx +236 -0
  130. package/src/renderer/src/components/config/AiEngineSection.jsx +363 -0
  131. package/src/renderer/src/components/config/CameraSection.jsx +100 -0
  132. package/src/renderer/src/components/config/PermissionsSection.jsx +130 -0
  133. package/src/renderer/src/components/config/ShortcutSection.jsx +102 -0
  134. package/src/renderer/src/components/config/VoiceSection.jsx +110 -0
  135. package/src/renderer/src/components/config/index.js +5 -0
  136. package/src/renderer/src/components/core/Avatar.jsx +2197 -0
  137. package/src/renderer/src/components/core/ConfirmModal.jsx +80 -0
  138. package/src/renderer/src/components/core/DraggableHoloCard.jsx +125 -0
  139. package/src/renderer/src/components/core/FloatingMenu.jsx +272 -0
  140. package/src/renderer/src/components/core/HoloCard.jsx +64 -0
  141. package/src/renderer/src/components/core/InputBar.jsx +1346 -0
  142. package/src/renderer/src/components/core/ProcessPanel.jsx +216 -0
  143. package/src/renderer/src/components/core/ResponseArea.jsx +149 -0
  144. package/src/renderer/src/components/core/StatusIndicator.jsx +59 -0
  145. package/src/renderer/src/components/core/ThoughtNeuralFlow.jsx +168 -0
  146. package/src/renderer/src/components/core/ToolClustersDeck.jsx +210 -0
  147. package/src/renderer/src/components/core/native-skills.js +43 -0
  148. package/src/renderer/src/components/neural-core/EmotionalCortexLobe.jsx +359 -0
  149. package/src/renderer/src/components/neural-core/NodeDissectionModal.jsx +57 -0
  150. package/src/renderer/src/components/neural-core/ProceduralMatrixLobe.jsx +193 -0
  151. package/src/renderer/src/components/neural-core/ProceduralSkillModal.jsx +53 -0
  152. package/src/renderer/src/components/neural-core/ResetAiModal.jsx +198 -0
  153. package/src/renderer/src/components/neural-core/SynapticMemoryLobe.jsx +204 -0
  154. package/src/renderer/src/components/neural-core/traitMeta.js +102 -0
  155. package/src/renderer/src/components/subagent/AgentChatFeed.jsx +603 -0
  156. package/src/renderer/src/components/subagent/AgentInspector.jsx +224 -0
  157. package/src/renderer/src/components/subagent/AgentSidebar.jsx +216 -0
  158. package/src/renderer/src/components/subagent/SubagentIntercom.jsx +572 -0
  159. package/src/renderer/src/components/subagent/SubagentTopologyMap.jsx +423 -0
  160. package/src/renderer/src/contexts/ApprovalContext.jsx +425 -0
  161. package/src/renderer/src/contexts/ChatContext.jsx +61 -0
  162. package/src/renderer/src/contexts/YoutubeMusicContext.jsx +333 -0
  163. package/src/renderer/src/data/faqData.js +202 -0
  164. package/src/renderer/src/hooks/agent/executeAgentTool.js +895 -0
  165. package/src/renderer/src/hooks/agent/index.js +4 -0
  166. package/src/renderer/src/hooks/agent/useMarkMusic.js +75 -0
  167. package/src/renderer/src/hooks/agent/useMarkPlan.js +2739 -0
  168. package/src/renderer/src/hooks/agent/useMarkState.js +173 -0
  169. package/src/renderer/src/hooks/agent/useMarkYoutube.js +82 -0
  170. package/src/renderer/src/hooks/agent/useRelationalGrowth.js +201 -0
  171. package/src/renderer/src/hooks/telegram/useTelegramBot.js +63 -0
  172. package/src/renderer/src/hooks/useAwareness.js +289 -0
  173. package/src/renderer/src/hooks/useChatArchiver.js +52 -0
  174. package/src/renderer/src/hooks/useConfirm.jsx +68 -0
  175. package/src/renderer/src/hooks/useMarkAgent.js +454 -0
  176. package/src/renderer/src/hooks/useMemoryGroomer.js +152 -0
  177. package/src/renderer/src/hooks/useVAD.js +511 -0
  178. package/src/renderer/src/hooks/useWebSpeechWakeWord.js +122 -0
  179. package/src/renderer/src/main.jsx +12 -0
  180. package/src/renderer/src/pages/ChatStudio.jsx +922 -0
  181. package/src/renderer/src/pages/Configuration.jsx +360 -0
  182. package/src/renderer/src/pages/GoogleWorkspace.jsx +293 -0
  183. package/src/renderer/src/pages/Guidebook.jsx +917 -0
  184. package/src/renderer/src/pages/Knowledge.jsx +241 -0
  185. package/src/renderer/src/pages/MarkHome.jsx +491 -0
  186. package/src/renderer/src/pages/NeuralCore.jsx +786 -0
  187. package/src/renderer/src/pages/Plugins.jsx +630 -0
  188. package/src/renderer/src/pages/SkillEditor.jsx +380 -0
  189. package/src/renderer/src/pages/Skills.jsx +219 -0
  190. package/src/renderer/src/pages/Subagents.jsx +342 -0
  191. package/src/renderer/src/pages/TelegramBot.jsx +343 -0
  192. package/src/renderer/src/utils/chatContentParser.js +203 -0
  193. package/src/renderer/src/utils/toolClusters.js +201 -0
  194. package/src/server/bin/sha3_wasm_bg.wasm +0 -0
  195. package/src/server/config-manager.js +57 -0
  196. package/src/server/index.js +195 -0
  197. package/src/server/launcher.js +87 -0
  198. package/src/server/memory/db-store.js +758 -0
  199. package/src/server/memory/orama-store.js +177 -0
  200. package/src/server/memory/vector-engine.js +109 -0
  201. package/src/server/routes/ai.routes.js +280 -0
  202. package/src/server/routes/awareness.routes.js +62 -0
  203. package/src/server/routes/chat.routes.js +61 -0
  204. package/src/server/routes/config.routes.js +41 -0
  205. package/src/server/routes/index.js +35 -0
  206. package/src/server/routes/integrations.routes.js +116 -0
  207. package/src/server/routes/memory.routes.js +256 -0
  208. package/src/server/routes/plugins.routes.js +72 -0
  209. package/src/server/routes/skills.routes.js +139 -0
  210. package/src/server/routes/subagents.routes.js +70 -0
  211. package/src/server/routes/tasks.routes.js +175 -0
  212. package/src/server/routes/workspace.routes.js +127 -0
  213. package/src/server/services/ai/ai-utils.js +161 -0
  214. package/src/server/services/ai/openai-provider.js +757 -0
  215. package/src/server/services/ai/web-provider.js +729 -0
  216. package/src/server/services/ai-bridge.js +46 -0
  217. package/src/server/services/deepseek-web.js +956 -0
  218. package/src/server/services/gemini-web.js +170 -0
  219. package/src/server/services/notification-service.js +91 -0
  220. package/src/server/services/updater.js +125 -0
  221. package/src/server/tools/awareness-tracker.js +292 -0
  222. package/src/server/tools/group-tools.js +1164 -0
  223. package/src/server/tools/media-tools.js +244 -0
  224. package/src/server/tools/pc-agent.js +327 -0
  225. package/src/server/tools/screen-service.js +51 -0
  226. package/src/server/ws-hub.js +168 -0
  227. package/src/shared/parsers/mark-tag-parser.js +275 -0
@@ -0,0 +1,203 @@
1
+ /**
2
+ * chatContentParser.js
3
+ * Helper murni untuk parsing dan transformasi teks pada ChatList
4
+ * Memisahkan pemrosesan regex & parsing XML dari siklus render React
5
+ */
6
+ import { stripMarkTags } from '@shared/parsers/mark-tag-parser.js'
7
+
8
+ /**
9
+ * Parsing teks pesan user: bersihkan instruksi internal skill dan ambil tag skill
10
+ */
11
+ export function parseUserContent(content) {
12
+ let displayUserContent = content
13
+ let extractedSkillTag = null
14
+
15
+ if (typeof content === 'string') {
16
+ if (content.includes('=== SYSTEM INSTRUCTION: SKILL DIAKTIFKAN ===')) {
17
+ const parts = content.split('=== SYSTEM INSTRUCTION: SKILL DIAKTIFKAN ===')
18
+ const promptPart = (parts[0] || '').trim()
19
+
20
+ const skillTagMatch = content.match(
21
+ /---\s*SKILL\s+(?:BAWAAN|EXTERNAL):\s*([a-zA-Z0-9_-]+)\s*---/i
22
+ )
23
+ if (skillTagMatch) {
24
+ extractedSkillTag = skillTagMatch[1].toLowerCase()
25
+ }
26
+
27
+ displayUserContent =
28
+ promptPart || (extractedSkillTag ? `/${extractedSkillTag}` : 'Jalankan Skill')
29
+ }
30
+ } else if (Array.isArray(content)) {
31
+ displayUserContent = content.map((item) => {
32
+ if (item && item.type === 'text' && typeof item.text === 'string') {
33
+ if (item.text.includes('=== SYSTEM INSTRUCTION: SKILL DIAKTIFKAN ===')) {
34
+ const parts = item.text.split('=== SYSTEM INSTRUCTION: SKILL DIAKTIFKAN ===')
35
+ const promptPart = (parts[0] || '').trim()
36
+
37
+ const skillTagMatch = item.text.match(
38
+ /---\s*SKILL\s+(?:BAWAAN|EXTERNAL):\s*([a-zA-Z0-9_-]+)\s*---/i
39
+ )
40
+ if (skillTagMatch) {
41
+ extractedSkillTag = skillTagMatch[1].toLowerCase()
42
+ }
43
+
44
+ return {
45
+ ...item,
46
+ text: promptPart || (extractedSkillTag ? `/${extractedSkillTag}` : 'Jalankan Skill')
47
+ }
48
+ }
49
+ }
50
+ return item
51
+ })
52
+ }
53
+
54
+ if (typeof displayUserContent === 'string') {
55
+ displayUserContent = displayUserContent.replace(/(?:<mic>|\(Mikrofon\))\s*/gi, '').trim()
56
+ } else if (Array.isArray(displayUserContent)) {
57
+ displayUserContent = displayUserContent.map((item) => {
58
+ if (item && item.type === 'text' && typeof item.text === 'string') {
59
+ return {
60
+ ...item,
61
+ text: item.text.replace(/(?:<mic>|\(Mikrofon\))\s*/gi, '').trim()
62
+ }
63
+ }
64
+ return item
65
+ })
66
+ }
67
+
68
+ return { displayUserContent, extractedSkillTag }
69
+ }
70
+
71
+ /**
72
+ * Parsing konten AI: ekstraksi widget aksi bawaan (<FollowUp>, <ElicitationsGroup>, dll.)
73
+ * serta pembersihan tag XML internal dengan perlindungan terhadap code block markdown
74
+ */
75
+ export function parseAiContent(content) {
76
+ if (typeof content !== 'string') {
77
+ return {
78
+ cleanAiContent: content || '',
79
+ followUpChips: [],
80
+ elicitationGroup: null
81
+ }
82
+ }
83
+
84
+ // 1. Code Block Shielding: Pisahkan blok kode agar tag XML di dalam contoh kode tidak ikut terhapus
85
+ const codeBlocks = []
86
+ let maskedContent = content.replace(/(```[\s\S]*?```|`[^`\n]+`)/g, (match) => {
87
+ codeBlocks.push(match)
88
+ return `__CODE_BLOCK_${codeBlocks.length - 1}__`
89
+ })
90
+
91
+ // Bersihkan tag kontrol (<mark ... />, legacy mood)
92
+ maskedContent = stripMarkTags(maskedContent)
93
+
94
+ let elicitationGroup = null
95
+
96
+ // 2. Ekstraksi <ElicitationsGroup message="..."> ... </ElicitationsGroup>
97
+ const groupRegex = /<ElicitationsGroup\b([^>]*?)>([\s\S]*?)<\/ElicitationsGroup>/i
98
+ const groupMatch = groupRegex.exec(maskedContent)
99
+ if (groupMatch) {
100
+ const groupAttrs = groupMatch[1] || ''
101
+ const groupBody = groupMatch[2] || ''
102
+
103
+ const msgMatch = groupAttrs.match(/message=(?:"([^"]*)"|'([^']*)')/i)
104
+ const groupMessage = msgMatch ? (msgMatch[1] || msgMatch[2] || '').trim() : null
105
+
106
+ const options = []
107
+ const optRegex = /<([A-Za-z0-9_-]+)\b([^>]*?)(?:\/>|>([\s\S]*?)<\/\1>)/g
108
+ let optMatch
109
+ while ((optMatch = optRegex.exec(groupBody)) !== null) {
110
+ const attrsStr = optMatch[2] || ''
111
+ const bodyStr = (optMatch[3] || '').trim()
112
+ const labelMatch = attrsStr.match(/label=(?:"([^"]*)"|'([^']*)')/i)
113
+ const queryMatch = attrsStr.match(/query=(?:"([^"]*)"|'([^']*)')/i)
114
+ const label = labelMatch ? labelMatch[1] || labelMatch[2] : bodyStr
115
+ const query = queryMatch ? queryMatch[1] || queryMatch[2] : null
116
+
117
+ if (label && query) {
118
+ options.push({ label: label.trim(), query: query.trim() })
119
+ }
120
+ }
121
+
122
+ if (options.length > 0) {
123
+ elicitationGroup = {
124
+ message: groupMessage,
125
+ options
126
+ }
127
+ }
128
+ maskedContent = maskedContent.replace(groupMatch[0], '')
129
+ }
130
+
131
+ // 3. Ekstraksi tag-tag saran mandiri / bebas: <FollowUp>, <Suggestion>, <SuggestedAction>, <Elicitation>
132
+ const followUpChips = []
133
+ const chipTagRegex =
134
+ /<(?:FollowUp|Suggestion|SuggestedAction|Elicitation)\b([^>]*?)(?:\/>|>([\s\S]*?)<\/(?:FollowUp|Suggestion|SuggestedAction|Elicitation)>)/gi
135
+ let chipMatch
136
+ while ((chipMatch = chipTagRegex.exec(maskedContent)) !== null) {
137
+ const fullTag = chipMatch[0]
138
+ const attrsStr = chipMatch[1] || ''
139
+ const bodyStr = (chipMatch[2] || '').trim()
140
+
141
+ const labelMatch = attrsStr.match(/label=(?:"([^"]*)"|'([^']*)')/i)
142
+ const queryMatch = attrsStr.match(/query=(?:"([^"]*)"|'([^']*)')/i)
143
+
144
+ const label = labelMatch ? labelMatch[1] || labelMatch[2] : bodyStr
145
+ const query = queryMatch ? queryMatch[1] || queryMatch[2] : null
146
+
147
+ if (label && query) {
148
+ followUpChips.push({ label: label.trim(), query: query.trim() })
149
+ maskedContent = maskedContent.replace(fullTag, '')
150
+ }
151
+ }
152
+
153
+ // 4. Bersihkan sisa tag XML internal non-teks lainnya (seperti <cite>, <source>, <grounding_metadata>)
154
+ maskedContent = maskedContent
155
+ .replace(
156
+ /<(?:cite|source|grounding_metadata|image_query|table_action|chart_spec|widget)\b[^>]*?(?:\/>|>[\s\S]*?<\/(?:cite|source|grounding_metadata|image_query|table_action|chart_spec|widget)>)/gi,
157
+ ''
158
+ )
159
+ .replace(/<[A-Za-z0-9_-]+(?:\s+[^>]*?)?>/g, (m) => (m.startsWith('<think') ? m : ''))
160
+ .replace(/<\/[A-Za-z0-9_-]+>/g, (m) => (m.startsWith('</think') ? m : ''))
161
+
162
+ // 5. Kembalikan blok kode yang dilindungi
163
+ codeBlocks.forEach((block, idx) => {
164
+ maskedContent = maskedContent.replace(`__CODE_BLOCK_${idx}__`, () => block)
165
+ })
166
+
167
+ const cleanAiContent = maskedContent.replace(/\n{3,}/g, '\n\n').trim()
168
+
169
+ return {
170
+ cleanAiContent,
171
+ followUpChips,
172
+ elicitationGroup
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Parsing laporan subagent: ekstraksi artefak dan pembersihan header otomatis
178
+ */
179
+ export function parseSubagentReport(content) {
180
+ let cleanReportContent = typeof content === 'string' ? content : JSON.stringify(content)
181
+ let artifactInfo = null
182
+
183
+ const artifactMatch = cleanReportContent.match(/\nArtefak:\s*(.+)$/m)
184
+ if (artifactMatch) {
185
+ artifactInfo = artifactMatch[1].trim()
186
+ cleanReportContent = cleanReportContent.replace(/\nArtefak:\s*(.+)$/m, '').trim()
187
+ }
188
+
189
+ const reportPrefixMatch = cleanReportContent.match(
190
+ /^\[SUB-AGENT REPORT RECEIVED\]:\s*Sub-agent\s*(@\w+)\s*telah menyelesaikan tugasnya dan melaporkan hasil berikut:\s*/i
191
+ )
192
+ if (reportPrefixMatch) {
193
+ cleanReportContent = cleanReportContent.substring(reportPrefixMatch[0].length).trim()
194
+ if (cleanReportContent.startsWith('"') && cleanReportContent.endsWith('"')) {
195
+ cleanReportContent = cleanReportContent.slice(1, -1).trim()
196
+ }
197
+ }
198
+
199
+ return {
200
+ cleanReportContent,
201
+ artifactInfo
202
+ }
203
+ }
@@ -0,0 +1,201 @@
1
+ import { core_tools_schema } from '../api/tools/core-tools'
2
+ import { webApi } from '../api/web-bridge'
3
+
4
+ /**
5
+ * Format string cluster key menjadi judul display yang rapi & ramah dibaca
6
+ */
7
+ export function formatClusterName(key) {
8
+ if (!key) return 'TOOLS'
9
+ return key.replace(/_/g, ' ').replace(/\b\w/g, (char) => char.toUpperCase())
10
+ }
11
+
12
+ /**
13
+ * Deterministic hash-based neon/holographic color generator
14
+ * Memberikan warna cerah/neon acak namun konsisten untuk setiap tool / cluster key.
15
+ */
16
+ export function getDeterministicColor(str = '') {
17
+ let hash = 0
18
+ for (let i = 0; i < str.length; i++) {
19
+ hash = str.charCodeAt(i) + ((hash << 5) - hash)
20
+ }
21
+ // Ambil hue dari 0 - 360, saturasi tinggi 85-100%, lightness 55-65% untuk efek neon futuristik
22
+ const hue = Math.abs(hash % 360)
23
+ const sat = 85 + Math.abs((hash >> 3) % 15)
24
+ const light = 55 + Math.abs((hash >> 6) % 10)
25
+ return `hsl(${hue}, ${sat}%, ${light}%)`
26
+ }
27
+
28
+ /**
29
+ * Membangun registry kluster tools secara dinamis dari groupToolsSchema, core_tools_schema, & dynamicPlugins
30
+ */
31
+ export function buildCompleteToolClusters(dynamicPlugins = [], groupToolsSchema = null) {
32
+ const effectiveSchema =
33
+ groupToolsSchema && Object.keys(groupToolsSchema).length > 0
34
+ ? groupToolsSchema
35
+ : webApi._groupToolsCache?.schema || {}
36
+ const clusters = []
37
+ const hasDynamicPlugins = Array.isArray(dynamicPlugins) && dynamicPlugins.length > 0
38
+
39
+ let currentRadius = 170
40
+ let isClockwise = true
41
+
42
+ // 1. Ambil seluruh tool groups dari groupToolsSchema
43
+ Object.entries(effectiveSchema).forEach(([groupKey, groupData]) => {
44
+ // Jika custom_plugins memiliki dynamic plugin terpasang, tangani terpisah di bawah
45
+ if (groupKey === 'custom_plugins' && hasDynamicPlugins) return
46
+
47
+ const clusterName = formatClusterName(groupKey)
48
+ const clusterColor = getDeterministicColor(groupKey)
49
+ const speed = (0.0008 - clusters.length * 0.00005) * (isClockwise ? 1 : -1)
50
+ isClockwise = !isClockwise
51
+
52
+ const tools = (groupData.tools || []).map((t) => {
53
+ const fn = t.function || t
54
+ return {
55
+ id: `tool-${fn.name}`,
56
+ name: fn.name,
57
+ label: fn.name.replace(/^(browser|os|gdrive|gcalendar|gmail|music|git|tg)-/, ''),
58
+ description: fn.description,
59
+ clusterKey: groupKey,
60
+ color: getDeterministicColor(fn.name),
61
+ matchTools: [fn.name, fn.name.replace(/-/g, '_'), fn.name.replace(/_/g, '-')]
62
+ }
63
+ })
64
+
65
+ clusters.push({
66
+ id: `cluster-${groupKey}`,
67
+ key: groupKey,
68
+ name: clusterName,
69
+ color: clusterColor,
70
+ radius: currentRadius,
71
+ speed: speed || 0.0004,
72
+ size: 6.5,
73
+ tools
74
+ })
75
+
76
+ currentRadius += 55
77
+ })
78
+
79
+ // 2. Kumpulkan core tools dari core_tools_schema yang belum ada di dalam group tools
80
+ const existingToolNames = new Set()
81
+ clusters.forEach((c) => c.tools.forEach((t) => existingToolNames.add(t.name)))
82
+
83
+ const subagentTools = []
84
+ const fileTools = []
85
+ const memoryTools = []
86
+ const coreControlTools = []
87
+
88
+ core_tools_schema.forEach((t) => {
89
+ const fn = t.function || t
90
+ if (existingToolNames.has(fn.name)) return
91
+
92
+ const item = {
93
+ id: `tool-${fn.name}`,
94
+ name: fn.name,
95
+ label: fn.name.replace(/^(browser|os|gdrive|gcalendar|gmail|music|git|tg)-/, ''),
96
+ description: fn.description,
97
+ color: getDeterministicColor(fn.name),
98
+ matchTools: [fn.name, fn.name.replace(/-/g, '_'), fn.name.replace(/_/g, '-')]
99
+ }
100
+
101
+ if (
102
+ fn.name.includes('subagent') ||
103
+ fn.name.includes('message_agent') ||
104
+ fn.name.includes('send_message') ||
105
+ fn.name.includes('report_to_lead')
106
+ ) {
107
+ item.clusterKey = 'subagents'
108
+ subagentTools.push(item)
109
+ } else if (
110
+ fn.name.includes('file') ||
111
+ fn.name.includes('replace') ||
112
+ fn.name.includes('list-dir') ||
113
+ fn.name.includes('find-files') ||
114
+ fn.name.includes('grep')
115
+ ) {
116
+ item.clusterKey = 'file_system'
117
+ fileTools.push(item)
118
+ } else if (
119
+ fn.name.includes('memory') ||
120
+ fn.name.includes('document') ||
121
+ fn.name.includes('skill')
122
+ ) {
123
+ item.clusterKey = 'memory_rag'
124
+ memoryTools.push(item)
125
+ } else {
126
+ item.clusterKey = 'core_system'
127
+ coreControlTools.push(item)
128
+ }
129
+ })
130
+
131
+ const additionalCoreGroups = [
132
+ { key: 'subagents', name: 'Sub-Agents', tools: subagentTools, size: 8 },
133
+ { key: 'file_system', name: 'File System', tools: fileTools, size: 7 },
134
+ { key: 'memory_rag', name: 'Memory RAG', tools: memoryTools, size: 6.5 },
135
+ { key: 'core_system', name: 'Core System', tools: coreControlTools, size: 6.5 }
136
+ ]
137
+
138
+ additionalCoreGroups.forEach((cg) => {
139
+ if (cg.tools.length > 0) {
140
+ const speed = (0.0008 - clusters.length * 0.00005) * (isClockwise ? 1 : -1)
141
+ isClockwise = !isClockwise
142
+
143
+ clusters.push({
144
+ id: `cluster-${cg.key}`,
145
+ key: cg.key,
146
+ name: cg.name,
147
+ color: getDeterministicColor(cg.key),
148
+ radius: currentRadius,
149
+ speed: speed || 0.0003,
150
+ size: cg.size,
151
+ tools: cg.tools
152
+ })
153
+ currentRadius += 55
154
+ }
155
+ })
156
+
157
+ // 3. Tambahkan Custom Plugins jika ada
158
+ if (Array.isArray(dynamicPlugins) && dynamicPlugins.length > 0) {
159
+ const pluginTools = []
160
+ dynamicPlugins.forEach((p) => {
161
+ if (p.isEnabled !== false && Array.isArray(p.actions)) {
162
+ p.actions.forEach((act) => {
163
+ const actionName = act.name
164
+ const fullPrefix = `plugin-${p.name}-${actionName}`
165
+ pluginTools.push({
166
+ id: `plugin-tool-${p.name}-${actionName}`,
167
+ name: `${p.name}/${actionName}`,
168
+ label: actionName,
169
+ description: act.description || p.description || `Custom plugin ${p.name}`,
170
+ clusterKey: 'custom_plugins',
171
+ color: getDeterministicColor(`${p.name}-${actionName}`),
172
+ matchTools: [
173
+ actionName,
174
+ fullPrefix,
175
+ `plugin-${actionName}`,
176
+ `plugin_${p.name}_${actionName}`,
177
+ `plugin_${actionName}`
178
+ ]
179
+ })
180
+ })
181
+ }
182
+ })
183
+
184
+ if (pluginTools.length > 0) {
185
+ const speed = (0.0008 - clusters.length * 0.00005) * (isClockwise ? 1 : -1)
186
+ clusters.push({
187
+ id: 'cluster-custom_plugins',
188
+ key: 'custom_plugins',
189
+ name: 'Custom Plugins',
190
+ color: getDeterministicColor('custom_plugins'),
191
+ radius: currentRadius,
192
+ speed: speed || -0.0002,
193
+ size: 7,
194
+ tools: pluginTools
195
+ })
196
+ currentRadius += 55
197
+ }
198
+ }
199
+
200
+ return clusters
201
+ }
Binary file
@@ -0,0 +1,57 @@
1
+ import path from 'path'
2
+ import fs from 'fs'
3
+ import os from 'os'
4
+
5
+ const CONFIG_DIR = path.join(os.homedir(), '.config', 'mark-agent')
6
+ const CONFIG_PATH = path.join(CONFIG_DIR, 'config.json')
7
+
8
+ export function loadConfig() {
9
+ try {
10
+ if (fs.existsSync(CONFIG_PATH)) {
11
+ const raw = fs.readFileSync(CONFIG_PATH, 'utf-8')
12
+ return JSON.parse(raw)
13
+ }
14
+ } catch (_) {}
15
+ return {
16
+ aiProvider: 'gemini-web',
17
+ geminiWebModel: 'gemini-3.6-flash',
18
+ model: 'local-model',
19
+ customModel: 'default-model',
20
+ temperature: 0.7,
21
+ wakeWordEnabled: true,
22
+ wakeWord: 'mark',
23
+ voiceEnabled: true,
24
+ voice: 'id-ID-ArdiNeural'
25
+ }
26
+ }
27
+
28
+ export function saveConfig(newConfig) {
29
+ try {
30
+ if (!fs.existsSync(CONFIG_DIR)) {
31
+ fs.mkdirSync(CONFIG_DIR, { recursive: true })
32
+ }
33
+ fs.writeFileSync(CONFIG_PATH, JSON.stringify(newConfig, null, 2), 'utf-8')
34
+ return true
35
+ } catch (_) {
36
+ return false
37
+ }
38
+ }
39
+
40
+ let activeConfig = loadConfig()
41
+
42
+ export function reloadConfig() {
43
+ activeConfig = loadConfig()
44
+ return activeConfig
45
+ }
46
+
47
+ export function getActiveConfig(forceReload = false) {
48
+ if (forceReload) {
49
+ return reloadConfig()
50
+ }
51
+ return activeConfig
52
+ }
53
+
54
+ export function setActiveConfig(newConfig) {
55
+ activeConfig = newConfig
56
+ return activeConfig
57
+ }
@@ -0,0 +1,195 @@
1
+ import http from 'http'
2
+ import path from 'path'
3
+ import fs from 'fs'
4
+ import { fileURLToPath } from 'url'
5
+ import express from 'express'
6
+ import cors from 'cors'
7
+ import { wsHub } from './ws-hub.js'
8
+ import { launchUI } from './launcher.js'
9
+ import { initOramaIndices } from './memory/orama-store.js'
10
+ import { dbStore } from './memory/db-store.js'
11
+ import { loadAllPlugins } from '../main/plugins/plugin-loader.js'
12
+ import { startOsActivityTracking } from './tools/awareness-tracker.js'
13
+ import { startTelegramBot } from '../main/telegram/telegram-service.js'
14
+ import { getActiveConfig } from './config-manager.js'
15
+ import { registerRoutes } from './routes/index.js'
16
+
17
+ const __filename = fileURLToPath(import.meta.url)
18
+ const __dirname = path.dirname(__filename)
19
+
20
+ // Global crash safety: prevent unhandled promise rejections from crashing the server
21
+ process.on('unhandledRejection', (reason) => {
22
+ console.warn(
23
+ '[Server Warning] Handled UnhandledRejection:',
24
+ typeof reason === 'string' ? reason : reason?.message || reason
25
+ )
26
+ })
27
+
28
+ process.on('uncaughtException', (err) => {
29
+ console.error('[Server Error] Handled UncaughtException:', err?.message || err)
30
+ })
31
+
32
+ // Filter out DaisyUI CSS banner from leaking into stdout
33
+ const _origStdoutWrite = process.stdout.write.bind(process.stdout)
34
+ process.stdout.write = (chunk, encoding, callback) => {
35
+ const str = typeof chunk === 'string' ? chunk : chunk ? chunk.toString() : ''
36
+ if (str.includes('daisyUI') || str.includes('daisyui')) {
37
+ return typeof callback === 'function' ? callback() : true
38
+ }
39
+ return _origStdoutWrite(chunk, encoding, callback)
40
+ }
41
+
42
+ const PORT = process.env.PORT || 3000
43
+ const app = express()
44
+ const server = http.createServer(app)
45
+
46
+ const allowedOrigins = [/^http:\/\/localhost(:\d+)?$/, /^http:\/\/127\.0\.0\.1(:\d+)?$/]
47
+
48
+ // --- Middleware ---
49
+ app.use(
50
+ cors({
51
+ origin: (origin, callback) => {
52
+ // Izinkan request tanpa origin (seperti Edge App Mode, native HTTP/CLI, bot)
53
+ if (!origin) return callback(null, true)
54
+ const isAllowed = allowedOrigins.some((pattern) => pattern.test(origin))
55
+ if (isAllowed) {
56
+ callback(null, true)
57
+ } else {
58
+ callback(new Error('Akses diblokir oleh kebijakan CORS MARK'))
59
+ }
60
+ },
61
+ credentials: true
62
+ })
63
+ )
64
+ app.use(express.json({ limit: '50mb' }))
65
+ app.use(express.urlencoded({ extended: true, limit: '50mb' }))
66
+
67
+ // Inisialisasi WebSocket Hub, Orama, Plugins & Awareness Tracking
68
+ wsHub.init(server)
69
+ initOramaIndices().catch(() => {})
70
+ loadAllPlugins().catch((e) => console.error('[Plugin Init Error]:', e))
71
+ startOsActivityTracking(10000)
72
+
73
+ // Daftarkan listener event abort dari WebSocket
74
+ wsHub.on('ai:abort', async () => {
75
+ try {
76
+ const { abortAllFetches } = await import('./services/ai-bridge.js')
77
+ abortAllFetches()
78
+ } catch (_) {}
79
+ return { success: true }
80
+ })
81
+
82
+ // Relaying WebSocket events dari client (misal: subagent:report) ke semua client
83
+ wsHub.on('ws:broadcast', async (payload) => {
84
+ const { event, data } = payload || {}
85
+ if (event) {
86
+ wsHub.broadcast(event, data)
87
+ }
88
+ return { success: true }
89
+ })
90
+
91
+ const activeConfig = getActiveConfig()
92
+
93
+ // Auto-start Telegram Bot pada booting server jika token tersedia
94
+ try {
95
+ const configs = dbStore.config.getAll()
96
+ const dbCfg = configs[0] || {}
97
+ const unpackedCfg =
98
+ dbCfg.data && typeof dbCfg.data === 'object' ? { ...dbCfg, ...dbCfg.data } : dbCfg
99
+ const initialTgToken = unpackedCfg.tgBotToken || activeConfig.tgBotToken
100
+ if (initialTgToken && initialTgToken.trim()) {
101
+ console.log('[Telegram] Mengaktifkan bot secara otomatis dari konfigurasi server...')
102
+ startTelegramBot(initialTgToken.trim()).catch((err) => {
103
+ console.error('[Telegram] Gagal auto-start bot:', err.message)
104
+ })
105
+ }
106
+ } catch (err) {
107
+ console.warn('[Telegram] Gagal membaca konfigurasi awal bot:', err.message)
108
+ }
109
+
110
+ // --- Register Modular REST API Routes ---
111
+ registerRoutes(app)
112
+
113
+ // --- WebUI Serving (Vite Dev Middleware atau Production Build) ---
114
+ const staticDir = path.resolve(__dirname, '../../out/renderer')
115
+ const rootDir = path.resolve(__dirname, '../../')
116
+ const rendererDir = path.resolve(__dirname, '../renderer')
117
+
118
+ async function setupWebUIServing() {
119
+ const isDev = process.env.NODE_ENV === 'development' || process.argv.includes('--dev')
120
+
121
+ if (!isDev && fs.existsSync(path.join(staticDir, 'index.html'))) {
122
+ app.use(express.static(staticDir))
123
+ app.get('*', (req, res, next) => {
124
+ if (req.path.startsWith('/api') || req.path.startsWith('/stream')) return next()
125
+ res.sendFile(path.join(staticDir, 'index.html'))
126
+ })
127
+ } else {
128
+ try {
129
+ const { createServer: createViteServer } = await import('vite')
130
+ const vite = await createViteServer({
131
+ root: rendererDir,
132
+ configFile: path.join(rootDir, 'vite.config.js'),
133
+ server: { middlewareMode: true, hmr: { port: 24679 } },
134
+ appType: 'spa'
135
+ })
136
+ app.use(vite.middlewares)
137
+
138
+ // Sajikan index.html yang ditransformasikan secara live oleh Vite
139
+ app.use('*', async (req, res, next) => {
140
+ if (req.path.startsWith('/api') || req.path.startsWith('/stream')) return next()
141
+ const url = req.originalUrl
142
+ try {
143
+ let template = fs.readFileSync(path.resolve(rendererDir, 'index.html'), 'utf-8')
144
+ template = await vite.transformIndexHtml(url, template)
145
+ res.status(200).set({ 'Content-Type': 'text/html' }).end(template)
146
+ } catch (e) {
147
+ next(e)
148
+ }
149
+ })
150
+ } catch (err) {
151
+ console.error('[WebUI Setup Error]:', err)
152
+ }
153
+ }
154
+ }
155
+
156
+ // Inisialisasi penyajian WebUI
157
+ await setupWebUIServing()
158
+
159
+ // --- Start Server dengan Auto-Fallback Port jika terjadi EADDRINUSE ---
160
+ const HOST = process.env.HOST || '127.0.0.1'
161
+ let activePort = Number(PORT)
162
+
163
+ function startServer(portToTry) {
164
+ server.listen(portToTry, HOST)
165
+ }
166
+
167
+ server.on('listening', async () => {
168
+ const address = server.address()
169
+ activePort = typeof address === 'object' && address ? address.port : activePort
170
+ const activeHost = typeof address === 'object' && address?.address ? address.address : HOST
171
+ app.set('port', activePort)
172
+ console.log(`\n======================================================`)
173
+ console.log(` Menyalakan Server MARK di http://${activeHost}:${activePort}`)
174
+ console.log(`======================================================\n`)
175
+
176
+ if (!process.argv.includes('--no-launch') && !process.argv.includes('--headless')) {
177
+ await launchUI({ port: activePort, mode: 'app' })
178
+ }
179
+ })
180
+
181
+ server.on('error', (err) => {
182
+ if (err.code === 'EADDRINUSE') {
183
+ console.warn(
184
+ `[Port Manager] Port ${activePort} sedang digunakan aplikasi lain. Mencoba port ${activePort + 1}...`
185
+ )
186
+ activePort += 1
187
+ setTimeout(() => startServer(activePort), 200)
188
+ } else {
189
+ console.error('[Server Error]', err)
190
+ }
191
+ })
192
+
193
+ startServer(activePort)
194
+
195
+ export { app, server, wsHub, activeConfig, activePort }