@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,177 @@
1
+ import { create, insert, insertMultiple, search, remove, removeMultiple } from '@orama/orama'
2
+ import { generateVector, cosineSimilarity } from './vector-engine.js'
3
+
4
+ const VECTOR_SIZE = 384
5
+
6
+ let memoryIndex = null
7
+ let archiveIndex = null
8
+ let documentIndex = null
9
+ let turnPairIndex = null
10
+
11
+ /**
12
+ * Inisialisasi 4 indeks pencarian Orama di server
13
+ */
14
+ export async function initOramaIndices() {
15
+ memoryIndex = await create({
16
+ schema: {
17
+ type: 'string',
18
+ summary: 'string',
19
+ memory: 'string',
20
+ timestamp: 'number',
21
+ dbId: 'string',
22
+ vector: `vector[${VECTOR_SIZE}]`
23
+ }
24
+ })
25
+
26
+ archiveIndex = await create({
27
+ schema: {
28
+ summary: 'string',
29
+ topic: 'string',
30
+ timestamp: 'number',
31
+ dbId: 'string',
32
+ vector: `vector[${VECTOR_SIZE}]`
33
+ }
34
+ })
35
+
36
+ documentIndex = await create({
37
+ schema: {
38
+ docName: 'string',
39
+ chunkIndex: 'number',
40
+ content: 'string',
41
+ timestamp: 'number',
42
+ dbId: 'string',
43
+ vector: `vector[${VECTOR_SIZE}]`
44
+ }
45
+ })
46
+
47
+ turnPairIndex = await create({
48
+ schema: {
49
+ pairId: 'string',
50
+ sessionId: 'string',
51
+ sessionTitle: 'string',
52
+ userText: 'string',
53
+ aiText: 'string',
54
+ combinedText: 'string',
55
+ timestamp: 'number',
56
+ vector: `vector[${VECTOR_SIZE}]`
57
+ }
58
+ })
59
+ }
60
+
61
+ /**
62
+ * Insert atau update item di memoryIndex
63
+ */
64
+ export async function insertMemoryIndex(item) {
65
+ if (!memoryIndex) return
66
+ await insert(memoryIndex, {
67
+ type: String(item.type || 'profile'),
68
+ summary: String(item.summary || ''),
69
+ memory: String(item.memory || item.content || ''),
70
+ timestamp: Number(item.timestamp) || Date.now(),
71
+ dbId: String(item.id || ''),
72
+ vector: item.vector
73
+ })
74
+ }
75
+
76
+ /**
77
+ * Search Memories di Orama
78
+ */
79
+ export async function searchMemories(queryText, threshold = 0.3, limit = 5) {
80
+ if (!memoryIndex || !queryText) return []
81
+ const queryVector = await generateVector(queryText)
82
+ if (!queryVector) return []
83
+
84
+ try {
85
+ const results = await search(memoryIndex, {
86
+ mode: 'vector',
87
+ vector: { value: queryVector, property: 'vector' },
88
+ similarity: threshold,
89
+ limit
90
+ })
91
+ return results.hits.map((h) => ({ ...h.document, score: h.score }))
92
+ } catch (_) {
93
+ return []
94
+ }
95
+ }
96
+
97
+ /**
98
+ * Insert turn pair ke turnPairIndex
99
+ */
100
+ export async function insertTurnPairIndex(item) {
101
+ if (!turnPairIndex) return
102
+ await insert(turnPairIndex, {
103
+ pairId: String(item.pairId || item.id || ''),
104
+ sessionId: String(item.sessionId || '1'),
105
+ sessionTitle: String(item.sessionTitle || 'Session'),
106
+ userText: String(item.userText || ''),
107
+ aiText: String(item.aiText || ''),
108
+ combinedText: String(item.combinedText || `${item.userText} ${item.aiText}`),
109
+ timestamp: Number(item.timestamp) || Date.now(),
110
+ vector: item.vector
111
+ })
112
+ }
113
+
114
+ /**
115
+ * Search Turn Pairs di Orama
116
+ */
117
+ export async function searchTurnPairs(queryText, threshold = 0.35, limit = 4) {
118
+ if (!turnPairIndex || !queryText) return []
119
+ const queryVector = await generateVector(queryText)
120
+ if (!queryVector) return []
121
+
122
+ try {
123
+ const results = await search(turnPairIndex, {
124
+ mode: 'vector',
125
+ vector: { value: queryVector, property: 'vector' },
126
+ similarity: threshold,
127
+ limit
128
+ })
129
+ return results.hits.map((h) => ({ ...h.document, score: h.score }))
130
+ } catch (_) {
131
+ return []
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Search Archives di Orama
137
+ */
138
+ export async function searchArchives(queryText, threshold = 0.3, limit = 3) {
139
+ if (!archiveIndex || !queryText) return []
140
+ const queryVector = await generateVector(queryText)
141
+ if (!queryVector) return []
142
+
143
+ try {
144
+ const results = await search(archiveIndex, {
145
+ mode: 'vector',
146
+ vector: { value: queryVector, property: 'vector' },
147
+ similarity: threshold,
148
+ limit
149
+ })
150
+ return results.hits.map((h) => ({ ...h.document, score: h.score }))
151
+ } catch (_) {
152
+ return []
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Search Documents (RAG) di Orama
158
+ */
159
+ export async function searchDocuments(queryText, threshold = 0.3, limit = 4) {
160
+ if (!documentIndex || !queryText) return []
161
+ const queryVector = await generateVector(queryText)
162
+ if (!queryVector) return []
163
+
164
+ try {
165
+ const results = await search(documentIndex, {
166
+ mode: 'vector',
167
+ vector: { value: queryVector, property: 'vector' },
168
+ similarity: threshold,
169
+ limit
170
+ })
171
+ return results.hits.map((h) => ({ ...h.document, score: h.score }))
172
+ } catch (_) {
173
+ return []
174
+ }
175
+ }
176
+
177
+ export { memoryIndex, archiveIndex, documentIndex, turnPairIndex, insertMultiple }
@@ -0,0 +1,109 @@
1
+ import path from 'path'
2
+ import os from 'os'
3
+ import fs from 'fs'
4
+ import { createRequire } from 'module'
5
+
6
+ const require = createRequire(import.meta.url)
7
+ const { pipeline, env } = require('@huggingface/transformers')
8
+
9
+ // Konfigurasi cache direktori lokal persisten
10
+ const CACHE_DIR = path.join(os.homedir(), '.cache', 'mark-transformers')
11
+ if (!fs.existsSync(CACHE_DIR)) {
12
+ fs.mkdirSync(CACHE_DIR, { recursive: true })
13
+ }
14
+
15
+ env.cacheDir = CACHE_DIR
16
+ env.allowLocalModels = true
17
+ env.allowRemoteModels = true
18
+
19
+ let extractor = null
20
+ let isInitializing = false
21
+ const initWaiters = []
22
+
23
+ /**
24
+ * Inisialisasi pipeline feature-extraction MiniLM-L12-v2 secara lokal
25
+ */
26
+ export async function getExtractor() {
27
+ if (extractor) return extractor
28
+ if (isInitializing) {
29
+ return new Promise((resolve) => initWaiters.push(resolve))
30
+ }
31
+
32
+ isInitializing = true
33
+
34
+ try {
35
+ extractor = await pipeline(
36
+ 'feature-extraction',
37
+ 'Xenova/paraphrase-multilingual-MiniLM-L12-v2',
38
+ {
39
+ quantized: true
40
+ }
41
+ )
42
+ isInitializing = false
43
+ initWaiters.forEach((resolve) => resolve(extractor))
44
+ initWaiters.length = 0
45
+ return extractor
46
+ } catch (err) {
47
+ isInitializing = false
48
+ initWaiters.forEach((resolve) => resolve(null))
49
+ initWaiters.length = 0
50
+ return null
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Menghasilkan vektor 384 dimensi untuk suatu teks
56
+ * @param {string} text
57
+ * @returns {Promise<number[]|null>}
58
+ */
59
+ export async function generateVector(text) {
60
+ if (!text || typeof text !== 'string' || !text.trim()) return null
61
+ const clean = text.trim().slice(0, 1000)
62
+
63
+ try {
64
+ const ext = await getExtractor()
65
+ if (!ext) return null
66
+
67
+ const output = await ext(clean, { pooling: 'mean', normalize: true })
68
+ return Array.from(output.data)
69
+ } catch (_) {
70
+ return null
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Menghasilkan vektor batch untuk beberapa teks sekaligus
76
+ * @param {Array<{id: any, text: string}>} items
77
+ * @returns {Promise<Array<{id: any, vector: number[]|null}>>}
78
+ */
79
+ export async function generateVectorBatch(items) {
80
+ if (!Array.isArray(items) || items.length === 0) return []
81
+ const results = []
82
+
83
+ for (const item of items) {
84
+ const vector = await generateVector(item.text)
85
+ results.push({ id: item.id, vector })
86
+ }
87
+
88
+ return results
89
+ }
90
+
91
+ /**
92
+ * Menghitung cosine similarity antara 2 vektor
93
+ * @param {number[]} vecA
94
+ * @param {number[]} vecB
95
+ * @returns {number}
96
+ */
97
+ export function cosineSimilarity(vecA, vecB) {
98
+ if (!vecA || !vecB || vecA.length !== vecB.length) return 0
99
+ let dotProduct = 0
100
+ let normA = 0
101
+ let normB = 0
102
+ for (let i = 0; i < vecA.length; i++) {
103
+ dotProduct += vecA[i] * vecB[i]
104
+ normA += vecA[i] * vecA[i]
105
+ normB += vecB[i] * vecB[i]
106
+ }
107
+ if (normA === 0 || normB === 0) return 0
108
+ return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB))
109
+ }
@@ -0,0 +1,280 @@
1
+ import { Router } from 'express'
2
+ import { getActiveConfig } from '../config-manager.js'
3
+ import { wsHub } from '../ws-hub.js'
4
+ import { launchUI } from '../launcher.js'
5
+ import {
6
+ GROUP_TOOLS_SCHEMA,
7
+ GROUP_TOOL_GROUP_NAMES,
8
+ GROUP_TOOLS_DEFINITION,
9
+ group_tools_flat
10
+ } from '../tools/group-tools.js'
11
+
12
+ export const aiRouter = Router()
13
+
14
+ // 1. AI Fetch Direct Gateway
15
+ aiRouter.post('/ai/fetch', async (req, res) => {
16
+ const {
17
+ messages,
18
+ config = {},
19
+ isSmallTask = false,
20
+ jsonSchema = null,
21
+ sessionId = null
22
+ } = req.body || {}
23
+ try {
24
+ const { fetchAI } = await import('../services/ai-bridge.js')
25
+ const finalConfig = { ...getActiveConfig(), ...config }
26
+ const provider = finalConfig.aiProvider || 'gemini-web'
27
+ const resolvedModel =
28
+ provider === 'gemini-web'
29
+ ? finalConfig.geminiWebModel || 'gemini-3.6-flash'
30
+ : provider === 'deepseek-web'
31
+ ? finalConfig.deepseekWebModel || 'deepseek-chat'
32
+ : provider === 'custom'
33
+ ? finalConfig.customModel || 'default-model'
34
+ : finalConfig.model || 'local-model'
35
+
36
+ wsHub.broadcast('ai:fetch', {
37
+ type: 'fetch',
38
+ provider,
39
+ model: resolvedModel,
40
+ messagesCount: Array.isArray(messages) ? messages.length : 0,
41
+ hasTools: false
42
+ })
43
+
44
+ const result = await fetchAI(messages, false, {
45
+ config: finalConfig,
46
+ isSmallTask,
47
+ jsonSchema,
48
+ sessionId
49
+ })
50
+ res.json(result)
51
+ } catch (err) {
52
+ res.status(500).json({ error: { message: err.message, code: err.code || 'AI_ERROR' } })
53
+ }
54
+ })
55
+
56
+ // 2. AI Streaming Gateway (Native Tool Calling + SSE & WebSocket Tokens)
57
+ aiRouter.post('/ai/stream', async (req, res) => {
58
+ const {
59
+ messages,
60
+ tools = null,
61
+ config = {},
62
+ isSmallTask = false,
63
+ sessionId = null
64
+ } = req.body || {}
65
+ try {
66
+ const { fetchAI } = await import('../services/ai-bridge.js')
67
+ const finalConfig = { ...getActiveConfig(), ...config }
68
+ const provider = finalConfig.aiProvider || 'gemini-web'
69
+ const resolvedModel =
70
+ provider === 'gemini-web'
71
+ ? finalConfig.geminiWebModel || 'gemini-3.6-flash'
72
+ : provider === 'deepseek-web'
73
+ ? finalConfig.deepseekWebModel || 'deepseek-chat'
74
+ : provider === 'custom'
75
+ ? finalConfig.customModel || 'default-model'
76
+ : finalConfig.model || 'local-model'
77
+
78
+ wsHub.broadcast('ai:fetch', {
79
+ type: 'stream',
80
+ provider,
81
+ model: resolvedModel,
82
+ messagesCount: Array.isArray(messages) ? messages.length : 0,
83
+ hasTools: Array.isArray(tools) && tools.length > 0,
84
+ toolsCount: Array.isArray(tools) ? tools.length : 0
85
+ })
86
+
87
+ const result = await fetchAI(messages, true, {
88
+ tools,
89
+ config: finalConfig,
90
+ isSmallTask,
91
+ sessionId,
92
+ onToken: (token) => {
93
+ wsHub.streamToken(token, 'answer')
94
+ },
95
+ onReasoning: (rToken) => {
96
+ wsHub.streamToken(rToken, 'thought')
97
+ },
98
+ onMood: (mood) => {
99
+ wsHub.broadcast('ai:mood', { mood })
100
+ },
101
+ onMeta: (meta) => {
102
+ wsHub.broadcast('ai:mark_meta', meta)
103
+ if (meta?.mood) {
104
+ wsHub.broadcast('ai:mood', { mood: meta.mood })
105
+ }
106
+ },
107
+ onToolCall: (toolCalls) => {
108
+ wsHub.broadcast('ai:tool_calls', { toolCalls })
109
+ }
110
+ })
111
+
112
+ res.json(result)
113
+ } catch (err) {
114
+ res.status(500).json({ error: { message: err.message, code: err.code || 'AI_ERROR' } })
115
+ }
116
+ })
117
+
118
+ aiRouter.post('/ai/abort', async (req, res) => {
119
+ try {
120
+ const { abortAllFetches } = await import('../services/ai-bridge.js')
121
+ abortAllFetches()
122
+ res.json({ success: true })
123
+ } catch (err) {
124
+ res.json({ success: false, error: err.message })
125
+ }
126
+ })
127
+
128
+ aiRouter.post('/ai/reset-session', async (req, res) => {
129
+ try {
130
+ const { clearDeepSeekSession } = await import('../services/deepseek-web.js')
131
+ clearDeepSeekSession()
132
+ res.json({ success: true })
133
+ } catch (err) {
134
+ res.json({ success: false, error: err.message })
135
+ }
136
+ })
137
+
138
+ // 3. Group Tools Schema & Registry API
139
+ aiRouter.get('/tools/groups', (_req, res) => {
140
+ res.json({
141
+ success: true,
142
+ data: {
143
+ schema: GROUP_TOOLS_SCHEMA,
144
+ names: GROUP_TOOL_GROUP_NAMES,
145
+ definition: GROUP_TOOLS_DEFINITION,
146
+ flat: group_tools_flat
147
+ }
148
+ })
149
+ })
150
+
151
+ // 4. Native Tools Execution API
152
+ aiRouter.post('/tools/execute', async (req, res) => {
153
+ const { tool, query, config } = req.body || {}
154
+ try {
155
+ if (typeof tool === 'string' && tool.startsWith('plugin-')) {
156
+ const { executePluginAction } = await import('../../main/plugins/plugin-loader.js')
157
+ const result = await executePluginAction(tool, query)
158
+ return res.json(result)
159
+ }
160
+ const { NATIVE_TOOLS } = await import('../../main/node-tools.js')
161
+ const nativeTool = NATIVE_TOOLS[tool]
162
+ if (!nativeTool) {
163
+ return res.json({ success: false, error: `Tool '${tool}' tidak ditemukan` })
164
+ }
165
+ const finalConfig = config || getActiveConfig()
166
+ const result = await nativeTool.handler(query, finalConfig)
167
+ if (result && typeof result === 'object' && 'success' in result) {
168
+ return res.json(result)
169
+ }
170
+ res.json({ success: true, data: result })
171
+ } catch (err) {
172
+ res.json({ success: false, error: err.message })
173
+ }
174
+ })
175
+
176
+ aiRouter.post('/tools/needs-approval', async (req, res) => {
177
+ const { tool, query } = req.body || {}
178
+ try {
179
+ const { NATIVE_TOOLS } = await import('../../main/node-tools.js')
180
+ const nativeTool = NATIVE_TOOLS[tool]
181
+ if (!nativeTool) return res.json({ needsApproval: false })
182
+ const needs =
183
+ typeof nativeTool.needsApproval === 'function'
184
+ ? nativeTool.needsApproval(query)
185
+ : nativeTool.needsApproval
186
+ res.json({
187
+ needsApproval: Boolean(needs),
188
+ message: needs && nativeTool.approvalMessage ? nativeTool.approvalMessage(query) : null
189
+ })
190
+ } catch {
191
+ res.json({ needsApproval: false })
192
+ }
193
+ })
194
+
195
+ // 4. Edge-TTS Speech Synthesis API
196
+ aiRouter.get('/tts/stream', async (req, res) => {
197
+ const { text, voice, rate = 10, pitch = 55 } = req.query || {}
198
+ try {
199
+ const { streamTTS } = await import('../tools/media-tools.js')
200
+ const audioStream = await streamTTS(text, voice, rate, pitch)
201
+ res.setHeader('Content-Type', 'audio/mpeg')
202
+ res.setHeader('Transfer-Encoding', 'chunked')
203
+ res.setHeader('Cache-Control', 'no-cache, no-store')
204
+ res.setHeader('Access-Control-Allow-Origin', '*')
205
+
206
+ req.on('close', () => {
207
+ if (!audioStream.destroyed) {
208
+ audioStream.destroy()
209
+ }
210
+ })
211
+
212
+ audioStream.on('error', (err) => {
213
+ if (!res.headersSent) {
214
+ res.status(500).json({ success: false, error: err.message })
215
+ } else {
216
+ res.end()
217
+ }
218
+ })
219
+
220
+ audioStream.pipe(res)
221
+ } catch (err) {
222
+ if (!res.headersSent) {
223
+ res.status(500).json({ success: false, error: err.message })
224
+ }
225
+ }
226
+ })
227
+
228
+ aiRouter.post('/tts/stream', async (req, res) => {
229
+ const { text, voice, rate = 0, pitch = 0 } = req.body || {}
230
+ try {
231
+ const { streamTTS } = await import('../tools/media-tools.js')
232
+ const audioStream = await streamTTS(text, voice, rate, pitch)
233
+ res.setHeader('Content-Type', 'audio/mpeg')
234
+ res.setHeader('Transfer-Encoding', 'chunked')
235
+ res.setHeader('Cache-Control', 'no-cache, no-store')
236
+ res.setHeader('Access-Control-Allow-Origin', '*')
237
+
238
+ req.on('close', () => {
239
+ if (!audioStream.destroyed) {
240
+ audioStream.destroy()
241
+ }
242
+ })
243
+
244
+ audioStream.on('error', (err) => {
245
+ if (!res.headersSent) {
246
+ res.status(500).json({ success: false, error: err.message })
247
+ } else {
248
+ res.end()
249
+ }
250
+ })
251
+
252
+ audioStream.pipe(res)
253
+ } catch (err) {
254
+ if (!res.headersSent) {
255
+ res.status(500).json({ success: false, error: err.message })
256
+ }
257
+ }
258
+ })
259
+
260
+ aiRouter.post('/tts', async (req, res) => {
261
+ const { text, voice, rate = 0, pitch = 0 } = req.body || {}
262
+ try {
263
+ const { synthesizeTTS } = await import('../tools/media-tools.js')
264
+ const result = await synthesizeTTS(text, voice, rate, pitch)
265
+ res.json({ success: true, ...result })
266
+ } catch (err) {
267
+ res.status(500).json({ success: false, error: err.message })
268
+ }
269
+ })
270
+
271
+ // 5. Launcher API
272
+ aiRouter.post('/launcher/open', async (req, res) => {
273
+ const { mode = 'app', port = 3000 } = req.body || {}
274
+ try {
275
+ await launchUI({ port: req.app.get('port') || port, mode })
276
+ res.json({ success: true, message: `Launcher triggered for mode: ${mode}` })
277
+ } catch (err) {
278
+ res.status(500).json({ success: false, error: err.message })
279
+ }
280
+ })
@@ -0,0 +1,62 @@
1
+ import { Router } from 'express'
2
+ import {
3
+ getActivityBuffer,
4
+ clearActivityBuffer,
5
+ getSystemIdleSeconds,
6
+ getSystemTelemetry
7
+ } from '../tools/awareness-tracker.js'
8
+ import { captureDesktopScreenshotsBase64 } from '../tools/screen-service.js'
9
+
10
+ export const awarenessRouter = Router()
11
+
12
+ awarenessRouter.get('/awareness/activity-buffer', async (_req, res) => {
13
+ try {
14
+ const telemetry = await getSystemTelemetry()
15
+ res.json({ success: true, data: getActivityBuffer(), telemetry })
16
+ } catch {
17
+ res.json({ success: true, data: getActivityBuffer(), telemetry: null })
18
+ }
19
+ })
20
+
21
+ awarenessRouter.get('/awareness/telemetry', async (_req, res) => {
22
+ try {
23
+ const telemetry = await getSystemTelemetry()
24
+ res.json({ success: true, telemetry })
25
+ } catch (err) {
26
+ res.status(500).json({ success: false, error: err.message })
27
+ }
28
+ })
29
+
30
+ awarenessRouter.post('/awareness/clear-buffer', (_req, res) => {
31
+ clearActivityBuffer()
32
+ res.json({ success: true })
33
+ })
34
+
35
+ awarenessRouter.get('/awareness/idle-time', async (_req, res) => {
36
+ try {
37
+ const idleSeconds = await getSystemIdleSeconds()
38
+ res.json({ success: true, idleSeconds })
39
+ } catch {
40
+ res.json({ success: true, idleSeconds: 0 })
41
+ }
42
+ })
43
+
44
+ awarenessRouter.post('/os/screenshot', async (_req, res) => {
45
+ try {
46
+ const screens = await captureDesktopScreenshotsBase64()
47
+ res.json({ success: true, data: screens })
48
+ } catch (err) {
49
+ res.status(500).json({ success: false, error: err.message })
50
+ }
51
+ })
52
+
53
+ awarenessRouter.post('/system/notify', async (req, res) => {
54
+ const { title = 'Mark', body = '' } = req.body || {}
55
+ try {
56
+ const { showNativeNotification } = await import('../services/notification-service.js')
57
+ showNativeNotification(title, body)
58
+ res.json({ success: true })
59
+ } catch (err) {
60
+ res.status(500).json({ success: false, error: err.message })
61
+ }
62
+ })
@@ -0,0 +1,61 @@
1
+ import { Router } from 'express'
2
+ import fs from 'fs'
3
+ import path from 'path'
4
+ import os from 'os'
5
+ import { fetchAI } from '../services/ai-bridge.js'
6
+ import { getActiveConfig } from '../config-manager.js'
7
+
8
+ export const chatRouter = Router()
9
+
10
+ chatRouter.post('/chat/upload-temp', async (req, res) => {
11
+ const { fileName, dataUrl, base64Data } = req.body || {}
12
+ try {
13
+ const rawBase64 = base64Data || (dataUrl ? dataUrl.replace(/^data:[^;]+;base64,/, '') : null)
14
+ if (!rawBase64) {
15
+ return res.status(400).json({ success: false, error: 'Data base64 kosong' })
16
+ }
17
+ const tempDir = path.join(os.homedir(), '.config', 'mark-agent', 'temp-uploads')
18
+ if (!fs.existsSync(tempDir)) {
19
+ fs.mkdirSync(tempDir, { recursive: true })
20
+ }
21
+ const safeName = (fileName || `image-${Date.now()}.png`).replace(/[^a-zA-Z0-9_.-]/g, '_')
22
+ const finalName = `${Date.now()}-${safeName}`
23
+ const targetPath = path.join(tempDir, finalName)
24
+ const buffer = Buffer.from(rawBase64, 'base64')
25
+ await fs.promises.writeFile(targetPath, buffer)
26
+
27
+ res.json({
28
+ success: true,
29
+ path: targetPath,
30
+ name: finalName,
31
+ url: `/api/chat/temp-file/${encodeURIComponent(finalName)}`,
32
+ size: buffer.length
33
+ })
34
+ } catch (err) {
35
+ res.status(500).json({ success: false, error: err.message })
36
+ }
37
+ })
38
+
39
+ chatRouter.get('/chat/temp-file/:fileName', (req, res) => {
40
+ try {
41
+ const safeName = path.basename(req.params.fileName)
42
+ const filePath = path.join(os.homedir(), '.config', 'mark-agent', 'temp-uploads', safeName)
43
+ if (fs.existsSync(filePath)) {
44
+ return res.sendFile(filePath)
45
+ }
46
+ res.status(404).send('File tidak ditemukan')
47
+ } catch (err) {
48
+ res.status(500).send(err.message)
49
+ }
50
+ })
51
+
52
+ chatRouter.post('/chat', async (req, res) => {
53
+ const { message, messages } = req.body || {}
54
+ const effectiveMessages = messages || [{ role: 'user', content: message || '' }]
55
+ try {
56
+ const result = await fetchAI(effectiveMessages, false, { config: getActiveConfig() })
57
+ res.json(result)
58
+ } catch (err) {
59
+ res.status(500).json({ success: false, error: err.message })
60
+ }
61
+ })