@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,455 @@
1
+ import { exec } from 'child_process'
2
+ import util from 'util'
3
+ import path from 'path'
4
+ import fs from 'fs'
5
+
6
+ const execPromise = util.promisify(exec)
7
+
8
+ /**
9
+ * Universal Delimiter & Bracket Balancer
10
+ * Memindai kode sambil mengabaikan string literal dan komentar,
11
+ * lalu memeriksa pasangan { }, [ ], ( ).
12
+ */
13
+ function checkBracketBalance(content, commentType = 'c-style') {
14
+ const stack = []
15
+ let inSingleQuote = false
16
+ let inDoubleQuote = false
17
+ let inBacktick = false
18
+ let inSingleLineComment = false
19
+ let inMultiLineComment = false
20
+ let inPythonTripleDouble = false
21
+ let inPythonTripleSingle = false
22
+
23
+ let line = 1
24
+ let col = 0
25
+
26
+ for (let i = 0; i < content.length; i++) {
27
+ const char = content[i]
28
+ const nextChar = i + 1 < content.length ? content[i + 1] : ''
29
+ const prevChar = i > 0 ? content[i - 1] : ''
30
+ const isEscaped = prevChar === '\\' && (i < 2 || content[i - 2] !== '\\')
31
+
32
+ if (char === '\n') {
33
+ line++
34
+ col = 0
35
+ inSingleLineComment = false
36
+ continue
37
+ }
38
+ col++
39
+
40
+ // --- State: Dalam komentar satu baris ---
41
+ if (inSingleLineComment) {
42
+ continue
43
+ }
44
+
45
+ // --- State: Dalam komentar multi-baris C-Style /* ... */ ---
46
+ if (inMultiLineComment) {
47
+ if (char === '*' && nextChar === '/') {
48
+ inMultiLineComment = false
49
+ i++
50
+ col++
51
+ }
52
+ continue
53
+ }
54
+
55
+ // --- State: Dalam Python Triple Quotes """ ... """ ---
56
+ if (inPythonTripleDouble) {
57
+ if (char === '"' && nextChar === '"' && i + 2 < content.length && content[i + 2] === '"' && !isEscaped) {
58
+ inPythonTripleDouble = false
59
+ i += 2
60
+ col += 2
61
+ }
62
+ continue
63
+ }
64
+
65
+ // --- State: Dalam Python Triple Quotes ''' ... ''' ---
66
+ if (inPythonTripleSingle) {
67
+ if (char === "'" && nextChar === "'" && i + 2 < content.length && content[i + 2] === "'" && !isEscaped) {
68
+ inPythonTripleSingle = false
69
+ i += 2
70
+ col += 2
71
+ }
72
+ continue
73
+ }
74
+
75
+ // --- State: Dalam String ---
76
+ if (inSingleQuote) {
77
+ if (char === "'" && !isEscaped) inSingleQuote = false
78
+ continue
79
+ }
80
+ if (inDoubleQuote) {
81
+ if (char === '"' && !isEscaped) inDoubleQuote = false
82
+ continue
83
+ }
84
+ if (inBacktick) {
85
+ if (char === '`' && !isEscaped) inBacktick = false
86
+ continue
87
+ }
88
+
89
+ // --- Deteksi Pembuka Komentar ---
90
+ if (commentType === 'c-style' || commentType === 'slash-only') {
91
+ if (char === '/' && nextChar === '/') {
92
+ inSingleLineComment = true
93
+ i++
94
+ col++
95
+ continue
96
+ }
97
+ if (char === '/' && nextChar === '*') {
98
+ inMultiLineComment = true
99
+ i++
100
+ col++
101
+ continue
102
+ }
103
+ }
104
+
105
+ if (commentType === 'hash-style' || commentType === 'python') {
106
+ if (commentType === 'python') {
107
+ if (char === '"' && nextChar === '"' && i + 2 < content.length && content[i + 2] === '"') {
108
+ inPythonTripleDouble = true
109
+ i += 2
110
+ col += 2
111
+ continue
112
+ }
113
+ if (char === "'" && nextChar === "'" && i + 2 < content.length && content[i + 2] === "'") {
114
+ inPythonTripleSingle = true
115
+ i += 2
116
+ col += 2
117
+ continue
118
+ }
119
+ }
120
+ if (char === '#') {
121
+ inSingleLineComment = true
122
+ continue
123
+ }
124
+ }
125
+
126
+ if (commentType === 'sql') {
127
+ if (char === '-' && nextChar === '-') {
128
+ inSingleLineComment = true
129
+ i++
130
+ col++
131
+ continue
132
+ }
133
+ if (char === '/' && nextChar === '*') {
134
+ inMultiLineComment = true
135
+ i++
136
+ col++
137
+ continue
138
+ }
139
+ }
140
+
141
+ if (commentType === 'html') {
142
+ if (char === '<' && nextChar === '!' && content.slice(i, i + 4) === '<!--') {
143
+ const closeIdx = content.indexOf('-->', i + 4)
144
+ if (closeIdx === -1) {
145
+ return { valid: false, error: `Unclosed HTML comment '<!--' at line ${line}` }
146
+ }
147
+ i = closeIdx + 2
148
+ continue
149
+ }
150
+ }
151
+
152
+ // --- Deteksi Pembuka String ---
153
+ if (char === "'" && !isEscaped) {
154
+ inSingleQuote = true
155
+ continue
156
+ }
157
+ if (char === '"' && !isEscaped) {
158
+ inDoubleQuote = true
159
+ continue
160
+ }
161
+ if (char === '`' && !isEscaped) {
162
+ inBacktick = true
163
+ continue
164
+ }
165
+
166
+ // --- Deteksi Kurung Buka / Tutup ---
167
+ if (char === '{' || char === '[' || char === '(') {
168
+ stack.push({ char, line, col })
169
+ } else if (char === '}' || char === ']' || char === ')') {
170
+ if (stack.length === 0) {
171
+ return {
172
+ valid: false,
173
+ error: `Unmatched closing delimiter '${char}' at line ${line}, col ${col}`
174
+ }
175
+ }
176
+ const last = stack.pop()
177
+ const pairs = { '}': '{', ']': '[', ')': '(' }
178
+ if (last.char !== pairs[char]) {
179
+ return {
180
+ valid: false,
181
+ error: `Mismatched delimiter: expected closing for '${last.char}' (from line ${last.line}), but found '${char}' at line ${line}, col ${col}`
182
+ }
183
+ }
184
+ }
185
+ }
186
+
187
+ if (inSingleQuote) {
188
+ return { valid: false, error: "Unclosed single quote (') string literal" }
189
+ }
190
+ if (inDoubleQuote) {
191
+ return { valid: false, error: 'Unclosed double quote (") string literal' }
192
+ }
193
+ if (inBacktick) {
194
+ return { valid: false, error: 'Unclosed backtick (`) template literal' }
195
+ }
196
+ if (inMultiLineComment) {
197
+ return { valid: false, error: 'Unclosed multi-line comment (/* ... */)' }
198
+ }
199
+ if (inPythonTripleDouble || inPythonTripleSingle) {
200
+ return { valid: false, error: 'Unclosed Python triple-quoted string' }
201
+ }
202
+
203
+ if (stack.length > 0) {
204
+ const unclosed = stack[stack.length - 1]
205
+ return {
206
+ valid: false,
207
+ error: `Unclosed opening delimiter '${unclosed.char}' at line ${unclosed.line}, col ${unclosed.col}`
208
+ }
209
+ }
210
+
211
+ return { valid: true }
212
+ }
213
+
214
+ /**
215
+ * Validasi Pasangan Tag HTML / XML / SVG / Vue / Svelte
216
+ */
217
+ function checkXmlTagBalance(content) {
218
+ const voidTags = new Set([
219
+ 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',
220
+ 'link', 'meta', 'param', 'source', 'track', 'wbr'
221
+ ])
222
+
223
+ // Hapus komentar HTML
224
+ let sanitized = content.replace(/<!--[\s\S]*?-->/g, '')
225
+ // Hapus konten script & style
226
+ sanitized = sanitized.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, '<script></script>')
227
+ sanitized = sanitized.replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, '<style></style>')
228
+
229
+ const tagRegex = /<\/?([a-zA-Z0-9_\-:]+)([^>]*?)(\/?)>/g
230
+ const stack = []
231
+ let match
232
+
233
+ while ((match = tagRegex.exec(sanitized)) !== null) {
234
+ const fullTag = match[0]
235
+ const tagName = match[1].toLowerCase()
236
+ const isClosing = fullTag.startsWith('</')
237
+ const isSelfClosing = match[3] === '/' || fullTag.endsWith('/>') || voidTags.has(tagName)
238
+
239
+ if (isSelfClosing && !isClosing) {
240
+ continue
241
+ }
242
+
243
+ if (!isClosing) {
244
+ stack.push(tagName)
245
+ } else {
246
+ if (stack.length === 0) {
247
+ return {
248
+ valid: false,
249
+ error: `Unexpected closing tag </${tagName}> without matching opening tag`
250
+ }
251
+ }
252
+ const last = stack.pop()
253
+ if (last !== tagName && !voidTags.has(last)) {
254
+ return {
255
+ valid: false,
256
+ error: `Tag mismatch: expected </${last}>, but found </${tagName}>`
257
+ }
258
+ }
259
+ }
260
+ }
261
+
262
+ if (stack.length > 0) {
263
+ const unclosed = stack.filter((t) => !voidTags.has(t))
264
+ if (unclosed.length > 0) {
265
+ return {
266
+ valid: false,
267
+ error: `Unclosed tag(s): <${unclosed.join('>, <')}>`
268
+ }
269
+ }
270
+ }
271
+
272
+ return { valid: true }
273
+ }
274
+
275
+ /**
276
+ * Memvalidasi sintaks file berdasarkan ekstensi secara lokal
277
+ * Mendukung 20+ bahasa dan format file
278
+ * @param {string} filePath Path absolut ke berkas
279
+ * @param {string} content Konten berkas
280
+ * @returns {Promise<{ valid: boolean, error?: string }>}
281
+ */
282
+ export async function validateFileSyntax(filePath, content) {
283
+ if (!filePath || !content) return { valid: true }
284
+ const ext = path.extname(filePath).toLowerCase()
285
+
286
+ try {
287
+ // 1. JSON & JSONC
288
+ if (['.json', '.jsonc', '.json5'].includes(ext)) {
289
+ try {
290
+ const cleanJson = content.replace(/\/\/.*$/gm, '').replace(/\/\*[\s\S]*?\*\//g, '')
291
+ JSON.parse(cleanJson)
292
+ return { valid: true }
293
+ } catch (jsonErr) {
294
+ return { valid: false, error: `JSON Parse Error: ${jsonErr.message}` }
295
+ }
296
+ }
297
+
298
+ // 2. YAML (.yaml, .yml)
299
+ if (['.yaml', '.yml'].includes(ext)) {
300
+ const yamlBracket = checkBracketBalance(content, 'hash-style')
301
+ if (!yamlBracket.valid) return yamlBracket
302
+ // Cek indentasi tab terlarang di YAML
303
+ const lines = content.split('\n')
304
+ for (let i = 0; i < lines.length; i++) {
305
+ if (lines[i].startsWith('\t')) {
306
+ return {
307
+ valid: false,
308
+ error: `YAML SyntaxError: Tab character used for indentation at line ${i + 1}`
309
+ }
310
+ }
311
+ }
312
+ return { valid: true }
313
+ }
314
+
315
+ // 3. JavaScript (.js, .mjs, .cjs)
316
+ if (['.js', '.mjs', '.cjs'].includes(ext)) {
317
+ const bracketCheck = checkBracketBalance(content, 'c-style')
318
+ if (!bracketCheck.valid) return bracketCheck
319
+
320
+ if (fs.existsSync(filePath)) {
321
+ try {
322
+ await execPromise(`node -c "${filePath}"`)
323
+ } catch (nodeErr) {
324
+ const cleanErr = (nodeErr.stderr || nodeErr.message || '').trim()
325
+ return { valid: false, error: `JavaScript SyntaxError:\n${cleanErr}` }
326
+ }
327
+ }
328
+ return { valid: true }
329
+ }
330
+
331
+ // 4. JSX, TypeScript (.jsx, .ts, .tsx)
332
+ if (['.jsx', '.ts', '.tsx'].includes(ext)) {
333
+ const bracketCheck = checkBracketBalance(content, 'c-style')
334
+ if (!bracketCheck.valid) return bracketCheck
335
+ return { valid: true }
336
+ }
337
+
338
+ // 5. Python (.py, .pyw)
339
+ if (['.py', '.pyw'].includes(ext)) {
340
+ const bracketCheck = checkBracketBalance(content, 'python')
341
+ if (!bracketCheck.valid) return bracketCheck
342
+
343
+ if (fs.existsSync(filePath)) {
344
+ try {
345
+ await execPromise(`python -m py_compile "${filePath}"`)
346
+ } catch (_) {
347
+ try {
348
+ await execPromise(`py -m py_compile "${filePath}"`)
349
+ } catch (pyErr) {
350
+ const cleanErr = (pyErr.stderr || pyErr.message || '').trim()
351
+ if (cleanErr && !cleanErr.includes('not found') && !cleanErr.includes('is not recognized')) {
352
+ return { valid: false, error: `Python SyntaxError:\n${cleanErr}` }
353
+ }
354
+ }
355
+ }
356
+ }
357
+ return { valid: true }
358
+ }
359
+
360
+ // 6. HTML, XML, SVG, Vue, Svelte
361
+ if (['.html', '.htm', '.xml', '.svg', '.vue', '.svelte'].includes(ext)) {
362
+ const tagCheck = checkXmlTagBalance(content)
363
+ if (!tagCheck.valid) return tagCheck
364
+ return { valid: true }
365
+ }
366
+
367
+ // 7. CSS, SCSS, LESS
368
+ if (['.css', '.scss', '.less'].includes(ext)) {
369
+ const bracketCheck = checkBracketBalance(content, 'c-style')
370
+ if (!bracketCheck.valid) return bracketCheck
371
+ return { valid: true }
372
+ }
373
+
374
+ // 8. PHP (.php)
375
+ if (ext === '.php') {
376
+ const bracketCheck = checkBracketBalance(content, 'c-style')
377
+ if (!bracketCheck.valid) return bracketCheck
378
+ if (fs.existsSync(filePath)) {
379
+ try {
380
+ await execPromise(`php -l "${filePath}"`)
381
+ } catch (phpErr) {
382
+ const cleanErr = (phpErr.stderr || phpErr.stdout || phpErr.message || '').trim()
383
+ if (cleanErr && !cleanErr.includes('not found') && !cleanErr.includes('is not recognized')) {
384
+ return { valid: false, error: `PHP SyntaxError:\n${cleanErr}` }
385
+ }
386
+ }
387
+ }
388
+ return { valid: true }
389
+ }
390
+
391
+ // 9. Ruby (.rb)
392
+ if (ext === '.rb') {
393
+ const bracketCheck = checkBracketBalance(content, 'hash-style')
394
+ if (!bracketCheck.valid) return bracketCheck
395
+ if (fs.existsSync(filePath)) {
396
+ try {
397
+ await execPromise(`ruby -c "${filePath}"`)
398
+ } catch (rbErr) {
399
+ const cleanErr = (rbErr.stderr || rbErr.message || '').trim()
400
+ if (cleanErr && !cleanErr.includes('not found') && !cleanErr.includes('is not recognized')) {
401
+ return { valid: false, error: `Ruby SyntaxError:\n${cleanErr}` }
402
+ }
403
+ }
404
+ }
405
+ return { valid: true }
406
+ }
407
+
408
+ // 10. Go (.go)
409
+ if (ext === '.go') {
410
+ const bracketCheck = checkBracketBalance(content, 'c-style')
411
+ if (!bracketCheck.valid) return bracketCheck
412
+ if (fs.existsSync(filePath)) {
413
+ try {
414
+ await execPromise(`gofmt -e "${filePath}"`)
415
+ } catch (goErr) {
416
+ const cleanErr = (goErr.stderr || goErr.message || '').trim()
417
+ if (cleanErr && !cleanErr.includes('not found') && !cleanErr.includes('is not recognized')) {
418
+ return { valid: false, error: `Go SyntaxError:\n${cleanErr}` }
419
+ }
420
+ }
421
+ }
422
+ return { valid: true }
423
+ }
424
+
425
+ // 11. SQL (.sql)
426
+ if (ext === '.sql') {
427
+ const bracketCheck = checkBracketBalance(content, 'sql')
428
+ if (!bracketCheck.valid) return bracketCheck
429
+ return { valid: true }
430
+ }
431
+
432
+ // 12. C, C++, C#, Java, Kotlin, Rust, Swift, Dart
433
+ if (['.c', '.cpp', '.h', '.hpp', '.cs', '.java', '.kt', '.rs', '.swift', '.dart'].includes(ext)) {
434
+ const bracketCheck = checkBracketBalance(content, 'c-style')
435
+ if (!bracketCheck.valid) return bracketCheck
436
+ return { valid: true }
437
+ }
438
+
439
+ // 13. Shell & PowerShell (.sh, .bash, .ps1, .psm1)
440
+ if (['.sh', '.bash', '.ps1', '.psm1'].includes(ext)) {
441
+ const bracketCheck = checkBracketBalance(content, ext.startsWith('.ps') ? 'c-style' : 'hash-style')
442
+ if (!bracketCheck.valid) return bracketCheck
443
+ return { valid: true }
444
+ }
445
+
446
+ // Default: Universal bracket balance check
447
+ const genericBracket = checkBracketBalance(content, 'c-style')
448
+ if (!genericBracket.valid) return genericBracket
449
+
450
+ return { valid: true }
451
+ } catch (err) {
452
+ return { valid: true } // Jangan halangi jika terjadi error internal pada validator
453
+ }
454
+ }
455
+
@@ -0,0 +1,117 @@
1
+ import { spawn } from 'child_process'
2
+
3
+ const activeTasks = new Map()
4
+
5
+ /**
6
+ * Menjalankan background terminal task secara non-blocking
7
+ */
8
+ export function spawnBackgroundTask(taskId, command, cwd) {
9
+ if (!taskId || !command) {
10
+ return { success: false, message: 'TaskId dan command wajib diisi.' }
11
+ }
12
+
13
+ if (activeTasks.has(taskId)) {
14
+ killBackgroundTask(taskId)
15
+ }
16
+
17
+ const child = spawn('powershell.exe', ['-NoProfile', '-Command', command], {
18
+ cwd: cwd || process.cwd(),
19
+ shell: true
20
+ })
21
+
22
+ const taskState = {
23
+ id: taskId,
24
+ command,
25
+ pid: child.pid,
26
+ outputBuffer: [],
27
+ status: 'running',
28
+ startedAt: Date.now(),
29
+ process: child
30
+ }
31
+
32
+ child.stdout.on('data', (data) => {
33
+ const lines = data.toString().split('\n')
34
+ taskState.outputBuffer.push(...lines)
35
+ if (taskState.outputBuffer.length > 300) {
36
+ taskState.outputBuffer.splice(0, taskState.outputBuffer.length - 300)
37
+ }
38
+ })
39
+
40
+ child.stderr.on('data', (data) => {
41
+ taskState.outputBuffer.push(`[STDERR] ${data.toString()}`)
42
+ if (taskState.outputBuffer.length > 300) {
43
+ taskState.outputBuffer.splice(0, taskState.outputBuffer.length - 300)
44
+ }
45
+ })
46
+
47
+ child.on('close', (code) => {
48
+ taskState.status = code === 0 ? 'completed' : 'failed'
49
+ taskState.exitCode = code
50
+ })
51
+
52
+ child.on('error', (err) => {
53
+ taskState.status = 'error'
54
+ taskState.error = err.message
55
+ })
56
+
57
+ activeTasks.set(taskId, taskState)
58
+ return {
59
+ success: true,
60
+ taskId,
61
+ pid: child.pid,
62
+ message: `Background task '${taskId}' berhasil dijalankan (PID: ${child.pid}). Gunakan 'read-task-output' untuk melihat log atau 'kill-task' untuk menghentikan.`
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Membaca output log terbaru dari background task
68
+ */
69
+ export function readBackgroundTaskOutput(taskId, lineCount = 40) {
70
+ const task = activeTasks.get(taskId)
71
+ if (!task) return { success: false, message: `Task '${taskId}' tidak ditemukan.` }
72
+
73
+ const lines = task.outputBuffer.slice(-1 * lineCount)
74
+ return {
75
+ success: true,
76
+ taskId,
77
+ status: task.status,
78
+ pid: task.pid,
79
+ output: lines.join('\n').trim() || '(Belum ada output teks dari proses ini)'
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Menghentikan background task
85
+ */
86
+ export function killBackgroundTask(taskId) {
87
+ const task = activeTasks.get(taskId)
88
+ if (!task) return { success: false, message: `Task '${taskId}' tidak ditemukan.` }
89
+
90
+ try {
91
+ task.process.kill('SIGTERM')
92
+ } catch (e) {
93
+ try {
94
+ task.process.kill()
95
+ } catch (_) {}
96
+ }
97
+
98
+ activeTasks.delete(taskId)
99
+ return { success: true, message: `Task '${taskId}' (PID: ${task.pid}) berhasil dihentikan.` }
100
+ }
101
+
102
+ /**
103
+ * Mendapatkan daftar seluruh background tasks yang aktif
104
+ */
105
+ export function listBackgroundTasks() {
106
+ const list = []
107
+ for (const [id, t] of activeTasks.entries()) {
108
+ list.push({
109
+ taskId: id,
110
+ command: t.command,
111
+ pid: t.pid,
112
+ status: t.status,
113
+ startedAt: t.startedAt
114
+ })
115
+ }
116
+ return { success: true, count: list.length, tasks: list }
117
+ }