@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,425 @@
|
|
|
1
|
+
import React, { createContext, useState, useContext, useCallback, useRef, useEffect } from 'react'
|
|
2
|
+
import { useLocation } from 'react-router-dom'
|
|
3
|
+
import { ShieldAlert, Terminal, FileCode } from 'lucide-react'
|
|
4
|
+
import { getAlwaysAllowedPaths, addAlwaysAllowedPath, getSessionAutoMode } from '../api/db'
|
|
5
|
+
|
|
6
|
+
const ApprovalContext = createContext()
|
|
7
|
+
|
|
8
|
+
export function extractToolTarget(tool, query) {
|
|
9
|
+
if (!query) return { type: 'unknown', value: '' }
|
|
10
|
+
|
|
11
|
+
if (tool === 'run-powershell') {
|
|
12
|
+
const cmd =
|
|
13
|
+
typeof query === 'object' && query !== null
|
|
14
|
+
? query.command || query.cmd || ''
|
|
15
|
+
: String(query || '')
|
|
16
|
+
return { type: 'command', value: String(cmd).trim() }
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let rawPath = ''
|
|
20
|
+
if (typeof query === 'object' && query !== null) {
|
|
21
|
+
rawPath = query.path || query.filePath || query.cwd || query.target || ''
|
|
22
|
+
} else if (typeof query === 'string') {
|
|
23
|
+
rawPath = query.split('||')[0].trim()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
type: 'path',
|
|
28
|
+
value: String(rawPath)
|
|
29
|
+
.replace(/^["']|["']$/g, '')
|
|
30
|
+
.trim()
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function getFolderFromPath(filePath) {
|
|
35
|
+
if (!filePath) return ''
|
|
36
|
+
const normalized = filePath.replace(/[\\/]+/g, '/')
|
|
37
|
+
const lastSlash = normalized.lastIndexOf('/')
|
|
38
|
+
if (lastSlash !== -1) {
|
|
39
|
+
return normalized.substring(0, lastSlash)
|
|
40
|
+
}
|
|
41
|
+
return normalized
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const ApprovalProvider = ({ children }) => {
|
|
45
|
+
const [approvalData, setApprovalData] = useState(null)
|
|
46
|
+
const approvalRef = useRef(null)
|
|
47
|
+
const [alwaysAllowedPaths, setAlwaysAllowedPaths] = useState([])
|
|
48
|
+
const sessionAllowedMapRef = useRef(new Map())
|
|
49
|
+
const autoModeSessionsRef = useRef(new Set())
|
|
50
|
+
|
|
51
|
+
const location = useLocation()
|
|
52
|
+
const isChatStudio =
|
|
53
|
+
location?.pathname === '/chat' ||
|
|
54
|
+
(typeof window !== 'undefined' && window.location.hash.includes('/chat'))
|
|
55
|
+
|
|
56
|
+
const alwaysAllowedPathsRef = useRef(alwaysAllowedPaths)
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
alwaysAllowedPathsRef.current = alwaysAllowedPaths
|
|
59
|
+
}, [alwaysAllowedPaths])
|
|
60
|
+
|
|
61
|
+
// Muat data alwaysAllowedPaths dari SQLite database saat startup
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
getAlwaysAllowedPaths().then((paths) => {
|
|
64
|
+
if (Array.isArray(paths)) {
|
|
65
|
+
setAlwaysAllowedPaths(paths)
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
const handleConfigUpdated = (e) => {
|
|
70
|
+
if (Array.isArray(e.detail?.alwaysAllowedPaths)) {
|
|
71
|
+
setAlwaysAllowedPaths(e.detail.alwaysAllowedPaths)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
window.addEventListener('config-updated', handleConfigUpdated)
|
|
75
|
+
return () => window.removeEventListener('config-updated', handleConfigUpdated)
|
|
76
|
+
}, [])
|
|
77
|
+
|
|
78
|
+
// Pastikan ref selalu sinkron dengan state
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
approvalRef.current = approvalData
|
|
81
|
+
}, [approvalData])
|
|
82
|
+
|
|
83
|
+
const handleApproveAlwaysInternal = useCallback(async (tool, targetQuery) => {
|
|
84
|
+
const target = extractToolTarget(tool, targetQuery)
|
|
85
|
+
let identifierToAdd = ''
|
|
86
|
+
|
|
87
|
+
if (target.type === 'command') {
|
|
88
|
+
identifierToAdd = `cmd:${target.value.toLowerCase()}`
|
|
89
|
+
} else if (target.type === 'path' && target.value) {
|
|
90
|
+
const normalizedPath = target.value.replace(/[\\/]+/g, '/').toLowerCase()
|
|
91
|
+
const folder = getFolderFromPath(normalizedPath)
|
|
92
|
+
identifierToAdd = folder || normalizedPath
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (identifierToAdd) {
|
|
96
|
+
const updated = await addAlwaysAllowedPath(identifierToAdd)
|
|
97
|
+
if (Array.isArray(updated)) {
|
|
98
|
+
setAlwaysAllowedPaths(updated)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}, [])
|
|
102
|
+
|
|
103
|
+
const resolveApproval = useCallback(
|
|
104
|
+
(approvalId, decisionType) => {
|
|
105
|
+
// decisionType: 'approve_once' | 'approve_session' | 'approve_always' | 'reject'
|
|
106
|
+
const current = approvalRef.current
|
|
107
|
+
if (!current) return
|
|
108
|
+
if (approvalId && current.id !== approvalId) return
|
|
109
|
+
|
|
110
|
+
approvalRef.current = null
|
|
111
|
+
setApprovalData(null)
|
|
112
|
+
|
|
113
|
+
const sid = current.meta?.sessionId ? String(current.meta.sessionId) : '1'
|
|
114
|
+
|
|
115
|
+
if (decisionType === 'approve_always') {
|
|
116
|
+
handleApproveAlwaysInternal(current.tool, current.query)
|
|
117
|
+
} else if (decisionType === 'approve_session') {
|
|
118
|
+
if (!sessionAllowedMapRef.current.has(sid)) {
|
|
119
|
+
sessionAllowedMapRef.current.set(sid, new Set())
|
|
120
|
+
}
|
|
121
|
+
const target = extractToolTarget(current.tool, current.query)
|
|
122
|
+
const targetKey =
|
|
123
|
+
target.type === 'command'
|
|
124
|
+
? `cmd:${target.value.toLowerCase()}`
|
|
125
|
+
: target.value
|
|
126
|
+
? target.value.replace(/[\\/]+/g, '/').toLowerCase()
|
|
127
|
+
: current.tool
|
|
128
|
+
sessionAllowedMapRef.current.get(sid).add(targetKey)
|
|
129
|
+
if (target.type === 'path' && target.value) {
|
|
130
|
+
const folder = getFolderFromPath(target.value.replace(/[\\/]+/g, '/').toLowerCase())
|
|
131
|
+
if (folder) sessionAllowedMapRef.current.get(sid).add(folder)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const isApproved =
|
|
136
|
+
decisionType === 'approve_once' ||
|
|
137
|
+
decisionType === 'approve_session' ||
|
|
138
|
+
decisionType === 'approve_always'
|
|
139
|
+
|
|
140
|
+
// Update bubble state in chat feed if targetSetChatData exists
|
|
141
|
+
if (typeof current.meta?.targetSetChatData === 'function') {
|
|
142
|
+
current.meta.targetSetChatData((prev) =>
|
|
143
|
+
(prev || []).map((msg) =>
|
|
144
|
+
msg.id === current.id || msg.approvalId === current.id
|
|
145
|
+
? { ...msg, status: decisionType }
|
|
146
|
+
: msg
|
|
147
|
+
)
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (typeof current.resolve === 'function') {
|
|
152
|
+
current.resolve(isApproved)
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
[handleApproveAlwaysInternal]
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
const handleRemoteDecision = useCallback(
|
|
159
|
+
(decisionType, chatId) => {
|
|
160
|
+
const current = approvalRef.current
|
|
161
|
+
if (current) {
|
|
162
|
+
resolveApproval(current.id, decisionType)
|
|
163
|
+
|
|
164
|
+
if (chatId && window.api?.tgSendMessage) {
|
|
165
|
+
let msg = '[INFO]: Permintaan persetujuan telah ditolak.'
|
|
166
|
+
if (decisionType === 'approve_always') {
|
|
167
|
+
msg = '[INFO]: Permintaan persetujuan diizinkan SELAMANYA.'
|
|
168
|
+
} else if (decisionType === 'approve_session') {
|
|
169
|
+
msg = '[INFO]: Permintaan persetujuan telah diizinkan untuk sesi ini.'
|
|
170
|
+
} else if (decisionType === 'approve_once') {
|
|
171
|
+
msg = '[INFO]: Permintaan persetujuan telah diizinkan sekali.'
|
|
172
|
+
}
|
|
173
|
+
window.api.tgSendMessage(chatId, msg)
|
|
174
|
+
}
|
|
175
|
+
} else {
|
|
176
|
+
if (chatId && window.api?.tgSendMessage) {
|
|
177
|
+
window.api.tgSendMessage(
|
|
178
|
+
chatId,
|
|
179
|
+
'[INFO]: Tidak ada permintaan persetujuan yang sedang menunggu.'
|
|
180
|
+
)
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
[resolveApproval]
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
useEffect(() => {
|
|
188
|
+
const unsubAccept = window.api?.onTgCommandAccept
|
|
189
|
+
? window.api.onTgCommandAccept((data) => {
|
|
190
|
+
handleRemoteDecision('approve_once', data?.chatId)
|
|
191
|
+
})
|
|
192
|
+
: null
|
|
193
|
+
|
|
194
|
+
const unsubSession = window.api?.onTgCommandSession
|
|
195
|
+
? window.api.onTgCommandSession((data) => {
|
|
196
|
+
handleRemoteDecision('approve_session', data?.chatId)
|
|
197
|
+
})
|
|
198
|
+
: null
|
|
199
|
+
|
|
200
|
+
const unsubAlways = window.api?.onTgCommandAlways
|
|
201
|
+
? window.api.onTgCommandAlways((data) => {
|
|
202
|
+
handleRemoteDecision('approve_always', data?.chatId)
|
|
203
|
+
})
|
|
204
|
+
: null
|
|
205
|
+
|
|
206
|
+
const unsubReject = window.api?.onTgCommandReject
|
|
207
|
+
? window.api.onTgCommandReject((data) => {
|
|
208
|
+
handleRemoteDecision('reject', data?.chatId)
|
|
209
|
+
})
|
|
210
|
+
: null
|
|
211
|
+
|
|
212
|
+
return () => {
|
|
213
|
+
if (typeof unsubAccept === 'function') unsubAccept()
|
|
214
|
+
if (typeof unsubSession === 'function') unsubSession()
|
|
215
|
+
if (typeof unsubAlways === 'function') unsubAlways()
|
|
216
|
+
if (typeof unsubReject === 'function') unsubReject()
|
|
217
|
+
}
|
|
218
|
+
}, [handleRemoteDecision])
|
|
219
|
+
|
|
220
|
+
useEffect(() => {
|
|
221
|
+
const handleAutoModeUpdate = (e) => {
|
|
222
|
+
const sid = String(e.detail?.sessionId || '1')
|
|
223
|
+
if (e.detail?.isAutoMode) {
|
|
224
|
+
autoModeSessionsRef.current.add(sid)
|
|
225
|
+
const current = approvalRef.current
|
|
226
|
+
if (current && String(current.meta?.sessionId || '1') === sid) {
|
|
227
|
+
resolveApproval(current.id, 'approve_session')
|
|
228
|
+
}
|
|
229
|
+
} else {
|
|
230
|
+
autoModeSessionsRef.current.delete(sid)
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
window.addEventListener('session-auto-mode-updated', handleAutoModeUpdate)
|
|
234
|
+
return () => window.removeEventListener('session-auto-mode-updated', handleAutoModeUpdate)
|
|
235
|
+
}, [resolveApproval])
|
|
236
|
+
|
|
237
|
+
const checkIsAlwaysAllowed = useCallback((tool, query) => {
|
|
238
|
+
const target = extractToolTarget(tool, query)
|
|
239
|
+
const allowedList = alwaysAllowedPathsRef.current || []
|
|
240
|
+
|
|
241
|
+
if (target.type === 'command') {
|
|
242
|
+
const cmdKey = `cmd:${target.value.toLowerCase()}`
|
|
243
|
+
return allowedList.some((item) => (item || '').toLowerCase() === cmdKey)
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (target.type === 'path' && target.value) {
|
|
247
|
+
const normTarget = target.value.replace(/[\\/]+/g, '/').toLowerCase()
|
|
248
|
+
const targetFolder = getFolderFromPath(normTarget)
|
|
249
|
+
|
|
250
|
+
return allowedList.some((item) => {
|
|
251
|
+
const normAllowed = (item || '').toLowerCase().replace(/[\\/]+/g, '/')
|
|
252
|
+
if (!normAllowed || normAllowed.startsWith('cmd:')) return false
|
|
253
|
+
return (
|
|
254
|
+
normTarget === normAllowed ||
|
|
255
|
+
targetFolder === normAllowed ||
|
|
256
|
+
normTarget.startsWith(normAllowed.endsWith('/') ? normAllowed : normAllowed + '/')
|
|
257
|
+
)
|
|
258
|
+
})
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return false
|
|
262
|
+
}, [])
|
|
263
|
+
|
|
264
|
+
const checkIsSessionAllowed = useCallback((sessionId, tool, query) => {
|
|
265
|
+
const sid = sessionId ? String(sessionId) : '1'
|
|
266
|
+
const sessionSet = sessionAllowedMapRef.current.get(sid)
|
|
267
|
+
if (!sessionSet) return false
|
|
268
|
+
|
|
269
|
+
const target = extractToolTarget(tool, query)
|
|
270
|
+
const targetKey =
|
|
271
|
+
target.type === 'command'
|
|
272
|
+
? `cmd:${target.value.toLowerCase()}`
|
|
273
|
+
: target.value
|
|
274
|
+
? target.value.replace(/[\\/]+/g, '/').toLowerCase()
|
|
275
|
+
: tool
|
|
276
|
+
const folder = target.type === 'path' ? getFolderFromPath(targetKey) : ''
|
|
277
|
+
|
|
278
|
+
return sessionSet.has(targetKey) || (folder && sessionSet.has(folder))
|
|
279
|
+
}, [])
|
|
280
|
+
|
|
281
|
+
const requestApproval = useCallback(
|
|
282
|
+
async (message, tool, query, meta = {}) => {
|
|
283
|
+
const sid = meta.sessionId ? String(meta.sessionId) : '1'
|
|
284
|
+
|
|
285
|
+
// 1. Bypass otomatis jika is_auto_mode aktif pada sesi ini (in-memory ref ATAU database)
|
|
286
|
+
if (autoModeSessionsRef.current.has(sid)) {
|
|
287
|
+
return true
|
|
288
|
+
}
|
|
289
|
+
const isAuto = await getSessionAutoMode(sid)
|
|
290
|
+
if (isAuto) {
|
|
291
|
+
autoModeSessionsRef.current.add(sid)
|
|
292
|
+
return true
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// 2. Bypass jika sudah diizinkan selamanya (whitelist permanen)
|
|
296
|
+
if (checkIsAlwaysAllowed(tool, query)) {
|
|
297
|
+
return true
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// 3. Bypass jika sudah diizinkan untuk sesi ini (in-memory)
|
|
301
|
+
if (checkIsSessionAllowed(sid, tool, query)) {
|
|
302
|
+
return true
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (window.api?.tgBroadcastToAdmins) {
|
|
306
|
+
window.api.tgBroadcastToAdmins(
|
|
307
|
+
`[INFO]: Persetujuan Dibutuhkan\nTool: \`${tool}\`\n\n${message}\n\nKetik /accept untuk sekali, /session untuk sesi ini, /always untuk selamanya, atau /reject untuk menolak.`
|
|
308
|
+
)
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const approvalId = `approval_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`
|
|
312
|
+
|
|
313
|
+
return new Promise((resolve) => {
|
|
314
|
+
const dataObj = {
|
|
315
|
+
id: approvalId,
|
|
316
|
+
message,
|
|
317
|
+
tool,
|
|
318
|
+
query,
|
|
319
|
+
meta,
|
|
320
|
+
status: 'pending',
|
|
321
|
+
resolve
|
|
322
|
+
}
|
|
323
|
+
approvalRef.current = dataObj
|
|
324
|
+
setApprovalData(dataObj)
|
|
325
|
+
})
|
|
326
|
+
},
|
|
327
|
+
[checkIsAlwaysAllowed, checkIsSessionAllowed]
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
const activeTargetInfo = approvalData
|
|
331
|
+
? extractToolTarget(approvalData.tool, approvalData.query)
|
|
332
|
+
: null
|
|
333
|
+
|
|
334
|
+
return (
|
|
335
|
+
<ApprovalContext.Provider
|
|
336
|
+
value={{
|
|
337
|
+
requestApproval,
|
|
338
|
+
resolveApproval,
|
|
339
|
+
activeApproval: approvalData,
|
|
340
|
+
alwaysAllowedPaths,
|
|
341
|
+
setAlwaysAllowedPaths
|
|
342
|
+
}}
|
|
343
|
+
>
|
|
344
|
+
{children}
|
|
345
|
+
|
|
346
|
+
{/* Floating Dialog: Hanya ditampilkan di MarkHome atau halaman non-ChatStudio */}
|
|
347
|
+
{approvalData && !isChatStudio && (
|
|
348
|
+
<div className="fixed inset-0 z-[9999] flex items-center justify-center bg-black/60 backdrop-blur-sm animate-[response-fade-in_0.15s_ease-out_forwards]">
|
|
349
|
+
<div className="bg-base-200 border border-warning/30 p-6 rounded-2xl shadow-2xl max-w-lg w-full">
|
|
350
|
+
<div className="flex items-center justify-between gap-2 mb-2">
|
|
351
|
+
<h3 className="text-base font-bold text-warning flex items-center gap-2">
|
|
352
|
+
<ShieldAlert className="w-5 h-5 text-warning" /> Mark Meminta Izin
|
|
353
|
+
</h3>
|
|
354
|
+
{approvalData.tool && (
|
|
355
|
+
<span className="badge badge-sm font-mono bg-warning/15 text-warning border-warning/30 font-semibold">
|
|
356
|
+
{approvalData.tool}
|
|
357
|
+
</span>
|
|
358
|
+
)}
|
|
359
|
+
</div>
|
|
360
|
+
|
|
361
|
+
<p className="mb-3 text-xs text-base-content/70">
|
|
362
|
+
Mark membutuhkan persetujuan Anda untuk mengeksekusi aksi berikut.
|
|
363
|
+
</p>
|
|
364
|
+
|
|
365
|
+
<div className="bg-base-300 p-3.5 rounded-xl overflow-x-auto max-h-56 overflow-y-auto shadow-inner border border-white/5 mb-4 text-xs font-mono text-base-content/90 custom-scrollbar">
|
|
366
|
+
{activeTargetInfo?.value ? (
|
|
367
|
+
<div className="space-y-1.5">
|
|
368
|
+
<div className="text-[10px] uppercase font-bold text-warning/80 flex items-center gap-1.5 select-none">
|
|
369
|
+
{activeTargetInfo.type === 'command' ? (
|
|
370
|
+
<>
|
|
371
|
+
<Terminal className="w-3.5 h-3.5" />
|
|
372
|
+
<span>Perintah Shell:</span>
|
|
373
|
+
</>
|
|
374
|
+
) : (
|
|
375
|
+
<>
|
|
376
|
+
<FileCode className="w-3.5 h-3.5" />
|
|
377
|
+
<span>Target Berkas:</span>
|
|
378
|
+
</>
|
|
379
|
+
)}
|
|
380
|
+
</div>
|
|
381
|
+
<div className="text-white whitespace-pre-wrap break-all">
|
|
382
|
+
{activeTargetInfo.value}
|
|
383
|
+
</div>
|
|
384
|
+
</div>
|
|
385
|
+
) : (
|
|
386
|
+
<div className="whitespace-pre-wrap break-all">{approvalData.message}</div>
|
|
387
|
+
)}
|
|
388
|
+
</div>
|
|
389
|
+
|
|
390
|
+
<div className="flex flex-wrap items-center justify-between gap-2 mt-4">
|
|
391
|
+
<button
|
|
392
|
+
className="btn btn-ghost btn-sm text-error hover:bg-error/10 border border-error/20 cursor-pointer"
|
|
393
|
+
onClick={() => resolveApproval(approvalData.id, 'reject')}
|
|
394
|
+
>
|
|
395
|
+
Tolak
|
|
396
|
+
</button>
|
|
397
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
398
|
+
<button
|
|
399
|
+
className="btn btn-outline btn-sm cursor-pointer"
|
|
400
|
+
onClick={() => resolveApproval(approvalData.id, 'approve_once')}
|
|
401
|
+
>
|
|
402
|
+
Izinkan Sekali
|
|
403
|
+
</button>
|
|
404
|
+
<button
|
|
405
|
+
className="btn btn-outline btn-warning btn-sm cursor-pointer"
|
|
406
|
+
onClick={() => resolveApproval(approvalData.id, 'approve_session')}
|
|
407
|
+
>
|
|
408
|
+
Izinkan Sesi Ini
|
|
409
|
+
</button>
|
|
410
|
+
<button
|
|
411
|
+
className="btn btn-error btn-sm shadow-md cursor-pointer font-semibold"
|
|
412
|
+
onClick={() => resolveApproval(approvalData.id, 'approve_always')}
|
|
413
|
+
>
|
|
414
|
+
Izinkan Selamanya
|
|
415
|
+
</button>
|
|
416
|
+
</div>
|
|
417
|
+
</div>
|
|
418
|
+
</div>
|
|
419
|
+
</div>
|
|
420
|
+
)}
|
|
421
|
+
</ApprovalContext.Provider>
|
|
422
|
+
)
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
export const useApproval = () => useContext(ApprovalContext)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { useContext, createContext } from 'react'
|
|
2
|
+
import { useMarkAgent } from '../hooks/useMarkAgent'
|
|
3
|
+
|
|
4
|
+
const ChatContext = createContext()
|
|
5
|
+
|
|
6
|
+
export const ChatProvider = ({ children }) => {
|
|
7
|
+
const markAgent = useMarkAgent()
|
|
8
|
+
|
|
9
|
+
return <ChatContext.Provider value={markAgent}>{children}</ChatContext.Provider>
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const defaultChatContext = {
|
|
13
|
+
chatData: [],
|
|
14
|
+
setChatData: () => {},
|
|
15
|
+
clearChat: () => {},
|
|
16
|
+
message: '',
|
|
17
|
+
setMessage: () => {},
|
|
18
|
+
isLoading: false,
|
|
19
|
+
setIsLoading: () => {},
|
|
20
|
+
isAgentBusy: false,
|
|
21
|
+
setIsAgentBusy: () => {},
|
|
22
|
+
runningSessionId: null,
|
|
23
|
+
setRunningSessionId: () => {},
|
|
24
|
+
runningSessionIds: [],
|
|
25
|
+
setRunningSessionIds: () => {},
|
|
26
|
+
addRunningSessionId: () => {},
|
|
27
|
+
removeRunningSessionId: () => {},
|
|
28
|
+
isSpeak: false,
|
|
29
|
+
setIsSpeak: () => {},
|
|
30
|
+
orbStatus: 'idle',
|
|
31
|
+
setOrbStatus: () => {},
|
|
32
|
+
currentResponse: null,
|
|
33
|
+
setCurrentResponse: () => {},
|
|
34
|
+
notifications: [],
|
|
35
|
+
pushNotification: () => {},
|
|
36
|
+
activeProcesses: [],
|
|
37
|
+
setActiveProcesses: () => {},
|
|
38
|
+
pushProcess: () => {},
|
|
39
|
+
dismissProcess: () => {},
|
|
40
|
+
inputSource: 'pc',
|
|
41
|
+
setInputSource: () => {},
|
|
42
|
+
handlePlanningCommand: () => {},
|
|
43
|
+
handleIntervention: () => {},
|
|
44
|
+
handleStop: () => {},
|
|
45
|
+
handleSubmit: () => {},
|
|
46
|
+
isBooting: false,
|
|
47
|
+
isRecording: false,
|
|
48
|
+
isProcessing: false,
|
|
49
|
+
audioIntensity: 0,
|
|
50
|
+
startRecording: () => {},
|
|
51
|
+
stopRecording: () => {},
|
|
52
|
+
toggleRecording: () => {},
|
|
53
|
+
toastMessage: null,
|
|
54
|
+
currentActiveSessionId: '1',
|
|
55
|
+
setCurrentActiveSessionId: () => {}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const useChat = () => {
|
|
59
|
+
const context = useContext(ChatContext)
|
|
60
|
+
return context || defaultChatContext
|
|
61
|
+
}
|