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