total-recall-brain 3.4.0 → 3.6.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/bin/antigravity.mjs +19 -8
- package/bin/total-recall.mjs +3 -0
- package/extension/content-script.js +354 -11
- package/extension/popup/popup.html +5 -0
- package/extension/popup/popup.js +25 -0
- package/frontend/src/App.tsx +29 -0
- package/frontend/src/api.ts +40 -0
- package/frontend/src/pages/ChatPage.tsx +101 -8
- package/frontend/src/pages/CollabPage.tsx +470 -0
- package/frontend/src/pages/GraphPage.tsx +62 -0
- package/frontend/src/pages/HealthPage.tsx +185 -8
- package/frontend/src/pages/HelpPage.tsx +141 -0
- package/frontend/src/pages/SettingsPage.tsx +197 -2
- package/frontend/src/types.ts +16 -0
- package/package.json +3 -2
- package/scaffold/.agent/skills/total-recall/research-queue.jsonl +1 -0
- package/src/cli/collab.mjs +49 -0
- package/src/cli/help.mjs +1 -1
- package/src/core/dream.mjs +41 -0
- package/src/core/runtime.mjs +30 -7
- package/src/core/schema.mjs +481 -0
- package/src/core/source-adapters.mjs +14 -6
- package/src/server/index.mjs +21 -1
- package/src/server/rest.mjs +158 -3
- package/src/server/route-manifest.json +12 -0
- package/src/server/routes/collab.mjs +286 -0
package/bin/antigravity.mjs
CHANGED
|
@@ -83,10 +83,9 @@ if (!process.env.GOOGLE_API_KEY) {
|
|
|
83
83
|
}
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
const apiKey = process.env.GOOGLE_API_KEY;
|
|
86
|
+
const apiKey = process.env.GOOGLE_API_KEY || '';
|
|
87
87
|
if (!apiKey) {
|
|
88
|
-
console.
|
|
89
|
-
process.exit(1);
|
|
88
|
+
console.warn('Warning: GOOGLE_API_KEY environment variable is not configured. Attempting connection via GCP Application Default Credentials or ambient environment...');
|
|
90
89
|
}
|
|
91
90
|
|
|
92
91
|
// Resolve the model dynamically from the API registry based on the category/alias selected
|
|
@@ -109,7 +108,13 @@ async function resolveGenerativeModel(selectedModel) {
|
|
|
109
108
|
}
|
|
110
109
|
|
|
111
110
|
try {
|
|
112
|
-
const
|
|
111
|
+
const keyParam = apiKey ? `key=${apiKey}` : '';
|
|
112
|
+
const headers = {};
|
|
113
|
+
if (!apiKey && process.env.GCLOUD_TOKEN) {
|
|
114
|
+
headers['Authorization'] = `Bearer ${process.env.GCLOUD_TOKEN}`;
|
|
115
|
+
}
|
|
116
|
+
const res = await fetch(`https://generativelanguage.googleapis.com/v1beta/models?${keyParam}`, {
|
|
117
|
+
headers,
|
|
113
118
|
signal: AbortSignal.timeout(5000)
|
|
114
119
|
});
|
|
115
120
|
if (res.ok) {
|
|
@@ -154,13 +159,19 @@ async function resolveGenerativeModel(selectedModel) {
|
|
|
154
159
|
async function main() {
|
|
155
160
|
try {
|
|
156
161
|
const resolvedModel = await resolveGenerativeModel(requestedModel);
|
|
157
|
-
const
|
|
162
|
+
const keyParam = apiKey ? `?key=${apiKey}` : '';
|
|
163
|
+
const url = `https://generativelanguage.googleapis.com/v1beta/models/${resolvedModel}:generateContent${keyParam}`;
|
|
164
|
+
|
|
165
|
+
const headers = {
|
|
166
|
+
'Content-Type': 'application/json',
|
|
167
|
+
};
|
|
168
|
+
if (!apiKey && process.env.GCLOUD_TOKEN) {
|
|
169
|
+
headers['Authorization'] = `Bearer ${process.env.GCLOUD_TOKEN}`;
|
|
170
|
+
}
|
|
158
171
|
|
|
159
172
|
const res = await fetch(url, {
|
|
160
173
|
method: 'POST',
|
|
161
|
-
headers
|
|
162
|
-
'Content-Type': 'application/json',
|
|
163
|
-
},
|
|
174
|
+
headers,
|
|
164
175
|
body: JSON.stringify({
|
|
165
176
|
contents: [{
|
|
166
177
|
parts: [{ text: prompt }]
|
package/bin/total-recall.mjs
CHANGED
|
@@ -78,6 +78,7 @@ const COMMANDS = {
|
|
|
78
78
|
config: 'config.mjs',
|
|
79
79
|
skill: 'skill.mjs',
|
|
80
80
|
help: 'help.mjs',
|
|
81
|
+
collab: 'collab.mjs',
|
|
81
82
|
};
|
|
82
83
|
function printHelp() {
|
|
83
84
|
console.log(`
|
|
@@ -126,6 +127,8 @@ function printHelp() {
|
|
|
126
127
|
Commands: start | stop | status | once | install | uninstall
|
|
127
128
|
uninstall Completely uninstall all services, launchd agents, VFS, and shims
|
|
128
129
|
help <topic> Interactive offline documentation, SSSS reference, and system help
|
|
130
|
+
collab Start Express/WebSockets backend & React collab sandbox dev server
|
|
131
|
+
|
|
129
132
|
|
|
130
133
|
|
|
131
134
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// Total Recall — Content Script
|
|
2
2
|
// Injected into every page. Queries the brain for related memories and shows
|
|
3
3
|
// a floating pill in the bottom-right corner when matches are found.
|
|
4
|
+
// Clicking the pill opens an interactive overlay listing the related memories
|
|
5
|
+
// and exposing quick actions (Remember page, Research page).
|
|
4
6
|
// Uses Shadow DOM to isolate styles from the host page.
|
|
5
7
|
|
|
6
8
|
(function () {
|
|
@@ -21,8 +23,9 @@
|
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
let pillHost = null;
|
|
26
|
+
let currentMemories = [];
|
|
24
27
|
|
|
25
|
-
// ---- Create floating pill with Shadow DOM ----
|
|
28
|
+
// ---- Create floating pill and interactive overlay with Shadow DOM ----
|
|
26
29
|
function createPill(count) {
|
|
27
30
|
if (pillHost) pillHost.remove();
|
|
28
31
|
|
|
@@ -42,20 +45,27 @@
|
|
|
42
45
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
43
46
|
}
|
|
44
47
|
|
|
48
|
+
.container {
|
|
49
|
+
position: relative;
|
|
50
|
+
display: flex;
|
|
51
|
+
flex-direction: column;
|
|
52
|
+
align-items: flex-end;
|
|
53
|
+
}
|
|
54
|
+
|
|
45
55
|
.pill {
|
|
46
56
|
display: flex;
|
|
47
57
|
align-items: center;
|
|
48
|
-
gap:
|
|
49
|
-
padding:
|
|
50
|
-
background: #
|
|
58
|
+
gap: 8px;
|
|
59
|
+
padding: 10px 16px;
|
|
60
|
+
background: #11111b;
|
|
51
61
|
color: #cdd6f4;
|
|
52
62
|
border: 1px solid #313244;
|
|
53
63
|
border-radius: 999px;
|
|
54
64
|
font-size: 13px;
|
|
55
65
|
font-weight: 600;
|
|
56
66
|
cursor: pointer;
|
|
57
|
-
box-shadow: 0 4px
|
|
58
|
-
transition:
|
|
67
|
+
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.4);
|
|
68
|
+
transition: all 0.25s cubic-bezier(0.16, 1, 0.3, 1);
|
|
59
69
|
user-select: none;
|
|
60
70
|
opacity: 0;
|
|
61
71
|
transform: translateY(10px);
|
|
@@ -65,7 +75,13 @@
|
|
|
65
75
|
.pill:hover {
|
|
66
76
|
border-color: #89b4fa;
|
|
67
77
|
transform: translateY(-2px);
|
|
68
|
-
box-shadow: 0 6px 24px rgba(137, 180, 250, 0.
|
|
78
|
+
box-shadow: 0 6px 24px rgba(137, 180, 250, 0.2);
|
|
79
|
+
background: #1e1e2e;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.pill.active {
|
|
83
|
+
border-color: #89b4fa;
|
|
84
|
+
background: #1e1e2e;
|
|
69
85
|
}
|
|
70
86
|
|
|
71
87
|
.pill .brain-icon {
|
|
@@ -87,6 +103,195 @@
|
|
|
87
103
|
font-weight: 700;
|
|
88
104
|
}
|
|
89
105
|
|
|
106
|
+
/* Interactive Overlay Card */
|
|
107
|
+
.card {
|
|
108
|
+
display: none;
|
|
109
|
+
flex-direction: column;
|
|
110
|
+
width: 320px;
|
|
111
|
+
max-height: 420px;
|
|
112
|
+
background: rgba(17, 17, 27, 0.95);
|
|
113
|
+
backdrop-filter: blur(12px);
|
|
114
|
+
-webkit-backdrop-filter: blur(12px);
|
|
115
|
+
border: 1px solid #313244;
|
|
116
|
+
border-radius: 16px;
|
|
117
|
+
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.6);
|
|
118
|
+
position: absolute;
|
|
119
|
+
bottom: 50px;
|
|
120
|
+
right: 0;
|
|
121
|
+
z-index: 2147483647;
|
|
122
|
+
overflow: hidden;
|
|
123
|
+
transform: scale(0.95) translateY(10px);
|
|
124
|
+
opacity: 0;
|
|
125
|
+
transition: all 0.25s cubic-bezier(0.16, 1, 0.3, 1);
|
|
126
|
+
pointer-events: none;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.card.visible {
|
|
130
|
+
display: flex;
|
|
131
|
+
transform: scale(1) translateY(0);
|
|
132
|
+
opacity: 1;
|
|
133
|
+
pointer-events: auto;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.card-header {
|
|
137
|
+
display: flex;
|
|
138
|
+
align-items: center;
|
|
139
|
+
justify-content: space-between;
|
|
140
|
+
padding: 14px 16px;
|
|
141
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.card-header h3 {
|
|
145
|
+
margin: 0;
|
|
146
|
+
font-size: 14px;
|
|
147
|
+
font-weight: 700;
|
|
148
|
+
color: #cdd6f4;
|
|
149
|
+
display: flex;
|
|
150
|
+
align-items: center;
|
|
151
|
+
gap: 6px;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.card-header .close-btn {
|
|
155
|
+
background: none;
|
|
156
|
+
border: none;
|
|
157
|
+
color: #a6adc8;
|
|
158
|
+
font-size: 18px;
|
|
159
|
+
cursor: pointer;
|
|
160
|
+
padding: 0;
|
|
161
|
+
line-height: 1;
|
|
162
|
+
display: flex;
|
|
163
|
+
align-items: center;
|
|
164
|
+
justify-content: center;
|
|
165
|
+
width: 20px;
|
|
166
|
+
height: 20px;
|
|
167
|
+
border-radius: 50%;
|
|
168
|
+
transition: background 0.2s;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
.card-header .close-btn:hover {
|
|
172
|
+
color: #f38ba8;
|
|
173
|
+
background: rgba(255, 255, 255, 0.05);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.memory-list {
|
|
177
|
+
flex: 1;
|
|
178
|
+
overflow-y: auto;
|
|
179
|
+
padding: 12px 16px;
|
|
180
|
+
display: flex;
|
|
181
|
+
flex-direction: column;
|
|
182
|
+
gap: 10px;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.memory-item {
|
|
186
|
+
background: rgba(255, 255, 255, 0.02);
|
|
187
|
+
border: 1px solid rgba(255, 255, 255, 0.04);
|
|
188
|
+
border-radius: 10px;
|
|
189
|
+
padding: 12px;
|
|
190
|
+
cursor: pointer;
|
|
191
|
+
transition: all 0.2s ease;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.memory-item:hover {
|
|
195
|
+
background: rgba(255, 255, 255, 0.05);
|
|
196
|
+
border-color: #89b4fa;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.memory-title {
|
|
200
|
+
font-size: 12px;
|
|
201
|
+
font-weight: 600;
|
|
202
|
+
color: #89b4fa;
|
|
203
|
+
margin-bottom: 4px;
|
|
204
|
+
line-height: 1.4;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.memory-excerpt {
|
|
208
|
+
font-size: 11px;
|
|
209
|
+
color: #bac2de;
|
|
210
|
+
line-height: 1.45;
|
|
211
|
+
display: -webkit-box;
|
|
212
|
+
-webkit-line-clamp: 3;
|
|
213
|
+
-webkit-box-orient: vertical;
|
|
214
|
+
overflow: hidden;
|
|
215
|
+
word-break: break-word;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.memory-excerpt.expanded {
|
|
219
|
+
display: block;
|
|
220
|
+
-webkit-line-clamp: unset;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.memory-meta {
|
|
224
|
+
display: flex;
|
|
225
|
+
justify-content: space-between;
|
|
226
|
+
font-size: 9px;
|
|
227
|
+
color: #6c7086;
|
|
228
|
+
margin-top: 8px;
|
|
229
|
+
border-top: 1px solid rgba(255, 255, 255, 0.03);
|
|
230
|
+
padding-top: 6px;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.card-actions {
|
|
234
|
+
display: flex;
|
|
235
|
+
gap: 8px;
|
|
236
|
+
padding: 12px 16px;
|
|
237
|
+
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
|
238
|
+
background: rgba(0, 0, 0, 0.2);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.action-btn {
|
|
242
|
+
flex: 1;
|
|
243
|
+
padding: 8px 12px;
|
|
244
|
+
border: 1px solid rgba(255, 255, 255, 0.05);
|
|
245
|
+
border-radius: 8px;
|
|
246
|
+
background: rgba(255, 255, 255, 0.04);
|
|
247
|
+
color: #cdd6f4;
|
|
248
|
+
font-size: 11px;
|
|
249
|
+
font-weight: 600;
|
|
250
|
+
cursor: pointer;
|
|
251
|
+
display: flex;
|
|
252
|
+
align-items: center;
|
|
253
|
+
justify-content: center;
|
|
254
|
+
gap: 4px;
|
|
255
|
+
transition: all 0.2s ease;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.action-btn:hover {
|
|
259
|
+
background: #89b4fa;
|
|
260
|
+
color: #11111b;
|
|
261
|
+
border-color: #89b4fa;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
.action-btn:disabled {
|
|
265
|
+
opacity: 0.5;
|
|
266
|
+
cursor: not-allowed;
|
|
267
|
+
background: rgba(255, 255, 255, 0.02) !important;
|
|
268
|
+
color: #6c7086 !important;
|
|
269
|
+
border-color: transparent !important;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.toast {
|
|
273
|
+
position: absolute;
|
|
274
|
+
bottom: 60px;
|
|
275
|
+
left: 50%;
|
|
276
|
+
transform: translateX(-50%);
|
|
277
|
+
background: #2ec471;
|
|
278
|
+
color: #11111b;
|
|
279
|
+
padding: 8px 16px;
|
|
280
|
+
border-radius: 8px;
|
|
281
|
+
font-size: 12px;
|
|
282
|
+
font-weight: 700;
|
|
283
|
+
z-index: 2147483647;
|
|
284
|
+
opacity: 0;
|
|
285
|
+
box-shadow: 0 4px 12px rgba(46, 196, 113, 0.3);
|
|
286
|
+
transition: opacity 0.3s ease, transform 0.3s ease;
|
|
287
|
+
pointer-events: none;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.toast.visible {
|
|
291
|
+
opacity: 1;
|
|
292
|
+
transform: translateX(-50%) translateY(-5px);
|
|
293
|
+
}
|
|
294
|
+
|
|
90
295
|
@keyframes pill-enter {
|
|
91
296
|
to {
|
|
92
297
|
opacity: 1;
|
|
@@ -95,20 +300,157 @@
|
|
|
95
300
|
}
|
|
96
301
|
`;
|
|
97
302
|
|
|
303
|
+
const container = document.createElement('div');
|
|
304
|
+
container.className = 'container';
|
|
305
|
+
|
|
306
|
+
// The Floating Pill
|
|
98
307
|
const pill = document.createElement('div');
|
|
99
308
|
pill.className = 'pill';
|
|
100
309
|
pill.innerHTML = `
|
|
101
310
|
<span class="brain-icon">🧠</span>
|
|
102
|
-
<span
|
|
311
|
+
<span>Related Memories</span>
|
|
103
312
|
<span class="count">${count}</span>
|
|
104
313
|
`;
|
|
105
314
|
|
|
106
|
-
|
|
107
|
-
|
|
315
|
+
// The Overlay Card
|
|
316
|
+
const card = document.createElement('div');
|
|
317
|
+
card.className = 'card';
|
|
318
|
+
|
|
319
|
+
// Header
|
|
320
|
+
const cardHeader = document.createElement('div');
|
|
321
|
+
cardHeader.className = 'card-header';
|
|
322
|
+
cardHeader.innerHTML = `
|
|
323
|
+
<h3>🧠 Related Memories</h3>
|
|
324
|
+
<button class="close-btn" title="Close Panel">×</button>
|
|
325
|
+
`;
|
|
326
|
+
card.appendChild(cardHeader);
|
|
327
|
+
|
|
328
|
+
// List of Memories
|
|
329
|
+
const memoryList = document.createElement('div');
|
|
330
|
+
memoryList.className = 'memory-list';
|
|
331
|
+
|
|
332
|
+
currentMemories.forEach(mem => {
|
|
333
|
+
const item = document.createElement('div');
|
|
334
|
+
item.className = 'memory-item';
|
|
335
|
+
|
|
336
|
+
const relevancePercent = mem.confidence ? Math.round(mem.confidence * 100) : null;
|
|
337
|
+
const relevanceStr = relevancePercent ? `${relevancePercent}% match` : 'Related';
|
|
338
|
+
|
|
339
|
+
item.innerHTML = `
|
|
340
|
+
<div class="memory-title">${mem.title}</div>
|
|
341
|
+
<div class="memory-excerpt">${mem.content}</div>
|
|
342
|
+
<div class="memory-meta">
|
|
343
|
+
<span style="text-transform: capitalize; font-weight: 600;">${mem.category}</span>
|
|
344
|
+
<span>${relevanceStr}</span>
|
|
345
|
+
</div>
|
|
346
|
+
`;
|
|
347
|
+
|
|
348
|
+
// Click to toggle full content expansion
|
|
349
|
+
item.addEventListener('click', () => {
|
|
350
|
+
const excerpt = item.querySelector('.memory-excerpt');
|
|
351
|
+
excerpt.classList.toggle('expanded');
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
memoryList.appendChild(item);
|
|
108
355
|
});
|
|
356
|
+
card.appendChild(memoryList);
|
|
357
|
+
|
|
358
|
+
// Quick Actions
|
|
359
|
+
const cardActions = document.createElement('div');
|
|
360
|
+
cardActions.className = 'card-actions';
|
|
361
|
+
|
|
362
|
+
const rememberBtn = document.createElement('button');
|
|
363
|
+
rememberBtn.className = 'action-btn';
|
|
364
|
+
rememberBtn.innerHTML = '📌 Remember Page';
|
|
365
|
+
|
|
366
|
+
const researchBtn = document.createElement('button');
|
|
367
|
+
researchBtn.className = 'action-btn';
|
|
368
|
+
researchBtn.innerHTML = '🔬 Research Page';
|
|
369
|
+
|
|
370
|
+
cardActions.appendChild(rememberBtn);
|
|
371
|
+
cardActions.appendChild(researchBtn);
|
|
372
|
+
card.appendChild(cardActions);
|
|
373
|
+
|
|
374
|
+
// Tiny Toast message indicator
|
|
375
|
+
const toast = document.createElement('div');
|
|
376
|
+
toast.className = 'toast';
|
|
377
|
+
container.appendChild(toast);
|
|
378
|
+
|
|
379
|
+
// Action button listeners
|
|
380
|
+
const triggerAction = async (action, btn) => {
|
|
381
|
+
btn.disabled = true;
|
|
382
|
+
const originalText = btn.innerHTML;
|
|
383
|
+
btn.innerHTML = '⏳ Processing...';
|
|
384
|
+
|
|
385
|
+
chrome.runtime.sendMessage({
|
|
386
|
+
type: 'SHARE',
|
|
387
|
+
data: {
|
|
388
|
+
url: pageContext.url,
|
|
389
|
+
title: pageContext.title,
|
|
390
|
+
action: action,
|
|
391
|
+
source: 'chrome-extension-overlay'
|
|
392
|
+
}
|
|
393
|
+
}, (res) => {
|
|
394
|
+
btn.disabled = false;
|
|
395
|
+
btn.innerHTML = originalText;
|
|
396
|
+
if (res && res.success) {
|
|
397
|
+
toast.textContent = action === 'remember' ? '📌 Remembered page!' : '🔬 Research queued!';
|
|
398
|
+
toast.classList.add('visible');
|
|
399
|
+
setTimeout(() => toast.classList.remove('visible'), 2500);
|
|
400
|
+
} else {
|
|
401
|
+
toast.textContent = '❌ Action failed';
|
|
402
|
+
toast.style.background = '#e74c3c';
|
|
403
|
+
toast.style.boxShadow = '0 4px 12px rgba(231, 76, 60, 0.3)';
|
|
404
|
+
toast.classList.add('visible');
|
|
405
|
+
setTimeout(() => {
|
|
406
|
+
toast.classList.remove('visible');
|
|
407
|
+
toast.style.background = '#2ec471';
|
|
408
|
+
toast.style.boxShadow = '0 4px 12px rgba(46, 196, 113, 0.3)';
|
|
409
|
+
}, 2500);
|
|
410
|
+
}
|
|
411
|
+
});
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
rememberBtn.addEventListener('click', (e) => {
|
|
415
|
+
e.stopPropagation();
|
|
416
|
+
triggerAction('remember', rememberBtn);
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
researchBtn.addEventListener('click', (e) => {
|
|
420
|
+
e.stopPropagation();
|
|
421
|
+
triggerAction('research', researchBtn);
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
// Toggle overlay visibility on pill click
|
|
425
|
+
const toggleOverlay = (e) => {
|
|
426
|
+
e.stopPropagation();
|
|
427
|
+
const isVisible = card.classList.contains('visible');
|
|
428
|
+
if (isVisible) {
|
|
429
|
+
card.classList.remove('visible');
|
|
430
|
+
pill.classList.remove('active');
|
|
431
|
+
} else {
|
|
432
|
+
card.classList.add('visible');
|
|
433
|
+
pill.classList.add('active');
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
pill.addEventListener('click', toggleOverlay);
|
|
438
|
+
cardHeader.querySelector('.close-btn').addEventListener('click', toggleOverlay);
|
|
439
|
+
|
|
440
|
+
// Prevent clicks inside card from closing it
|
|
441
|
+
card.addEventListener('click', (e) => e.stopPropagation());
|
|
442
|
+
|
|
443
|
+
// Close overlay if clicking outside the host
|
|
444
|
+
document.addEventListener('click', () => {
|
|
445
|
+
card.classList.remove('visible');
|
|
446
|
+
pill.classList.remove('active');
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
container.appendChild(card);
|
|
450
|
+
container.appendChild(pill);
|
|
109
451
|
|
|
110
452
|
shadow.appendChild(style);
|
|
111
|
-
shadow.appendChild(
|
|
453
|
+
shadow.appendChild(container);
|
|
112
454
|
document.body.appendChild(pillHost);
|
|
113
455
|
}
|
|
114
456
|
|
|
@@ -140,6 +482,7 @@
|
|
|
140
482
|
if (chrome.runtime.lastError) return;
|
|
141
483
|
const memories = response?.memories || [];
|
|
142
484
|
if (memories.length > 0) {
|
|
485
|
+
currentMemories = memories;
|
|
143
486
|
createPill(memories.length);
|
|
144
487
|
}
|
|
145
488
|
}
|
|
@@ -36,6 +36,11 @@
|
|
|
36
36
|
</div>
|
|
37
37
|
|
|
38
38
|
<div id="status-msg" class="status-msg hidden"></div>
|
|
39
|
+
|
|
40
|
+
<footer class="popup-footer" style="margin-top: 16px; padding-top: 8px; border-top: 1px solid rgba(255, 255, 255, 0.05); display: flex; justify-content: space-between; font-size: 10px; color: #6c7086;">
|
|
41
|
+
<span>Total Recall Brain Connection</span>
|
|
42
|
+
<span id="version-label">v0.1.0</span>
|
|
43
|
+
</footer>
|
|
39
44
|
</div>
|
|
40
45
|
|
|
41
46
|
<script src="../lib/preconfigured.js"></script>
|
package/extension/popup/popup.js
CHANGED
|
@@ -26,6 +26,25 @@
|
|
|
26
26
|
try {
|
|
27
27
|
await self.BrainClient.healthCheck();
|
|
28
28
|
dot.className = 'dot connected';
|
|
29
|
+
|
|
30
|
+
// Check for extension updates
|
|
31
|
+
try {
|
|
32
|
+
const status = await self.BrainClient.brainFetch('/api/extension/status');
|
|
33
|
+
const clientVersion = chrome.runtime.getManifest().version;
|
|
34
|
+
if (status && status.version && status.version !== clientVersion) {
|
|
35
|
+
const config = await self.BrainClient.getConfig();
|
|
36
|
+
const brainUrl = config.brainUrl || 'http://127.0.0.1:3000';
|
|
37
|
+
const footer = document.querySelector('.popup-footer');
|
|
38
|
+
if (footer) {
|
|
39
|
+
footer.innerHTML = `
|
|
40
|
+
<span style="color: #f38ba8; font-weight: bold; display: flex; align-items: center; gap: 4px;">⚠️ Update Available (v${status.version})</span>
|
|
41
|
+
<a href="${brainUrl}/health" target="_blank" style="color: #89b4fa; text-decoration: none; font-weight: bold;">Download ZIP</a>
|
|
42
|
+
`;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
} catch (err) {
|
|
46
|
+
console.error('Failed to check extension update status:', err);
|
|
47
|
+
}
|
|
29
48
|
} catch {
|
|
30
49
|
dot.className = 'dot disconnected';
|
|
31
50
|
}
|
|
@@ -147,4 +166,10 @@
|
|
|
147
166
|
fillCurrentTab();
|
|
148
167
|
checkConnection();
|
|
149
168
|
loadTrackingPref();
|
|
169
|
+
|
|
170
|
+
// Populate version label
|
|
171
|
+
const versionLabel = document.getElementById('version-label');
|
|
172
|
+
if (versionLabel) {
|
|
173
|
+
versionLabel.textContent = 'v' + chrome.runtime.getManifest().version;
|
|
174
|
+
}
|
|
150
175
|
})();
|
package/frontend/src/App.tsx
CHANGED
|
@@ -18,6 +18,9 @@ import AutomationsPage from './pages/AutomationsPage'
|
|
|
18
18
|
import DeploymentsPage from './pages/DeploymentsPage'
|
|
19
19
|
import SkillsPage from './pages/SkillsPage'
|
|
20
20
|
import UsagePage from './pages/UsagePage'
|
|
21
|
+
import CollabPage from './pages/CollabPage'
|
|
22
|
+
import HelpPage from './pages/HelpPage'
|
|
23
|
+
import GraphPage from './pages/GraphPage'
|
|
21
24
|
|
|
22
25
|
// ─── Auth state type ──────────────────────────────────────────────────────────
|
|
23
26
|
type AuthState = 'loading' | 'authed' | 'unauthed'
|
|
@@ -143,6 +146,29 @@ function Sidebar({ onLogout, health, activeBrainId, onBrainChange }: SidebarProp
|
|
|
143
146
|
</svg>
|
|
144
147
|
Skills Manager
|
|
145
148
|
</NavLink>
|
|
149
|
+
<NavLink to="/collab" className={({ isActive }) => `nav-link ${isActive ? 'active' : ''}`} id="nav-collab">
|
|
150
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
|
|
151
|
+
<path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2" />
|
|
152
|
+
<circle cx="9" cy="7" r="4" />
|
|
153
|
+
<path d="M23 21v-2a4 4 0 00-3-3.87" />
|
|
154
|
+
<path d="M16 3.13a4 4 0 010 7.75" />
|
|
155
|
+
</svg>
|
|
156
|
+
Collaboration
|
|
157
|
+
</NavLink>
|
|
158
|
+
<NavLink to="/help" className={({ isActive }) => `nav-link ${isActive ? 'active' : ''}`} id="nav-help">
|
|
159
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
|
|
160
|
+
<circle cx="12" cy="12" r="10" />
|
|
161
|
+
<path d="M9.09 9a3 3 0 015.83 1c0 2-3 3-3 3" />
|
|
162
|
+
<line x1="12" y1="17" x2="12.01" y2="17" />
|
|
163
|
+
</svg>
|
|
164
|
+
Documentation
|
|
165
|
+
</NavLink>
|
|
166
|
+
<NavLink to="/graph" className={({ isActive }) => `nav-link ${isActive ? 'active' : ''}`} id="nav-graph">
|
|
167
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
|
|
168
|
+
<polygon points="12 2 2 22 22 22" />
|
|
169
|
+
</svg>
|
|
170
|
+
Sovereign Graph
|
|
171
|
+
</NavLink>
|
|
146
172
|
</nav>
|
|
147
173
|
<div className="sidebar-footer">
|
|
148
174
|
<div style={{ marginBottom: 8 }}>
|
|
@@ -293,6 +319,9 @@ function MainContent({ activeBrainId, onBrainChange }: { activeBrainId: string;
|
|
|
293
319
|
<Route path="/keys" element={<ApiKeysPage />} />
|
|
294
320
|
<Route path="/integrations" element={<IntegrationsPage />} />
|
|
295
321
|
<Route path="/skills" element={<SkillsPage />} />
|
|
322
|
+
<Route path="/collab" element={<CollabPage />} />
|
|
323
|
+
<Route path="/help" element={<HelpPage />} />
|
|
324
|
+
<Route path="/graph" element={<GraphPage activeBrainId={activeBrainId} />} />
|
|
296
325
|
</Routes>
|
|
297
326
|
</div>
|
|
298
327
|
)}
|
package/frontend/src/api.ts
CHANGED
|
@@ -685,3 +685,43 @@ export async function fetchExtensionStatus(): Promise<{ available: boolean; conn
|
|
|
685
685
|
if (!res.ok) return { available: false, connected: false }
|
|
686
686
|
return res.json()
|
|
687
687
|
}
|
|
688
|
+
|
|
689
|
+
// ─── Auto-Update APIs ──────────────────────────────────────────────────────────
|
|
690
|
+
|
|
691
|
+
export interface UpdateCheckResult {
|
|
692
|
+
currentVersion: string
|
|
693
|
+
latestVersion: string
|
|
694
|
+
updateAvailable: boolean
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
export async function checkUpdate(): Promise<UpdateCheckResult> {
|
|
698
|
+
const res = await apiFetch(`${API_BASE}/api/update/check`)
|
|
699
|
+
if (!res.ok) throw new Error(`Check update API error: ${res.status}`)
|
|
700
|
+
return res.json()
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
export async function runUpdate(): Promise<{ success: boolean; message: string }> {
|
|
704
|
+
const res = await apiFetch(`${API_BASE}/api/update/run`, { method: 'POST' })
|
|
705
|
+
if (!res.ok) throw new Error(`Run update API error: ${res.status}`)
|
|
706
|
+
return res.json()
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// ─── Help API ──────────────────────────────────────────────────────────────────
|
|
710
|
+
|
|
711
|
+
export interface HelpTopic {
|
|
712
|
+
id: string
|
|
713
|
+
title: string
|
|
714
|
+
description: string
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
export async function fetchHelpTopics(): Promise<{ topics: HelpTopic[] }> {
|
|
718
|
+
const res = await apiFetch(`${API_BASE}/api/help`)
|
|
719
|
+
if (!res.ok) throw new Error('Failed to load help topics')
|
|
720
|
+
return res.json()
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
export async function fetchHelpContent(topicId: string): Promise<{ topic: string; content: string }> {
|
|
724
|
+
const res = await apiFetch(`${API_BASE}/api/help?topic=${encodeURIComponent(topicId)}`)
|
|
725
|
+
if (!res.ok) throw new Error('Failed to load help content')
|
|
726
|
+
return res.json()
|
|
727
|
+
}
|