total-recall-brain 3.1.2 → 3.2.2
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 +99 -0
- package/extension/content-script.css +16 -0
- package/extension/content-script.js +134 -0
- package/extension/icons/README.md +27 -0
- package/extension/icons/icon-128.png +0 -0
- package/extension/icons/icon-16.png +0 -0
- package/extension/icons/icon-48.png +0 -0
- package/extension/lib/brain-client.js +47 -0
- package/extension/manifest.json +39 -0
- package/extension/options/options.css +218 -0
- package/extension/options/options.html +72 -0
- package/extension/options/options.js +99 -0
- package/extension/popup/popup.css +221 -0
- package/extension/popup/popup.html +44 -0
- package/extension/popup/popup.js +107 -0
- package/extension/sidepanel/sidepanel.css +340 -0
- package/extension/sidepanel/sidepanel.html +38 -0
- package/extension/sidepanel/sidepanel.js +229 -0
- package/frontend/README.md +73 -0
- package/frontend/index.html +17 -0
- package/frontend/package.json +31 -0
- package/frontend/src/App.css +2 -0
- package/frontend/src/App.tsx +390 -0
- package/frontend/src/api.ts +688 -0
- package/frontend/src/assets/hero.png +0 -0
- package/frontend/src/assets/react.svg +1 -0
- package/frontend/src/assets/vite.svg +1 -0
- package/frontend/src/components/BrainSelector.tsx +272 -0
- package/frontend/src/components/DaemonLogsTab.tsx +54 -0
- package/frontend/src/components/Graph3D.tsx +850 -0
- package/frontend/src/components/MarkdownUtils.tsx +99 -0
- package/frontend/src/components/ResearchAgendaTab.tsx +707 -0
- package/frontend/src/components/TaskQueueTab.tsx +185 -0
- package/frontend/src/index.css +1589 -0
- package/frontend/src/main.tsx +10 -0
- package/frontend/src/pages/ApiKeysPage.tsx +288 -0
- package/frontend/src/pages/AutomationsPage.tsx +354 -0
- package/frontend/src/pages/ChatPage.tsx +831 -0
- package/frontend/src/pages/DeploymentsPage.tsx +275 -0
- package/frontend/src/pages/FilesPage.tsx +384 -0
- package/frontend/src/pages/HealthPage.tsx +91 -0
- package/frontend/src/pages/IntegrationsPage.tsx +382 -0
- package/frontend/src/pages/LoginPage.tsx +237 -0
- package/frontend/src/pages/MemoryPage.tsx +1192 -0
- package/frontend/src/pages/SandboxPage.tsx +113 -0
- package/frontend/src/pages/SettingsPage.tsx +662 -0
- package/frontend/src/pages/SkillsPage.tsx +618 -0
- package/frontend/src/pages/TasksPage.tsx +392 -0
- package/frontend/src/pages/UsagePage.tsx +260 -0
- package/frontend/src/types.ts +163 -0
- package/frontend/tsconfig.app.json +25 -0
- package/frontend/tsconfig.json +7 -0
- package/frontend/tsconfig.node.json +24 -0
- package/frontend/vite.config.ts +24 -0
- package/package.json +8 -1
- package/scaffold/.agent/skills/total-recall/memory-vault/concepts/cli-help-reference.md +35 -8
- package/scaffold/.agent/skills/total-recall/memory-vault/invariants/operating-instructions.md +13 -8
- package/scaffold/.agent/skills/total-recall/memory-vault/preferences/always-websearch-gap.md +28 -1
- package/scaffold/.agent/skills/total-recall/memory-vault/preferences/topic-research-sop.md +28 -1
- package/scaffold/.agent/skills/total-recall/skills/ssss/references/ssss-spec.md +830 -81
- package/src/cli/backup.mjs +172 -1
- package/src/cli/setup.mjs +24 -0
- package/src/server/rest.mjs +56 -7
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { useState, useRef, useEffect } from "react"
|
|
2
|
+
import { login, changePassword, getAuthStatus, setupPassword } from "../api"
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
onAuthenticated: () => void
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export default function LoginPage({ onAuthenticated }: Props) {
|
|
9
|
+
const [password, setPassword] = useState("")
|
|
10
|
+
const [newPassword, setNewPassword] = useState("")
|
|
11
|
+
const [error, setError] = useState("")
|
|
12
|
+
const [loading, setLoading] = useState(false)
|
|
13
|
+
const [needsReset, setNeedsReset] = useState(false)
|
|
14
|
+
const [isFirstTime, setIsFirstTime] = useState(false)
|
|
15
|
+
const inputRef = useRef<HTMLInputElement>(null)
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
async function checkConfig() {
|
|
19
|
+
try {
|
|
20
|
+
const res = await getAuthStatus()
|
|
21
|
+
if (!res.configured) {
|
|
22
|
+
setIsFirstTime(true)
|
|
23
|
+
}
|
|
24
|
+
} catch {
|
|
25
|
+
// ignore network error
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
checkConfig()
|
|
29
|
+
}, [])
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
inputRef.current?.focus()
|
|
33
|
+
}, [needsReset, isFirstTime])
|
|
34
|
+
|
|
35
|
+
async function handleSubmit(e: React.FormEvent) {
|
|
36
|
+
e.preventDefault()
|
|
37
|
+
if (loading) return
|
|
38
|
+
setLoading(true)
|
|
39
|
+
setError("")
|
|
40
|
+
try {
|
|
41
|
+
if (isFirstTime) {
|
|
42
|
+
if (newPassword.length < 8) {
|
|
43
|
+
setError("New password must be at least 8 characters")
|
|
44
|
+
setLoading(false)
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
const res = await setupPassword(newPassword)
|
|
48
|
+
if (res.ok) {
|
|
49
|
+
const loginRes = await login(newPassword)
|
|
50
|
+
if (loginRes.ok) {
|
|
51
|
+
onAuthenticated()
|
|
52
|
+
} else {
|
|
53
|
+
setError(loginRes.error || "Password was set, but automatic sign in failed.")
|
|
54
|
+
setIsFirstTime(false)
|
|
55
|
+
}
|
|
56
|
+
} else {
|
|
57
|
+
setError(res.error || "Failed to setup password")
|
|
58
|
+
}
|
|
59
|
+
} else if (needsReset) {
|
|
60
|
+
if (newPassword.length < 8) {
|
|
61
|
+
setError("New password must be at least 8 characters")
|
|
62
|
+
setLoading(false)
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
const res = await changePassword(newPassword)
|
|
66
|
+
if (res.ok) {
|
|
67
|
+
onAuthenticated()
|
|
68
|
+
} else {
|
|
69
|
+
setError(res.error || "Failed to change password")
|
|
70
|
+
}
|
|
71
|
+
} else {
|
|
72
|
+
const res = await login(password)
|
|
73
|
+
if (res.ok) {
|
|
74
|
+
if (res.requiresPasswordReset) {
|
|
75
|
+
setNeedsReset(true)
|
|
76
|
+
setPassword("") // Clear for security
|
|
77
|
+
} else {
|
|
78
|
+
onAuthenticated()
|
|
79
|
+
}
|
|
80
|
+
} else {
|
|
81
|
+
setError(res.error || "Invalid password")
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
} catch {
|
|
85
|
+
setError("Network error — is the server running?")
|
|
86
|
+
} finally {
|
|
87
|
+
setLoading(false)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<div style={{
|
|
93
|
+
minHeight: '100vh',
|
|
94
|
+
display: 'flex',
|
|
95
|
+
alignItems: 'center',
|
|
96
|
+
justifyContent: 'center',
|
|
97
|
+
background: 'var(--bg-primary)',
|
|
98
|
+
}}>
|
|
99
|
+
<div style={{
|
|
100
|
+
width: 360,
|
|
101
|
+
background: 'var(--bg-secondary)',
|
|
102
|
+
border: '1px solid var(--border)',
|
|
103
|
+
borderRadius: 16,
|
|
104
|
+
padding: '40px 36px',
|
|
105
|
+
boxShadow: '0 8px 40px rgba(0,0,0,0.4)',
|
|
106
|
+
display: 'flex',
|
|
107
|
+
flexDirection: 'column',
|
|
108
|
+
gap: 24,
|
|
109
|
+
}}>
|
|
110
|
+
{/* Brand */}
|
|
111
|
+
<div style={{ textAlign: "center" }}>
|
|
112
|
+
<div style={{
|
|
113
|
+
width: 48,
|
|
114
|
+
height: 48,
|
|
115
|
+
borderRadius: 12,
|
|
116
|
+
background: (needsReset || isFirstTime) ? "linear-gradient(135deg, #f59e0b, #d97706)" : "linear-gradient(135deg, var(--accent), #7c3aed)",
|
|
117
|
+
margin: "0 auto 16px",
|
|
118
|
+
display: "flex",
|
|
119
|
+
alignItems: "center",
|
|
120
|
+
justifyContent: "center",
|
|
121
|
+
}}>
|
|
122
|
+
{(needsReset || isFirstTime) ? (
|
|
123
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
124
|
+
<rect x="3" y="11" width="18" height="11" rx="2" ry="2"/>
|
|
125
|
+
<path d="M7 11V7a5 5 0 0 1 10 0v4"/>
|
|
126
|
+
</svg>
|
|
127
|
+
) : (
|
|
128
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
129
|
+
<path d="M12 2a10 10 0 1 0 10 10H12V2z"/>
|
|
130
|
+
<path d="M12 2a10 10 0 0 1 10 10"/>
|
|
131
|
+
<circle cx="12" cy="12" r="3"/>
|
|
132
|
+
</svg>
|
|
133
|
+
)}
|
|
134
|
+
</div>
|
|
135
|
+
<h1 style={{ fontSize: 20, fontWeight: 700, color: "var(--text-primary)", margin: 0 }}>
|
|
136
|
+
{needsReset ? "Action Required" : isFirstTime ? "Setup Admin Password" : "Total Recall"}
|
|
137
|
+
</h1>
|
|
138
|
+
<p style={{ fontSize: 13, color: "var(--text-tertiary)", margin: "4px 0 0" }}>
|
|
139
|
+
{needsReset ? "You must change your temporary password to continue." : isFirstTime ? "Choose a strong password to secure your dashboard." : "Sovereign Brain v3.0"}
|
|
140
|
+
</p>
|
|
141
|
+
</div>
|
|
142
|
+
|
|
143
|
+
{/* Form */}
|
|
144
|
+
<form onSubmit={handleSubmit} style={{ display: "flex", flexDirection: "column", gap: 16 }}>
|
|
145
|
+
<div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
|
|
146
|
+
<label htmlFor="login-password" style={{ fontSize: 12, fontWeight: 600, color: "var(--text-secondary)", textTransform: "uppercase", letterSpacing: "0.05em" }}>
|
|
147
|
+
{(needsReset || isFirstTime) ? "New Password" : "Password"}
|
|
148
|
+
</label>
|
|
149
|
+
<input
|
|
150
|
+
id="login-password"
|
|
151
|
+
ref={inputRef}
|
|
152
|
+
type="password"
|
|
153
|
+
value={(isFirstTime || needsReset) ? newPassword : password}
|
|
154
|
+
onChange={e => {
|
|
155
|
+
if (isFirstTime || needsReset) setNewPassword(e.target.value)
|
|
156
|
+
else setPassword(e.target.value)
|
|
157
|
+
setError("")
|
|
158
|
+
}}
|
|
159
|
+
placeholder={(isFirstTime || needsReset) ? "Minimum 8 characters" : "Enter admin password"}
|
|
160
|
+
autoComplete={(isFirstTime || needsReset) ? "new-password" : "current-password"}
|
|
161
|
+
disabled={loading}
|
|
162
|
+
style={{
|
|
163
|
+
background: "var(--bg-primary)",
|
|
164
|
+
border: `1px solid ${error ? "#ef4444" : "var(--border)"}`,
|
|
165
|
+
borderRadius: 8,
|
|
166
|
+
padding: "10px 14px",
|
|
167
|
+
color: "var(--text-primary)",
|
|
168
|
+
fontSize: 14,
|
|
169
|
+
outline: "none",
|
|
170
|
+
transition: "border-color 0.15s",
|
|
171
|
+
}}
|
|
172
|
+
/>
|
|
173
|
+
</div>
|
|
174
|
+
|
|
175
|
+
{error && (
|
|
176
|
+
<div style={{
|
|
177
|
+
background: "rgba(239,68,68,0.1)",
|
|
178
|
+
border: "1px solid rgba(239,68,68,0.3)",
|
|
179
|
+
borderRadius: 8,
|
|
180
|
+
padding: "10px 14px",
|
|
181
|
+
fontSize: 13,
|
|
182
|
+
color: "#f87171",
|
|
183
|
+
display: "flex",
|
|
184
|
+
alignItems: "center",
|
|
185
|
+
gap: 8,
|
|
186
|
+
}}>
|
|
187
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0 }}>
|
|
188
|
+
<circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/>
|
|
189
|
+
</svg>
|
|
190
|
+
{error}
|
|
191
|
+
</div>
|
|
192
|
+
)}
|
|
193
|
+
|
|
194
|
+
<button
|
|
195
|
+
id="login-submit"
|
|
196
|
+
type="submit"
|
|
197
|
+
disabled={loading || ((isFirstTime || needsReset) ? !newPassword : !password)}
|
|
198
|
+
style={{
|
|
199
|
+
background: (loading || ((isFirstTime || needsReset) ? !newPassword : !password)) ? "var(--bg-tertiary)" : "var(--accent)",
|
|
200
|
+
color: (loading || ((isFirstTime || needsReset) ? !newPassword : !password)) ? "var(--text-tertiary)" : "white",
|
|
201
|
+
border: "none",
|
|
202
|
+
borderRadius: 8,
|
|
203
|
+
padding: "11px 0",
|
|
204
|
+
fontSize: 14,
|
|
205
|
+
fontWeight: 600,
|
|
206
|
+
cursor: (loading || ((isFirstTime || needsReset) ? !newPassword : !password)) ? "not-allowed" : "pointer",
|
|
207
|
+
transition: "background 0.15s, color 0.15s",
|
|
208
|
+
display: "flex",
|
|
209
|
+
alignItems: "center",
|
|
210
|
+
justifyContent: "center",
|
|
211
|
+
gap: 8,
|
|
212
|
+
}}
|
|
213
|
+
>
|
|
214
|
+
{loading ? (
|
|
215
|
+
<>
|
|
216
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" style={{ animation: "spin 0.8s linear infinite" }}>
|
|
217
|
+
<path d="M21 12a9 9 0 1 1-6.219-8.56"/>
|
|
218
|
+
</svg>
|
|
219
|
+
{(isFirstTime || needsReset) ? "Saving…" : "Authenticating…"}
|
|
220
|
+
</>
|
|
221
|
+
) : isFirstTime ? "Create Password & Continue" : needsReset ? "Save & Continue" : "Sign In"}
|
|
222
|
+
</button>
|
|
223
|
+
</form>
|
|
224
|
+
|
|
225
|
+
{(!needsReset && !isFirstTime) && (
|
|
226
|
+
<p style={{ textAlign: "center", fontSize: 11, color: "var(--text-tertiary)", margin: 0 }}>
|
|
227
|
+
Set password in <code style={{ color: "var(--accent)", fontSize: 11 }}>~/.agent/config/security.yml</code>
|
|
228
|
+
</p>
|
|
229
|
+
)}
|
|
230
|
+
</div>
|
|
231
|
+
|
|
232
|
+
<style>{`
|
|
233
|
+
@keyframes spin { to { transform: rotate(360deg); } }
|
|
234
|
+
`}</style>
|
|
235
|
+
</div>
|
|
236
|
+
)
|
|
237
|
+
}
|