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