total-recall-brain 3.13.2 → 3.14.3
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/total-recall.mjs +3 -0
- package/frontend/src/App.tsx +8 -7
- package/frontend/src/api.ts +25 -0
- package/frontend/src/components/UsageChart.tsx +1 -1
- package/frontend/src/pages/ApiKeysPage.tsx +118 -42
- package/frontend/src/pages/ChatPage.tsx +1 -1
- package/frontend/src/pages/DesignDocsPage.tsx +28 -21
- package/frontend/src/pages/FilesPage.tsx +6 -1
- package/frontend/src/pages/GraphPage.tsx +5 -1
- package/frontend/src/pages/HealthPage.tsx +1 -1
- package/frontend/src/pages/InboxPage.tsx +2 -1
- package/frontend/src/pages/InstructionsPage.tsx +2 -2
- package/frontend/src/pages/IntegrationsPage.tsx +11 -5
- package/frontend/src/pages/MemoryPage.tsx +27 -118
- package/frontend/src/pages/ModelsPage.tsx +2 -50
- package/frontend/src/pages/OnboardingPage.tsx +5 -10
- package/frontend/src/pages/OpenWikiPage.tsx +1 -0
- package/frontend/src/pages/SettingsPage.tsx +136 -131
- package/frontend/src/pages/SkillsPage.tsx +102 -46
- package/frontend/src/pages/TasksPage.tsx +60 -25
- package/frontend/src/pages/UsagePage.tsx +206 -95
- package/frontend/src/types.ts +17 -11
- package/frontend/src/utils/onboarding.ts +10 -0
- package/package.json +3 -2
- package/scaffold/.agent/skills/ssss/SKILL.md +235 -0
- package/scaffold/.agent/skills/ssss/evals/evals.json +37 -0
- package/scaffold/.agent/skills/ssss/references/contract-reference.md +95 -0
- package/scaffold/.agent/skills/ssss/scripts/validate-schema.mjs +215 -0
- package/scaffold/.agent/skills/ssss/subagents/audit-ssss-contract.md +22 -0
- package/scaffold/.agent/skills/total-recall/SKILL.md +1 -0
- package/src/cli/backup.mjs +3 -3
- package/src/cli/chat.mjs +2 -2
- package/src/cli/config.mjs +2 -9
- package/src/cli/connect.mjs +1 -1
- package/src/cli/deploy-ui.mjs +1 -1
- package/src/cli/deploy.mjs +2 -2
- package/src/cli/doctor.mjs +1 -1
- package/src/cli/help.mjs +1 -1
- package/src/cli/init.mjs +19 -1
- package/src/cli/key.mjs +150 -0
- package/src/cli/secret.mjs +30 -1
- package/src/cli/skill.mjs +0 -2
- package/src/cli/sync.mjs +19 -0
- package/src/cli/upgrade.mjs +1 -1
- package/src/cli/wizard.html +5 -5
- package/src/core/crons.mjs +11 -0
- package/src/core/daemon-loop.mjs +11 -0
- package/src/core/import-rules.mjs +4 -3
- package/src/core/okf-adapter.mjs +10 -10
- package/src/core/runtime.mjs +0 -2
- package/src/core/secrets-store.mjs +16 -0
- package/src/core/skills-registry.mjs +38 -0
- package/src/core/surface.mjs +1 -1
- package/src/core/task_runner.mjs +6 -0
- package/src/core/usage-tracker.mjs +16 -3
- package/src/core/vault-watcher.mjs +92 -0
- package/src/core/vault.mjs +0 -2
- package/src/server/api.mjs +1 -1
- package/src/server/rest.mjs +69 -57
- package/src/server/route-manifest.json +8 -0
- package/src/server/routes/_shared.mjs +1 -1
- package/src/server/routes/secrets.mjs +4 -4
- package/src/server/routes/skills.mjs +92 -10
|
@@ -1,20 +1,29 @@
|
|
|
1
1
|
import React, { useState, useEffect } from 'react';
|
|
2
|
-
import { fetchConfigJson, saveConfigJson, runSandbox, fetchHealth, runAgentDiagnostics } from '../api';
|
|
2
|
+
import { fetchConfigJson, saveConfigJson, runSandbox, fetchHealth, runAgentDiagnostics, checkUpdate, runUpdate, fetchBrains } from '../api';
|
|
3
3
|
import type { HealthData } from '../types';
|
|
4
4
|
import type { ConfigJson } from '../types';
|
|
5
5
|
|
|
6
|
-
export default function SettingsPage() {
|
|
6
|
+
export default function SettingsPage({ activeBrainId }: { activeBrainId?: string }) {
|
|
7
7
|
const [configData, setConfigData] = useState<ConfigJson | null>(null);
|
|
8
8
|
const [saveStatus, setSaveStatus] = useState<'idle' | 'saving' | 'saved' | 'error'>('idle');
|
|
9
9
|
const [sandboxLog, setSandboxLog] = useState('');
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
|
|
11
12
|
const [health, setHealth] = useState<HealthData | null>(null);
|
|
12
13
|
const [runningDiagnostics, setRunningDiagnostics] = useState(false);
|
|
13
14
|
const [diagnosticLogs, setDiagnosticLogs] = useState<string | null>(null);
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
const [updateInfo, setUpdateInfo] = useState<{ updateAvailable: boolean, latestVersion?: string } | null>(null);
|
|
17
|
+
const [updating, setUpdating] = useState(false);
|
|
18
|
+
const [updateMessage, setUpdateMessage] = useState<string | null>(null);
|
|
19
|
+
|
|
20
|
+
const [brains, setBrains] = useState<unknown[]>([]);
|
|
21
|
+
const activeBrain = activeBrainId || localStorage.getItem('total-recall-active-brain') || '';
|
|
22
|
+
|
|
16
23
|
useEffect(() => {
|
|
17
24
|
fetchHealth().then(setHealth).catch(console.error);
|
|
25
|
+
checkUpdate().then(setUpdateInfo).catch(console.error);
|
|
26
|
+
fetchBrains().then(setBrains).catch(console.error);
|
|
18
27
|
fetchConfigJson()
|
|
19
28
|
.then(data => {
|
|
20
29
|
// Ensure all nested structures exist for controlled inputs
|
|
@@ -33,14 +42,15 @@ export default function SettingsPage() {
|
|
|
33
42
|
setConfigData(data);
|
|
34
43
|
})
|
|
35
44
|
.catch(console.error);
|
|
36
|
-
}, []);
|
|
45
|
+
}, [activeBrainId]);
|
|
37
46
|
|
|
38
47
|
|
|
39
48
|
const AGENTS_LIST = [
|
|
40
49
|
{ id: 'antigravity', name: 'Antigravity (Gemini SDK)', desc: 'Primary core developer agent' },
|
|
41
50
|
{ id: 'gemini', name: 'Gemini CLI', desc: 'Direct Gemini assistant binary' },
|
|
42
51
|
{ id: 'claude', name: 'Claude Code', desc: 'Anthropic developer CLI wrapper' },
|
|
43
|
-
{ id: 'codex', name: 'Codex CLI', desc: 'OpenAI agent binary integration' }
|
|
52
|
+
{ id: 'codex', name: 'Codex CLI', desc: 'OpenAI agent binary integration' },
|
|
53
|
+
{ id: 'grok', name: 'Grok CLI', desc: 'xAI developer binary integration' }
|
|
44
54
|
];
|
|
45
55
|
|
|
46
56
|
const handleRunDiagnostics = async (e: React.FormEvent) => {
|
|
@@ -51,13 +61,26 @@ export default function SettingsPage() {
|
|
|
51
61
|
const res = await runAgentDiagnostics();
|
|
52
62
|
setDiagnosticLogs(res.output);
|
|
53
63
|
fetchHealth().then(setHealth).catch(console.error);
|
|
54
|
-
} catch (err:
|
|
55
|
-
setDiagnosticLogs('Error: ' + err.message);
|
|
64
|
+
} catch (err: unknown) {
|
|
65
|
+
setDiagnosticLogs('Error: ' + (err as Error).message);
|
|
56
66
|
} finally {
|
|
57
67
|
setRunningDiagnostics(false);
|
|
58
68
|
}
|
|
59
69
|
};
|
|
60
70
|
|
|
71
|
+
const handleRunUpdate = async () => {
|
|
72
|
+
setUpdating(true);
|
|
73
|
+
setUpdateMessage(null);
|
|
74
|
+
try {
|
|
75
|
+
const res = await runUpdate();
|
|
76
|
+
setUpdateMessage(res.success ? 'Update complete. Restarting...' : 'Update failed: ' + res.message);
|
|
77
|
+
} catch (err: unknown) {
|
|
78
|
+
setUpdateMessage('Update error: ' + (err as Error).message);
|
|
79
|
+
} finally {
|
|
80
|
+
setUpdating(false);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
61
84
|
const handleSaveVisual = async () => {
|
|
62
85
|
if (!configData) return;
|
|
63
86
|
setSaveStatus('saving');
|
|
@@ -87,11 +110,9 @@ export default function SettingsPage() {
|
|
|
87
110
|
};
|
|
88
111
|
|
|
89
112
|
// State Update Helpers
|
|
90
|
-
const updateSecurityProp = (key: string, value: any) => {
|
|
91
|
-
setConfigData(prev => prev ? { ...prev, security: { ...prev.security, [key]: value } } : null);
|
|
92
|
-
};
|
|
93
113
|
|
|
94
|
-
|
|
114
|
+
|
|
115
|
+
const updateSecurityNested = (category: string, key: string, value: unknown) => {
|
|
95
116
|
setConfigData(prev => {
|
|
96
117
|
if (!prev) return prev;
|
|
97
118
|
return {
|
|
@@ -99,7 +120,7 @@ export default function SettingsPage() {
|
|
|
99
120
|
security: {
|
|
100
121
|
...prev.security,
|
|
101
122
|
[category]: {
|
|
102
|
-
...((prev.security as
|
|
123
|
+
...((prev.security as Record<string, unknown>)[category] as Record<string, unknown> || {}),
|
|
103
124
|
[key]: value
|
|
104
125
|
}
|
|
105
126
|
}
|
|
@@ -107,7 +128,7 @@ export default function SettingsPage() {
|
|
|
107
128
|
});
|
|
108
129
|
};
|
|
109
130
|
|
|
110
|
-
const updateBudgetProp = (key: string, value:
|
|
131
|
+
const updateBudgetProp = (key: string, value: unknown) => {
|
|
111
132
|
setConfigData(prev => {
|
|
112
133
|
if (!prev) return prev;
|
|
113
134
|
return {
|
|
@@ -123,13 +144,16 @@ export default function SettingsPage() {
|
|
|
123
144
|
});
|
|
124
145
|
};
|
|
125
146
|
|
|
126
|
-
const updateBrainProp = (key: string, value:
|
|
147
|
+
const updateBrainProp = (key: string, value: unknown) => {
|
|
127
148
|
setConfigData(prev => prev ? { ...prev, brain: { ...prev.brain, [key]: value } } : null);
|
|
128
149
|
};
|
|
129
150
|
|
|
130
151
|
|
|
131
|
-
|
|
132
|
-
|
|
152
|
+
|
|
153
|
+
// Instead of parsing it back and forth on every keystroke,
|
|
154
|
+
// we just use simple helpers that don't aggressively trim trailing commas.
|
|
155
|
+
const getCsv = (arr: unknown) => Array.isArray(arr) ? arr.join(', ') : '';
|
|
156
|
+
const setCsv = (str: string) => str.split(',').map(s => s.trimStart());
|
|
133
157
|
|
|
134
158
|
if (!configData) {
|
|
135
159
|
return (
|
|
@@ -362,7 +386,7 @@ export default function SettingsPage() {
|
|
|
362
386
|
System Settings
|
|
363
387
|
</h1>
|
|
364
388
|
<p style={{ margin: '4px 0 0', color: 'var(--text-secondary)', fontSize: 14 }}>
|
|
365
|
-
Configure core
|
|
389
|
+
Configure core system behavior, security, network bindings, and integration layers.
|
|
366
390
|
</p>
|
|
367
391
|
</div>
|
|
368
392
|
<div style={{ display: 'flex', gap: 12, alignItems: 'center' }}>
|
|
@@ -509,23 +533,10 @@ export default function SettingsPage() {
|
|
|
509
533
|
<div className="settings-icon-wrap" style={{ background: 'linear-gradient(135deg, rgba(16, 185, 129, 0.2), rgba(5, 150, 105, 0.05))', borderColor: 'rgba(16, 185, 129, 0.2)' }}>🛡️</div>
|
|
510
534
|
<div>
|
|
511
535
|
<h3 style={{ fontSize: 16, fontWeight: 600, margin: 0 }}>Security & Privacy</h3>
|
|
512
|
-
<p style={{ fontSize: 12, color: 'var(--text-tertiary)', margin: '4px 0 0' }}>
|
|
536
|
+
<p style={{ fontSize: 12, color: 'var(--text-tertiary)', margin: '4px 0 0' }}>Manage access control and local sandboxing</p>
|
|
513
537
|
</div>
|
|
514
538
|
</div>
|
|
515
539
|
|
|
516
|
-
<div className="field-row">
|
|
517
|
-
<div>
|
|
518
|
-
<div style={{ fontSize: 14, fontWeight: 500 }}>YOLO Mode</div>
|
|
519
|
-
<div style={{ fontSize: 12, color: 'var(--text-tertiary)' }}>Disable write protection for AI agents</div>
|
|
520
|
-
</div>
|
|
521
|
-
<input
|
|
522
|
-
type="checkbox"
|
|
523
|
-
className="settings-checkbox"
|
|
524
|
-
checked={!!configData.security.yolo_mode}
|
|
525
|
-
onChange={(e) => updateSecurityProp('yolo_mode', e.target.checked)}
|
|
526
|
-
/>
|
|
527
|
-
</div>
|
|
528
|
-
|
|
529
540
|
<div className="field-row">
|
|
530
541
|
<div>
|
|
531
542
|
<div style={{ fontSize: 14, fontWeight: 500 }}>Sandbox Enabled</div>
|
|
@@ -534,24 +545,11 @@ export default function SettingsPage() {
|
|
|
534
545
|
<input
|
|
535
546
|
type="checkbox"
|
|
536
547
|
className="settings-checkbox"
|
|
537
|
-
checked={!!
|
|
548
|
+
checked={!!configData.security.sandbox?.enabled}
|
|
538
549
|
onChange={(e) => updateSecurityNested('sandbox', 'enabled', e.target.checked)}
|
|
539
550
|
/>
|
|
540
551
|
</div>
|
|
541
552
|
|
|
542
|
-
<div className="field-row">
|
|
543
|
-
<div>
|
|
544
|
-
<div style={{ fontSize: 14, fontWeight: 500 }}>Enforce Local Only</div>
|
|
545
|
-
<div style={{ fontSize: 12, color: 'var(--text-tertiary)' }}>Force agents to run offline models</div>
|
|
546
|
-
</div>
|
|
547
|
-
<input
|
|
548
|
-
type="checkbox"
|
|
549
|
-
className="settings-checkbox"
|
|
550
|
-
checked={!!configData.security.privacy?.enforce_local_only}
|
|
551
|
-
onChange={(e) => updateSecurityNested('privacy', 'enforce_local_only', e.target.checked)}
|
|
552
|
-
/>
|
|
553
|
-
</div>
|
|
554
|
-
|
|
555
553
|
<div className="field-row">
|
|
556
554
|
<div>
|
|
557
555
|
<div style={{ fontSize: 14, fontWeight: 500 }}>Force Password Reset</div>
|
|
@@ -573,24 +571,12 @@ export default function SettingsPage() {
|
|
|
573
571
|
<input
|
|
574
572
|
type="checkbox"
|
|
575
573
|
className="settings-checkbox"
|
|
576
|
-
checked={!!
|
|
574
|
+
checked={!!configData.security.api?.allow_static_pats}
|
|
577
575
|
onChange={(e) => updateSecurityNested('api', 'allow_static_pats', e.target.checked)}
|
|
578
576
|
/>
|
|
579
577
|
</div>
|
|
580
578
|
|
|
581
|
-
|
|
582
|
-
<label htmlFor="allow_frontier_export" className="field-label">Allow Frontier Export</label>
|
|
583
|
-
<select
|
|
584
|
-
id="allow_frontier_export"
|
|
585
|
-
className="settings-select"
|
|
586
|
-
value={configData.security.privacy?.allow_frontier_export || 'ask_per_skill'}
|
|
587
|
-
onChange={(e) => updateSecurityNested('privacy', 'allow_frontier_export', e.target.value)}
|
|
588
|
-
>
|
|
589
|
-
<option value="always">Always (Not Recommended)</option>
|
|
590
|
-
<option value="ask_per_skill">Ask Per Skill (Recommended)</option>
|
|
591
|
-
<option value="never">Never (Air-Gapped)</option>
|
|
592
|
-
</select>
|
|
593
|
-
</div>
|
|
579
|
+
|
|
594
580
|
|
|
595
581
|
<div className="field-col">
|
|
596
582
|
<label htmlFor="session_ttl_hours" className="field-label">Dashboard Session TTL (Hours)</label>
|
|
@@ -636,7 +622,7 @@ export default function SettingsPage() {
|
|
|
636
622
|
step="0.01"
|
|
637
623
|
className="settings-input"
|
|
638
624
|
value={configData.budget.budget?.daily_cap_usd ?? 5.0}
|
|
639
|
-
onChange={(e) => updateBudgetProp('
|
|
625
|
+
onChange={(e) => updateBudgetProp('monthly_soft_cap', e.target.value === '' ? null : Number(e.target.value))}
|
|
640
626
|
/>
|
|
641
627
|
</div>
|
|
642
628
|
<div className="field-col" style={{ flex: 1 }}>
|
|
@@ -653,24 +639,59 @@ export default function SettingsPage() {
|
|
|
653
639
|
</div>
|
|
654
640
|
</div>
|
|
655
641
|
|
|
656
|
-
{/* Brain
|
|
642
|
+
{/* Brain Configuration */}
|
|
657
643
|
<div className="settings-card">
|
|
658
644
|
<div style={{ display: 'flex', alignItems: 'center', gap: 16, borderBottom: '1px solid rgba(255,255,255,0.05)', paddingBottom: 20 }}>
|
|
659
645
|
<div className="settings-icon-wrap" style={{ background: 'linear-gradient(135deg, rgba(168, 85, 247, 0.2), rgba(147, 51, 234, 0.05))', borderColor: 'rgba(168, 85, 247, 0.2)' }}>🧠</div>
|
|
660
646
|
<div>
|
|
661
|
-
<h3 style={{ fontSize: 16, fontWeight: 600, margin: 0 }}>Brain
|
|
662
|
-
<p style={{ fontSize: 12, color: 'var(--text-tertiary)', margin: '4px 0 0' }}>
|
|
647
|
+
<h3 style={{ fontSize: 16, fontWeight: 600, margin: 0 }}>Brain Configuration</h3>
|
|
648
|
+
<p style={{ fontSize: 12, color: 'var(--text-tertiary)', margin: '4px 0 0' }}>Manage memory kernel identities</p>
|
|
663
649
|
</div>
|
|
664
650
|
</div>
|
|
665
651
|
|
|
666
652
|
<div className="field-col">
|
|
667
|
-
<label className="field-label">Brain
|
|
668
|
-
<
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
value={
|
|
672
|
-
onChange={(e) =>
|
|
673
|
-
|
|
653
|
+
<label className="field-label" style={{ color: 'var(--text-accent)', fontWeight: 600 }}>Select Active Brain</label>
|
|
654
|
+
<select
|
|
655
|
+
className="settings-select"
|
|
656
|
+
style={{ border: '1px solid var(--border-accent)', background: 'rgba(59, 130, 246, 0.05)' }}
|
|
657
|
+
value={activeBrain}
|
|
658
|
+
onChange={(e) => {
|
|
659
|
+
const val = e.target.value;
|
|
660
|
+
if (val) {
|
|
661
|
+
localStorage.setItem('total-recall-active-brain', val);
|
|
662
|
+
} else {
|
|
663
|
+
localStorage.removeItem('total-recall-active-brain');
|
|
664
|
+
}
|
|
665
|
+
window.location.reload();
|
|
666
|
+
}}
|
|
667
|
+
>
|
|
668
|
+
<option value="">Global Brain (Root)</option>
|
|
669
|
+
{brains.map((b: Record<string, unknown>) => (
|
|
670
|
+
<option key={b.name} value={b.name}>{b.name} ({b.role} - {b.nodes} nodes)</option>
|
|
671
|
+
))}
|
|
672
|
+
</select>
|
|
673
|
+
</div>
|
|
674
|
+
|
|
675
|
+
<div style={{ display: 'flex', gap: 16 }}>
|
|
676
|
+
<div className="field-col" style={{ flex: 1 }}>
|
|
677
|
+
<label className="field-label">Brain URL</label>
|
|
678
|
+
<input
|
|
679
|
+
type="text"
|
|
680
|
+
className="settings-input"
|
|
681
|
+
value={configData.brain?.url || ''}
|
|
682
|
+
onChange={(e) => updateBrainProp('url', e.target.value)}
|
|
683
|
+
/>
|
|
684
|
+
</div>
|
|
685
|
+
<div className="field-col" style={{ flex: 1 }}>
|
|
686
|
+
<label className="field-label">Brain Token</label>
|
|
687
|
+
<input
|
|
688
|
+
type="password"
|
|
689
|
+
className="settings-input"
|
|
690
|
+
placeholder={configData.brain?.has_token ? '•••••••••••••••• (Leave blank)' : 'No token set'}
|
|
691
|
+
value={configData.brain?.token || ''}
|
|
692
|
+
onChange={(e) => updateBrainProp('token', e.target.value)}
|
|
693
|
+
/>
|
|
694
|
+
</div>
|
|
674
695
|
</div>
|
|
675
696
|
|
|
676
697
|
<div className="field-col">
|
|
@@ -736,9 +757,11 @@ export default function SettingsPage() {
|
|
|
736
757
|
onChange={(e) => updateBrainProp('preferred_agent', e.target.value)}
|
|
737
758
|
>
|
|
738
759
|
<option value="auto">Auto-Select (Recommended)</option>
|
|
739
|
-
<option value="
|
|
760
|
+
<option value="antigravity">Antigravity (Google Deepmind)</option>
|
|
761
|
+
<option value="gemini">Gemini (Google)</option>
|
|
740
762
|
<option value="claude">Claude (Anthropic)</option>
|
|
741
763
|
<option value="openai">OpenAI (O-Series)</option>
|
|
764
|
+
<option value="grok">Grok (xAI)</option>
|
|
742
765
|
</select>
|
|
743
766
|
</div>
|
|
744
767
|
</div>
|
|
@@ -760,29 +783,22 @@ export default function SettingsPage() {
|
|
|
760
783
|
<p style={{ fontSize: 13, color: 'var(--text-tertiary)', margin: '0 0 16px', lineHeight: 1.5 }}>
|
|
761
784
|
Capture context directly from your browser. Install from the Chrome Web Store or load unpacked from <code>/extension</code> directory.
|
|
762
785
|
</p>
|
|
763
|
-
<
|
|
786
|
+
<a href="/api/extension/download" download className="btn-primary" style={{ display: 'block', textAlign: 'center', textDecoration: 'none', width: '100%', boxSizing: 'border-box' }}>Download Extension</a>
|
|
764
787
|
</div>
|
|
765
788
|
|
|
766
789
|
<div style={{ background: 'rgba(255,255,255,0.03)', padding: 20, borderRadius: 12, border: '1px solid rgba(255,255,255,0.06)' }}>
|
|
767
|
-
<div style={{ fontSize: 24, marginBottom: 12 }}
|
|
768
|
-
<h4 style={{ margin: '0 0 8px', fontSize: 15 }}>
|
|
790
|
+
<div style={{ fontSize: 24, marginBottom: 12 }}>🛠️</div>
|
|
791
|
+
<h4 style={{ margin: '0 0 8px', fontSize: 15 }}>Developer Utilities</h4>
|
|
769
792
|
<p style={{ fontSize: 13, color: 'var(--text-tertiary)', margin: '0 0 16px', lineHeight: 1.5 }}>
|
|
770
|
-
|
|
793
|
+
Run background maintenance tasks or re-index the local search tree.
|
|
771
794
|
</p>
|
|
772
795
|
<div style={{ display: 'flex', gap: 12 }}>
|
|
773
796
|
<button
|
|
774
797
|
className="btn-secondary"
|
|
775
798
|
style={{ flex: 1 }}
|
|
776
|
-
onClick={() => executeSandboxCommand('npx total-recall compile', '
|
|
799
|
+
onClick={() => executeSandboxCommand('npx total-recall compile', 'Rebuilding Search Indexes')}
|
|
777
800
|
>
|
|
778
|
-
|
|
779
|
-
</button>
|
|
780
|
-
<button
|
|
781
|
-
className="btn-primary"
|
|
782
|
-
style={{ flex: 1 }}
|
|
783
|
-
onClick={() => executeSandboxCommand('npx @ssss/cli export ./bundle.ucw', 'Exporting UCW Bundle')}
|
|
784
|
-
>
|
|
785
|
-
Export Bundle
|
|
801
|
+
Rebuild Index
|
|
786
802
|
</button>
|
|
787
803
|
</div>
|
|
788
804
|
</div>
|
|
@@ -795,55 +811,43 @@ export default function SettingsPage() {
|
|
|
795
811
|
)}
|
|
796
812
|
</div>
|
|
797
813
|
|
|
798
|
-
|
|
799
|
-
{
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
id="local_endpoint"
|
|
813
|
-
type="text"
|
|
814
|
-
placeholder="http://127.0.0.1:11434 or http://127.0.0.1:1234/v1"
|
|
815
|
-
value={configData?.brain?.local_endpoint || ''}
|
|
816
|
-
onChange={(e) => updateBrainProp('local_endpoint', e.target.value)}
|
|
817
|
-
style={{
|
|
818
|
-
background: 'var(--bg-tertiary)',
|
|
819
|
-
color: 'var(--text-primary)',
|
|
820
|
-
border: '1px solid var(--border)',
|
|
821
|
-
padding: '8px 12px',
|
|
822
|
-
borderRadius: 6,
|
|
823
|
-
outline: 'none',
|
|
824
|
-
fontSize: 13
|
|
825
|
-
}}
|
|
826
|
-
/>
|
|
827
|
-
<span style={{ fontSize: 10, color: 'var(--text-tertiary)' }}>Leave blank to use the default localhost endpoint.</span>
|
|
814
|
+
{/* System Updates */}
|
|
815
|
+
<div className="settings-card" style={{ gridColumn: '1 / -1' }}>
|
|
816
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 16, borderBottom: '1px solid rgba(255,255,255,0.05)', paddingBottom: 20 }}>
|
|
817
|
+
<div className="settings-icon-wrap" style={{ background: 'linear-gradient(135deg, rgba(63, 185, 80, 0.2), rgba(34, 197, 94, 0.05))', borderColor: 'rgba(63, 185, 80, 0.2)' }}>🚀</div>
|
|
818
|
+
<div>
|
|
819
|
+
<h3 style={{ fontSize: 16, fontWeight: 600, margin: 0 }}>System Update</h3>
|
|
820
|
+
<p style={{ fontSize: 12, color: 'var(--text-tertiary)', margin: '4px 0 0' }}>Manage Total Recall version updates</p>
|
|
821
|
+
</div>
|
|
822
|
+
</div>
|
|
823
|
+
|
|
824
|
+
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginTop: 24, padding: '16px 20px', background: 'rgba(0,0,0,0.2)', borderRadius: 12, border: '1px solid rgba(255,255,255,0.05)' }}>
|
|
825
|
+
<div>
|
|
826
|
+
<div style={{ fontSize: 14, fontWeight: 600, color: 'var(--text-primary)' }}>
|
|
827
|
+
{updateInfo?.updateAvailable ? `Update Available (v${updateInfo.latestVersion})` : 'System is up to date'}
|
|
828
828
|
</div>
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
<span className="badge" style={{
|
|
832
|
-
background: health?.ollama === 'running' ? 'rgba(16, 185, 129, 0.1)' : 'rgba(239, 68, 68, 0.1)',
|
|
833
|
-
color: health?.ollama === 'running' ? '#10b981' : '#ef4444',
|
|
834
|
-
border: health?.ollama === 'running' ? '1px solid rgba(16, 185, 129, 0.2)' : '1px solid rgba(239, 68, 68, 0.2)',
|
|
835
|
-
fontSize: 10,
|
|
836
|
-
padding: '4px 8px'
|
|
837
|
-
}}>
|
|
838
|
-
{health?.ollama === 'running' ? 'Connected' : 'Offline'}
|
|
839
|
-
</span>
|
|
840
|
-
{health?.ollama === 'running' && health?.ollama_models && (
|
|
841
|
-
<span style={{ fontSize: 11, color: 'var(--text-secondary)' }}>
|
|
842
|
-
{health.ollama_models.length} local models available
|
|
843
|
-
</span>
|
|
844
|
-
)}
|
|
829
|
+
<div style={{ fontSize: 12, color: 'var(--text-tertiary)', marginTop: 4 }}>
|
|
830
|
+
{updateInfo?.updateAvailable ? 'A new version of Total Recall is available for download.' : 'You are running the latest version.'}
|
|
845
831
|
</div>
|
|
832
|
+
{updateMessage && (
|
|
833
|
+
<div style={{ fontSize: 12, color: updateMessage.includes('error') || updateMessage.includes('failed') ? '#ef4444' : '#10b981', marginTop: 8 }}>
|
|
834
|
+
{updateMessage}
|
|
835
|
+
</div>
|
|
836
|
+
)}
|
|
846
837
|
</div>
|
|
838
|
+
{updateInfo?.updateAvailable && (
|
|
839
|
+
<button
|
|
840
|
+
className="btn-primary"
|
|
841
|
+
onClick={handleRunUpdate}
|
|
842
|
+
disabled={updating}
|
|
843
|
+
style={{ background: '#3fb950', border: 'none' }}
|
|
844
|
+
>
|
|
845
|
+
{updating ? 'Updating...' : 'Download & Restart'}
|
|
846
|
+
</button>
|
|
847
|
+
)}
|
|
848
|
+
</div>
|
|
849
|
+
</div>
|
|
850
|
+
|
|
847
851
|
{/* CLI Agents */}
|
|
848
852
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(360px, 1fr))', gap: 24 }}>
|
|
849
853
|
|
|
@@ -937,6 +941,7 @@ export default function SettingsPage() {
|
|
|
937
941
|
</div>
|
|
938
942
|
)}
|
|
939
943
|
</div>
|
|
944
|
+
</div>
|
|
940
945
|
</div>
|
|
941
946
|
</div>
|
|
942
947
|
);
|