total-recall-brain 3.6.4 → 3.6.6
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/extension/background.js +4 -0
- package/extension/content-script.js +9 -1
- package/extension/manifest.json +0 -1
- package/frontend/src/App.tsx +5 -8
- package/frontend/src/components/BrainSelector.tsx +10 -4
- package/frontend/src/index.css +1 -0
- package/frontend/src/pages/ChatPage.tsx +2 -0
- package/frontend/src/pages/{DeploymentsPage.tsx → ModelsPage.tsx} +183 -87
- package/frontend/src/types.ts +3 -0
- package/frontend/vite.config.ts +3 -3
- package/package.json +1 -1
- package/src/core/daemon-loop.mjs +162 -58
- package/src/core/fact-seeker.mjs +6 -3
- package/src/core/research-queue.mjs +8 -1
- package/src/core/scheduler.mjs +23 -5
- package/src/core/schema.mjs +23 -0
- package/src/server/index.mjs +19 -0
- package/src/server/rest.mjs +43 -189
- package/src/server/route-manifest.json +4 -0
- package/src/server/routes/auth.mjs +32 -0
- package/src/server/routes/research.mjs +55 -0
- package/src/server/routes/sandbox.mjs +55 -0
- package/src/server/routes/skills.mjs +118 -0
package/extension/background.js
CHANGED
|
@@ -10,6 +10,10 @@ const { share, search, healthCheck } = self.BrainClient;
|
|
|
10
10
|
// Context menu registration
|
|
11
11
|
// ---------------------------------------------------------------------------
|
|
12
12
|
chrome.runtime.onInstalled.addListener(() => {
|
|
13
|
+
if (chrome.sidePanel && chrome.sidePanel.setPanelBehavior) {
|
|
14
|
+
chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true }).catch(console.error);
|
|
15
|
+
}
|
|
16
|
+
|
|
13
17
|
chrome.contextMenus.create({
|
|
14
18
|
id: 'send-to-brain',
|
|
15
19
|
title: 'Send to Brain',
|
|
@@ -398,7 +398,15 @@
|
|
|
398
398
|
toast.classList.add('visible');
|
|
399
399
|
setTimeout(() => toast.classList.remove('visible'), 2500);
|
|
400
400
|
} else {
|
|
401
|
-
|
|
401
|
+
let errText = '❌ Action failed';
|
|
402
|
+
if (res && res.error) {
|
|
403
|
+
if (res.error.includes('Authentication failed')) {
|
|
404
|
+
errText = '❌ Auth Error - Check PAT';
|
|
405
|
+
} else {
|
|
406
|
+
errText = `❌ ${res.error}`;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
toast.textContent = errText;
|
|
402
410
|
toast.style.background = '#e74c3c';
|
|
403
411
|
toast.style.boxShadow = '0 4px 12px rgba(231, 76, 60, 0.3)';
|
|
404
412
|
toast.classList.add('visible');
|
package/extension/manifest.json
CHANGED
package/frontend/src/App.tsx
CHANGED
|
@@ -15,7 +15,7 @@ import ApiKeysPage from './pages/ApiKeysPage'
|
|
|
15
15
|
import IntegrationsPage from './pages/IntegrationsPage'
|
|
16
16
|
import BrainSelector from './components/BrainSelector'
|
|
17
17
|
import AutomationsPage from './pages/AutomationsPage'
|
|
18
|
-
import
|
|
18
|
+
import ModelsPage from './pages/ModelsPage'
|
|
19
19
|
import SkillsPage from './pages/SkillsPage'
|
|
20
20
|
import UsagePage from './pages/UsagePage'
|
|
21
21
|
import CollabPage from './pages/CollabPage'
|
|
@@ -99,14 +99,11 @@ function Sidebar({ onLogout, health, activeBrainId, onBrainChange }: SidebarProp
|
|
|
99
99
|
</svg>
|
|
100
100
|
Sandbox
|
|
101
101
|
</NavLink>
|
|
102
|
-
<NavLink to="/
|
|
102
|
+
<NavLink to="/models" className={({ isActive }) => `nav-link ${isActive ? 'active' : ''}`} id="nav-models">
|
|
103
103
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
|
|
104
|
-
<
|
|
105
|
-
<rect x="2" y="14" width="20" height="8" rx="2" ry="2" />
|
|
106
|
-
<line x1="6" y1="6" x2="6.01" y2="6" />
|
|
107
|
-
<line x1="6" y1="18" x2="6.01" y2="18" />
|
|
104
|
+
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"></path><polyline points="3.27 6.96 12 12.01 20.73 6.96"></polyline><line x1="12" y1="22.08" x2="12" y2="12"></line>
|
|
108
105
|
</svg>
|
|
109
|
-
|
|
106
|
+
Models & Agents
|
|
110
107
|
</NavLink>
|
|
111
108
|
<NavLink to="/health" className={({ isActive }) => `nav-link ${isActive ? 'active' : ''}`} id="nav-health">
|
|
112
109
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
|
|
@@ -312,7 +309,7 @@ function MainContent({ activeBrainId, onBrainChange }: { activeBrainId: string;
|
|
|
312
309
|
<Route path="/automations" element={<AutomationsPage />} />
|
|
313
310
|
<Route path="/files" element={<FilesPage />} />
|
|
314
311
|
<Route path="/sandbox" element={<SandboxPage />} />
|
|
315
|
-
<Route path="/
|
|
312
|
+
<Route path="/models" element={<ModelsPage />} />
|
|
316
313
|
<Route path="/health" element={<HealthPage />} />
|
|
317
314
|
<Route path="/usage" element={<UsagePage />} />
|
|
318
315
|
<Route path="/settings" element={<SettingsPage />} />
|
|
@@ -181,9 +181,14 @@ export default function BrainSelector({ activeBrainId, onBrainChange }: BrainSel
|
|
|
181
181
|
{brains.map(brain => {
|
|
182
182
|
const isSelected = selectedIds.includes(brain.id);
|
|
183
183
|
return (
|
|
184
|
-
<
|
|
184
|
+
<div
|
|
185
185
|
key={brain.id}
|
|
186
|
-
onClick={(e) =>
|
|
186
|
+
onClick={(e) => {
|
|
187
|
+
if (brain.exists) {
|
|
188
|
+
handleRowClick(brain.id, e);
|
|
189
|
+
setExpanded(false); // Close dropdown on single selection
|
|
190
|
+
}
|
|
191
|
+
}}
|
|
187
192
|
style={{
|
|
188
193
|
width: '100%',
|
|
189
194
|
padding: '8px 10px',
|
|
@@ -201,7 +206,8 @@ export default function BrainSelector({ activeBrainId, onBrainChange }: BrainSel
|
|
|
201
206
|
}}
|
|
202
207
|
onMouseEnter={e => { if (brain.exists) (e.currentTarget as HTMLElement).style.background = 'var(--bg-tertiary)' }}
|
|
203
208
|
onMouseLeave={e => { (e.currentTarget as HTMLElement).style.background = isSelected ? 'var(--accent-faint)' : 'transparent' }}
|
|
204
|
-
|
|
209
|
+
role="button"
|
|
210
|
+
tabIndex={0}
|
|
205
211
|
>
|
|
206
212
|
<input
|
|
207
213
|
type="checkbox"
|
|
@@ -247,7 +253,7 @@ export default function BrainSelector({ activeBrainId, onBrainChange }: BrainSel
|
|
|
247
253
|
}}>
|
|
248
254
|
{brain.layer}
|
|
249
255
|
</span>
|
|
250
|
-
</
|
|
256
|
+
</div>
|
|
251
257
|
);
|
|
252
258
|
})}
|
|
253
259
|
</div>
|
package/frontend/src/index.css
CHANGED
|
@@ -718,6 +718,8 @@ export default function ChatPage({ activeBrainId, onBrainChange }: { activeBrain
|
|
|
718
718
|
flexDirection: 'column',
|
|
719
719
|
gap: '16px',
|
|
720
720
|
width: '100%',
|
|
721
|
+
maxWidth: 800,
|
|
722
|
+
margin: '0 auto',
|
|
721
723
|
pointerEvents: 'auto'
|
|
722
724
|
}}>
|
|
723
725
|
{messages.map((m, index) => (
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { useState, useEffect,
|
|
2
|
-
import { fetchHealth,
|
|
3
|
-
import type { HealthData } from '../types';
|
|
1
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
2
|
+
import { fetchHealth, runAgentDiagnostics, fetchConfigJson, saveConfigJson } from '../api';
|
|
3
|
+
import type { HealthData, ConfigJson } from '../types';
|
|
4
4
|
|
|
5
|
-
export default function
|
|
5
|
+
export default function ModelsPage() {
|
|
6
6
|
const [health, setHealth] = useState<HealthData | null>(null);
|
|
7
|
-
const [
|
|
7
|
+
const [configData, setConfigData] = useState<ConfigJson | null>(null);
|
|
8
8
|
const [loading, setLoading] = useState(true);
|
|
9
|
+
const [saving, setSaving] = useState(false);
|
|
9
10
|
const [error, setError] = useState<string | null>(null);
|
|
10
11
|
const [success, setSuccess] = useState<string | null>(null);
|
|
11
12
|
|
|
@@ -13,36 +14,44 @@ export default function DeploymentsPage() {
|
|
|
13
14
|
const [runningDiagnostics, setRunningDiagnostics] = useState(false);
|
|
14
15
|
const [diagnosticLogs, setDiagnosticLogs] = useState<string | null>(null);
|
|
15
16
|
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
const fetchSystemData = async () => {
|
|
17
|
+
const fetchSystemData = useCallback(async () => {
|
|
19
18
|
try {
|
|
20
19
|
const systemHealth = await fetchHealth();
|
|
21
20
|
setHealth(systemHealth);
|
|
22
21
|
|
|
23
|
-
const
|
|
24
|
-
|
|
22
|
+
const data = await fetchConfigJson();
|
|
23
|
+
if (!data.secrets) data.secrets = {};
|
|
24
|
+
if (!data.brain) data.brain = {};
|
|
25
|
+
setConfigData(data);
|
|
26
|
+
|
|
25
27
|
setError(null);
|
|
26
28
|
} catch (err: unknown) {
|
|
27
|
-
setError((err as Error).message || 'Failed to sync system
|
|
29
|
+
setError((err as Error).message || 'Failed to sync system data.');
|
|
28
30
|
} finally {
|
|
29
31
|
setLoading(false);
|
|
30
32
|
}
|
|
31
|
-
};
|
|
33
|
+
}, []);
|
|
32
34
|
|
|
33
35
|
useEffect(() => {
|
|
34
36
|
// eslint-disable-next-line react-hooks/set-state-in-effect -- legitimate metrics polling on mount
|
|
35
37
|
void fetchSystemData();
|
|
36
|
-
|
|
37
|
-
return () => clearInterval(interval);
|
|
38
|
-
}, []);
|
|
38
|
+
}, [fetchSystemData]);
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
const handleSaveConfig = async () => {
|
|
41
|
+
if (!configData) return;
|
|
42
|
+
setSaving(true);
|
|
43
|
+
setError(null);
|
|
44
|
+
setSuccess(null);
|
|
45
|
+
try {
|
|
46
|
+
await saveConfigJson(configData);
|
|
47
|
+
setSuccess('Configuration saved successfully. Kernel will hot-reload settings.');
|
|
48
|
+
setTimeout(() => setSuccess(null), 4000);
|
|
49
|
+
} catch (err: unknown) {
|
|
50
|
+
setError((err as Error).message || 'Failed to save configuration.');
|
|
51
|
+
} finally {
|
|
52
|
+
setSaving(false);
|
|
44
53
|
}
|
|
45
|
-
}
|
|
54
|
+
};
|
|
46
55
|
|
|
47
56
|
const handleRunDiagnostics = async (e: React.FormEvent) => {
|
|
48
57
|
e.preventDefault();
|
|
@@ -66,6 +75,28 @@ export default function DeploymentsPage() {
|
|
|
66
75
|
}
|
|
67
76
|
};
|
|
68
77
|
|
|
78
|
+
const updateSecretsProp = (prop: string, value: string) => {
|
|
79
|
+
if (!configData) return;
|
|
80
|
+
setConfigData({
|
|
81
|
+
...configData,
|
|
82
|
+
secrets: {
|
|
83
|
+
...configData.secrets,
|
|
84
|
+
[prop]: value
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const updateBrainProp = (prop: string, value: string) => {
|
|
90
|
+
if (!configData) return;
|
|
91
|
+
setConfigData({
|
|
92
|
+
...configData,
|
|
93
|
+
brain: {
|
|
94
|
+
...configData.brain,
|
|
95
|
+
[prop]: value
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
|
|
69
100
|
const AGENTS_LIST = [
|
|
70
101
|
{ id: 'antigravity', name: 'Antigravity (Gemini SDK)', desc: 'Primary core developer agent' },
|
|
71
102
|
{ id: 'gemini', name: 'Gemini CLI', desc: 'Direct Gemini assistant binary' },
|
|
@@ -76,9 +107,21 @@ export default function DeploymentsPage() {
|
|
|
76
107
|
|
|
77
108
|
return (
|
|
78
109
|
<div className="page" style={{ display: 'flex', flexDirection: 'column', height: '100%', overflowY: 'auto' }}>
|
|
79
|
-
<div className="page-header" style={{ marginBottom: 24 }}>
|
|
80
|
-
<
|
|
81
|
-
|
|
110
|
+
<div className="page-header" style={{ marginBottom: 24, display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
|
|
111
|
+
<div>
|
|
112
|
+
<h1>Models & Agents</h1>
|
|
113
|
+
<p>Configure Bring Your Own Model (BYOM) settings and manage active reasoning agents</p>
|
|
114
|
+
</div>
|
|
115
|
+
{configData && (
|
|
116
|
+
<button
|
|
117
|
+
onClick={handleSaveConfig}
|
|
118
|
+
disabled={saving}
|
|
119
|
+
className="btn btn-primary"
|
|
120
|
+
style={{ minWidth: 120 }}
|
|
121
|
+
>
|
|
122
|
+
{saving ? 'Saving...' : 'Save Configuration'}
|
|
123
|
+
</button>
|
|
124
|
+
)}
|
|
82
125
|
</div>
|
|
83
126
|
|
|
84
127
|
{error && (
|
|
@@ -97,63 +140,143 @@ export default function DeploymentsPage() {
|
|
|
97
140
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" style={{ animation: 'spin 1s linear infinite', marginBottom: 12 }}>
|
|
98
141
|
<path d="M21 12a9 9 0 1 1-6.219-8.56"/>
|
|
99
142
|
</svg>
|
|
100
|
-
<div>
|
|
143
|
+
<div>Loading models and agents...</div>
|
|
101
144
|
</div>
|
|
102
145
|
) : (
|
|
103
146
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 24, flex: 1, minHeight: 0 }}>
|
|
104
147
|
|
|
105
|
-
{
|
|
106
|
-
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))', gap: 20 }}>
|
|
148
|
+
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(360px, 1fr))', gap: 24 }}>
|
|
107
149
|
|
|
108
|
-
{/*
|
|
109
|
-
<div className="card" style={{ padding:
|
|
110
|
-
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
|
111
|
-
<span style={{ fontSize:
|
|
150
|
+
{/* Ollama Panel */}
|
|
151
|
+
<div className="card" style={{ padding: 24, background: 'rgba(18, 18, 26, 0.6)', backdropFilter: 'blur(8px)', border: '1px solid var(--border)', display: 'flex', flexDirection: 'column', gap: 16 }}>
|
|
152
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 10, borderBottom: '1px solid var(--border)', paddingBottom: 12 }}>
|
|
153
|
+
<span style={{ fontSize: 20 }}>🦙</span>
|
|
112
154
|
<div>
|
|
113
|
-
<
|
|
114
|
-
<p style={{ fontSize:
|
|
155
|
+
<h3 style={{ fontSize: 14, fontWeight: 600 }}>Local Provider</h3>
|
|
156
|
+
<p style={{ fontSize: 11, color: 'var(--text-tertiary)' }}>Connect Ollama, LM Studio, vLLM, or Llama.cpp</p>
|
|
115
157
|
</div>
|
|
116
158
|
</div>
|
|
117
|
-
<span className="badge" style={{
|
|
118
|
-
background: health?.daemon === 'running' ? 'rgba(16, 185, 129, 0.1)' : 'rgba(239, 68, 68, 0.1)',
|
|
119
|
-
color: health?.daemon === 'running' ? '#10b981' : '#ef4444',
|
|
120
|
-
border: health?.daemon === 'running' ? '1px solid rgba(16, 185, 129, 0.2)' : '1px solid rgba(239, 68, 68, 0.2)'
|
|
121
|
-
}}>
|
|
122
|
-
{health?.daemon || 'dead'}
|
|
123
|
-
</span>
|
|
124
|
-
</div>
|
|
125
159
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
160
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
|
|
161
|
+
<label htmlFor="local_endpoint" style={{ fontSize: 13, fontWeight: 500 }}>Provider Base URL</label>
|
|
162
|
+
<input
|
|
163
|
+
id="local_endpoint"
|
|
164
|
+
type="text"
|
|
165
|
+
placeholder="http://127.0.0.1:11434 or http://127.0.0.1:1234/v1"
|
|
166
|
+
value={configData?.brain?.local_endpoint || ''}
|
|
167
|
+
onChange={(e) => updateBrainProp('local_endpoint', e.target.value)}
|
|
168
|
+
style={{
|
|
169
|
+
background: 'var(--bg-tertiary)',
|
|
170
|
+
color: 'var(--text-primary)',
|
|
171
|
+
border: '1px solid var(--border)',
|
|
172
|
+
padding: '8px 12px',
|
|
173
|
+
borderRadius: 6,
|
|
174
|
+
outline: 'none',
|
|
175
|
+
fontSize: 13
|
|
176
|
+
}}
|
|
177
|
+
/>
|
|
178
|
+
<span style={{ fontSize: 10, color: 'var(--text-tertiary)' }}>Leave blank to use the default localhost endpoint.</span>
|
|
179
|
+
</div>
|
|
180
|
+
|
|
181
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 12, marginTop: 8 }}>
|
|
182
|
+
<span className="badge" style={{
|
|
183
|
+
background: health?.ollama === 'running' ? 'rgba(16, 185, 129, 0.1)' : 'rgba(239, 68, 68, 0.1)',
|
|
184
|
+
color: health?.ollama === 'running' ? '#10b981' : '#ef4444',
|
|
185
|
+
border: health?.ollama === 'running' ? '1px solid rgba(16, 185, 129, 0.2)' : '1px solid rgba(239, 68, 68, 0.2)',
|
|
186
|
+
fontSize: 10,
|
|
187
|
+
padding: '4px 8px'
|
|
188
|
+
}}>
|
|
189
|
+
{health?.ollama === 'running' ? 'Connected' : 'Offline'}
|
|
190
|
+
</span>
|
|
191
|
+
{health?.ollama === 'running' && health?.ollama_models && (
|
|
192
|
+
<span style={{ fontSize: 11, color: 'var(--text-secondary)' }}>
|
|
193
|
+
{health.ollama_models.length} local models available
|
|
194
|
+
</span>
|
|
195
|
+
)}
|
|
134
196
|
</div>
|
|
135
|
-
<span className="badge" style={{ background: 'rgba(16, 185, 129, 0.1)', color: '#10b981', border: '1px solid rgba(16, 185, 129, 0.2)' }}>
|
|
136
|
-
active
|
|
137
|
-
</span>
|
|
138
197
|
</div>
|
|
139
198
|
|
|
140
|
-
{/*
|
|
141
|
-
<div className="card" style={{ padding:
|
|
142
|
-
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
|
143
|
-
<span style={{ fontSize:
|
|
199
|
+
{/* Cloud API Keys Panel */}
|
|
200
|
+
<div className="card" style={{ padding: 24, background: 'rgba(18, 18, 26, 0.6)', backdropFilter: 'blur(8px)', border: '1px solid var(--border)', display: 'flex', flexDirection: 'column', gap: 16 }}>
|
|
201
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 10, borderBottom: '1px solid var(--border)', paddingBottom: 12 }}>
|
|
202
|
+
<span style={{ fontSize: 20 }}>🔑</span>
|
|
144
203
|
<div>
|
|
145
|
-
<
|
|
146
|
-
<p style={{ fontSize:
|
|
204
|
+
<h3 style={{ fontSize: 14, fontWeight: 600 }}>Cloud Models (API Keys)</h3>
|
|
205
|
+
<p style={{ fontSize: 11, color: 'var(--text-tertiary)' }}>Bring your own cloud models</p>
|
|
147
206
|
</div>
|
|
148
207
|
</div>
|
|
149
|
-
|
|
150
|
-
|
|
208
|
+
|
|
209
|
+
{/* Google Gemini API Key */}
|
|
210
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
|
|
211
|
+
<label htmlFor="google_api_key" style={{ fontSize: 13, fontWeight: 500 }}>Google Gemini API Key</label>
|
|
212
|
+
<input
|
|
213
|
+
id="google_api_key"
|
|
214
|
+
type="password"
|
|
215
|
+
placeholder="AIzaSy..."
|
|
216
|
+
value={configData?.secrets?.google_api_key || ''}
|
|
217
|
+
onChange={(e) => updateSecretsProp('google_api_key', e.target.value)}
|
|
218
|
+
style={{
|
|
219
|
+
background: 'var(--bg-tertiary)',
|
|
220
|
+
color: 'var(--text-primary)',
|
|
221
|
+
border: '1px solid var(--border)',
|
|
222
|
+
padding: '8px 12px',
|
|
223
|
+
borderRadius: 6,
|
|
224
|
+
outline: 'none',
|
|
225
|
+
fontSize: 13
|
|
226
|
+
}}
|
|
227
|
+
/>
|
|
228
|
+
</div>
|
|
229
|
+
|
|
230
|
+
{/* Anthropic API Key */}
|
|
231
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
|
|
232
|
+
<label htmlFor="anthropic_api_key" style={{ fontSize: 13, fontWeight: 500 }}>Anthropic API Key (Claude)</label>
|
|
233
|
+
<input
|
|
234
|
+
id="anthropic_api_key"
|
|
235
|
+
type="password"
|
|
236
|
+
placeholder="sk-ant-..."
|
|
237
|
+
value={configData?.secrets?.anthropic_api_key || ''}
|
|
238
|
+
onChange={(e) => updateSecretsProp('anthropic_api_key', e.target.value)}
|
|
239
|
+
style={{
|
|
240
|
+
background: 'var(--bg-tertiary)',
|
|
241
|
+
color: 'var(--text-primary)',
|
|
242
|
+
border: '1px solid var(--border)',
|
|
243
|
+
padding: '8px 12px',
|
|
244
|
+
borderRadius: 6,
|
|
245
|
+
outline: 'none',
|
|
246
|
+
fontSize: 13
|
|
247
|
+
}}
|
|
248
|
+
/>
|
|
249
|
+
</div>
|
|
250
|
+
|
|
251
|
+
{/* OpenAI API Key */}
|
|
252
|
+
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
|
|
253
|
+
<label htmlFor="openai_api_key" style={{ fontSize: 13, fontWeight: 500 }}>OpenAI API Key (Codex)</label>
|
|
254
|
+
<input
|
|
255
|
+
id="openai_api_key"
|
|
256
|
+
type="password"
|
|
257
|
+
placeholder="sk-proj-..."
|
|
258
|
+
value={configData?.secrets?.openai_api_key || ''}
|
|
259
|
+
onChange={(e) => updateSecretsProp('openai_api_key', e.target.value)}
|
|
260
|
+
style={{
|
|
261
|
+
background: 'var(--bg-tertiary)',
|
|
262
|
+
color: 'var(--text-primary)',
|
|
263
|
+
border: '1px solid var(--border)',
|
|
264
|
+
padding: '8px 12px',
|
|
265
|
+
borderRadius: 6,
|
|
266
|
+
outline: 'none',
|
|
267
|
+
fontSize: 13
|
|
268
|
+
}}
|
|
269
|
+
/>
|
|
270
|
+
</div>
|
|
271
|
+
|
|
272
|
+
<span style={{ fontSize: 10, color: 'var(--text-tertiary)' }}>
|
|
273
|
+
These keys are stored locally and injected into the CLI reasoning agents on dispatch.
|
|
151
274
|
</span>
|
|
152
275
|
</div>
|
|
153
276
|
|
|
154
277
|
</div>
|
|
155
278
|
|
|
156
|
-
|
|
279
|
+
{/* CLI Agents Catalog & Diagnostics */}
|
|
157
280
|
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(360px, 1fr))', gap: 24 }}>
|
|
158
281
|
|
|
159
282
|
{/* Catalog */}
|
|
@@ -249,33 +372,6 @@ export default function DeploymentsPage() {
|
|
|
249
372
|
|
|
250
373
|
</div>
|
|
251
374
|
|
|
252
|
-
{/* Active Terminal Console Logs */}
|
|
253
|
-
<div className="card" style={{ flex: 1, minHeight: 300, background: '#07070a', border: '1px solid var(--border)', display: 'flex', flexDirection: 'column', borderRadius: 10, overflow: 'hidden' }}>
|
|
254
|
-
<div style={{ background: '#0e0e15', padding: '10px 16px', borderBottom: '1px solid var(--border)', display: 'flex', alignItems: 'center', gap: 8, flexShrink: 0 }}>
|
|
255
|
-
<span style={{ width: 8, height: 8, borderRadius: '50%', background: '#ff6b6b' }} />
|
|
256
|
-
<span style={{ width: 8, height: 8, borderRadius: '50%', background: '#fdcb6e' }} />
|
|
257
|
-
<span style={{ width: 8, height: 8, borderRadius: '50%', background: '#00cec9' }} />
|
|
258
|
-
<span style={{ fontSize: 11, color: 'var(--text-tertiary)', fontFamily: 'var(--font-mono)', marginLeft: 8 }}>bash — daemon-activity.log — 80x24</span>
|
|
259
|
-
</div>
|
|
260
|
-
|
|
261
|
-
<pre style={{
|
|
262
|
-
flex: 1,
|
|
263
|
-
overflowY: 'auto',
|
|
264
|
-
padding: 16,
|
|
265
|
-
margin: 0,
|
|
266
|
-
fontFamily: 'var(--font-mono)',
|
|
267
|
-
fontSize: 12,
|
|
268
|
-
color: '#34d399',
|
|
269
|
-
background: '#07070a',
|
|
270
|
-
lineHeight: 1.5,
|
|
271
|
-
whiteSpace: 'pre-wrap',
|
|
272
|
-
wordBreak: 'break-all'
|
|
273
|
-
}}>
|
|
274
|
-
{logs}
|
|
275
|
-
<div ref={consoleEndRef} />
|
|
276
|
-
</pre>
|
|
277
|
-
</div>
|
|
278
|
-
|
|
279
375
|
</div>
|
|
280
376
|
)}
|
|
281
377
|
|
package/frontend/src/types.ts
CHANGED
|
@@ -47,6 +47,8 @@ export interface HealthData {
|
|
|
47
47
|
ollama_models?: string[]
|
|
48
48
|
cli_agents?: string[]
|
|
49
49
|
daemon?: string
|
|
50
|
+
caddy?: string
|
|
51
|
+
cloudflare?: string
|
|
50
52
|
emergency_alerts?: string | null
|
|
51
53
|
}
|
|
52
54
|
|
|
@@ -158,6 +160,7 @@ export interface ConfigJson {
|
|
|
158
160
|
}
|
|
159
161
|
brain?: {
|
|
160
162
|
preferred_agent?: string
|
|
163
|
+
local_endpoint?: string
|
|
161
164
|
}
|
|
162
165
|
secrets?: {
|
|
163
166
|
google_api_key?: string
|
package/frontend/vite.config.ts
CHANGED
|
@@ -8,15 +8,15 @@ export default defineConfig({
|
|
|
8
8
|
port: 5173,
|
|
9
9
|
proxy: {
|
|
10
10
|
'/v1': {
|
|
11
|
-
target: 'http://
|
|
11
|
+
target: 'http://127.0.0.1:3000',
|
|
12
12
|
changeOrigin: true,
|
|
13
13
|
},
|
|
14
14
|
'/health': {
|
|
15
|
-
target: 'http://
|
|
15
|
+
target: 'http://127.0.0.1:3000',
|
|
16
16
|
changeOrigin: true,
|
|
17
17
|
},
|
|
18
18
|
'/api': {
|
|
19
|
-
target: 'http://
|
|
19
|
+
target: 'http://127.0.0.1:3000',
|
|
20
20
|
changeOrigin: true,
|
|
21
21
|
},
|
|
22
22
|
},
|