total-recall-brain 3.14.4 → 3.14.7
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/frontend/dist/assets/index-D9-jAOr9.js +502 -0
- package/frontend/dist/assets/index-L0eEIoBf.css +1 -0
- package/frontend/dist/brand/total-recall-badge.jpg +0 -0
- package/frontend/dist/brand/total-recall-icon.svg +6 -0
- package/frontend/dist/brand/total-recall-lockup.png +0 -0
- package/frontend/dist/brand/total-recall-mark.svg +9 -0
- package/frontend/dist/favicon.svg +6 -0
- package/frontend/dist/icons.svg +24 -0
- package/frontend/dist/index.html +27 -0
- package/frontend/dist/manifest.json +25 -0
- package/frontend/dist/sw.js +272 -0
- package/frontend/index.html +7 -2
- package/frontend/package.json +2 -0
- package/frontend/src/App.spec.tsx +82 -0
- package/frontend/src/api/_base.ts +35 -0
- package/frontend/src/api/api.spec.ts +297 -0
- package/frontend/src/api/auth.ts +73 -0
- package/frontend/src/api/chat.ts +93 -0
- package/frontend/src/api/config.ts +35 -0
- package/frontend/src/api/docs.ts +146 -0
- package/frontend/src/api/extension.ts +9 -0
- package/frontend/src/api/index.ts +20 -0
- package/frontend/src/api/integrations.ts +22 -0
- package/frontend/src/api/keys.ts +384 -0
- package/frontend/src/api/memory.ts +234 -0
- package/frontend/src/api/models.ts +36 -0
- package/frontend/src/api/research.ts +43 -0
- package/frontend/src/api/sandbox.ts +19 -0
- package/frontend/src/api/sessions.ts +27 -0
- package/frontend/src/api/skills.ts +127 -0
- package/frontend/src/api/system.ts +165 -0
- package/frontend/src/api/update.ts +21 -0
- package/frontend/src/components/BrainSelector.spec.tsx +72 -0
- package/frontend/src/components/DaemonLogsTab.spec.tsx +21 -0
- package/frontend/src/components/DocumentEditorModal.spec.tsx +80 -0
- package/frontend/src/components/DocumentTable.spec.tsx +45 -0
- package/frontend/src/components/Graph3D.spec.tsx +75 -0
- package/frontend/src/components/MarkdownUtils.spec.tsx +82 -0
- package/frontend/src/components/MobilePairing.spec.tsx +24 -0
- package/frontend/src/components/MobilePairing.tsx +37 -0
- package/frontend/src/components/ResearchAgendaTab.spec.tsx +127 -0
- package/frontend/src/components/TaskQueueTab.spec.tsx +70 -0
- package/frontend/src/components/TaskQueueTab.tsx +9 -1
- package/frontend/src/components/UsageChart.spec.tsx +89 -0
- package/frontend/src/components/VoiceInput.spec.tsx +38 -0
- package/frontend/src/components/VoiceInput.tsx +68 -0
- package/frontend/src/index.css +30 -0
- package/frontend/src/main.tsx +13 -0
- package/frontend/src/pages/ApiKeysPage.spec.tsx +32 -0
- package/frontend/src/pages/AutomationsPage.spec.tsx +60 -0
- package/frontend/src/pages/ChatPage.spec.tsx +37 -0
- package/frontend/src/pages/ChatPage.tsx +14 -0
- package/frontend/src/pages/CollabPage.spec.tsx +64 -0
- package/frontend/src/pages/DesignDocsPage.spec.tsx +43 -0
- package/frontend/src/pages/FilesPage.spec.tsx +46 -0
- package/frontend/src/pages/GraphPage.spec.tsx +24 -0
- package/frontend/src/pages/HealthPage.spec.tsx +31 -0
- package/frontend/src/pages/HelpPage.spec.tsx +63 -0
- package/frontend/src/pages/InboxPage.spec.tsx +55 -0
- package/frontend/src/pages/InstructionsPage.spec.tsx +52 -0
- package/frontend/src/pages/InstructionsPage.tsx +1 -1
- package/frontend/src/pages/IntegrationsPage.spec.tsx +55 -0
- package/frontend/src/pages/LoginPage.spec.tsx +73 -0
- package/frontend/src/pages/LoginPage.tsx +20 -0
- package/frontend/src/pages/MemoryPage.spec.tsx +51 -0
- package/frontend/src/pages/ModelsPage.spec.tsx +64 -0
- package/frontend/src/pages/ModelsPage.tsx +18 -11
- package/frontend/src/pages/OkfPage.spec.tsx +46 -0
- package/frontend/src/pages/OnboardingPage.spec.tsx +58 -0
- package/frontend/src/pages/OpenWikiPage.spec.tsx +32 -0
- package/frontend/src/pages/SandboxPage.spec.tsx +33 -0
- package/frontend/src/pages/SettingsPage.spec.tsx +33 -0
- package/frontend/src/pages/SettingsPage.tsx +6 -1
- package/frontend/src/pages/SkillsPage.spec.tsx +20 -0
- package/frontend/src/pages/TasksPage.spec.tsx +17 -0
- package/frontend/src/pages/UsagePage.spec.tsx +35 -0
- package/frontend/src/pages/VaultPage.spec.tsx +26 -0
- package/frontend/src/setupTests.ts +23 -0
- package/frontend/vite.config.ts +6 -0
- package/package.json +16 -4
- package/scaffold/.agent/skills/total-recall/SKILL.md +6 -2
- package/scaffold/.agent/skills/total-recall/skills-registry/index.yaml +2575 -0
- package/src/cli/connect.mjs +7 -1
- package/src/cli/deploy.mjs +8 -8
- package/src/cli/init.mjs +14 -4
- package/src/cli/recall.mjs +3 -1
- package/src/cli/remember.mjs +2 -1
- package/src/cli/restore.mjs +2 -2
- package/src/cli/upgrade.mjs +2 -2
- package/src/core/clarity-rewriter.mjs +2 -2
- package/src/core/crons.mjs +23 -55
- package/src/core/crypto.mjs +4 -8
- package/src/core/daemon-control.mjs +5 -0
- package/src/core/embeddings.mjs +207 -374
- package/src/core/fact-seeker.mjs +114 -0
- package/src/core/github-sync.mjs +319 -0
- package/src/core/obsidian-sync.mjs +129 -0
- package/src/core/project-brain.mjs +2 -1
- package/src/core/remote-vault-sync.mjs +6 -6
- package/src/core/runtime.mjs +1 -1
- package/src/core/skills-registry.mjs +11 -0
- package/src/core/snapshot.mjs +1 -1
- package/src/core/vault-watcher.mjs +6 -3
- package/src/server/api.mjs +1 -1
- package/src/server/rest.mjs +50 -2163
- package/src/server/route-manifest.json +57 -5
- package/src/server/routes/brains.mjs +212 -0
- package/src/server/routes/capture.mjs +33 -0
- package/src/server/routes/collab.mjs +1 -1
- package/src/server/routes/config.mjs +206 -0
- package/src/server/routes/context.mjs +68 -0
- package/src/server/routes/dashboard.mjs +53 -0
- package/src/server/routes/dream.mjs +30 -0
- package/src/server/routes/export.mjs +40 -0
- package/src/server/routes/extension.mjs +83 -0
- package/src/server/routes/field.mjs +48 -0
- package/src/server/routes/files.mjs +34 -0
- package/src/server/routes/graph.mjs +323 -0
- package/src/server/routes/help.mjs +184 -0
- package/src/server/routes/import.mjs +39 -0
- package/src/server/routes/instructions.mjs +29 -0
- package/src/server/routes/integrations.mjs +123 -0
- package/src/server/routes/memory.mjs +19 -1
- package/src/server/routes/models.mjs +388 -0
- package/src/server/routes/scripts.mjs +110 -0
- package/src/server/routes/sessions.mjs +1 -1
- package/src/server/routes/ssss.mjs +173 -0
- package/src/server/routes/system.mjs +167 -0
- package/src/server/routes/tasks.mjs +96 -0
- package/src/server/routes/tts.mjs +39 -0
- package/src/server/routes/update.mjs +42 -0
- package/src/server/routes/vault.mjs +99 -0
- package/frontend/src/api.ts +0 -1441
- package/scaffold/.agent/skills/ssss/SKILL.md +0 -235
- package/scaffold/.agent/skills/ssss/evals/evals.json +0 -37
- package/scaffold/.agent/skills/ssss/references/contract-reference.md +0 -95
- package/scaffold/.agent/skills/ssss/scripts/validate-schema.mjs +0 -215
- package/scaffold/.agent/skills/ssss/subagents/audit-ssss-contract.md +0 -22
- package/src/core/pattern_detector.mjs +0 -113
- package/src/core/promotion-pipeline.mjs +0 -54
- package/src/core/semantic-index.mjs +0 -276
- package/src/core/task_runner.mjs +0 -131
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest'
|
|
3
|
+
|
|
4
|
+
// Mock fetch globally before importing API functions
|
|
5
|
+
const mockFetch = vi.fn()
|
|
6
|
+
vi.stubGlobal('fetch', mockFetch)
|
|
7
|
+
|
|
8
|
+
// Mock localStorage
|
|
9
|
+
const localStorageMock = (() => {
|
|
10
|
+
let store: Record<string, string> = {}
|
|
11
|
+
return {
|
|
12
|
+
getItem: (key: string) => store[key] ?? null,
|
|
13
|
+
setItem: (key: string, value: string) => { store[key] = value },
|
|
14
|
+
removeItem: (key: string) => { delete store[key] },
|
|
15
|
+
clear: () => { store = {} },
|
|
16
|
+
}
|
|
17
|
+
})()
|
|
18
|
+
vi.stubGlobal('localStorage', localStorageMock)
|
|
19
|
+
|
|
20
|
+
// Import from the barrel
|
|
21
|
+
import {
|
|
22
|
+
// _base
|
|
23
|
+
getApiBase,
|
|
24
|
+
setApiBase,
|
|
25
|
+
registerUnauthedCallback,
|
|
26
|
+
clearUnauthedCallback,
|
|
27
|
+
// auth
|
|
28
|
+
checkSession,
|
|
29
|
+
login,
|
|
30
|
+
logout,
|
|
31
|
+
changePassword,
|
|
32
|
+
getAuthStatus,
|
|
33
|
+
setupPassword,
|
|
34
|
+
// chat
|
|
35
|
+
sendChat,
|
|
36
|
+
fetchChatHistory,
|
|
37
|
+
fetchChatThreads,
|
|
38
|
+
deleteChatThread,
|
|
39
|
+
fetchChatSuggestions,
|
|
40
|
+
fetchTtsStatus,
|
|
41
|
+
fetchTtsAudio,
|
|
42
|
+
// memory
|
|
43
|
+
listMemory,
|
|
44
|
+
searchMemory,
|
|
45
|
+
readMemory,
|
|
46
|
+
saveMemory,
|
|
47
|
+
createMemory,
|
|
48
|
+
deleteMemory,
|
|
49
|
+
fetchMemoryStats,
|
|
50
|
+
fetchGraph,
|
|
51
|
+
fetchConflicts,
|
|
52
|
+
resolveConflict,
|
|
53
|
+
fetchVaultStatus,
|
|
54
|
+
fetchOpenWikiNodes,
|
|
55
|
+
// sandbox
|
|
56
|
+
runSandbox,
|
|
57
|
+
// skills
|
|
58
|
+
listSkills,
|
|
59
|
+
fetchSkill,
|
|
60
|
+
fetchSkillFiles,
|
|
61
|
+
saveSkill,
|
|
62
|
+
deleteSkill,
|
|
63
|
+
searchSkillsRegistry,
|
|
64
|
+
installRegistrySkill,
|
|
65
|
+
toggleSkillRepo,
|
|
66
|
+
auditSkill,
|
|
67
|
+
// keys
|
|
68
|
+
listApiKeys,
|
|
69
|
+
issueApiKey,
|
|
70
|
+
revokeApiKey,
|
|
71
|
+
scanEnvSecrets,
|
|
72
|
+
parseEnvPaste,
|
|
73
|
+
importEnvSecrets,
|
|
74
|
+
fetchSecretsCatalog,
|
|
75
|
+
updateSecretMeta,
|
|
76
|
+
rotateSecretValue,
|
|
77
|
+
deleteProviderSecret,
|
|
78
|
+
recordSecretUsage,
|
|
79
|
+
exportEnvFromSecrets,
|
|
80
|
+
fetchRotationDue,
|
|
81
|
+
enqueueRotationDue,
|
|
82
|
+
revealSecretValue,
|
|
83
|
+
fetchWebAuthnStatus,
|
|
84
|
+
webauthnRegisterOptions,
|
|
85
|
+
webauthnRegisterVerify,
|
|
86
|
+
webauthnAssertOptions,
|
|
87
|
+
webauthnAssertVerify,
|
|
88
|
+
webauthnPasswordStepUp,
|
|
89
|
+
deletePasskey,
|
|
90
|
+
// research
|
|
91
|
+
listResearch,
|
|
92
|
+
createResearch,
|
|
93
|
+
patchResearch,
|
|
94
|
+
deleteResearch,
|
|
95
|
+
// system
|
|
96
|
+
fetchHealth,
|
|
97
|
+
fetchUsageStats,
|
|
98
|
+
fetchLogs,
|
|
99
|
+
triggerRecompile,
|
|
100
|
+
triggerDream,
|
|
101
|
+
runAgentDiagnostics,
|
|
102
|
+
fetchBrains,
|
|
103
|
+
listTasks,
|
|
104
|
+
createTask,
|
|
105
|
+
listFiles,
|
|
106
|
+
listScripts,
|
|
107
|
+
readScript,
|
|
108
|
+
saveScript,
|
|
109
|
+
runScript,
|
|
110
|
+
shareToApi,
|
|
111
|
+
runOkfLint,
|
|
112
|
+
triggerOkfExport,
|
|
113
|
+
postDecision,
|
|
114
|
+
// update
|
|
115
|
+
checkUpdate,
|
|
116
|
+
runUpdate,
|
|
117
|
+
// models
|
|
118
|
+
fetchGeminiModels,
|
|
119
|
+
fetchClaudeModels,
|
|
120
|
+
fetchOpenaiModels,
|
|
121
|
+
fetchOpenRouterModels,
|
|
122
|
+
// docs
|
|
123
|
+
fetchDesignDocs,
|
|
124
|
+
fetchDesignDocContent,
|
|
125
|
+
fetchDocs,
|
|
126
|
+
readDoc,
|
|
127
|
+
createDoc,
|
|
128
|
+
updateDoc,
|
|
129
|
+
deleteDoc,
|
|
130
|
+
fetchViews,
|
|
131
|
+
createView,
|
|
132
|
+
deleteView,
|
|
133
|
+
fetchInstructions,
|
|
134
|
+
fetchInstructionContent,
|
|
135
|
+
fetchHelpTopics,
|
|
136
|
+
fetchHelpContent,
|
|
137
|
+
// extension
|
|
138
|
+
fetchExtensionStatus,
|
|
139
|
+
// sessions
|
|
140
|
+
fetchSessions,
|
|
141
|
+
deleteSession,
|
|
142
|
+
// integrations
|
|
143
|
+
connectClient,
|
|
144
|
+
fetchActiveIntegrations,
|
|
145
|
+
// config
|
|
146
|
+
fetchConfig,
|
|
147
|
+
saveConfig,
|
|
148
|
+
fetchConfigJson,
|
|
149
|
+
saveConfigJson,
|
|
150
|
+
} from './index'
|
|
151
|
+
|
|
152
|
+
function makeOkResponse(body: unknown = {}) {
|
|
153
|
+
return Promise.resolve({
|
|
154
|
+
ok: true,
|
|
155
|
+
status: 200,
|
|
156
|
+
json: () => Promise.resolve(body),
|
|
157
|
+
blob: () => Promise.resolve(new Blob()),
|
|
158
|
+
} as Response)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
beforeEach(() => {
|
|
162
|
+
mockFetch.mockReset()
|
|
163
|
+
mockFetch.mockImplementation(() => makeOkResponse())
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
describe('API barrel exports — all functions exist', () => {
|
|
167
|
+
const fns = [
|
|
168
|
+
// _base
|
|
169
|
+
getApiBase, setApiBase, registerUnauthedCallback, clearUnauthedCallback,
|
|
170
|
+
// auth
|
|
171
|
+
checkSession, login, logout, changePassword, getAuthStatus, setupPassword,
|
|
172
|
+
// chat
|
|
173
|
+
sendChat, fetchChatHistory, fetchChatThreads, deleteChatThread, fetchChatSuggestions,
|
|
174
|
+
fetchTtsStatus, fetchTtsAudio,
|
|
175
|
+
// memory
|
|
176
|
+
listMemory, searchMemory, readMemory, saveMemory, createMemory, deleteMemory,
|
|
177
|
+
fetchMemoryStats, fetchGraph, fetchConflicts, resolveConflict, fetchVaultStatus, fetchOpenWikiNodes,
|
|
178
|
+
// sandbox
|
|
179
|
+
runSandbox,
|
|
180
|
+
// skills
|
|
181
|
+
listSkills, fetchSkill, fetchSkillFiles, saveSkill, deleteSkill,
|
|
182
|
+
searchSkillsRegistry, installRegistrySkill, toggleSkillRepo, auditSkill,
|
|
183
|
+
// keys
|
|
184
|
+
listApiKeys, issueApiKey, revokeApiKey, scanEnvSecrets, parseEnvPaste, importEnvSecrets,
|
|
185
|
+
fetchSecretsCatalog, updateSecretMeta, rotateSecretValue, deleteProviderSecret, recordSecretUsage,
|
|
186
|
+
exportEnvFromSecrets, fetchRotationDue, enqueueRotationDue, revealSecretValue,
|
|
187
|
+
fetchWebAuthnStatus, webauthnRegisterOptions, webauthnRegisterVerify,
|
|
188
|
+
webauthnAssertOptions, webauthnAssertVerify, webauthnPasswordStepUp, deletePasskey,
|
|
189
|
+
// research
|
|
190
|
+
listResearch, createResearch, patchResearch, deleteResearch,
|
|
191
|
+
// system
|
|
192
|
+
fetchHealth, fetchUsageStats, fetchLogs, triggerRecompile, triggerDream, runAgentDiagnostics,
|
|
193
|
+
fetchBrains, listTasks, createTask, listFiles, listScripts, readScript, saveScript, runScript,
|
|
194
|
+
shareToApi, runOkfLint, triggerOkfExport, postDecision,
|
|
195
|
+
// update
|
|
196
|
+
checkUpdate, runUpdate,
|
|
197
|
+
// models
|
|
198
|
+
fetchGeminiModels, fetchClaudeModels, fetchOpenaiModels, fetchOpenRouterModels,
|
|
199
|
+
// docs
|
|
200
|
+
fetchDesignDocs, fetchDesignDocContent, fetchDocs, readDoc, createDoc, updateDoc, deleteDoc,
|
|
201
|
+
fetchViews, createView, deleteView, fetchInstructions, fetchInstructionContent,
|
|
202
|
+
fetchHelpTopics, fetchHelpContent,
|
|
203
|
+
// extension
|
|
204
|
+
fetchExtensionStatus,
|
|
205
|
+
// sessions
|
|
206
|
+
fetchSessions, deleteSession,
|
|
207
|
+
// integrations
|
|
208
|
+
connectClient, fetchActiveIntegrations,
|
|
209
|
+
// config
|
|
210
|
+
fetchConfig, saveConfig, fetchConfigJson, saveConfigJson,
|
|
211
|
+
]
|
|
212
|
+
|
|
213
|
+
it('exports all functions as typeof function', () => {
|
|
214
|
+
for (const fn of fns) {
|
|
215
|
+
expect(typeof fn, `${fn.name} should be a function`).toBe('function')
|
|
216
|
+
}
|
|
217
|
+
})
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
describe('checkSession', () => {
|
|
221
|
+
it('returns true when fetch succeeds with ok response', async () => {
|
|
222
|
+
mockFetch.mockResolvedValueOnce({ ok: true } as Response)
|
|
223
|
+
const result = await checkSession()
|
|
224
|
+
expect(result).toBe(true)
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
it('returns false on network error', async () => {
|
|
228
|
+
mockFetch.mockRejectedValueOnce(new Error('Network error'))
|
|
229
|
+
const result = await checkSession()
|
|
230
|
+
expect(result).toBe(false)
|
|
231
|
+
})
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
describe('login', () => {
|
|
235
|
+
it('returns ok:true on successful login', async () => {
|
|
236
|
+
mockFetch.mockResolvedValueOnce({
|
|
237
|
+
ok: true,
|
|
238
|
+
json: () => Promise.resolve({ requiresPasswordReset: false }),
|
|
239
|
+
} as Response)
|
|
240
|
+
const result = await login('password123')
|
|
241
|
+
expect(result.ok).toBe(true)
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
it('returns ok:false with error on failed login', async () => {
|
|
245
|
+
mockFetch.mockResolvedValueOnce({
|
|
246
|
+
ok: false,
|
|
247
|
+
json: () => Promise.resolve({ error: 'Invalid password' }),
|
|
248
|
+
} as Response)
|
|
249
|
+
const result = await login('wrongpassword')
|
|
250
|
+
expect(result.ok).toBe(false)
|
|
251
|
+
expect(result.error).toBe('Invalid password')
|
|
252
|
+
})
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
describe('getApiBase', () => {
|
|
256
|
+
it('returns the current API base URL', () => {
|
|
257
|
+
const base = getApiBase()
|
|
258
|
+
expect(typeof base).toBe('string')
|
|
259
|
+
})
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
describe('fetchExtensionStatus', () => {
|
|
263
|
+
it('returns unavailable on non-ok response', async () => {
|
|
264
|
+
mockFetch.mockResolvedValueOnce({ ok: false, status: 404 } as Response)
|
|
265
|
+
const result = await fetchExtensionStatus()
|
|
266
|
+
expect(result).toEqual({ available: false, connected: false })
|
|
267
|
+
})
|
|
268
|
+
|
|
269
|
+
it('returns extension status on ok response', async () => {
|
|
270
|
+
mockFetch.mockResolvedValueOnce({
|
|
271
|
+
ok: true,
|
|
272
|
+
json: () => Promise.resolve({ available: true, connected: true }),
|
|
273
|
+
} as Response)
|
|
274
|
+
const result = await fetchExtensionStatus()
|
|
275
|
+
expect(result).toEqual({ available: true, connected: true })
|
|
276
|
+
})
|
|
277
|
+
})
|
|
278
|
+
|
|
279
|
+
describe('runSandbox', () => {
|
|
280
|
+
it('returns success result on ok response', async () => {
|
|
281
|
+
mockFetch.mockResolvedValueOnce({
|
|
282
|
+
ok: true,
|
|
283
|
+
json: () => Promise.resolve({ success: true, output: 'hello' }),
|
|
284
|
+
} as Response)
|
|
285
|
+
const result = await runSandbox('console.log("hello")')
|
|
286
|
+
expect(result.success).toBe(true)
|
|
287
|
+
expect(result.output).toBe('hello')
|
|
288
|
+
expect(result.isError).toBe(false)
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
it('returns error result on fetch exception', async () => {
|
|
292
|
+
mockFetch.mockRejectedValueOnce(new Error('Connection refused'))
|
|
293
|
+
const result = await runSandbox('bad code')
|
|
294
|
+
expect(result.success).toBe(false)
|
|
295
|
+
expect(result.isError).toBe(true)
|
|
296
|
+
})
|
|
297
|
+
})
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// ─── Auth domain ──────────────────────────────────────────────────────────────
|
|
2
|
+
|
|
3
|
+
import { API_BASE } from './_base'
|
|
4
|
+
|
|
5
|
+
export async function checkSession(): Promise<boolean> {
|
|
6
|
+
try {
|
|
7
|
+
const res = await fetch(API_BASE + '/auth/me', { credentials: 'include' })
|
|
8
|
+
return res.ok
|
|
9
|
+
} catch {
|
|
10
|
+
return false
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function login(password: string): Promise<{ ok: boolean; requiresPasswordReset?: boolean; error?: string }> {
|
|
15
|
+
try {
|
|
16
|
+
const res = await fetch(API_BASE + '/auth/login', {
|
|
17
|
+
method: 'POST',
|
|
18
|
+
headers: { 'Content-Type': 'application/json' },
|
|
19
|
+
credentials: 'include',
|
|
20
|
+
body: JSON.stringify({ password }),
|
|
21
|
+
})
|
|
22
|
+
const data = await res.json().catch(() => ({}))
|
|
23
|
+
if (res.ok) return { ok: true, requiresPasswordReset: data.requiresPasswordReset }
|
|
24
|
+
return { ok: false, error: data.error || 'Invalid password' }
|
|
25
|
+
} catch {
|
|
26
|
+
return { ok: false, error: 'Network error — is the server running?' }
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function changePassword(newPassword: string): Promise<{ ok: boolean; error?: string }> {
|
|
31
|
+
try {
|
|
32
|
+
const res = await fetch(API_BASE + '/auth/change-password', {
|
|
33
|
+
method: 'POST',
|
|
34
|
+
headers: { 'Content-Type': 'application/json' },
|
|
35
|
+
credentials: 'include',
|
|
36
|
+
body: JSON.stringify({ newPassword }),
|
|
37
|
+
})
|
|
38
|
+
if (res.ok) return { ok: true }
|
|
39
|
+
const data = await res.json().catch(() => ({}))
|
|
40
|
+
return { ok: false, error: data.error || 'Failed to change password' }
|
|
41
|
+
} catch {
|
|
42
|
+
return { ok: false, error: 'Network error' }
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function getAuthStatus(): Promise<{ configured: boolean }> {
|
|
47
|
+
try {
|
|
48
|
+
const res = await fetch(API_BASE + "/auth/status")
|
|
49
|
+
if (res.ok) return await res.json()
|
|
50
|
+
return { configured: true }
|
|
51
|
+
} catch {
|
|
52
|
+
return { configured: true }
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function setupPassword(newPassword: string): Promise<{ ok: boolean; error?: string }> {
|
|
57
|
+
try {
|
|
58
|
+
const res = await fetch(API_BASE + "/auth/setup", {
|
|
59
|
+
method: "POST",
|
|
60
|
+
headers: { "Content-Type": "application/json" },
|
|
61
|
+
body: JSON.stringify({ newPassword }),
|
|
62
|
+
})
|
|
63
|
+
const data = await res.json().catch(() => ({}))
|
|
64
|
+
if (res.ok) return { ok: true }
|
|
65
|
+
return { ok: false, error: data.error || "Failed to setup password" }
|
|
66
|
+
} catch {
|
|
67
|
+
return { ok: false, error: "Network error — is the server running?" }
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export async function logout(): Promise<void> {
|
|
72
|
+
await fetch(API_BASE + '/auth/logout', { method: 'POST', credentials: 'include' }).catch(() => {})
|
|
73
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// ─── Chat domain ──────────────────────────────────────────────────────────────
|
|
2
|
+
|
|
3
|
+
import { apiFetch, API_BASE } from './_base'
|
|
4
|
+
|
|
5
|
+
export interface ChatThread {
|
|
6
|
+
id: string
|
|
7
|
+
title: string
|
|
8
|
+
turns: number
|
|
9
|
+
lastUpdated: number
|
|
10
|
+
brainId: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface ChatSuggestion {
|
|
14
|
+
type: string
|
|
15
|
+
title: string
|
|
16
|
+
text: string
|
|
17
|
+
nodes: string[]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function sendChat(
|
|
21
|
+
messages: { role: string; content: string }[],
|
|
22
|
+
signal?: AbortSignal,
|
|
23
|
+
sessionId?: string,
|
|
24
|
+
groundingNodes?: string[],
|
|
25
|
+
model?: string,
|
|
26
|
+
brainId?: string
|
|
27
|
+
): Promise<string> {
|
|
28
|
+
const headers: Record<string, string> = { 'Content-Type': 'application/json' }
|
|
29
|
+
if (sessionId) {
|
|
30
|
+
headers['x-session-id'] = sessionId
|
|
31
|
+
}
|
|
32
|
+
const res = await apiFetch(API_BASE + '/v1/chat/completions', {
|
|
33
|
+
method: 'POST',
|
|
34
|
+
signal,
|
|
35
|
+
headers,
|
|
36
|
+
body: JSON.stringify({ messages, groundingNodes, model, brainId }),
|
|
37
|
+
})
|
|
38
|
+
if (!res.ok) throw new Error(`Chat API error: ${res.status}`)
|
|
39
|
+
const data = await res.json()
|
|
40
|
+
return data.choices?.[0]?.message?.content ?? '(empty response)'
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function fetchChatHistory(sessionId?: string): Promise<{ role: string; content: string }[]> {
|
|
44
|
+
const headers: Record<string, string> = {}
|
|
45
|
+
if (sessionId) {
|
|
46
|
+
headers['x-session-id'] = sessionId
|
|
47
|
+
}
|
|
48
|
+
const res = await apiFetch(API_BASE + '/v1/chat/history', { headers })
|
|
49
|
+
if (!res.ok) throw new Error(`Chat history error: ${res.status}`)
|
|
50
|
+
const data = await res.json()
|
|
51
|
+
return data.messages ?? []
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function fetchChatThreads(): Promise<ChatThread[]> {
|
|
55
|
+
const res = await apiFetch(API_BASE + '/v1/chat/threads')
|
|
56
|
+
if (!res.ok) throw new Error(`Chat threads list error: ${res.status}`)
|
|
57
|
+
return res.json()
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export async function deleteChatThread(id: string): Promise<{ deleted: boolean; id: string }> {
|
|
61
|
+
const res = await apiFetch(API_BASE + `/v1/chat/threads/${id}`, { method: 'DELETE' })
|
|
62
|
+
if (!res.ok) throw new Error(`Delete chat thread error: ${res.status}`)
|
|
63
|
+
return res.json()
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export async function fetchChatSuggestions(): Promise<ChatSuggestion[]> {
|
|
67
|
+
const res = await apiFetch(API_BASE + '/v1/chat/suggestions')
|
|
68
|
+
if (!res.ok) throw new Error(`Chat suggestions list error: ${res.status}`)
|
|
69
|
+
return res.json()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ─── TTS (Kokoro-82M) ──────────────────────────────────────────────────────────
|
|
73
|
+
|
|
74
|
+
export async function fetchTtsStatus(): Promise<{ enabled: boolean }> {
|
|
75
|
+
try {
|
|
76
|
+
const res = await apiFetch(API_BASE + '/api/tts/status')
|
|
77
|
+
if (!res.ok) return { enabled: false }
|
|
78
|
+
return res.json()
|
|
79
|
+
} catch {
|
|
80
|
+
return { enabled: false }
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export async function fetchTtsAudio(text: string): Promise<Blob | null> {
|
|
85
|
+
const res = await apiFetch(API_BASE + '/api/tts', {
|
|
86
|
+
method: 'POST',
|
|
87
|
+
headers: { 'Content-Type': 'application/json' },
|
|
88
|
+
body: JSON.stringify({ text }),
|
|
89
|
+
})
|
|
90
|
+
if (res.status === 503) return null // Kokoro not configured — caller should fall back.
|
|
91
|
+
if (!res.ok) throw new Error(`TTS error: ${res.status}`)
|
|
92
|
+
return res.blob()
|
|
93
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// ─── Config domain ────────────────────────────────────────────────────────────
|
|
2
|
+
|
|
3
|
+
import { apiFetch, API_BASE } from './_base'
|
|
4
|
+
import type { ConfigJson } from './_base'
|
|
5
|
+
|
|
6
|
+
export async function fetchConfig(name: string): Promise<string> {
|
|
7
|
+
const res = await apiFetch(`${API_BASE}/api/config/${name}`)
|
|
8
|
+
if (!res.ok) throw new Error(`Config API error: ${res.status}`)
|
|
9
|
+
const data = await res.json()
|
|
10
|
+
return data.content
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function saveConfig(name: string, content: string): Promise<void> {
|
|
14
|
+
const res = await apiFetch(`${API_BASE}/api/config/${name}`, {
|
|
15
|
+
method: 'PUT',
|
|
16
|
+
headers: { 'Content-Type': 'application/json' },
|
|
17
|
+
body: JSON.stringify({ content }),
|
|
18
|
+
})
|
|
19
|
+
if (!res.ok) throw new Error(`Config API error: ${res.status}`)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function fetchConfigJson(): Promise<ConfigJson> {
|
|
23
|
+
const res = await apiFetch(`${API_BASE}/api/config-json`)
|
|
24
|
+
if (!res.ok) throw new Error(`Config JSON API error: ${res.status}`)
|
|
25
|
+
return res.json()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function saveConfigJson(config: ConfigJson): Promise<void> {
|
|
29
|
+
const res = await apiFetch(`${API_BASE}/api/config-json`, {
|
|
30
|
+
method: 'POST',
|
|
31
|
+
headers: { 'Content-Type': 'application/json' },
|
|
32
|
+
body: JSON.stringify(config),
|
|
33
|
+
})
|
|
34
|
+
if (!res.ok) throw new Error(`Config JSON API error: ${res.status}`)
|
|
35
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// ─── Docs domain ──────────────────────────────────────────────────────────────
|
|
2
|
+
|
|
3
|
+
import { apiFetch, API_BASE } from './_base'
|
|
4
|
+
|
|
5
|
+
// ─── Design Docs ──────────────────────────────────────────────────────────────
|
|
6
|
+
|
|
7
|
+
export async function fetchDesignDocs(): Promise<Array<{ name: string; path: string; size: number; modified: string; category: string }>> {
|
|
8
|
+
const res = await apiFetch(`${API_BASE}/api/files?path=docs`)
|
|
9
|
+
if (!res.ok) throw new Error(`Design docs error: ${res.status}`)
|
|
10
|
+
return res.json()
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function fetchDesignDocContent(path: string): Promise<{ content: string }> {
|
|
14
|
+
const res = await apiFetch(`${API_BASE}/api/files?path=${encodeURIComponent(path)}&content=true`)
|
|
15
|
+
if (!res.ok) throw new Error(`Design doc content error: ${res.status}`)
|
|
16
|
+
return res.json()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// ─── Vault Documents ──────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
export interface VaultDocument extends Record<string, unknown> {
|
|
22
|
+
id?: string
|
|
23
|
+
type?: string
|
|
24
|
+
name?: string
|
|
25
|
+
path: string
|
|
26
|
+
status?: string
|
|
27
|
+
portability?: string
|
|
28
|
+
updatedAt?: string | number
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface DocsResponse {
|
|
32
|
+
docs: VaultDocument[]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface DocumentResponse {
|
|
36
|
+
raw: string
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface SavedView {
|
|
40
|
+
id: string
|
|
41
|
+
name: string
|
|
42
|
+
filters: Record<string, string>
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export async function fetchDocs(brain?: string, params?: Record<string, string>): Promise<DocsResponse> {
|
|
46
|
+
const p = new URLSearchParams()
|
|
47
|
+
if (brain) p.set('brain', brain)
|
|
48
|
+
if (params) {
|
|
49
|
+
for (const [k, v] of Object.entries(params)) if (v) p.set(k, v)
|
|
50
|
+
}
|
|
51
|
+
const res = await apiFetch(`/api/docs?${p.toString()}`)
|
|
52
|
+
if (!res.ok) throw new Error('Failed to fetch docs')
|
|
53
|
+
return res.json()
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export async function readDoc(path: string, brain?: string): Promise<DocumentResponse> {
|
|
57
|
+
const p = new URLSearchParams({ path })
|
|
58
|
+
if (brain) p.set('brain', brain)
|
|
59
|
+
const res = await apiFetch(`/api/docs/read?${p.toString()}`)
|
|
60
|
+
if (!res.ok) throw new Error('Failed to read doc')
|
|
61
|
+
return res.json()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function createDoc(path: string, content: string, brain?: string) {
|
|
65
|
+
const res = await apiFetch('/api/docs', {
|
|
66
|
+
method: 'POST',
|
|
67
|
+
headers: { 'Content-Type': 'application/json' },
|
|
68
|
+
body: JSON.stringify({ path, content, brainId: brain })
|
|
69
|
+
})
|
|
70
|
+
if (!res.ok) throw new Error('Failed to create doc')
|
|
71
|
+
return res.json()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export async function updateDoc(path: string, content: string, brain?: string) {
|
|
75
|
+
const res = await apiFetch('/api/docs', {
|
|
76
|
+
method: 'PUT',
|
|
77
|
+
headers: { 'Content-Type': 'application/json' },
|
|
78
|
+
body: JSON.stringify({ path, content, brainId: brain })
|
|
79
|
+
})
|
|
80
|
+
if (!res.ok) throw new Error('Failed to update doc')
|
|
81
|
+
return res.json()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export async function deleteDoc(path: string, brain?: string) {
|
|
85
|
+
const p = new URLSearchParams({ path })
|
|
86
|
+
if (brain) p.set('brain', brain)
|
|
87
|
+
const res = await apiFetch(`/api/docs?${p.toString()}`, { method: 'DELETE' })
|
|
88
|
+
if (!res.ok) throw new Error('Failed to delete doc')
|
|
89
|
+
return res.json()
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export async function fetchViews(): Promise<SavedView[]> {
|
|
93
|
+
const res = await apiFetch('/api/views')
|
|
94
|
+
if (!res.ok) throw new Error('Failed to fetch views')
|
|
95
|
+
return res.json()
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export async function createView(name: string, filters: Record<string, string>) {
|
|
99
|
+
const res = await apiFetch('/api/views', {
|
|
100
|
+
method: 'POST',
|
|
101
|
+
headers: { 'Content-Type': 'application/json' },
|
|
102
|
+
body: JSON.stringify({ name, filters })
|
|
103
|
+
})
|
|
104
|
+
if (!res.ok) throw new Error('Failed to create view')
|
|
105
|
+
return res.json()
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export async function deleteView(id: string) {
|
|
109
|
+
const res = await apiFetch(`/api/views/${id}`, { method: 'DELETE' })
|
|
110
|
+
if (!res.ok) throw new Error('Failed to delete view')
|
|
111
|
+
return res.json()
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ─── Instructions ─────────────────────────────────────────────────────────────
|
|
115
|
+
|
|
116
|
+
export async function fetchInstructions(): Promise<{ surfaces: Array<{ name: string; filename: string; size: number; lastCompiled: string; active: boolean }>; lastCompileTimestamp: string; totalNodes: number }> {
|
|
117
|
+
const res = await apiFetch(`${API_BASE}/api/dashboard/instructions`)
|
|
118
|
+
if (!res.ok) throw new Error(`Instructions API error: ${res.status}`)
|
|
119
|
+
return res.json()
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export async function fetchInstructionContent(name: string): Promise<{ name: string; content: string }> {
|
|
123
|
+
const res = await apiFetch(`${API_BASE}/api/ssss/instructions?surface=${encodeURIComponent(name)}`)
|
|
124
|
+
if (!res.ok) throw new Error(`Instruction content error: ${res.status}`)
|
|
125
|
+
return res.json()
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ─── Help ──────────────────────────────────────────────────────────────────────
|
|
129
|
+
|
|
130
|
+
export interface HelpTopic {
|
|
131
|
+
id: string
|
|
132
|
+
title: string
|
|
133
|
+
description: string
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export async function fetchHelpTopics(): Promise<{ topics: HelpTopic[] }> {
|
|
137
|
+
const res = await apiFetch(`${API_BASE}/api/help`)
|
|
138
|
+
if (!res.ok) throw new Error('Failed to load help topics')
|
|
139
|
+
return res.json()
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export async function fetchHelpContent(topicId: string): Promise<{ topic: string; content: string }> {
|
|
143
|
+
const res = await apiFetch(`${API_BASE}/api/help?topic=${encodeURIComponent(topicId)}`)
|
|
144
|
+
if (!res.ok) throw new Error('Failed to load help content')
|
|
145
|
+
return res.json()
|
|
146
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// ─── Extension domain ─────────────────────────────────────────────────────────
|
|
2
|
+
|
|
3
|
+
import { apiFetch, API_BASE } from './_base'
|
|
4
|
+
|
|
5
|
+
export async function fetchExtensionStatus(): Promise<{ available: boolean; connected: boolean }> {
|
|
6
|
+
const res = await apiFetch(`${API_BASE}/api/extension/status`)
|
|
7
|
+
if (!res.ok) return { available: false, connected: false }
|
|
8
|
+
return res.json()
|
|
9
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// ─── Barrel re-export — all domain modules ────────────────────────────────────
|
|
2
|
+
// Imports from '../api' or '../../api' will resolve here automatically via
|
|
3
|
+
// TypeScript module resolution (directory index).
|
|
4
|
+
|
|
5
|
+
export * from './_base'
|
|
6
|
+
export * from './auth'
|
|
7
|
+
export * from './chat'
|
|
8
|
+
export * from './memory'
|
|
9
|
+
export * from './sandbox'
|
|
10
|
+
export * from './skills'
|
|
11
|
+
export * from './keys'
|
|
12
|
+
export * from './research'
|
|
13
|
+
export * from './system'
|
|
14
|
+
export * from './update'
|
|
15
|
+
export * from './models'
|
|
16
|
+
export * from './docs'
|
|
17
|
+
export * from './extension'
|
|
18
|
+
export * from './sessions'
|
|
19
|
+
export * from './integrations'
|
|
20
|
+
export * from './config'
|