@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,1013 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client DB Layer (MARK SQLite Proxy Adapter)
|
|
3
|
+
* Menyediakan antarmuka async yang kompatibel dengan seluruh komponen UI/hooks,
|
|
4
|
+
* namun menyimpan data secara persisten dan terpusat di server SQLite backend.
|
|
5
|
+
*/
|
|
6
|
+
import { generateVector } from './vectorMemory'
|
|
7
|
+
import { insertMemoryToOrama, updateMemoryInOrama, deleteMemoryFromOrama } from './oramaStore'
|
|
8
|
+
import { API_BASE } from './web-bridge'
|
|
9
|
+
export { SERVER_CONFIG, SERVER_HOST, SERVER_PORT, API_BASE } from './web-bridge'
|
|
10
|
+
|
|
11
|
+
// Helper fetch JSON
|
|
12
|
+
async function apiGet(path) {
|
|
13
|
+
try {
|
|
14
|
+
const res = await fetch(`${API_BASE}${path}`)
|
|
15
|
+
if (!res.ok) {
|
|
16
|
+
if (res.status === 404) return null
|
|
17
|
+
throw new Error(`HTTP ${res.status}: ${res.statusText}`)
|
|
18
|
+
}
|
|
19
|
+
const contentType = res.headers.get('content-type') || ''
|
|
20
|
+
if (!contentType.includes('application/json')) {
|
|
21
|
+
return null
|
|
22
|
+
}
|
|
23
|
+
const json = await res.json()
|
|
24
|
+
return json.data ?? json
|
|
25
|
+
} catch (err) {
|
|
26
|
+
console.error(`[DB Proxy] GET ${path} error:`, err)
|
|
27
|
+
return null
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function apiPost(path, body) {
|
|
32
|
+
try {
|
|
33
|
+
const res = await fetch(`${API_BASE}${path}`, {
|
|
34
|
+
method: 'POST',
|
|
35
|
+
headers: { 'Content-Type': 'application/json' },
|
|
36
|
+
body: JSON.stringify(body)
|
|
37
|
+
})
|
|
38
|
+
if (!res.ok) {
|
|
39
|
+
if (res.status === 404) return null
|
|
40
|
+
throw new Error(`HTTP ${res.status}: ${res.statusText}`)
|
|
41
|
+
}
|
|
42
|
+
const contentType = res.headers.get('content-type') || ''
|
|
43
|
+
if (!contentType.includes('application/json')) {
|
|
44
|
+
return null
|
|
45
|
+
}
|
|
46
|
+
const json = await res.json()
|
|
47
|
+
return json.data ?? json
|
|
48
|
+
} catch (err) {
|
|
49
|
+
console.error(`[DB Proxy] POST ${path} error:`, err)
|
|
50
|
+
return null
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function apiDelete(path) {
|
|
55
|
+
try {
|
|
56
|
+
const res = await fetch(`${API_BASE}${path}`, { method: 'DELETE' })
|
|
57
|
+
if (!res.ok) {
|
|
58
|
+
return false
|
|
59
|
+
}
|
|
60
|
+
const contentType = res.headers.get('content-type') || ''
|
|
61
|
+
if (!contentType.includes('application/json')) {
|
|
62
|
+
return false
|
|
63
|
+
}
|
|
64
|
+
const json = await res.json()
|
|
65
|
+
return json.success ?? false
|
|
66
|
+
} catch (err) {
|
|
67
|
+
console.error(`[DB Proxy] DELETE ${path} error:`, err)
|
|
68
|
+
return false
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Normalisasi ID SQLite / DB: Memastikan seluruh ID bertipe string bersih tanpa pecahan desimal (.0).
|
|
74
|
+
* Menstandarkan format ID di seluruh lapisan frontend dan komunikasi REST API.
|
|
75
|
+
*/
|
|
76
|
+
export function normalizeDbId(id) {
|
|
77
|
+
if (id === null || id === undefined) return ''
|
|
78
|
+
let clean = String(id).trim()
|
|
79
|
+
if (clean.endsWith('.0')) {
|
|
80
|
+
clean = clean.slice(0, -2)
|
|
81
|
+
}
|
|
82
|
+
clean = clean.replace(/^(\d+)\.0+$/, '$1')
|
|
83
|
+
if (clean === '1' || clean === '1.0' || clean === '1.00') {
|
|
84
|
+
clean = '1'
|
|
85
|
+
}
|
|
86
|
+
return clean
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function normalizeItemIds(item, idField = 'id') {
|
|
90
|
+
if (!item || typeof item !== 'object') return item
|
|
91
|
+
const res = { ...item }
|
|
92
|
+
if (res[idField] !== undefined && res[idField] !== null) {
|
|
93
|
+
res[idField] = normalizeDbId(res[idField])
|
|
94
|
+
}
|
|
95
|
+
if (res.id !== undefined && res.id !== null) {
|
|
96
|
+
res.id = normalizeDbId(res.id)
|
|
97
|
+
}
|
|
98
|
+
if (res.sessionId !== undefined && res.sessionId !== null) {
|
|
99
|
+
res.sessionId = normalizeDbId(res.sessionId)
|
|
100
|
+
}
|
|
101
|
+
if (res.session_id !== undefined && res.session_id !== null) {
|
|
102
|
+
res.session_id = normalizeDbId(res.session_id)
|
|
103
|
+
}
|
|
104
|
+
return res
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Collection Proxy untuk chaining Dexie-like queries:
|
|
109
|
+
* where().equals(), anyOf(), filter(), sortBy(), limit(), reverse(), toArray(), delete(), first()
|
|
110
|
+
*/
|
|
111
|
+
class CollectionProxy {
|
|
112
|
+
constructor(tableProxy, filterFn = null, sortField = null, isReverse = false, limitCount = null) {
|
|
113
|
+
this.tableProxy = tableProxy
|
|
114
|
+
this.filterFn = filterFn
|
|
115
|
+
this.sortField = sortField
|
|
116
|
+
this.isReverse = isReverse
|
|
117
|
+
this.limitCount = limitCount
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
filter(fn) {
|
|
121
|
+
const prev = this.filterFn
|
|
122
|
+
const combined = prev ? (item) => prev(item) && fn(item) : fn
|
|
123
|
+
return new CollectionProxy(
|
|
124
|
+
this.tableProxy,
|
|
125
|
+
combined,
|
|
126
|
+
this.sortField,
|
|
127
|
+
this.isReverse,
|
|
128
|
+
this.limitCount
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
where(field) {
|
|
133
|
+
const snake = field.replace(/[A-Z]/g, (l) => '_' + l.toLowerCase())
|
|
134
|
+
return {
|
|
135
|
+
equals: (val) => {
|
|
136
|
+
return new CollectionProxy(
|
|
137
|
+
this.tableProxy,
|
|
138
|
+
(item) => {
|
|
139
|
+
const v = item[field] !== undefined ? item[field] : item[snake]
|
|
140
|
+
return String(v) === String(val)
|
|
141
|
+
},
|
|
142
|
+
this.sortField,
|
|
143
|
+
this.isReverse,
|
|
144
|
+
this.limitCount
|
|
145
|
+
)
|
|
146
|
+
},
|
|
147
|
+
anyOf: (arr) => {
|
|
148
|
+
const set = new Set((arr || []).map((v) => String(v)))
|
|
149
|
+
return new CollectionProxy(
|
|
150
|
+
this.tableProxy,
|
|
151
|
+
(item) => {
|
|
152
|
+
const v = item[field] !== undefined ? item[field] : item[snake]
|
|
153
|
+
return set.has(String(v))
|
|
154
|
+
},
|
|
155
|
+
this.sortField,
|
|
156
|
+
this.isReverse,
|
|
157
|
+
this.limitCount
|
|
158
|
+
)
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
sortBy(field) {
|
|
164
|
+
return new CollectionProxy(
|
|
165
|
+
this.tableProxy,
|
|
166
|
+
this.filterFn,
|
|
167
|
+
field,
|
|
168
|
+
this.isReverse,
|
|
169
|
+
this.limitCount
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
reverse() {
|
|
174
|
+
return new CollectionProxy(
|
|
175
|
+
this.tableProxy,
|
|
176
|
+
this.filterFn,
|
|
177
|
+
this.sortField,
|
|
178
|
+
!this.isReverse,
|
|
179
|
+
this.limitCount
|
|
180
|
+
)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
limit(count) {
|
|
184
|
+
return new CollectionProxy(
|
|
185
|
+
this.tableProxy,
|
|
186
|
+
this.filterFn,
|
|
187
|
+
this.sortField,
|
|
188
|
+
this.isReverse,
|
|
189
|
+
count
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async toArray() {
|
|
194
|
+
let all = await this.tableProxy.toArray()
|
|
195
|
+
if (this.filterFn) {
|
|
196
|
+
all = all.filter(this.filterFn)
|
|
197
|
+
}
|
|
198
|
+
if (this.sortField) {
|
|
199
|
+
const sf = this.sortField
|
|
200
|
+
all = all.slice().sort((a, b) => {
|
|
201
|
+
const valA = a[sf] ?? ''
|
|
202
|
+
const valB = b[sf] ?? ''
|
|
203
|
+
if (typeof valA === 'number' && typeof valB === 'number') {
|
|
204
|
+
return valA - valB
|
|
205
|
+
}
|
|
206
|
+
return String(valA).localeCompare(String(valB))
|
|
207
|
+
})
|
|
208
|
+
}
|
|
209
|
+
if (this.isReverse) {
|
|
210
|
+
all.reverse()
|
|
211
|
+
}
|
|
212
|
+
if (typeof this.limitCount === 'number' && this.limitCount > 0) {
|
|
213
|
+
all = all.slice(0, this.limitCount)
|
|
214
|
+
}
|
|
215
|
+
return all
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async first() {
|
|
219
|
+
const list = await this.limit(1).toArray()
|
|
220
|
+
return list[0] || null
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async count() {
|
|
224
|
+
const list = await this.toArray()
|
|
225
|
+
return list.length
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
async delete() {
|
|
229
|
+
const list = await this.toArray()
|
|
230
|
+
for (const item of list) {
|
|
231
|
+
const id = item[this.tableProxy.idField] || item.id || item.pairId
|
|
232
|
+
if (id) await this.tableProxy.delete(id)
|
|
233
|
+
}
|
|
234
|
+
return list.length
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Tabel Proxy untuk kompatibilitas kode lama yang memanggil `db.namaTabel`
|
|
240
|
+
*/
|
|
241
|
+
class TableProxy {
|
|
242
|
+
constructor(endpoint, idField = 'id') {
|
|
243
|
+
this.endpoint = endpoint
|
|
244
|
+
this.idField = idField
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async toArray() {
|
|
248
|
+
const data = await apiGet(this.endpoint)
|
|
249
|
+
return Array.isArray(data) ? data : []
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
async get(id) {
|
|
253
|
+
const cleanId = normalizeDbId(id)
|
|
254
|
+
const item = await apiGet(`${this.endpoint}/${cleanId}`)
|
|
255
|
+
return item || null
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
async add(item) {
|
|
259
|
+
const normalized = normalizeItemIds(item, this.idField)
|
|
260
|
+
const res = await apiPost(this.endpoint, normalized)
|
|
261
|
+
return res?.[this.idField] || res?.id
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
async put(item) {
|
|
265
|
+
const normalized = normalizeItemIds(item, this.idField)
|
|
266
|
+
return await apiPost(this.endpoint, normalized)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
async bulkAdd(items) {
|
|
270
|
+
const normalized = (items || []).map((it) => normalizeItemIds(it, this.idField))
|
|
271
|
+
return await apiPost(`${this.endpoint}/batch`, normalized)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
async bulkPut(items) {
|
|
275
|
+
const normalized = (items || []).map((it) => normalizeItemIds(it, this.idField))
|
|
276
|
+
return await apiPost(`${this.endpoint}/batch`, normalized)
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
async delete(id) {
|
|
280
|
+
const cleanId = normalizeDbId(id)
|
|
281
|
+
return await apiDelete(`${this.endpoint}/${cleanId}`)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
async update(id, updates) {
|
|
285
|
+
const cleanId = normalizeDbId(id)
|
|
286
|
+
const existing = await this.get(cleanId)
|
|
287
|
+
if (!existing) return null
|
|
288
|
+
return await this.put({ ...existing, ...updates })
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async count() {
|
|
292
|
+
const arr = await this.toArray()
|
|
293
|
+
return arr.length
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
async clear() {
|
|
297
|
+
const arr = await this.toArray()
|
|
298
|
+
for (const item of arr) {
|
|
299
|
+
const id = item[this.idField] || item.id || item.pairId
|
|
300
|
+
if (id) await this.delete(id)
|
|
301
|
+
}
|
|
302
|
+
return true
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
where(field) {
|
|
306
|
+
const snake = field.replace(/[A-Z]/g, (l) => '_' + l.toLowerCase())
|
|
307
|
+
return {
|
|
308
|
+
equals: (val) => {
|
|
309
|
+
return new CollectionProxy(this, (item) => {
|
|
310
|
+
const v =
|
|
311
|
+
item[field] !== undefined
|
|
312
|
+
? item[field]
|
|
313
|
+
: item[snake] !== undefined
|
|
314
|
+
? item[snake]
|
|
315
|
+
: field === 'subagentId'
|
|
316
|
+
? item.subagent_id
|
|
317
|
+
: field === 'subagent_id'
|
|
318
|
+
? item.subagentId
|
|
319
|
+
: undefined
|
|
320
|
+
return String(v) === String(val)
|
|
321
|
+
})
|
|
322
|
+
},
|
|
323
|
+
equalsIgnoreCase: (val) => {
|
|
324
|
+
const lowerVal = String(val || '').toLowerCase()
|
|
325
|
+
return new CollectionProxy(this, (item) => {
|
|
326
|
+
const v =
|
|
327
|
+
item[field] !== undefined
|
|
328
|
+
? item[field]
|
|
329
|
+
: item[snake] !== undefined
|
|
330
|
+
? item[snake]
|
|
331
|
+
: field === 'subagentId'
|
|
332
|
+
? item.subagent_id
|
|
333
|
+
: field === 'subagent_id'
|
|
334
|
+
? item.subagentId
|
|
335
|
+
: undefined
|
|
336
|
+
return String(v || '').toLowerCase() === lowerVal
|
|
337
|
+
})
|
|
338
|
+
},
|
|
339
|
+
anyOf: (values) => {
|
|
340
|
+
const set = new Set((Array.isArray(values) ? values : [values]).map((v) => String(v)))
|
|
341
|
+
return new CollectionProxy(this, (item) => {
|
|
342
|
+
const v =
|
|
343
|
+
item[field] !== undefined
|
|
344
|
+
? item[field]
|
|
345
|
+
: item[snake] !== undefined
|
|
346
|
+
? item[snake]
|
|
347
|
+
: field === 'subagentId'
|
|
348
|
+
? item.subagent_id
|
|
349
|
+
: field === 'subagent_id'
|
|
350
|
+
? item.subagentId
|
|
351
|
+
: undefined
|
|
352
|
+
return set.has(String(v))
|
|
353
|
+
})
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
orderBy(field) {
|
|
359
|
+
return new CollectionProxy(this, null, field, false, null)
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Objek db proxy menggantikan Dexie instances
|
|
364
|
+
export const db = {
|
|
365
|
+
config: new TableProxy('/api/config'),
|
|
366
|
+
memory: new TableProxy('/api/memories'),
|
|
367
|
+
memories: new TableProxy('/api/memories'),
|
|
368
|
+
sessions: new TableProxy('/api/sessions'),
|
|
369
|
+
sessionCompact: new TableProxy('/api/session-compact', 'sessionId'),
|
|
370
|
+
session_compact: new TableProxy('/api/session-compact', 'sessionId'),
|
|
371
|
+
chatTurns: new TableProxy('/api/turns', 'pairId'),
|
|
372
|
+
chatArchive: new TableProxy('/api/archives'),
|
|
373
|
+
documents: new TableProxy('/api/documents'),
|
|
374
|
+
relationships: new TableProxy('/api/relationships', 'userId'),
|
|
375
|
+
subagents: new TableProxy('/api/subagents'),
|
|
376
|
+
subagent_messages: new TableProxy('/api/subagents/messages'),
|
|
377
|
+
subagentMessages: new TableProxy('/api/subagents/messages'),
|
|
378
|
+
learnedSkills: new TableProxy('/api/learned-skills'),
|
|
379
|
+
agentTasks: new TableProxy('/api/tasks'),
|
|
380
|
+
agentTaskSteps: new TableProxy('/api/tasks/steps'),
|
|
381
|
+
transaction: async (...args) => {
|
|
382
|
+
// Penanganan db.transaction(mode, ...tables, callback)
|
|
383
|
+
const callback = args[args.length - 1]
|
|
384
|
+
if (typeof callback === 'function') {
|
|
385
|
+
return await callback()
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// --- VALIDATION ---
|
|
391
|
+
const VALID_TYPES = ['profile', 'preference', 'notes', 'learn']
|
|
392
|
+
|
|
393
|
+
function getValidType(type) {
|
|
394
|
+
const t = (type || '').toLowerCase().trim()
|
|
395
|
+
return VALID_TYPES.includes(t) ? t : 'notes'
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// --- CREATE ---
|
|
399
|
+
export async function insertMemory(data) {
|
|
400
|
+
const memoryText = data.memory.trim()
|
|
401
|
+
const type = getValidType(data.type)
|
|
402
|
+
const vector = (await generateVector(memoryText)) || []
|
|
403
|
+
|
|
404
|
+
try {
|
|
405
|
+
const record = await db.memory.put({
|
|
406
|
+
type: type,
|
|
407
|
+
summary: data.summary || '',
|
|
408
|
+
memory: memoryText,
|
|
409
|
+
vector: vector
|
|
410
|
+
})
|
|
411
|
+
const id = record?.id || record
|
|
412
|
+
insertMemoryToOrama({
|
|
413
|
+
id,
|
|
414
|
+
type,
|
|
415
|
+
summary: data.summary || '',
|
|
416
|
+
memory: memoryText,
|
|
417
|
+
vector
|
|
418
|
+
}).catch(console.error)
|
|
419
|
+
return id
|
|
420
|
+
} catch (error) {
|
|
421
|
+
console.error('Error Save Memory:', error)
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
export async function saveMainThread(data) {
|
|
426
|
+
return await saveSession('1', data, 'Main Thread')
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
export async function getMainThread() {
|
|
430
|
+
return await getChatData('1')
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// --- SESSIONS & CHAT DATA HELPERS ---
|
|
434
|
+
export async function getAllSessions() {
|
|
435
|
+
try {
|
|
436
|
+
return await db.sessions.toArray()
|
|
437
|
+
} catch (error) {
|
|
438
|
+
console.error('Error getAllSessions:', error)
|
|
439
|
+
return []
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
export async function getSession(id) {
|
|
444
|
+
try {
|
|
445
|
+
const cleanId = normalizeDbId(id)
|
|
446
|
+
return await db.sessions.get(cleanId)
|
|
447
|
+
} catch (error) {
|
|
448
|
+
console.error(`Error getSession ${id}:`, error)
|
|
449
|
+
return null
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
export async function createSession(title = 'Percakapan Baru') {
|
|
454
|
+
try {
|
|
455
|
+
const id = String(Date.now())
|
|
456
|
+
const session = { id, title, data: [], timestamp: Date.now() }
|
|
457
|
+
await db.sessions.put(session)
|
|
458
|
+
return session
|
|
459
|
+
} catch (error) {
|
|
460
|
+
console.error('Error createSession:', error)
|
|
461
|
+
return null
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
export async function saveSession(id, data, title = null) {
|
|
466
|
+
try {
|
|
467
|
+
const cleanId = normalizeDbId(id)
|
|
468
|
+
const existing = await db.sessions.get(cleanId)
|
|
469
|
+
const session = {
|
|
470
|
+
...(existing || { id: cleanId, title: cleanId === '1' ? 'Main Thread' : 'Percakapan Baru' }),
|
|
471
|
+
id: cleanId,
|
|
472
|
+
data: Array.isArray(data) ? data : [],
|
|
473
|
+
...(title ? { title } : {}),
|
|
474
|
+
timestamp: Date.now()
|
|
475
|
+
}
|
|
476
|
+
await db.sessions.put(session)
|
|
477
|
+
return session
|
|
478
|
+
} catch (error) {
|
|
479
|
+
console.error(`Error saveSession ${id}:`, error)
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
export async function deleteSession(id) {
|
|
484
|
+
try {
|
|
485
|
+
const cleanId = normalizeDbId(id)
|
|
486
|
+
await db.sessions.delete(cleanId)
|
|
487
|
+
return { success: true }
|
|
488
|
+
} catch (error) {
|
|
489
|
+
console.error(`Error deleteSession ${id}:`, error)
|
|
490
|
+
return { success: false, error: error.message }
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
export async function renameSession(id, title) {
|
|
495
|
+
try {
|
|
496
|
+
const cleanId = normalizeDbId(id)
|
|
497
|
+
const existing = await db.sessions.get(cleanId)
|
|
498
|
+
if (existing) {
|
|
499
|
+
await db.sessions.put({ ...existing, id: cleanId, title, timestamp: Date.now() })
|
|
500
|
+
}
|
|
501
|
+
} catch (error) {
|
|
502
|
+
console.error(`Error renameSession ${id}:`, error)
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
export async function setSessionAutoMode(id, isAutoMode) {
|
|
507
|
+
try {
|
|
508
|
+
const cleanId = normalizeDbId(id)
|
|
509
|
+
const existing = await db.sessions.get(cleanId)
|
|
510
|
+
const val = isAutoMode ? 1 : 0
|
|
511
|
+
const updated = {
|
|
512
|
+
...(existing || { id: cleanId, title: cleanId === '1' ? 'Main Thread' : 'Percakapan Baru' }),
|
|
513
|
+
id: cleanId,
|
|
514
|
+
is_auto_mode: val,
|
|
515
|
+
isAutoMode: val,
|
|
516
|
+
timestamp: Date.now()
|
|
517
|
+
}
|
|
518
|
+
await db.sessions.put(updated)
|
|
519
|
+
window.dispatchEvent(
|
|
520
|
+
new CustomEvent('session-auto-mode-updated', {
|
|
521
|
+
detail: { sessionId: cleanId, isAutoMode: Boolean(isAutoMode) }
|
|
522
|
+
})
|
|
523
|
+
)
|
|
524
|
+
return updated
|
|
525
|
+
} catch (error) {
|
|
526
|
+
console.error(`Error setSessionAutoMode ${id}:`, error)
|
|
527
|
+
return null
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
export async function getSessionAutoMode(id) {
|
|
532
|
+
try {
|
|
533
|
+
const cleanId = normalizeDbId(id)
|
|
534
|
+
const session = await db.sessions.get(cleanId)
|
|
535
|
+
return Boolean(session?.is_auto_mode || session?.isAutoMode)
|
|
536
|
+
} catch {
|
|
537
|
+
return false
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
export async function getChatData(sessionId = '1') {
|
|
542
|
+
try {
|
|
543
|
+
const cleanId = normalizeDbId(sessionId)
|
|
544
|
+
const session = await db.sessions.get(cleanId)
|
|
545
|
+
return session && Array.isArray(session.data) ? session.data : []
|
|
546
|
+
} catch (error) {
|
|
547
|
+
console.error(`Error getChatData ${sessionId}:`, error)
|
|
548
|
+
return []
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
export async function setSessionWorkspace(sessionId, workspaceRoot) {
|
|
553
|
+
try {
|
|
554
|
+
const cleanId = normalizeDbId(sessionId)
|
|
555
|
+
const existing = await db.sessions.get(cleanId)
|
|
556
|
+
await db.sessions.put({
|
|
557
|
+
...(existing || {
|
|
558
|
+
id: cleanId,
|
|
559
|
+
title: cleanId === '1' ? 'Main Thread' : 'Percakapan Baru',
|
|
560
|
+
data: []
|
|
561
|
+
}),
|
|
562
|
+
id: cleanId,
|
|
563
|
+
workspaceRoot: workspaceRoot || null,
|
|
564
|
+
timestamp: Date.now()
|
|
565
|
+
})
|
|
566
|
+
if (typeof window !== 'undefined') {
|
|
567
|
+
window.dispatchEvent(
|
|
568
|
+
new CustomEvent('session-workspace-updated', {
|
|
569
|
+
detail: { sessionId: cleanId, workspaceRoot }
|
|
570
|
+
})
|
|
571
|
+
)
|
|
572
|
+
}
|
|
573
|
+
} catch (error) {
|
|
574
|
+
console.error(`Error setSessionWorkspace ${sessionId}:`, error)
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
export async function getSessionWorkspace(sessionId) {
|
|
579
|
+
try {
|
|
580
|
+
const cleanId = normalizeDbId(sessionId)
|
|
581
|
+
const session = await db.sessions.get(cleanId)
|
|
582
|
+
return session?.workspaceRoot || null
|
|
583
|
+
} catch (error) {
|
|
584
|
+
console.error(`Error getSessionWorkspace ${sessionId}:`, error)
|
|
585
|
+
return null
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// Bulk insert chatTurns
|
|
590
|
+
export async function bulkInsertTurns(turns) {
|
|
591
|
+
try {
|
|
592
|
+
if (!Array.isArray(turns) || turns.length === 0) return []
|
|
593
|
+
const res = await db.chatTurns.bulkPut(turns)
|
|
594
|
+
return res
|
|
595
|
+
} catch (error) {
|
|
596
|
+
console.error('Error bulkInsertTurns:', error)
|
|
597
|
+
return []
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
export async function saveBatchChatTurns(turns) {
|
|
602
|
+
return await bulkInsertTurns(turns)
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
export async function saveChatTurn(turn) {
|
|
606
|
+
try {
|
|
607
|
+
const res = await db.chatTurns.put(turn)
|
|
608
|
+
return res
|
|
609
|
+
} catch (error) {
|
|
610
|
+
console.error('Error saveChatTurn:', error)
|
|
611
|
+
return null
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// Bulk insert documents
|
|
616
|
+
export async function bulkInsertDocuments(documents) {
|
|
617
|
+
try {
|
|
618
|
+
if (!Array.isArray(documents) || documents.length === 0) return []
|
|
619
|
+
const res = await db.documents.bulkPut(documents)
|
|
620
|
+
return res?.data?.map((d) => d.id) || documents.map((_, i) => i + 1)
|
|
621
|
+
} catch (error) {
|
|
622
|
+
console.error('Error bulkInsertDocuments:', error)
|
|
623
|
+
return []
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
export async function deleteDocumentByName(docName) {
|
|
628
|
+
try {
|
|
629
|
+
const docs = await db.documents.where('docName').equals(docName).toArray()
|
|
630
|
+
for (let d of docs) {
|
|
631
|
+
await db.documents.delete(d.id)
|
|
632
|
+
}
|
|
633
|
+
} catch (error) {
|
|
634
|
+
console.error('Error deleteDocumentByName:', error)
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
export async function getAllDocuments() {
|
|
639
|
+
try {
|
|
640
|
+
return await db.documents.toArray()
|
|
641
|
+
} catch (error) {
|
|
642
|
+
console.error('Error getAllDocuments:', error)
|
|
643
|
+
return []
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
export async function getAllChatArchives() {
|
|
648
|
+
try {
|
|
649
|
+
return await db.chatArchive.toArray()
|
|
650
|
+
} catch (error) {
|
|
651
|
+
console.error('Error getAllChatArchives:', error)
|
|
652
|
+
return []
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
export async function insertChatArchive(data) {
|
|
657
|
+
try {
|
|
658
|
+
const record = await db.chatArchive.put(data)
|
|
659
|
+
return record?.id || record
|
|
660
|
+
} catch (error) {
|
|
661
|
+
console.error('Error insertChatArchive:', error)
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
export async function deleteChatArchive(id) {
|
|
666
|
+
try {
|
|
667
|
+
await db.chatArchive.delete(id)
|
|
668
|
+
} catch (error) {
|
|
669
|
+
console.error('Error deleteChatArchive:', error)
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
export async function updateMemory(id, data) {
|
|
674
|
+
try {
|
|
675
|
+
const existing = await db.memory.get(id)
|
|
676
|
+
if (!existing) {
|
|
677
|
+
console.warn(`[DB Proxy] Memory dengan ID ${id} tidak ditemukan untuk diupdate.`)
|
|
678
|
+
return
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
const memoryText = (data.memory || existing.memory).trim()
|
|
682
|
+
const type = getValidType(data.type || existing.type)
|
|
683
|
+
const summary = data.summary || existing.summary || ''
|
|
684
|
+
|
|
685
|
+
let vector = existing.vector
|
|
686
|
+
if (data.memory && data.memory !== existing.memory) {
|
|
687
|
+
vector = (await generateVector(memoryText)) || existing.vector
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
const updatedRecord = {
|
|
691
|
+
...existing,
|
|
692
|
+
type,
|
|
693
|
+
summary,
|
|
694
|
+
memory: memoryText,
|
|
695
|
+
vector,
|
|
696
|
+
updated_at: Date.now()
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
await db.memory.put(updatedRecord)
|
|
700
|
+
if (vector && vector.length === 384) {
|
|
701
|
+
updateMemoryInOrama(id, updatedRecord).catch(console.error)
|
|
702
|
+
console.log(`[DB Proxy] Memory ID ${id} berhasil di-update.`)
|
|
703
|
+
}
|
|
704
|
+
} catch (error) {
|
|
705
|
+
console.error('Error in updateMemory logic:', error)
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// --- DELETE ---
|
|
710
|
+
export async function deleteMemory(data) {
|
|
711
|
+
try {
|
|
712
|
+
const id = typeof data === 'object' && data !== null ? data.id : String(data)
|
|
713
|
+
if (id) {
|
|
714
|
+
await db.memory.delete(id)
|
|
715
|
+
deleteMemoryFromOrama(id).catch(console.error)
|
|
716
|
+
return { success: true }
|
|
717
|
+
}
|
|
718
|
+
return { success: false, error: 'ID is required for deletion' }
|
|
719
|
+
} catch (error) {
|
|
720
|
+
console.error('Error in deleteMemory logic:', error)
|
|
721
|
+
return { success: false, error: error.message }
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
export async function getAllMemory() {
|
|
726
|
+
try {
|
|
727
|
+
const data = await db.memory.toArray()
|
|
728
|
+
return data || []
|
|
729
|
+
} catch (error) {
|
|
730
|
+
console.error('Error in getAllMemory logic:', error)
|
|
731
|
+
return []
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
export async function getAllConfig() {
|
|
736
|
+
try {
|
|
737
|
+
const configData = await apiGet('/api/config')
|
|
738
|
+
if (configData) {
|
|
739
|
+
const conf = { ...configData, id: 1 }
|
|
740
|
+
if (!conf.geminiWebModel) conf.geminiWebModel = 'gemini-3.6-flash'
|
|
741
|
+
if (!conf.aiProvider) conf.aiProvider = 'gemini-web'
|
|
742
|
+
if (conf.windowOpacity === undefined) conf.windowOpacity = 0.85
|
|
743
|
+
if (!conf.speechLanguage) conf.speechLanguage = 'id-ID'
|
|
744
|
+
return [conf]
|
|
745
|
+
}
|
|
746
|
+
return []
|
|
747
|
+
} catch (error) {
|
|
748
|
+
console.error('Error in getAllConfig logic:', error)
|
|
749
|
+
return []
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
export async function saveConfiguration(data) {
|
|
754
|
+
try {
|
|
755
|
+
await apiPost('/api/config', data)
|
|
756
|
+
window.dispatchEvent(new CustomEvent('config-updated', { detail: data }))
|
|
757
|
+
console.log('[DB Proxy] Configuration saved:', data)
|
|
758
|
+
} catch (error) {
|
|
759
|
+
console.error('Error in saveConfiguration logic:', error)
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
export async function getAlwaysAllowedPaths() {
|
|
764
|
+
try {
|
|
765
|
+
const configs = await getAllConfig()
|
|
766
|
+
if (configs && configs.length > 0 && Array.isArray(configs[0].alwaysAllowedPaths)) {
|
|
767
|
+
return configs[0].alwaysAllowedPaths
|
|
768
|
+
}
|
|
769
|
+
return []
|
|
770
|
+
} catch (error) {
|
|
771
|
+
console.error('Error in getAlwaysAllowedPaths logic:', error)
|
|
772
|
+
return []
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
export async function addAlwaysAllowedPath(pathToAdd) {
|
|
777
|
+
try {
|
|
778
|
+
if (!pathToAdd) return []
|
|
779
|
+
const configs = await getAllConfig()
|
|
780
|
+
const currentConfig = (configs && configs[0]) || { id: 1 }
|
|
781
|
+
const currentList = Array.isArray(currentConfig.alwaysAllowedPaths)
|
|
782
|
+
? currentConfig.alwaysAllowedPaths
|
|
783
|
+
: []
|
|
784
|
+
if (!currentList.includes(pathToAdd)) {
|
|
785
|
+
const updatedList = [...currentList, pathToAdd]
|
|
786
|
+
await saveConfiguration({ ...currentConfig, alwaysAllowedPaths: updatedList })
|
|
787
|
+
return updatedList
|
|
788
|
+
}
|
|
789
|
+
return currentList
|
|
790
|
+
} catch (error) {
|
|
791
|
+
console.error('Error in addAlwaysAllowedPath logic:', error)
|
|
792
|
+
return []
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
export async function removeAlwaysAllowedPath(pathToRemove) {
|
|
797
|
+
try {
|
|
798
|
+
if (!pathToRemove) return []
|
|
799
|
+
const configs = await getAllConfig()
|
|
800
|
+
const currentConfig = (configs && configs[0]) || { id: 1 }
|
|
801
|
+
const currentList = Array.isArray(currentConfig.alwaysAllowedPaths)
|
|
802
|
+
? currentConfig.alwaysAllowedPaths
|
|
803
|
+
: []
|
|
804
|
+
const updatedList = currentList.filter((p) => p !== pathToRemove)
|
|
805
|
+
await saveConfiguration({ ...currentConfig, alwaysAllowedPaths: updatedList })
|
|
806
|
+
return updatedList
|
|
807
|
+
} catch (error) {
|
|
808
|
+
console.error('Error in removeAlwaysAllowedPath logic:', error)
|
|
809
|
+
return []
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// --- RELATIONSHIP 4D ---
|
|
814
|
+
export async function getRelationship(userId = 'owner') {
|
|
815
|
+
try {
|
|
816
|
+
const rel = await db.relationships.get(userId)
|
|
817
|
+
return (
|
|
818
|
+
rel || {
|
|
819
|
+
userId,
|
|
820
|
+
warmth: 0.5,
|
|
821
|
+
sarcasm_level: 0.5,
|
|
822
|
+
trust: 0.5,
|
|
823
|
+
energy: 0.5,
|
|
824
|
+
obedience: 0.5,
|
|
825
|
+
evalCount: 0,
|
|
826
|
+
lastChatIndex: 0
|
|
827
|
+
}
|
|
828
|
+
)
|
|
829
|
+
} catch (error) {
|
|
830
|
+
console.error('Error in getRelationship logic:', error)
|
|
831
|
+
return {
|
|
832
|
+
userId,
|
|
833
|
+
warmth: 0.5,
|
|
834
|
+
sarcasm_level: 0.5,
|
|
835
|
+
trust: 0.5,
|
|
836
|
+
energy: 0.5,
|
|
837
|
+
obedience: 0.5,
|
|
838
|
+
evalCount: 0,
|
|
839
|
+
lastChatIndex: 0
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
export async function saveRelationship(data) {
|
|
845
|
+
try {
|
|
846
|
+
const userId = data.userId || 'owner'
|
|
847
|
+
const record = { ...data, userId }
|
|
848
|
+
await db.relationships.put(record)
|
|
849
|
+
return record
|
|
850
|
+
} catch (error) {
|
|
851
|
+
console.error('Error in saveRelationship logic:', error)
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
// --- LEARNED SKILLS ---
|
|
856
|
+
export async function getAllLearnedSkills() {
|
|
857
|
+
try {
|
|
858
|
+
return await db.learnedSkills.toArray()
|
|
859
|
+
} catch (error) {
|
|
860
|
+
console.error('Error in getAllLearnedSkills logic:', error)
|
|
861
|
+
return []
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
export async function getLearnedSkill(skillName) {
|
|
866
|
+
try {
|
|
867
|
+
const all = await getAllLearnedSkills()
|
|
868
|
+
const target = String(skillName || '')
|
|
869
|
+
.toLowerCase()
|
|
870
|
+
.trim()
|
|
871
|
+
return (
|
|
872
|
+
all.find(
|
|
873
|
+
(s) =>
|
|
874
|
+
String(s.id).toLowerCase() === target ||
|
|
875
|
+
String(s.name || '')
|
|
876
|
+
.toLowerCase()
|
|
877
|
+
.trim() === target
|
|
878
|
+
) || null
|
|
879
|
+
)
|
|
880
|
+
} catch (error) {
|
|
881
|
+
console.error('Error in getLearnedSkill logic:', error)
|
|
882
|
+
return null
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
export async function saveLearnedSkill(skill) {
|
|
887
|
+
try {
|
|
888
|
+
const all = await getAllLearnedSkills()
|
|
889
|
+
const targetName = String(skill.name || '')
|
|
890
|
+
.toLowerCase()
|
|
891
|
+
.trim()
|
|
892
|
+
const existing = all.find(
|
|
893
|
+
(s) =>
|
|
894
|
+
s.id === skill.id ||
|
|
895
|
+
String(s.name || '')
|
|
896
|
+
.toLowerCase()
|
|
897
|
+
.trim() === targetName
|
|
898
|
+
)
|
|
899
|
+
|
|
900
|
+
const id = existing ? existing.id : skill.id || `skill_${Date.now()}`
|
|
901
|
+
const record = {
|
|
902
|
+
...existing,
|
|
903
|
+
...skill,
|
|
904
|
+
id,
|
|
905
|
+
updatedAt: Date.now()
|
|
906
|
+
}
|
|
907
|
+
await db.learnedSkills.put(record)
|
|
908
|
+
return record
|
|
909
|
+
} catch (error) {
|
|
910
|
+
console.error('Error in saveLearnedSkill logic:', error)
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
export async function deleteLearnedSkill(id) {
|
|
915
|
+
try {
|
|
916
|
+
await db.learnedSkills.delete(id)
|
|
917
|
+
return { success: true }
|
|
918
|
+
} catch (error) {
|
|
919
|
+
console.error('Error in deleteLearnedSkill logic:', error)
|
|
920
|
+
return { success: false, error: error.message }
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
// --- RESET AI DATABASE (EXCEPT CONFIG) ---
|
|
925
|
+
export async function resetAiDatabase() {
|
|
926
|
+
try {
|
|
927
|
+
const res = await apiPost('/api/db/reset-ai', {})
|
|
928
|
+
// Re-inisialisasi Orama index di frontend agar sinkron
|
|
929
|
+
try {
|
|
930
|
+
const { initOramaIndices, hydrateFromDb } = await import('./oramaStore')
|
|
931
|
+
await initOramaIndices()
|
|
932
|
+
await hydrateFromDb()
|
|
933
|
+
} catch (e) {
|
|
934
|
+
console.warn('[DB Proxy] Gagal re-hydrate Orama setelah reset:', e)
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
if (typeof window !== 'undefined') {
|
|
938
|
+
window.dispatchEvent(new CustomEvent('ai-reset-complete', { detail: res }))
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
return res || { success: true }
|
|
942
|
+
} catch (error) {
|
|
943
|
+
console.error('Error in resetAiDatabase logic:', error)
|
|
944
|
+
return { success: false, error: error.message }
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
// --- DATABASE BACKUP & RESTORE HELPERS ---
|
|
949
|
+
export async function exportDatabaseDump() {
|
|
950
|
+
try {
|
|
951
|
+
const res = await apiGet('/api/db/export')
|
|
952
|
+
return res || null
|
|
953
|
+
} catch (error) {
|
|
954
|
+
console.error('Error in exportDatabaseDump:', error)
|
|
955
|
+
throw error
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
export async function restoreDatabaseDump(dumpData, overwrite = true) {
|
|
960
|
+
try {
|
|
961
|
+
const res = await fetch(`${API_BASE}/api/db/restore`, {
|
|
962
|
+
method: 'POST',
|
|
963
|
+
headers: { 'Content-Type': 'application/json' },
|
|
964
|
+
body: JSON.stringify({ dumpData, overwrite })
|
|
965
|
+
})
|
|
966
|
+
const json = await res.json()
|
|
967
|
+
if (!res.ok || !json.success) {
|
|
968
|
+
throw new Error(json.error || 'Gagal memulihkan database dari server.')
|
|
969
|
+
}
|
|
970
|
+
return json
|
|
971
|
+
} catch (error) {
|
|
972
|
+
console.error('Error in restoreDatabaseDump:', error)
|
|
973
|
+
throw error
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
// --- SESSION COMPACT HELPERS ---
|
|
978
|
+
export async function getSessionCompact(sessionId) {
|
|
979
|
+
try {
|
|
980
|
+
const cleanId = normalizeDbId(sessionId)
|
|
981
|
+
const res = await apiGet(`/api/session-compact/${cleanId}`)
|
|
982
|
+
return res || null
|
|
983
|
+
} catch (err) {
|
|
984
|
+
console.error('[DB Proxy] Error getSessionCompact:', err)
|
|
985
|
+
return null
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
export async function saveSessionCompact(sessionId, data) {
|
|
990
|
+
try {
|
|
991
|
+
const cleanId = normalizeDbId(sessionId)
|
|
992
|
+
const payload = {
|
|
993
|
+
sessionId: cleanId,
|
|
994
|
+
...data,
|
|
995
|
+
lastCompactedAt: data.lastCompactedAt || Date.now()
|
|
996
|
+
}
|
|
997
|
+
const res = await apiPost('/api/session-compact', payload)
|
|
998
|
+
return res || null
|
|
999
|
+
} catch (err) {
|
|
1000
|
+
console.error('[DB Proxy] Error saveSessionCompact:', err)
|
|
1001
|
+
return null
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
export async function deleteSessionCompact(sessionId) {
|
|
1006
|
+
try {
|
|
1007
|
+
const cleanId = normalizeDbId(sessionId)
|
|
1008
|
+
return await apiDelete(`/api/session-compact/${cleanId}`)
|
|
1009
|
+
} catch (err) {
|
|
1010
|
+
console.error('[DB Proxy] Error deleteSessionCompact:', err)
|
|
1011
|
+
return false
|
|
1012
|
+
}
|
|
1013
|
+
}
|