@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,170 @@
1
+ /**
2
+ * Gemini Web RPC Engine for MARK Node.js Core
3
+ */
4
+ import https from 'https'
5
+ import crypto from 'crypto'
6
+
7
+ export const GEMINI_WEB_MODELS = {
8
+ 'gemini-3.6-flash': {
9
+ mode: 1,
10
+ think: 4,
11
+ name: 'gemini-3.6-flash',
12
+ desc: 'Model utama serbaguna versi terbaru'
13
+ },
14
+ 'gemini-3.5-flash': {
15
+ mode: 1,
16
+ think: 4,
17
+ name: 'gemini-3.5-flash',
18
+ desc: 'Model Flash seimbang dan stabil'
19
+ },
20
+ 'gemini-3.5-flash-thinking': {
21
+ mode: 2,
22
+ think: 0,
23
+ name: 'gemini-3.5-flash-thinking',
24
+ desc: 'Mode penalaran mendalam untuk analisis rumit'
25
+ },
26
+ 'gemini-3.5-flash-thinking-lite': {
27
+ mode: 5,
28
+ think: 0,
29
+ name: 'gemini-3.5-flash-thinking-lite',
30
+ desc: 'Mode penalaran cepat dengan kedalaman fleksibel'
31
+ },
32
+ 'gemini-auto': {
33
+ mode: 4,
34
+ think: 4,
35
+ name: 'gemini-auto',
36
+ desc: 'Penyesuaian model otomatis oleh server'
37
+ },
38
+ 'gemini-flash-lite': {
39
+ mode: 6,
40
+ think: 4,
41
+ name: 'gemini-flash-lite',
42
+ desc: 'Model super ringan dengan respon instan'
43
+ }
44
+ }
45
+
46
+ const DEFAULT_BL = 'boq_assistant-bard-web-server_20260730.01_p1'
47
+
48
+ function httpPost(urlStr, headers, bodyData) {
49
+ return new Promise((resolve, reject) => {
50
+ const parsedUrl = new URL(urlStr)
51
+ const options = {
52
+ hostname: parsedUrl.hostname,
53
+ port: parsedUrl.port || 443,
54
+ path: parsedUrl.pathname + parsedUrl.search,
55
+ method: 'POST',
56
+ headers: headers
57
+ }
58
+ const req = https.request(options, (res) => {
59
+ let data = ''
60
+ res.on('data', (chunk) => (data += chunk.toString()))
61
+ res.on('end', () => resolve(data))
62
+ })
63
+ req.on('error', (err) => reject(err))
64
+ req.write(bodyData)
65
+ req.end()
66
+ })
67
+ }
68
+
69
+ function findRc(arr) {
70
+ if (!Array.isArray(arr)) return null
71
+ if (arr[0] && typeof arr[0] === 'string' && arr[0].startsWith('rc_')) {
72
+ return arr[1][0]
73
+ }
74
+ for (const item of arr) {
75
+ const res = findRc(item)
76
+ if (res) return res
77
+ }
78
+ return null
79
+ }
80
+
81
+ export async function generateGeminiResponse(
82
+ prompt,
83
+ modelName = 'gemini-3.6-flash',
84
+ cookie = '',
85
+ bl = DEFAULT_BL
86
+ ) {
87
+ const reqModel = (modelName || 'gemini-3.6-flash').toLowerCase()
88
+
89
+ let selected = GEMINI_WEB_MODELS[reqModel]
90
+ if (!selected) {
91
+ if (reqModel.includes('thinking-lite')) selected = GEMINI_WEB_MODELS['gemini-3.5-flash-thinking-lite']
92
+ else if (reqModel.includes('think')) selected = GEMINI_WEB_MODELS['gemini-3.5-flash-thinking']
93
+ else if (reqModel.includes('auto')) selected = GEMINI_WEB_MODELS['gemini-auto']
94
+ else if (reqModel.includes('lite') || reqModel.includes('fast')) selected = GEMINI_WEB_MODELS['gemini-flash-lite']
95
+ else selected = GEMINI_WEB_MODELS['gemini-3.6-flash']
96
+ }
97
+
98
+ const modelId = selected.mode
99
+ const thinkMode = selected.think
100
+
101
+ const inner = new Array(80).fill(null)
102
+ inner[0] = [prompt, 0, null, null, null, null, 0]
103
+ inner[1] = ['en']
104
+ inner[2] = ['', '', '', null, null, null, null, null, null, '']
105
+ inner[6] = [0]
106
+ inner[7] = 1
107
+ inner[10] = 1
108
+ inner[11] = 0
109
+ inner[17] = [[thinkMode]]
110
+ inner[18] = 0
111
+ inner[27] = 1
112
+ inner[30] = [4]
113
+ inner[41] = [2]
114
+ inner[53] = 0
115
+ inner[59] = crypto.randomUUID
116
+ ? crypto.randomUUID()
117
+ : Math.random().toString(36).substring(2) + Date.now().toString(36)
118
+ inner[61] = []
119
+ inner[68] = 1
120
+ inner[79] = modelId
121
+
122
+ const outer = [null, JSON.stringify(inner)]
123
+ const bodyParams = new URLSearchParams()
124
+ bodyParams.set('f.req', JSON.stringify(outer))
125
+
126
+ const reqid = Math.floor(Date.now() / 1000) % 1000000
127
+ const url = `https://gemini.google.com/_/BardChatUi/data/assistant.lamda.BardFrontendService/StreamGenerate?bl=${bl}&hl=id&_reqid=${reqid}&rt=c`
128
+
129
+ const headers = {
130
+ 'content-type': 'application/x-www-form-urlencoded;charset=UTF-8',
131
+ accept: '*/*',
132
+ 'x-same-domain': '1',
133
+ 'user-agent':
134
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36',
135
+ referer: 'https://gemini.google.com/'
136
+ }
137
+
138
+ if (cookie) {
139
+ headers['cookie'] = cookie
140
+ }
141
+
142
+ const rawText = await httpPost(url, headers, bodyParams.toString())
143
+
144
+ let finalAnswer = ''
145
+ const lines = rawText.split('\n')
146
+ for (const line of lines) {
147
+ if (line.trim().startsWith('[["wrb.fr"')) {
148
+ try {
149
+ const parsed = JSON.parse(line.trim())
150
+ const innerStr = parsed[0][2]
151
+ if (innerStr) {
152
+ const innerParsed = JSON.parse(innerStr)
153
+ const text = findRc(innerParsed)
154
+ if (text && text.length > finalAnswer.length) {
155
+ finalAnswer = text
156
+ }
157
+ }
158
+ } catch (e) {}
159
+ }
160
+ }
161
+
162
+ if (!finalAnswer) {
163
+ if (rawText.includes('BardErrorInfo')) {
164
+ throw new Error('Google menolak request (Session / Cookie mungkin expired atau terblokir).')
165
+ }
166
+ throw new Error('Gagal mengekstrak jawaban dari Gemini Web. Balasan mentah: ' + rawText.substring(0, 200))
167
+ }
168
+
169
+ return finalAnswer
170
+ }
@@ -0,0 +1,91 @@
1
+ import { exec } from 'child_process'
2
+ import os from 'os'
3
+ import path from 'path'
4
+ import fs from 'fs'
5
+ import { fileURLToPath } from 'url'
6
+ import { stripMarkTags } from '../../shared/parsers/mark-tag-parser.js'
7
+
8
+ const __filename = fileURLToPath(import.meta.url)
9
+ const __dirname = path.dirname(__filename)
10
+
11
+ /**
12
+ * Mendapatkan absolute path icon MARK untuk Windows Toast
13
+ */
14
+ function getNotificationIconPath() {
15
+ const candidates = [
16
+ path.resolve(__dirname, '../../../src/renderer/public/icon-256.png'),
17
+ path.resolve(__dirname, '../../renderer/public/icon-256.png'),
18
+ path.resolve(__dirname, '../../../out/renderer/icon-256.png'),
19
+ path.resolve(__dirname, '../../out/renderer/icon-256.png')
20
+ ]
21
+ for (const c of candidates) {
22
+ if (fs.existsSync(c)) return c
23
+ }
24
+ return null
25
+ }
26
+
27
+ /**
28
+ * Menampilkan notifikasi desktop native Windows (Toast Notification) tanpa izin WebUI.
29
+ * Menggunakan Windows Runtime (WinRT) PowerShell yang terintegrasi langsung dengan Action Center Windows 10/11
30
+ * dan menampilkan logo MARK secara native di samping teks.
31
+ */
32
+ export function showNativeNotification(title = 'Mark', body = '') {
33
+ const isWindows = os.platform() === 'win32'
34
+ const safeTitle = stripMarkTags(title || 'Mark')
35
+ .replace(/["`$\\]/g, ' ')
36
+ .trim()
37
+ const safeBody = stripMarkTags(body || '')
38
+ .replace(/["`$\\]/g, ' ')
39
+ .trim()
40
+ const iconPath = getNotificationIconPath()
41
+
42
+ if (!isWindows) {
43
+ if (os.platform() === 'darwin') {
44
+ exec(`osascript -e 'display notification "${safeBody}" with title "${safeTitle}"'`)
45
+ } else {
46
+ const iconArg = iconPath ? `-i "${iconPath}"` : ''
47
+ exec(`notify-send ${iconArg} "${safeTitle}" "${safeBody}"`)
48
+ }
49
+ return
50
+ }
51
+
52
+ // Windows Toast Notification via PowerShell WindowsRuntime dengan icon & app id
53
+ const hasIcon = Boolean(iconPath && fs.existsSync(iconPath))
54
+ const templateType = hasIcon ? 'ToastImageAndText02' : 'ToastText02'
55
+ const formattedIconUri = hasIcon ? 'file:///' + iconPath.replace(/\\/g, '/') : ''
56
+
57
+ const psScript = `
58
+ [Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null
59
+ [Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime] | Out-Null
60
+ $template = [Windows.UI.Notifications.ToastNotificationManager]::GetTemplateContent([Windows.UI.Notifications.ToastTemplateType]::${templateType})
61
+
62
+ ${
63
+ hasIcon
64
+ ? `
65
+ $imageNodes = $template.GetElementsByTagName("image")
66
+ $imageNodes.Item(0).Attributes.GetNamedItem("src").NodeValue = "${formattedIconUri}"
67
+ `
68
+ : ''
69
+ }
70
+
71
+ $textNodes = $template.GetElementsByTagName("text")
72
+ $textNodes.Item(0).AppendChild($template.CreateTextNode("${safeTitle}")) | Out-Null
73
+ $textNodes.Item(1).AppendChild($template.CreateTextNode("${safeBody}")) | Out-Null
74
+
75
+ $toast = [Windows.UI.Notifications.ToastNotification]::new($template)
76
+ [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier("MARK Agent").Show($toast)
77
+ `.trim()
78
+
79
+ const encodedCommand = Buffer.from(psScript, 'utf16le').toString('base64')
80
+ exec(
81
+ `powershell.exe -NoProfile -ExecutionPolicy Bypass -EncodedCommand ${encodedCommand}`,
82
+ {
83
+ windowsHide: true
84
+ },
85
+ (err) => {
86
+ if (err) {
87
+ console.warn('[notification-service] Gagal memicu Windows Toast:', err.message)
88
+ }
89
+ }
90
+ )
91
+ }
@@ -0,0 +1,125 @@
1
+ import fs from 'fs'
2
+ import path from 'path'
3
+ import os from 'os'
4
+ import { fileURLToPath } from 'url'
5
+
6
+ const __filename = fileURLToPath(import.meta.url)
7
+ const __dirname = path.dirname(__filename)
8
+
9
+ export const PACKAGE_NAME = '@mazees/mark'
10
+ const CACHE_FILE = path.join(os.homedir(), '.config', 'mark-agent', 'update-cache.json')
11
+ const CHECK_INTERVAL_MS = 6 * 60 * 60 * 1000 // 6 jam
12
+
13
+ let currentPackageVersion = '5.0.0'
14
+ try {
15
+ const pkgPath = path.resolve(__dirname, '../../../package.json')
16
+ if (fs.existsSync(pkgPath)) {
17
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'))
18
+ if (pkg.version) currentPackageVersion = pkg.version
19
+ }
20
+ } catch (_) {}
21
+
22
+ export function getCurrentVersion() {
23
+ return currentPackageVersion
24
+ }
25
+
26
+ export function getUpdateInstruction() {
27
+ return `npm install -g ${PACKAGE_NAME}@latest`
28
+ }
29
+
30
+ export function parseSemver(v) {
31
+ if (!v) return [0, 0, 0]
32
+ return String(v)
33
+ .replace(/^v/, '')
34
+ .split('.')
35
+ .map((n) => parseInt(n, 10) || 0)
36
+ }
37
+
38
+ export function isNewerVersion(latest, current) {
39
+ if (!latest || !current) return false
40
+ const [lMaj, lMin, lPat] = parseSemver(latest)
41
+ const [cMaj, cMin, cPat] = parseSemver(current)
42
+ if (lMaj > cMaj) return true
43
+ if (lMaj < cMaj) return false
44
+ if (lMin > cMin) return true
45
+ if (lMin < cMin) return false
46
+ return lPat > cPat
47
+ }
48
+
49
+ export async function checkForUpdate(options = {}) {
50
+ const { force = false } = options
51
+ const currentVersion = getCurrentVersion()
52
+
53
+ // 1. Dukungan Mock untuk Testing Lokal (sebelum rilis resmi di registry npm)
54
+ if (process.env.MARK_MOCK_LATEST_VERSION) {
55
+ const mockVersion = process.env.MARK_MOCK_LATEST_VERSION.trim()
56
+ return {
57
+ hasUpdate: isNewerVersion(mockVersion, currentVersion),
58
+ latestVersion: mockVersion,
59
+ currentVersion,
60
+ instruction: getUpdateInstruction(),
61
+ isMock: true
62
+ }
63
+ }
64
+
65
+ // 2. Periksa Cache Lokal
66
+ if (!force && fs.existsSync(CACHE_FILE)) {
67
+ try {
68
+ const cache = JSON.parse(fs.readFileSync(CACHE_FILE, 'utf8'))
69
+ if (Date.now() - (cache.lastChecked || 0) < CHECK_INTERVAL_MS) {
70
+ return {
71
+ hasUpdate: isNewerVersion(cache.latestVersion, currentVersion),
72
+ latestVersion: cache.latestVersion,
73
+ currentVersion,
74
+ instruction: getUpdateInstruction(),
75
+ cached: true
76
+ }
77
+ }
78
+ } catch (_) {}
79
+ }
80
+
81
+ // 3. Query Registry NPM (timeout 2500ms agar non-blocking)
82
+ try {
83
+ const res = await fetch(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`, {
84
+ headers: { Accept: 'application/json' },
85
+ signal: AbortSignal.timeout(2500)
86
+ })
87
+
88
+ if (!res.ok) {
89
+ return {
90
+ hasUpdate: false,
91
+ latestVersion: currentVersion,
92
+ currentVersion,
93
+ instruction: getUpdateInstruction()
94
+ }
95
+ }
96
+
97
+ const data = await res.json()
98
+ const latestVersion = data.version || currentVersion
99
+
100
+ // 4. Simpan ke Cache Lokal
101
+ try {
102
+ fs.mkdirSync(path.dirname(CACHE_FILE), { recursive: true })
103
+ fs.writeFileSync(
104
+ CACHE_FILE,
105
+ JSON.stringify({ lastChecked: Date.now(), latestVersion }, null, 2),
106
+ 'utf8'
107
+ )
108
+ } catch (_) {}
109
+
110
+ return {
111
+ hasUpdate: isNewerVersion(latestVersion, currentVersion),
112
+ latestVersion,
113
+ currentVersion,
114
+ instruction: getUpdateInstruction()
115
+ }
116
+ } catch (_) {
117
+ // Gagal fetch (offline, DNS timeout, dll) -> gracefully return false
118
+ return {
119
+ hasUpdate: false,
120
+ latestVersion: currentVersion,
121
+ currentVersion,
122
+ instruction: getUpdateInstruction()
123
+ }
124
+ }
125
+ }
@@ -0,0 +1,292 @@
1
+ import activeWin from 'active-win'
2
+ import { exec } from 'child_process'
3
+ import util from 'util'
4
+ import os from 'os'
5
+ import { wsHub } from '../ws-hub.js'
6
+ import { isDaemonAlive, startDaemon, sendCommand } from './pc-agent.js'
7
+ import { getActiveConfig } from '../config-manager.js'
8
+
9
+ const execPromise = util.promisify(exec)
10
+
11
+ const activityBuffer = []
12
+ const MAX_BUFFER_SIZE = 30
13
+ let trackerInterval = null
14
+
15
+ let currentActiveApp = null
16
+ let currentActiveTitle = null
17
+ let currentAppStartTime = Date.now()
18
+
19
+ let lastCpuSample = null
20
+ let cachedCpuPercent = 0
21
+ let cachedBattery = { hasBattery: false, percent: 100, isCharging: true }
22
+ let lastBatteryFetch = 0
23
+
24
+ function sampleCpuPercent() {
25
+ const cpus = os.cpus()
26
+ if (!cpus || cpus.length === 0) return 0
27
+ let user = 0
28
+ let nice = 0
29
+ let sys = 0
30
+ let idle = 0
31
+ let irq = 0
32
+ for (const cpu of cpus) {
33
+ user += cpu.times.user
34
+ nice += cpu.times.nice
35
+ sys += cpu.times.sys
36
+ idle += cpu.times.idle
37
+ irq += cpu.times.irq
38
+ }
39
+ const currentSample = { idle, total: user + nice + sys + idle + irq }
40
+ if (lastCpuSample) {
41
+ const idleDelta = currentSample.idle - lastCpuSample.idle
42
+ const totalDelta = currentSample.total - lastCpuSample.total
43
+ if (totalDelta > 0) {
44
+ cachedCpuPercent = Math.max(0, Math.min(100, Math.round((1 - idleDelta / totalDelta) * 100)))
45
+ }
46
+ }
47
+ lastCpuSample = currentSample
48
+ return cachedCpuPercent
49
+ }
50
+
51
+ async function getBatteryTelemetry() {
52
+ const now = Date.now()
53
+ if (now - lastBatteryFetch < 60000 && lastBatteryFetch > 0) {
54
+ return cachedBattery
55
+ }
56
+ lastBatteryFetch = now
57
+ if (process.platform !== 'win32') return cachedBattery
58
+
59
+ try {
60
+ const { stdout } = await execPromise(
61
+ 'powershell -NoProfile -NonInteractive -Command "Get-CimInstance Win32_Battery | Select-Object -Property EstimatedChargeRemaining, BatteryStatus | ConvertTo-Json"'
62
+ )
63
+ const text = stdout ? stdout.trim() : ''
64
+ if (text && text.startsWith('{')) {
65
+ const parsed = JSON.parse(text)
66
+ const percent = Number(parsed.EstimatedChargeRemaining ?? 100)
67
+ const isCharging = [2, 6, 7, 8, 3].includes(parsed.BatteryStatus)
68
+ cachedBattery = {
69
+ hasBattery: true,
70
+ percent,
71
+ isCharging,
72
+ status: parsed.BatteryStatus
73
+ }
74
+ } else if (text && text.startsWith('[')) {
75
+ const arr = JSON.parse(text)
76
+ const parsed = arr[0] || {}
77
+ cachedBattery = {
78
+ hasBattery: true,
79
+ percent: Number(parsed.EstimatedChargeRemaining ?? 100),
80
+ isCharging: [2, 6, 7, 8, 3].includes(parsed.BatteryStatus),
81
+ status: parsed.BatteryStatus
82
+ }
83
+ }
84
+ } catch {
85
+ cachedBattery = { hasBattery: false, percent: 100, isCharging: true }
86
+ }
87
+ return cachedBattery
88
+ }
89
+
90
+ export function recordActivityEntry(winData) {
91
+ if (!winData || !winData.title) return
92
+
93
+ const appName = winData.owner?.name || winData.app || 'System'
94
+ const title = winData.title.trim()
95
+
96
+ if (currentActiveApp !== appName || currentActiveTitle !== title) {
97
+ currentActiveApp = appName
98
+ currentActiveTitle = title
99
+ currentAppStartTime = Date.now()
100
+ }
101
+
102
+ if (
103
+ activityBuffer.length > 0 &&
104
+ activityBuffer[0].title === title &&
105
+ activityBuffer[0].app === appName
106
+ ) {
107
+ return
108
+ }
109
+
110
+ const timeStr = new Date().toLocaleTimeString('id-ID', {
111
+ hour: '2-digit',
112
+ minute: '2-digit',
113
+ second: '2-digit'
114
+ })
115
+
116
+ activityBuffer.unshift({
117
+ app: appName,
118
+ title: title,
119
+ url: winData.url || null,
120
+ timestamp: timeStr,
121
+ time: timeStr
122
+ })
123
+
124
+ wsHub.broadcast('awareness:entry', { app: appName, title, time: timeStr })
125
+
126
+ if (activityBuffer.length > MAX_BUFFER_SIZE) {
127
+ activityBuffer.pop()
128
+ }
129
+ }
130
+
131
+ async function getActiveWindowFallback() {
132
+ // 1. Coba persistent Win32 PC-Daemon tercepat jika aktif (<1ms, tanpa spawn proses)
133
+ if (process.platform === 'win32' && isDaemonAlive()) {
134
+ try {
135
+ const raw = await sendCommand({ cmd: 'get-active-window' })
136
+ const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw
137
+ if (parsed && parsed.title && parsed.title.trim()) {
138
+ return {
139
+ title: parsed.title.trim(),
140
+ owner: { name: parsed.process || 'Windows App' },
141
+ app: parsed.process || 'Windows App',
142
+ url: null
143
+ }
144
+ }
145
+ } catch {
146
+ // ignore
147
+ }
148
+ }
149
+
150
+ // 2. Coba activeWin native binding sebagai fallback (macOS / Linux / jika tersedia)
151
+ try {
152
+ const win = await activeWin()
153
+ if (win && win.title && win.title.trim()) {
154
+ return {
155
+ title: win.title.trim(),
156
+ owner: { name: win.owner?.name || win.app || 'System' },
157
+ app: win.owner?.name || win.app || 'System',
158
+ url: win.url || null
159
+ }
160
+ }
161
+ } catch {
162
+ // ignore
163
+ }
164
+
165
+ // 3. Jika di Windows dan daemon belum aktif, jalankan daemon di background
166
+ if (process.platform === 'win32' && !isDaemonAlive()) {
167
+ startDaemon().catch(() => {})
168
+ }
169
+
170
+ return null
171
+ }
172
+
173
+ export async function getSystemIdleSeconds() {
174
+ if (process.platform !== 'win32') return 0
175
+
176
+ // 1. Prioritaskan Win32 PC-Daemon instan (<0.1ms tanpa spawn PowerShell atau csc.exe)
177
+ if (isDaemonAlive()) {
178
+ try {
179
+ const raw = await sendCommand({ cmd: 'get-idle' })
180
+ const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw
181
+ if (parsed && typeof parsed.idleSeconds === 'number') {
182
+ return parsed.idleSeconds
183
+ }
184
+ } catch {
185
+ // fallback jika daemon sibuk
186
+ }
187
+ }
188
+
189
+ // 2. Fallback jika daemon belum siap (jalankan daemon untuk panggilan berikutnya)
190
+ startDaemon().catch(() => {})
191
+
192
+ try {
193
+ const psScript = `
194
+ $signature = @'
195
+ [DllImport("user32.dll")]
196
+ public static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
197
+ [StructLayout(LayoutKind.Sequential)]
198
+ public struct LASTINPUTINFO {
199
+ public uint cbSize;
200
+ public uint dwTime;
201
+ }
202
+ '@
203
+ $type = Add-Type -MemberDefinition $signature -Name Win32GetLastInput -Namespace Win32API -PassThru
204
+ $info = New-Object Win32API.Win32GetLastInput+LASTINPUTINFO
205
+ $info.cbSize = [System.Runtime.InteropServices.Marshal]::SizeOf($info)
206
+ if ($type::GetLastInputInfo([ref]$info)) {
207
+ $idleMs = [Environment]::TickCount - $info.dwTime
208
+ [Math]::Round($idleMs / 1000)
209
+ } else {
210
+ 0
211
+ }
212
+ `
213
+ const { stdout } = await execPromise(
214
+ `powershell -NoProfile -NonInteractive -Command "${psScript.replace(/\n/g, ' ')}"`
215
+ )
216
+ return parseInt(stdout.trim(), 10) || 0
217
+ } catch {
218
+ return 0
219
+ }
220
+ }
221
+
222
+ export function startOsActivityTracking(intervalMs = 10000) {
223
+ if (trackerInterval) clearInterval(trackerInterval)
224
+
225
+ // Langsung panggil sekali saat start jika awareness aktif
226
+ const cfg = getActiveConfig() || {}
227
+ if (cfg.awarenessEnabled !== false) {
228
+ if (process.platform === 'win32' && !isDaemonAlive()) {
229
+ startDaemon().catch(() => {})
230
+ }
231
+ getActiveWindowFallback().then((win) => {
232
+ if (win) recordActivityEntry(win)
233
+ })
234
+ }
235
+
236
+ trackerInterval = setInterval(async () => {
237
+ try {
238
+ const currentCfg = getActiveConfig() || {}
239
+ if (currentCfg.awarenessEnabled === false) return
240
+
241
+ const windowInfo = await getActiveWindowFallback()
242
+ if (windowInfo) {
243
+ recordActivityEntry(windowInfo)
244
+ }
245
+ } catch {
246
+ // ignore
247
+ }
248
+ }, intervalMs)
249
+ }
250
+
251
+ export function stopOsActivityTracking() {
252
+ if (trackerInterval) {
253
+ clearInterval(trackerInterval)
254
+ trackerInterval = null
255
+ }
256
+ }
257
+
258
+ export function getActivityBuffer() {
259
+ return [...activityBuffer]
260
+ }
261
+
262
+ export function clearActivityBuffer() {
263
+ activityBuffer.length = 0
264
+ }
265
+
266
+ export async function getSystemTelemetry() {
267
+ const idleSeconds = await getSystemIdleSeconds()
268
+ const battery = await getBatteryTelemetry()
269
+ const totalMem = os.totalmem()
270
+ const freeMem = os.freemem()
271
+ const ramPercent = Math.round(((totalMem - freeMem) / totalMem) * 100)
272
+ const cpuPercent = sampleCpuPercent()
273
+ const uptimeSeconds = os.uptime()
274
+ const uptimeHours = Math.round((uptimeSeconds / 3600) * 10) / 10
275
+ const isFreshBoot = uptimeSeconds < 1800
276
+ const activeDurationMinutes = Math.max(0, Math.floor((Date.now() - currentAppStartTime) / 60000))
277
+
278
+ return {
279
+ idleSeconds,
280
+ isUserAFK: idleSeconds >= 900,
281
+ activeApp: currentActiveApp || 'System',
282
+ activeTitle: currentActiveTitle || '',
283
+ activeAppDurationMinutes: activeDurationMinutes,
284
+ battery,
285
+ hardware: {
286
+ ramPercent,
287
+ cpuPercent,
288
+ uptimeHours,
289
+ isFreshBoot
290
+ }
291
+ }
292
+ }