@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,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
|
+
}
|